mirror of
https://github.com/golang/go.git
synced 2024-09-21 18:38:37 +00:00
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>
This commit is contained in:
parent
a08cb9f473
commit
df380693f3
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user