Commit 24555c7b authored by Brad Fitzpatrick's avatar Brad Fitzpatrick

mime/multipart: don't strip leading space/tab in quoted-printable decoding

Late bug fix, but this is arguably a regression from Go 1.0,
since we added this transparent decoding since then. Without
this fix, Go 1.0 users could decode this correctly, but Go 1.1
users would not be able to.

The newly added test is from the RFC itself.

The updated tests had the wrong "want" values before. They
were there to test \r\n vs \n equivalence (which is
unchanged), not leading whitespace.

The skipWhite decoder struct field was added in the battles of
Issue 4771 in revision b3bb265bfecf. It was just a wrong
strategy, from an earlier round of attempts in
https://golang.org/cl/7300092/

Update #4771
Fixes #5295

R=golang-dev, r
CC=golang-dev
https://golang.org/cl/8536045
parent 67e26fce
...@@ -18,16 +18,14 @@ import ( ...@@ -18,16 +18,14 @@ import (
) )
type qpReader struct { type qpReader struct {
br *bufio.Reader br *bufio.Reader
skipWhite bool rerr error // last read error
rerr error // last read error line []byte // to be consumed before more of br
line []byte // to be consumed before more of br
} }
func newQuotedPrintableReader(r io.Reader) io.Reader { func newQuotedPrintableReader(r io.Reader) io.Reader {
return &qpReader{ return &qpReader{
br: bufio.NewReader(r), br: bufio.NewReader(r),
skipWhite: true,
} }
} }
...@@ -55,10 +53,6 @@ func (q *qpReader) readHexByte(v []byte) (b byte, err error) { ...@@ -55,10 +53,6 @@ func (q *qpReader) readHexByte(v []byte) (b byte, err error) {
return hb<<4 | lb, nil return hb<<4 | lb, nil
} }
func isQPSkipWhiteByte(b byte) bool {
return b == ' ' || b == '\t'
}
func isQPDiscardWhitespace(r rune) bool { func isQPDiscardWhitespace(r rune) bool {
switch r { switch r {
case '\n', '\r', ' ', '\t': case '\n', '\r', ' ', '\t':
...@@ -79,7 +73,6 @@ func (q *qpReader) Read(p []byte) (n int, err error) { ...@@ -79,7 +73,6 @@ func (q *qpReader) Read(p []byte) (n int, err error) {
if q.rerr != nil { if q.rerr != nil {
return n, q.rerr return n, q.rerr
} }
q.skipWhite = true
q.line, q.rerr = q.br.ReadSlice('\n') q.line, q.rerr = q.br.ReadSlice('\n')
// Does the line end in CRLF instead of just LF? // Does the line end in CRLF instead of just LF?
...@@ -103,11 +96,6 @@ func (q *qpReader) Read(p []byte) (n int, err error) { ...@@ -103,11 +96,6 @@ func (q *qpReader) Read(p []byte) (n int, err error) {
continue continue
} }
b := q.line[0] b := q.line[0]
if q.skipWhite && isQPSkipWhiteByte(b) {
q.line = q.line[1:]
continue
}
q.skipWhite = false
switch { switch {
case b == '=': case b == '=':
......
...@@ -32,8 +32,9 @@ func TestQuotedPrintable(t *testing.T) { ...@@ -32,8 +32,9 @@ func TestQuotedPrintable(t *testing.T) {
{in: "foo bar=0", want: "foo bar", err: io.ErrUnexpectedEOF}, {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=ab", want: "foo bar", err: "multipart: invalid quoted-printable hex byte 0x61"},
{in: "foo bar=0D=0A", want: "foo bar\r\n"}, {in: "foo bar=0D=0A", want: "foo bar\r\n"},
{in: " A B =\r\n C ", want: "A B C"}, {in: " A B \r\n C ", want: " A B\r\n C"},
{in: " A B =\n C ", want: "A B C"}, // lax. treating LF as CRLF {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=\nbar", want: "foobar"},
{in: "foo\x00bar", want: "foo", err: "multipart: invalid unescaped byte 0x00 in quoted-printable body"}, {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 bar\xff", want: "foo bar", err: "multipart: invalid unescaped byte 0xff in quoted-printable body"},
...@@ -57,6 +58,10 @@ func TestQuotedPrintable(t *testing.T) { ...@@ -57,6 +58,10 @@ func TestQuotedPrintable(t *testing.T) {
{in: "foo=\nbar", want: "foobar"}, {in: "foo=\nbar", want: "foobar"},
{in: "foo=\rbar", want: "foo", err: "multipart: invalid quoted-printable hex byte 0x0d"}, {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"`}, {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 { for _, tt := range tests {
var buf bytes.Buffer var buf bytes.Buffer
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment