1 // Copyright 2012 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
5 // The file define a quoted-printable decoder, as specified in RFC 2045.
7 // 1. in addition to "=\r\n", "=\n" is also treated as soft line break.
8 // 2. it will pass through a '\r' or '\n' not preceded by '=', consistent
9 // with other broken QP encoders & decoders.
20 type qpReader
struct {
22 rerr error
// last read error
23 line
[]byte // to be consumed before more of br
26 func newQuotedPrintableReader(r io
.Reader
) io
.Reader
{
28 br
: bufio
.NewReader(r
),
32 func fromHex(b
byte) (byte, error
) {
34 case b
>= '0' && b
<= '9':
36 case b
>= 'A' && b
<= 'F':
37 return b
- 'A' + 10, nil
39 return 0, fmt
.Errorf("multipart: invalid quoted-printable hex byte 0x%02x", b
)
42 func (q
*qpReader
) readHexByte(v
[]byte) (b
byte, err error
) {
44 return 0, io
.ErrUnexpectedEOF
47 if hb
, err
= fromHex(v
[0]); err
!= nil {
50 if lb
, err
= fromHex(v
[1]); err
!= nil {
53 return hb
<<4 | lb
, nil
56 func isQPDiscardWhitespace(r rune
) bool {
58 case '\n', '\r', ' ', '\t':
67 softSuffix
= []byte("=")
70 func (q
*qpReader
) Read(p
[]byte) (n
int, err error
) {
76 q
.line
, q
.rerr
= q
.br
.ReadSlice('\n')
78 // Does the line end in CRLF instead of just LF?
79 hasLF
:= bytes
.HasSuffix(q
.line
, lf
)
80 hasCR
:= bytes
.HasSuffix(q
.line
, crlf
)
82 q
.line
= bytes
.TrimRightFunc(wholeLine
, isQPDiscardWhitespace
)
83 if bytes
.HasSuffix(q
.line
, softSuffix
) {
84 rightStripped
:= wholeLine
[len(q
.line
):]
85 q
.line
= q
.line
[:len(q
.line
)-1]
86 if !bytes
.HasPrefix(rightStripped
, lf
) && !bytes
.HasPrefix(rightStripped
, crlf
) {
87 q
.rerr
= fmt
.Errorf("multipart: invalid bytes after =: %q", rightStripped
)
91 q
.line
= append(q
.line
, '\r', '\n')
93 q
.line
= append(q
.line
, '\n')
102 b
, err
= q
.readHexByte(q
.line
[1:])
106 q
.line
= q
.line
[2:] // 2 of the 3; other 1 is done below
107 case b
== '\t' || b
== '\r' || b
== '\n':
109 case b
< ' ' || b
> '~':
110 return n
, fmt
.Errorf("multipart: invalid unescaped byte 0x%02x in quoted-printable body", b
)