diff options
author | Brad Fitzpatrick <bradfitz@golang.org> | 2012-04-23 22:26:48 -0700 |
---|---|---|
committer | Brad Fitzpatrick <bradfitz@golang.org> | 2012-04-23 22:26:48 -0700 |
commit | b4ea696b49427919bbae6d24bedecb2750567da0 (patch) | |
tree | 0b978b97be51372184fc90f5f9b9fe7c2c9912ce | |
parent | c619931610c53441685a34babe391073002c2e42 (diff) | |
download | go-b4ea696b49427919bbae6d24bedecb2750567da0.tar.gz go-b4ea696b49427919bbae6d24bedecb2750567da0.zip |
[release-branch.go1] mime/multipart: report io.EOF correctly on part ending without newlines
««« backport 4ecca118488b
mime/multipart: report io.EOF correctly on part ending without newlines
If a part ends with "--boundary--", without a final "\r\n",
that's also a graceful EOF, and we should return io.EOF instead
of the fmt-wrapped io.EOF from bufio.Reader.ReadSlice.
I found this bug parsing an image attachment from gmail.
Minimal test case stripped down from the original
gmail-generated attachment included.
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/6118043
»»»
-rw-r--r-- | src/pkg/mime/multipart/multipart.go | 8 | ||||
-rw-r--r-- | src/pkg/mime/multipart/multipart_test.go | 50 | ||||
-rwxr-xr-x | src/pkg/mime/multipart/testdata/nested-mime | 29 |
3 files changed, 87 insertions, 0 deletions
diff --git a/src/pkg/mime/multipart/multipart.go b/src/pkg/mime/multipart/multipart.go index d733130abb..6ace4be564 100644 --- a/src/pkg/mime/multipart/multipart.go +++ b/src/pkg/mime/multipart/multipart.go @@ -185,6 +185,14 @@ func (r *Reader) NextPart() (*Part, error) { expectNewPart := false for { line, err := r.bufReader.ReadSlice('\n') + if err == io.EOF && bytes.Equal(line, r.dashBoundaryDash) { + // If the buffer ends in "--boundary--" without the + // trailing "\r\n", ReadSlice will return an error + // (since it's missing the '\n'), but this is a valid + // multipart EOF so we need to return io.EOF instead of + // a fmt-wrapped one. + return nil, io.EOF + } if err != nil { return nil, fmt.Errorf("multipart: NextPart: %v", err) } diff --git a/src/pkg/mime/multipart/multipart_test.go b/src/pkg/mime/multipart/multipart_test.go index 89ff5e489e..ca7108d7ad 100644 --- a/src/pkg/mime/multipart/multipart_test.go +++ b/src/pkg/mime/multipart/multipart_test.go @@ -10,6 +10,7 @@ import ( "fmt" "io" "io/ioutil" + "os" "strings" "testing" ) @@ -377,3 +378,52 @@ func TestLineContinuation(t *testing.T) { } } } + +// Test parsing an image attachment from gmail, which previously failed. +func TestNested(t *testing.T) { + // nested-mime is the body part of a multipart/mixed email + // with boundary e89a8ff1c1e83553e304be640612 + f, err := os.Open("testdata/nested-mime") + if err != nil { + t.Fatal(err) + } + defer f.Close() + mr := NewReader(f, "e89a8ff1c1e83553e304be640612") + p, err := mr.NextPart() + if err != nil { + t.Fatalf("error reading first section (alternative): %v", err) + } + + // Read the inner text/plain and text/html sections of the multipart/alternative. + mr2 := NewReader(p, "e89a8ff1c1e83553e004be640610") + p, err = mr2.NextPart() + if err != nil { + t.Fatalf("reading text/plain part: %v", err) + } + if b, err := ioutil.ReadAll(p); string(b) != "*body*\r\n" || err != nil { + t.Fatalf("reading text/plain part: got %q, %v", b, err) + } + p, err = mr2.NextPart() + if err != nil { + t.Fatalf("reading text/html part: %v", err) + } + if b, err := ioutil.ReadAll(p); string(b) != "<b>body</b>\r\n" || err != nil { + t.Fatalf("reading text/html part: got %q, %v", b, err) + } + + p, err = mr2.NextPart() + if err != io.EOF { + t.Fatalf("final inner NextPart = %v; want io.EOF", err) + } + + // Back to the outer multipart/mixed, reading the image attachment. + _, err = mr.NextPart() + if err != nil { + t.Fatalf("error reading the image attachment at the end: %v", err) + } + + _, err = mr.NextPart() + if err != io.EOF { + t.Fatalf("final outer NextPart = %v; want io.EOF", err) + } +} diff --git a/src/pkg/mime/multipart/testdata/nested-mime b/src/pkg/mime/multipart/testdata/nested-mime new file mode 100755 index 0000000000..71c238e389 --- /dev/null +++ b/src/pkg/mime/multipart/testdata/nested-mime @@ -0,0 +1,29 @@ +--e89a8ff1c1e83553e304be640612
+Content-Type: multipart/alternative; boundary=e89a8ff1c1e83553e004be640610
+
+--e89a8ff1c1e83553e004be640610
+Content-Type: text/plain; charset=UTF-8
+
+*body*
+
+--e89a8ff1c1e83553e004be640610
+Content-Type: text/html; charset=UTF-8
+
+<b>body</b>
+
+--e89a8ff1c1e83553e004be640610--
+--e89a8ff1c1e83553e304be640612
+Content-Type: image/png; name="x.png"
+Content-Disposition: attachment;
+ filename="x.png"
+Content-Transfer-Encoding: base64
+X-Attachment-Id: f_h1edgigu0
+
+iVBORw0KGgoAAAANSUhEUgAAAagAAADrCAIAAACza5XhAAAKMWlDQ1BJQ0MgUHJvZmlsZQAASImd
+lndUU9kWh8+9N71QkhCKlNBraFICSA29SJEuKjEJEErAkAAiNkRUcERRkaYIMijggKNDkbEiioUB
+8b2kqeGaj4aTNftesu5mob4pr07ecMywRwLBvDCJOksqlUyldAZD7g9fxIZRWWPMvXRNJROJRBIG
+Y7Vx0mva1HAwYqibdKONXye3dW4iUonhWFJnqK7OaanU1gGkErFYEgaj0cg8wK+zVPh2ziwnHy07
+U8lYTNapezSzOuevRwLB7CFkqQQCwaJDiBQIBIJFhwh8AoFg0SHUqQUCASRJKkwkhMy/JfODWPEJ
+BIJFhwh8AoFg0TFnQqQ55GtPFopcJsN97e1nYtNuIBYeGBgYCmYrmE3jZ05iaGAoMX0xzxkWz6Hv
+yO7WvrlwzA0uLzrD+VkKqViwl9IfTBVNFMyc/x9alloiPPlqhQAAAABJRU5ErkJggg==
+--e89a8ff1c1e83553e304be640612--
|