]> Cypherpunks repositories - gostls13.git/commitdiff
mime/multipart: parse LF-delimited messages, not just CRLF
authorBrad Fitzpatrick <bradfitz@golang.org>
Tue, 28 Jun 2011 04:59:51 +0000 (21:59 -0700)
committerBrad Fitzpatrick <bradfitz@golang.org>
Tue, 28 Jun 2011 04:59:51 +0000 (21:59 -0700)
Against the spec, but appear in the wild.

Fixes #1966

R=golang-dev, adg
CC=golang-dev
https://golang.org/cl/4662059

src/pkg/mime/multipart/multipart.go
src/pkg/mime/multipart/multipart_test.go

index 5c173f2283a95c605bb85e0f3d4125523ec65a58..4711fd78ba4a4536992dcfe914faec4b5291d6ee 100644 (file)
@@ -24,6 +24,11 @@ import (
        "regexp"
 )
 
+// TODO(bradfitz): inline these once the compiler can inline them in
+// read-only situation (such as bytes.HasSuffix)
+var lf = []byte("\n")
+var crlf = []byte("\r\n")
+
 var headerRegexp *regexp.Regexp = regexp.MustCompile("^([a-zA-Z0-9\\-]+): *([^\r\n]+)")
 
 var emptyParams = make(map[string]string)
@@ -81,6 +86,7 @@ func NewReader(reader io.Reader, boundary string) *Reader {
        return &Reader{
                bufReader: bufio.NewReader(reader),
 
+               nl:               b[:2],
                nlDashBoundary:   b[:len(b)-2],
                dashBoundaryDash: b[2:],
                dashBoundary:     b[2 : len(b)-2],
@@ -180,7 +186,7 @@ type Reader struct {
        currentPart *Part
        partsRead   int
 
-       nlDashBoundary, dashBoundaryDash, dashBoundary []byte
+       nl, nlDashBoundary, dashBoundaryDash, dashBoundary []byte
 }
 
 // NextPart returns the next part in the multipart or an error.
@@ -221,11 +227,11 @@ func (mr *Reader) NextPart() (*Part, os.Error) {
                        continue
                }
 
-               if bytes.Equal(line, []byte("\r\n")) {
-                       // Consume the "\r\n" separator between the
-                       // body of the previous part and the boundary
-                       // line we now expect will follow. (either a
-                       // new part or the end boundary)
+               // Consume the "\n" or "\r\n" separator between the
+               // body of the previous part and the boundary line we
+               // now expect will follow. (either a new part or the
+               // end boundary)
+               if bytes.Equal(line, mr.nl) {
                        expectNewPart = true
                        continue
                }
@@ -245,13 +251,17 @@ func (mr *Reader) isBoundaryDelimiterLine(line []byte) bool {
        if !bytes.HasPrefix(line, mr.dashBoundary) {
                return false
        }
-       if bytes.HasSuffix(line, []byte("\r\n")) {
-               return onlyHorizontalWhitespace(line[len(mr.dashBoundary) : len(line)-2])
+       if bytes.HasSuffix(line, mr.nl) {
+               return onlyHorizontalWhitespace(line[len(mr.dashBoundary) : len(line)-len(mr.nl)])
        }
        // Violate the spec and also support newlines without the
        // carriage return...
-       if bytes.HasSuffix(line, []byte("\n")) {
-               return onlyHorizontalWhitespace(line[len(mr.dashBoundary) : len(line)-1])
+       if mr.partsRead == 0 && bytes.HasSuffix(line, lf) {
+               if onlyHorizontalWhitespace(line[len(mr.dashBoundary) : len(line)-1]) {
+                       mr.nl = mr.nl[1:]
+                       mr.nlDashBoundary = mr.nlDashBoundary[1:]
+                       return true
+               }
        }
        return false
 }
@@ -268,5 +278,5 @@ func onlyHorizontalWhitespace(s []byte) bool {
 func hasPrefixThenNewline(s, prefix []byte) bool {
        return bytes.HasPrefix(s, prefix) &&
                (len(s) == len(prefix)+1 && s[len(s)-1] == '\n' ||
-                       len(s) == len(prefix)+2 && bytes.HasSuffix(s, []byte("\r\n")))
+                       len(s) == len(prefix)+2 && bytes.HasSuffix(s, crlf))
 }
index 8bc16bbf723e55cf53346ebc08c87cbab2a95b1a..1357466acd0b94be77fb7c19a41e724870c425ad 100644 (file)
@@ -81,7 +81,7 @@ func TestNameAccessors(t *testing.T) {
 
 var longLine = strings.Repeat("\n\n\r\r\r\n\r\000", (1<<20)/8)
 
-func testMultipartBody() string {
+func testMultipartBody(sep string) string {
        testBody := `
 This is a multi-part message.  This line is ignored.
 --MyBoundary
@@ -112,21 +112,26 @@ never read data
 
 useless trailer
 `
-       testBody = strings.Replace(testBody, "\n", "\r\n", -1)
+       testBody = strings.Replace(testBody, "\n", sep, -1)
        return strings.Replace(testBody, "[longline]", longLine, 1)
 }
 
 func TestMultipart(t *testing.T) {
-       bodyReader := strings.NewReader(testMultipartBody())
-       testMultipart(t, bodyReader)
+       bodyReader := strings.NewReader(testMultipartBody("\r\n"))
+       testMultipart(t, bodyReader, false)
+}
+
+func TestMultipartOnlyNewlines(t *testing.T) {
+       bodyReader := strings.NewReader(testMultipartBody("\n"))
+       testMultipart(t, bodyReader, true)
 }
 
 func TestMultipartSlowInput(t *testing.T) {
-       bodyReader := strings.NewReader(testMultipartBody())
-       testMultipart(t, &slowReader{bodyReader})
+       bodyReader := strings.NewReader(testMultipartBody("\r\n"))
+       testMultipart(t, &slowReader{bodyReader}, false)
 }
 
-func testMultipart(t *testing.T, r io.Reader) {
+func testMultipart(t *testing.T, r io.Reader, onlyNewlines bool) {
        reader := NewReader(r, "MyBoundary")
        buf := new(bytes.Buffer)
 
@@ -149,8 +154,15 @@ func testMultipart(t *testing.T, r io.Reader) {
        if _, err := io.Copy(buf, part); err != nil {
                t.Errorf("part 1 copy: %v", err)
        }
-       expectEq(t, "My value\r\nThe end.",
-               buf.String(), "Value of first part")
+
+       adjustNewlines := func(s string) string {
+               if onlyNewlines {
+                       return strings.Replace(s, "\r\n", "\n", -1)
+               }
+               return s
+       }
+
+       expectEq(t, adjustNewlines("My value\r\nThe end."), buf.String(), "Value of first part")
 
        // Part2
        part, err = reader.NextPart()
@@ -187,7 +199,7 @@ func testMultipart(t *testing.T, r io.Reader) {
        if _, err := io.Copy(buf, part); err != nil {
                t.Errorf("part 3 copy: %v", err)
        }
-       expectEq(t, "Line 1\r\nLine 2\r\nLine 3 ends in a newline, but just one.\r\n",
+       expectEq(t, adjustNewlines("Line 1\r\nLine 2\r\nLine 3 ends in a newline, but just one.\r\n"),
                buf.String(), "body of part 3")
 
        // Part4