Commit a8e86d99 authored by Russ Cox's avatar Russ Cox

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/32174Reviewed-by: 's avatarBrad Fitzpatrick <bradfitz@golang.org>
Reviewed-by: 's avatarRobert Griesemer <gri@golang.org>
parent 864859d2
......@@ -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>
}
......
......@@ -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
......
......@@ -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)
}
......
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