From c9ea35886af4ae654f1082524df51627ba6c5833 Mon Sep 17 00:00:00 2001 From: Simon Ser Date: Sat, 21 Dec 2024 14:22:33 +0100 Subject: [PATCH] mime/quotedprintable: accept LWSP-char after = SP and HTAB are allowed after a = before the following CRLF. RFC 2045 section 6.7 describes the ABNF for the quoted-printable encoding: qp-line := *(qp-segment transport-padding CRLF) qp-part transport-padding qp-segment := qp-section *(SPACE / TAB) "=" transport-padding := *LWSP-char ; Composers MUST NOT generate ; non-zero length transport ; padding, but receivers MUST ; be able to handle padding ; added by message transports. RFC 822 defines LWSP-char as: LWSP-char = SPACE / HTAB Dovecot's imaptest contains such a message in src/tests/fetch-binary-mime-qp.mbox. --- src/mime/quotedprintable/reader.go | 3 ++- src/mime/quotedprintable/reader_test.go | 10 +++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/mime/quotedprintable/reader.go b/src/mime/quotedprintable/reader.go index 4239625402a2f7..9e70a1bb3d06d8 100644 --- a/src/mime/quotedprintable/reader.go +++ b/src/mime/quotedprintable/reader.go @@ -66,6 +66,7 @@ var ( crlf = []byte("\r\n") lf = []byte("\n") softSuffix = []byte("=") + lwspChar = " \t" ) // Read reads and decodes quoted-printable data from the underlying reader. @@ -92,7 +93,7 @@ func (r *Reader) Read(p []byte) (n int, err error) { wholeLine := r.line r.line = bytes.TrimRightFunc(wholeLine, isQPDiscardWhitespace) if bytes.HasSuffix(r.line, softSuffix) { - rightStripped := wholeLine[len(r.line):] + rightStripped := bytes.TrimLeft(wholeLine[len(r.line):], lwspChar) r.line = r.line[:len(r.line)-1] if !bytes.HasPrefix(rightStripped, lf) && !bytes.HasPrefix(rightStripped, crlf) && !(len(rightStripped) == 0 && len(r.line) > 0 && r.rerr == io.EOF) { diff --git a/src/mime/quotedprintable/reader_test.go b/src/mime/quotedprintable/reader_test.go index 504bd5ef292547..ee6addc8acc870 100644 --- a/src/mime/quotedprintable/reader_test.go +++ b/src/mime/quotedprintable/reader_test.go @@ -66,6 +66,10 @@ func TestReader(t *testing.T) { want: "Now's the time for all folk to come to the aid of their country."}, {in: "accept UTF-8 right quotation mark: ’", want: "accept UTF-8 right quotation mark: ’"}, + + // Transport padding + {in: "foo= \r\nbar", want: "foobar"}, + {in: "foo=\t \r\nbar", want: "foobar"}, } for _, tt := range tests { var buf strings.Builder @@ -199,9 +203,9 @@ func TestExhaustive(t *testing.T) { } slices.Sort(outcomes) got := strings.Join(outcomes, "\n") - want := `OK: 28934 -invalid bytes after =: 3949 -quotedprintable: invalid hex byte 0x0d: 2048 + want := `OK: 30638 +invalid bytes after =: 2243 +quotedprintable: invalid hex byte 0x0d: 2050 unexpected EOF: 194` if testing.Short() { want = `OK: 896