]> Cypherpunks.ru repositories - gostls13.git/commitdiff
mime/multipart: add Part.NextRawPart to avoid QP decoding
authorAlex Buchanan <buchanae@gmail.com>
Thu, 6 Dec 2018 06:31:53 +0000 (22:31 -0800)
committerIan Lance Taylor <iant@golang.org>
Fri, 11 Oct 2019 17:03:37 +0000 (17:03 +0000)
NextPart has automatic handling of quoted-printable encoding,
which is sometimes undesirable. NextRawPart adds a method
for reading a part while bypassing such automatic handling.

Fixes #29090

Change-Id: I6a042a4077c64091efa3f5dbecce0d9a34ac7065
Reviewed-on: https://go-review.googlesource.com/c/go/+/152877
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
src/mime/multipart/multipart.go
src/mime/multipart/multipart_test.go

index a222409d3ce6487ef9c4bb313acbd6912fbe7e9e..1750300fb53141285d04dbb4f849ea490cdb876f 100644 (file)
@@ -36,11 +36,6 @@ type Part struct {
        // The headers of the body, if any, with the keys canonicalized
        // in the same fashion that the Go http.Request headers are.
        // For example, "foo-bar" changes case to "Foo-Bar"
-       //
-       // As a special case, if the "Content-Transfer-Encoding" header
-       // has a value of "quoted-printable", that header is instead
-       // hidden from this map and the body is transparently decoded
-       // during Read calls.
        Header textproto.MIMEHeader
 
        mr *Reader
@@ -126,7 +121,7 @@ func (r *stickyErrorReader) Read(p []byte) (n int, _ error) {
        return n, r.err
 }
 
-func newPart(mr *Reader) (*Part, error) {
+func newPart(mr *Reader, rawPart bool) (*Part, error) {
        bp := &Part{
                Header: make(map[string][]string),
                mr:     mr,
@@ -135,10 +130,14 @@ func newPart(mr *Reader) (*Part, error) {
                return nil, err
        }
        bp.r = partReader{bp}
-       const cte = "Content-Transfer-Encoding"
-       if strings.EqualFold(bp.Header.Get(cte), "quoted-printable") {
-               bp.Header.Del(cte)
-               bp.r = quotedprintable.NewReader(bp.r)
+
+       // rawPart is used to switch between Part.NextPart and Part.NextRawPart.
+       if !rawPart {
+               const cte = "Content-Transfer-Encoding"
+               if strings.EqualFold(bp.Header.Get(cte), "quoted-printable") {
+                       bp.Header.Del(cte)
+                       bp.r = quotedprintable.NewReader(bp.r)
+               }
        }
        return bp, nil
 }
@@ -300,7 +299,24 @@ type Reader struct {
 
 // NextPart returns the next part in the multipart or an error.
 // When there are no more parts, the error io.EOF is returned.
+//
+// As a special case, if the "Content-Transfer-Encoding" header
+// has a value of "quoted-printable", that header is instead
+// hidden and the body is transparently decoded during Read calls.
 func (r *Reader) NextPart() (*Part, error) {
+       return r.nextPart(false)
+}
+
+// NextRawPart returns the next part in the multipart or an error.
+// When there are no more parts, the error io.EOF is returned.
+//
+// Unlike NextPart, it does not have special handling for
+// "Content-Transfer-Encoding: quoted-printable".
+func (r *Reader) NextRawPart() (*Part, error) {
+       return r.nextPart(true)
+}
+
+func (r *Reader) nextPart(rawPart bool) (*Part, error) {
        if r.currentPart != nil {
                r.currentPart.Close()
        }
@@ -325,7 +341,7 @@ func (r *Reader) NextPart() (*Part, error) {
 
                if r.isBoundaryDelimiterLine(line) {
                        r.partsRead++
-                       bp, err := newPart(r)
+                       bp, err := newPart(r, rawPart)
                        if err != nil {
                                return nil, err
                        }
index 5dc74b5ffe130d8f17e680df6b53392983ee9291..b60c54a2042c2f45a75f298871db04e9df8ae4c8 100644 (file)
@@ -449,6 +449,66 @@ func testQuotedPrintableEncoding(t *testing.T, cte string) {
        }
 }
 
+func TestRawPart(t *testing.T) {
+       // https://github.com/golang/go/issues/29090
+
+       body := strings.Replace(`--0016e68ee29c5d515f04cedf6733
+Content-Type: text/plain; charset="utf-8"
+Content-Transfer-Encoding: quoted-printable
+
+<div dir=3D"ltr">Hello World.</div>
+--0016e68ee29c5d515f04cedf6733
+Content-Type: text/plain; charset="utf-8"
+Content-Transfer-Encoding: quoted-printable
+
+<div dir=3D"ltr">Hello World.</div>
+--0016e68ee29c5d515f04cedf6733--`, "\n", "\r\n", -1)
+
+       r := NewReader(strings.NewReader(body), "0016e68ee29c5d515f04cedf6733")
+
+       // This part is expected to be raw, bypassing the automatic handling
+       // of quoted-printable.
+       part, err := r.NextRawPart()
+       if err != nil {
+               t.Fatal(err)
+       }
+       if _, ok := part.Header["Content-Transfer-Encoding"]; !ok {
+               t.Errorf("missing Content-Transfer-Encoding")
+       }
+       var buf bytes.Buffer
+       _, err = io.Copy(&buf, part)
+       if err != nil {
+               t.Error(err)
+       }
+       got := buf.String()
+       // Data is still quoted-printable.
+       want := `<div dir=3D"ltr">Hello World.</div>`
+       if got != want {
+               t.Errorf("wrong part value:\n got: %q\nwant: %q", got, want)
+       }
+
+       // This part is expected to have automatic decoding of quoted-printable.
+       part, err = r.NextPart()
+       if err != nil {
+               t.Fatal(err)
+       }
+       if te, ok := part.Header["Content-Transfer-Encoding"]; ok {
+               t.Errorf("unexpected Content-Transfer-Encoding of %q", te)
+       }
+
+       buf.Reset()
+       _, err = io.Copy(&buf, part)
+       if err != nil {
+               t.Error(err)
+       }
+       got = buf.String()
+       // QP data has been decoded.
+       want = `<div dir="ltr">Hello World.</div>`
+       if got != want {
+               t.Errorf("wrong part value:\n got: %q\nwant: %q", got, want)
+       }
+}
+
 // 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