1 // Copyright 2012 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
5 // The file define a quoted-printable decoder, as specified in RFC 2045.
16 type qpReader struct {
18 rerr error // last read error
19 line []byte // to be consumed before more of br
22 func newQuotedPrintableReader(r io.Reader) io.Reader {
24 br: bufio.NewReader(r),
28 func fromHex(b byte) (byte, error) {
30 case b >= '0' && b <= '9':
32 case b >= 'A' && b <= 'F':
33 return b - 'A' + 10, nil
35 return 0, fmt.Errorf("multipart: invalid quoted-printable hex byte 0x%02x", b)
38 func (q *qpReader) readHexByte(v []byte) (b byte, err error) {
40 return 0, io.ErrUnexpectedEOF
43 if hb, err = fromHex(v[0]); err != nil {
46 if lb, err = fromHex(v[1]); err != nil {
49 return hb<<4 | lb, nil
52 func isQPDiscardWhitespace(r rune) bool {
54 case '\n', '\r', ' ', '\t':
60 func (q *qpReader) Read(p []byte) (n int, err error) {
66 q.line, q.rerr = q.br.ReadSlice('\n')
67 q.line = bytes.TrimRightFunc(q.line, isQPDiscardWhitespace)
70 if len(q.line) == 1 && q.line[0] == '=' {
71 // Soft newline; skipped.
78 b, err = q.readHexByte(q.line[1:])
82 q.line = q.line[2:] // 2 of the 3; other 1 is done below
83 case b != '\t' && (b < ' ' || b > '~'):
84 return n, fmt.Errorf("multipart: invalid unescaped byte 0x%02x in quoted-printable body", b)