1
2
3
4
5 package quotedprintable
6
7 import "io"
8
9 const lineMaxLen = 76
10
11
12 type Writer struct {
13
14
15 Binary bool
16
17 w io.Writer
18 i int
19 line [78]byte
20 cr bool
21 }
22
23
24 func NewWriter(w io.Writer) *Writer {
25 return &Writer{w: w}
26 }
27
28
29
30
31 func (w *Writer) Write(p []byte) (n int, err error) {
32 for i, b := range p {
33 switch {
34
35 case b >= '!' && b <= '~' && b != '=':
36 continue
37 case isWhitespace(b) || !w.Binary && (b == '\n' || b == '\r'):
38 continue
39 }
40
41 if i > n {
42 if err := w.write(p[n:i]); err != nil {
43 return n, err
44 }
45 n = i
46 }
47
48 if err := w.encode(b); err != nil {
49 return n, err
50 }
51 n++
52 }
53
54 if n == len(p) {
55 return n, nil
56 }
57
58 if err := w.write(p[n:]); err != nil {
59 return n, err
60 }
61
62 return len(p), nil
63 }
64
65
66
67 func (w *Writer) Close() error {
68 if err := w.checkLastByte(); err != nil {
69 return err
70 }
71
72 return w.flush()
73 }
74
75
76 func (w *Writer) write(p []byte) error {
77 for _, b := range p {
78 if b == '\n' || b == '\r' {
79
80 if w.cr && b == '\n' {
81 w.cr = false
82 continue
83 }
84
85 if b == '\r' {
86 w.cr = true
87 }
88
89 if err := w.checkLastByte(); err != nil {
90 return err
91 }
92 if err := w.insertCRLF(); err != nil {
93 return err
94 }
95 continue
96 }
97
98 if w.i == lineMaxLen-1 {
99 if err := w.insertSoftLineBreak(); err != nil {
100 return err
101 }
102 }
103
104 w.line[w.i] = b
105 w.i++
106 w.cr = false
107 }
108
109 return nil
110 }
111
112 func (w *Writer) encode(b byte) error {
113 if lineMaxLen-1-w.i < 3 {
114 if err := w.insertSoftLineBreak(); err != nil {
115 return err
116 }
117 }
118
119 w.line[w.i] = '='
120 w.line[w.i+1] = upperhex[b>>4]
121 w.line[w.i+2] = upperhex[b&0x0f]
122 w.i += 3
123
124 return nil
125 }
126
127 const upperhex = "0123456789ABCDEF"
128
129
130 func (w *Writer) checkLastByte() error {
131 if w.i == 0 {
132 return nil
133 }
134
135 b := w.line[w.i-1]
136 if isWhitespace(b) {
137 w.i--
138 if err := w.encode(b); err != nil {
139 return err
140 }
141 }
142
143 return nil
144 }
145
146 func (w *Writer) insertSoftLineBreak() error {
147 w.line[w.i] = '='
148 w.i++
149
150 return w.insertCRLF()
151 }
152
153 func (w *Writer) insertCRLF() error {
154 w.line[w.i] = '\r'
155 w.line[w.i+1] = '\n'
156 w.i += 2
157
158 return w.flush()
159 }
160
161 func (w *Writer) flush() error {
162 if _, err := w.w.Write(w.line[:w.i]); err != nil {
163 return err
164 }
165
166 w.i = 0
167 return nil
168 }
169
170 func isWhitespace(b byte) bool {
171 return b == ' ' || b == '\t'
172 }
173
View as plain text