]> Cypherpunks repositories - gostls13.git/commitdiff
[release-branch.go1] mime/multipart: report io.EOF correctly on part ending without...
authorBrad Fitzpatrick <bradfitz@golang.org>
Tue, 24 Apr 2012 05:26:48 +0000 (22:26 -0700)
committerBrad Fitzpatrick <bradfitz@golang.org>
Tue, 24 Apr 2012 05:26:48 +0000 (22:26 -0700)
««« 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
»»»

src/pkg/mime/multipart/multipart.go
src/pkg/mime/multipart/multipart_test.go
src/pkg/mime/multipart/testdata/nested-mime [new file with mode: 0755]

index d733130abb2e0dbe32857191e0563ffe0e6405a7..6ace4be564c94a7782e5ab48557cc3b56ac1e975 100644 (file)
@@ -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)
                }
index 89ff5e489efd6c90fc11555d984dedb9097e4f0e..ca7108d7ad0a4bd666f9d9364cba70830008f1c2 100644 (file)
@@ -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 (executable)
index 0000000..71c238e
--- /dev/null
@@ -0,0 +1,29 @@
+--e89a8ff1c1e83553e304be640612\r
+Content-Type: multipart/alternative; boundary=e89a8ff1c1e83553e004be640610\r
+\r
+--e89a8ff1c1e83553e004be640610\r
+Content-Type: text/plain; charset=UTF-8\r
+\r
+*body*\r
+\r
+--e89a8ff1c1e83553e004be640610\r
+Content-Type: text/html; charset=UTF-8\r
+\r
+<b>body</b>\r
+\r
+--e89a8ff1c1e83553e004be640610--\r
+--e89a8ff1c1e83553e304be640612\r
+Content-Type: image/png; name="x.png"\r
+Content-Disposition: attachment; \r
+       filename="x.png"\r
+Content-Transfer-Encoding: base64\r
+X-Attachment-Id: f_h1edgigu0\r
+\r
+iVBORw0KGgoAAAANSUhEUgAAAagAAADrCAIAAACza5XhAAAKMWlDQ1BJQ0MgUHJvZmlsZQAASImd\r
+lndUU9kWh8+9N71QkhCKlNBraFICSA29SJEuKjEJEErAkAAiNkRUcERRkaYIMijggKNDkbEiioUB\r
+8b2kqeGaj4aTNftesu5mob4pr07ecMywRwLBvDCJOksqlUyldAZD7g9fxIZRWWPMvXRNJROJRBIG\r
+Y7Vx0mva1HAwYqibdKONXye3dW4iUonhWFJnqK7OaanU1gGkErFYEgaj0cg8wK+zVPh2ziwnHy07\r
+U8lYTNapezSzOuevRwLB7CFkqQQCwaJDiBQIBIJFhwh8AoFg0SHUqQUCASRJKkwkhMy/JfODWPEJ\r
+BIJFhwh8AoFg0TFnQqQ55GtPFopcJsN97e1nYtNuIBYeGBgYCmYrmE3jZ05iaGAoMX0xzxkWz6Hv\r
+yO7WvrlwzA0uLzrD+VkKqViwl9IfTBVNFMyc/x9alloiPPlqhQAAAABJRU5ErkJggg==\r
+--e89a8ff1c1e83553e304be640612--\r