Imported Upstream version 4.8.1
[platform/upstream/gcc48.git] / libgo / go / mime / multipart / quotedprintable.go
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.
4
5 // The file define a quoted-printable decoder, as specified in RFC 2045.
6
7 package multipart
8
9 import (
10         "bufio"
11         "bytes"
12         "fmt"
13         "io"
14 )
15
16 type qpReader struct {
17         br   *bufio.Reader
18         rerr error  // last read error
19         line []byte // to be consumed before more of br
20 }
21
22 func newQuotedPrintableReader(r io.Reader) io.Reader {
23         return &qpReader{
24                 br: bufio.NewReader(r),
25         }
26 }
27
28 func fromHex(b byte) (byte, error) {
29         switch {
30         case b >= '0' && b <= '9':
31                 return b - '0', nil
32         case b >= 'A' && b <= 'F':
33                 return b - 'A' + 10, nil
34         }
35         return 0, fmt.Errorf("multipart: invalid quoted-printable hex byte 0x%02x", b)
36 }
37
38 func (q *qpReader) readHexByte(v []byte) (b byte, err error) {
39         if len(v) < 2 {
40                 return 0, io.ErrUnexpectedEOF
41         }
42         var hb, lb byte
43         if hb, err = fromHex(v[0]); err != nil {
44                 return 0, err
45         }
46         if lb, err = fromHex(v[1]); err != nil {
47                 return 0, err
48         }
49         return hb<<4 | lb, nil
50 }
51
52 func isQPDiscardWhitespace(r rune) bool {
53         switch r {
54         case '\n', '\r', ' ', '\t':
55                 return true
56         }
57         return false
58 }
59
60 func (q *qpReader) Read(p []byte) (n int, err error) {
61         for len(p) > 0 {
62                 if len(q.line) == 0 {
63                         if q.rerr != nil {
64                                 return n, q.rerr
65                         }
66                         q.line, q.rerr = q.br.ReadSlice('\n')
67                         q.line = bytes.TrimRightFunc(q.line, isQPDiscardWhitespace)
68                         continue
69                 }
70                 if len(q.line) == 1 && q.line[0] == '=' {
71                         // Soft newline; skipped.
72                         q.line = nil
73                         continue
74                 }
75                 b := q.line[0]
76                 switch {
77                 case b == '=':
78                         b, err = q.readHexByte(q.line[1:])
79                         if err != nil {
80                                 return n, err
81                         }
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)
85                 }
86                 p[0] = b
87                 p = p[1:]
88                 q.line = q.line[1:]
89                 n++
90         }
91         return n, nil
92 }