aboutsummaryrefslogtreecommitdiff
path: root/src/mime
diff options
context:
space:
mode:
authorAlex Buchanan <buchanae@gmail.com>2018-12-05 22:31:53 -0800
committerIan Lance Taylor <iant@golang.org>2019-10-11 17:03:37 +0000
commitdf380693f3eddf0a4ae7d195042b48b40e651d0e (patch)
tree8332c17f1f140cecf65feee2acfb6c0eb1b259a3 /src/mime
parenta08cb9f473405769f292bd2ff455764a690fe3e7 (diff)
downloadgo-df380693f3eddf0a4ae7d195042b48b40e651d0e.tar.gz
go-df380693f3eddf0a4ae7d195042b48b40e651d0e.zip
mime/multipart: add Part.NextRawPart to avoid QP decoding
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>
Diffstat (limited to 'src/mime')
-rw-r--r--src/mime/multipart/multipart.go38
-rw-r--r--src/mime/multipart/multipart_test.go60
2 files changed, 87 insertions, 11 deletions
diff --git a/src/mime/multipart/multipart.go b/src/mime/multipart/multipart.go
index a222409d3c..1750300fb5 100644
--- a/src/mime/multipart/multipart.go
+++ b/src/mime/multipart/multipart.go
@@ -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
}
diff --git a/src/mime/multipart/multipart_test.go b/src/mime/multipart/multipart_test.go
index 5dc74b5ffe..b60c54a204 100644
--- a/src/mime/multipart/multipart_test.go
+++ b/src/mime/multipart/multipart_test.go
@@ -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