libgo: Update to Go 1.1.1.
[official-gcc.git] / libgo / go / mime / multipart / quotedprintable.go
blob9ff4ee703ee1d952231ba76441d67cf0a51f1908
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.
6 // Deviations:
7 // 1. in addition to "=\r\n", "=\n" is also treated as soft line break.
8 // 2. it will pass through a '\r' or '\n' not preceded by '=', consistent
9 // with other broken QP encoders & decoders.
11 package multipart
13 import (
14 "bufio"
15 "bytes"
16 "fmt"
17 "io"
20 type qpReader struct {
21 br *bufio.Reader
22 rerr error // last read error
23 line []byte // to be consumed before more of br
26 func newQuotedPrintableReader(r io.Reader) io.Reader {
27 return &qpReader{
28 br: bufio.NewReader(r),
32 func fromHex(b byte) (byte, error) {
33 switch {
34 case b >= '0' && b <= '9':
35 return b - '0', nil
36 case b >= 'A' && b <= 'F':
37 return b - 'A' + 10, nil
39 return 0, fmt.Errorf("multipart: invalid quoted-printable hex byte 0x%02x", b)
42 func (q *qpReader) readHexByte(v []byte) (b byte, err error) {
43 if len(v) < 2 {
44 return 0, io.ErrUnexpectedEOF
46 var hb, lb byte
47 if hb, err = fromHex(v[0]); err != nil {
48 return 0, err
50 if lb, err = fromHex(v[1]); err != nil {
51 return 0, err
53 return hb<<4 | lb, nil
56 func isQPDiscardWhitespace(r rune) bool {
57 switch r {
58 case '\n', '\r', ' ', '\t':
59 return true
61 return false
64 var (
65 crlf = []byte("\r\n")
66 lf = []byte("\n")
67 softSuffix = []byte("=")
70 func (q *qpReader) Read(p []byte) (n int, err error) {
71 for len(p) > 0 {
72 if len(q.line) == 0 {
73 if q.rerr != nil {
74 return n, q.rerr
76 q.line, q.rerr = q.br.ReadSlice('\n')
78 // Does the line end in CRLF instead of just LF?
79 hasLF := bytes.HasSuffix(q.line, lf)
80 hasCR := bytes.HasSuffix(q.line, crlf)
81 wholeLine := q.line
82 q.line = bytes.TrimRightFunc(wholeLine, isQPDiscardWhitespace)
83 if bytes.HasSuffix(q.line, softSuffix) {
84 rightStripped := wholeLine[len(q.line):]
85 q.line = q.line[:len(q.line)-1]
86 if !bytes.HasPrefix(rightStripped, lf) && !bytes.HasPrefix(rightStripped, crlf) {
87 q.rerr = fmt.Errorf("multipart: invalid bytes after =: %q", rightStripped)
89 } else if hasLF {
90 if hasCR {
91 q.line = append(q.line, '\r', '\n')
92 } else {
93 q.line = append(q.line, '\n')
96 continue
98 b := q.line[0]
100 switch {
101 case b == '=':
102 b, err = q.readHexByte(q.line[1:])
103 if err != nil {
104 return n, err
106 q.line = q.line[2:] // 2 of the 3; other 1 is done below
107 case b == '\t' || b == '\r' || b == '\n':
108 break
109 case b < ' ' || b > '~':
110 return n, fmt.Errorf("multipart: invalid unescaped byte 0x%02x in quoted-printable body", b)
112 p[0] = b
113 p = p[1:]
114 q.line = q.line[1:]
117 return n, nil