)
type qpReader struct {
- br *bufio.Reader
- skipWhite bool
- rerr error // last read error
- line []byte // to be consumed before more of br
+ br *bufio.Reader
+ rerr error // last read error
+ line []byte // to be consumed before more of br
}
func newQuotedPrintableReader(r io.Reader) io.Reader {
return &qpReader{
- br: bufio.NewReader(r),
- skipWhite: true,
+ br: bufio.NewReader(r),
}
}
return hb<<4 | lb, nil
}
-func isQPSkipWhiteByte(b byte) bool {
- return b == ' ' || b == '\t'
-}
-
func isQPDiscardWhitespace(r rune) bool {
switch r {
case '\n', '\r', ' ', '\t':
if q.rerr != nil {
return n, q.rerr
}
- q.skipWhite = true
q.line, q.rerr = q.br.ReadSlice('\n')
// Does the line end in CRLF instead of just LF?
continue
}
b := q.line[0]
- if q.skipWhite && isQPSkipWhiteByte(b) {
- q.line = q.line[1:]
- continue
- }
- q.skipWhite = false
switch {
case b == '=':
{in: "foo bar=0", want: "foo bar", err: io.ErrUnexpectedEOF},
{in: "foo bar=ab", want: "foo bar", err: "multipart: invalid quoted-printable hex byte 0x61"},
{in: "foo bar=0D=0A", want: "foo bar\r\n"},
- {in: " A B =\r\n C ", want: "A B C"},
- {in: " A B =\n C ", want: "A B C"}, // lax. treating LF as CRLF
+ {in: " A B \r\n C ", want: " A B\r\n C"},
+ {in: " A B =\r\n C ", want: " A B C"},
+ {in: " A B =\n C ", want: " A B C"}, // lax. treating LF as CRLF
{in: "foo=\nbar", want: "foobar"},
{in: "foo\x00bar", want: "foo", err: "multipart: invalid unescaped byte 0x00 in quoted-printable body"},
{in: "foo bar\xff", want: "foo bar", err: "multipart: invalid unescaped byte 0xff in quoted-printable body"},
{in: "foo=\nbar", want: "foobar"},
{in: "foo=\rbar", want: "foo", err: "multipart: invalid quoted-printable hex byte 0x0d"},
{in: "foo=\r\r\r \nbar", want: "foo", err: `multipart: invalid bytes after =: "\r\r\r \n"`},
+
+ // Example from RFC 2045:
+ {in: "Now's the time =\n" + "for all folk to come=\n" + " to the aid of their country.",
+ want: "Now's the time for all folk to come to the aid of their country."},
}
for _, tt := range tests {
var buf bytes.Buffer