1
0
mirror of https://github.com/golang/go synced 2024-09-30 05:24:29 -06:00

mime/quotedprintable: accept = not followed by 2 hex digits as literal equals

This lets quotedprintable handle some inputs found in the wild,
most notably generated by "Microsoft CDO for Exchange 2000",
and it also matches how Python's quopri package handles these inputs.

Fixes #13219.

Change-Id: I69d400659d01b6ea0f707b7053d61803a85b4799
Reviewed-on: https://go-review.googlesource.com/32174
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Reviewed-by: Robert Griesemer <gri@golang.org>
This commit is contained in:
Russ Cox 2016-10-26 13:34:55 -04:00
parent 864859d209
commit a8e86d99f1
3 changed files with 14 additions and 10 deletions

View File

@ -15,7 +15,7 @@ import (
func ExampleNewReader() {
for _, s := range []string{
`=48=65=6C=6C=6F=2C=20=47=6F=70=68=65=72=73=21`,
`invalid escape: =B`,
`invalid escape: <b style="font-size: 200%">hello</b>`,
"Hello, Gophers! This symbol will be unescaped: =3D and this will be written in =\r\none line.",
} {
b, err := ioutil.ReadAll(quotedprintable.NewReader(strings.NewReader(s)))
@ -23,7 +23,7 @@ func ExampleNewReader() {
}
// Output:
// Hello, Gophers! <nil>
// invalid escape: unexpected EOF
// invalid escape: <b style="font-size: 200%">hello</b> <nil>
// Hello, Gophers! This symbol will be unescaped: = and this will be written in one line. <nil>
}

View File

@ -77,6 +77,8 @@ func (r *Reader) Read(p []byte) (n int, err error) {
// 3. it accepts soft line-break (=) at end of message (issue 15486); i.e.
// the final byte read from the underlying reader is allowed to be '=',
// and it will be silently ignored.
// 4. it takes = as literal = if not followed by two hex digits
// but not at end of line (issue 13219).
for len(p) > 0 {
if len(r.line) == 0 {
if r.rerr != nil {
@ -111,6 +113,11 @@ func (r *Reader) Read(p []byte) (n int, err error) {
case b == '=':
b, err = readHexByte(r.line[1:])
if err != nil {
if len(r.line) >= 2 && r.line[1] != '\r' && r.line[1] != '\n' {
// Take the = as a literal =.
b = '='
break
}
return n, err
}
r.line = r.line[2:] // 2 of the 3; other 1 is done below

View File

@ -30,7 +30,7 @@ func TestReader(t *testing.T) {
{in: "foo bar=3d", want: "foo bar="}, // lax.
{in: "foo bar=\n", want: "foo bar"},
{in: "foo bar\n", want: "foo bar\n"}, // somewhat lax.
{in: "foo bar=0", want: "foo bar", err: io.ErrUnexpectedEOF},
{in: "foo bar=0", want: "foo bar=0"}, // lax
{in: "foo bar=0D=0A", want: "foo bar\r\n"},
{in: " A B \r\n C ", want: " A B\r\n C"},
{in: " A B =\r\n C ", want: " A B C"},
@ -194,13 +194,10 @@ func TestExhaustive(t *testing.T) {
}
sort.Strings(outcomes)
got := strings.Join(outcomes, "\n")
want := `OK: 21576
invalid bytes after =: 3397
quotedprintable: invalid hex byte 0x0a: 1400
quotedprintable: invalid hex byte 0x0d: 2700
quotedprintable: invalid hex byte 0x20: 2490
quotedprintable: invalid hex byte 0x3d: 440
unexpected EOF: 3122`
want := `OK: 28934
invalid bytes after =: 3949
quotedprintable: invalid hex byte 0x0d: 2048
unexpected EOF: 194`
if got != want {
t.Errorf("Got:\n%s\nWant:\n%s", got, want)
}