1
2
3
4
5 package textproto
6
7 import (
8 "bufio"
9 "bytes"
10 "io"
11 "io/ioutil"
12 "os"
13 "strconv"
14 )
15
16
17
18
19
20
21
22 type Reader struct {
23 R *bufio.Reader
24 dot *dotReader
25 }
26
27
28 func NewReader(r *bufio.Reader) *Reader {
29 return &Reader{R: r}
30 }
31
32
33
34 func (r *Reader) ReadLine() (string, os.Error) {
35 line, err := r.readLineSlice()
36 return string(line), err
37 }
38
39
40 func (r *Reader) ReadLineBytes() ([]byte, os.Error) {
41 line, err := r.readLineSlice()
42 if line != nil {
43 buf := make([]byte, len(line))
44 copy(buf, line)
45 line = buf
46 }
47 return line, err
48 }
49
50 func (r *Reader) readLineSlice() ([]byte, os.Error) {
51 r.closeDot()
52 line, _, err := r.R.ReadLine()
53 return line, err
54 }
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75 func (r *Reader) ReadContinuedLine() (string, os.Error) {
76 line, err := r.readContinuedLineSlice()
77 return string(line), err
78 }
79
80
81
82 func trim(s []byte) []byte {
83 i := 0
84 for i < len(s) && (s[i] == ' ' || s[i] == '\t') {
85 i++
86 }
87 n := len(s)
88 for n > i && (s[n-1] == ' ' || s[n-1] == '\t') {
89 n--
90 }
91 return s[i:n]
92 }
93
94
95
96 func (r *Reader) ReadContinuedLineBytes() ([]byte, os.Error) {
97 line, err := r.readContinuedLineSlice()
98 if line != nil {
99 buf := make([]byte, len(line))
100 copy(buf, line)
101 line = buf
102 }
103 return line, err
104 }
105
106 func (r *Reader) readContinuedLineSlice() ([]byte, os.Error) {
107
108 line, err := r.readLineSlice()
109 if err != nil {
110 return line, err
111 }
112 if len(line) == 0 {
113 return line, nil
114 }
115 line = trim(line)
116
117 copied := false
118 if r.R.Buffered() < 1 {
119
120 copied = true
121 line = append([]byte(nil), line...)
122 }
123
124
125 c, err := r.R.ReadByte()
126 if err != nil {
127
128 return line, nil
129 }
130 if c != ' ' && c != '\t' {
131
132 r.R.UnreadByte()
133 return line, nil
134 }
135
136 if !copied {
137
138 line = append(make([]byte, 0, len(line)*2), line...)
139 }
140
141
142 for {
143
144 for {
145 c, err = r.R.ReadByte()
146 if err != nil {
147 break
148 }
149 if c != ' ' && c != '\t' {
150 r.R.UnreadByte()
151 break
152 }
153 }
154 var cont []byte
155 cont, err = r.readLineSlice()
156 cont = trim(cont)
157 line = append(line, ' ')
158 line = append(line, cont...)
159 if err != nil {
160 break
161 }
162
163
164 if c, err = r.R.ReadByte(); err != nil {
165 break
166 }
167 if c != ' ' && c != '\t' {
168 r.R.UnreadByte()
169 break
170 }
171 }
172
173
174 if len(line) > 0 {
175 err = nil
176 }
177 return line, err
178 }
179
180 func (r *Reader) readCodeLine(expectCode int) (code int, continued bool, message string, err os.Error) {
181 line, err := r.ReadLine()
182 if err != nil {
183 return
184 }
185 if len(line) < 4 || line[3] != ' ' && line[3] != '-' {
186 err = ProtocolError("short response: " + line)
187 return
188 }
189 continued = line[3] == '-'
190 code, err = strconv.Atoi(line[0:3])
191 if err != nil || code < 100 {
192 err = ProtocolError("invalid response code: " + line)
193 return
194 }
195 message = line[4:]
196 if 1 <= expectCode && expectCode < 10 && code/100 != expectCode ||
197 10 <= expectCode && expectCode < 100 && code/10 != expectCode ||
198 100 <= expectCode && expectCode < 1000 && code != expectCode {
199 err = &Error{code, message}
200 }
201 return
202 }
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219 func (r *Reader) ReadCodeLine(expectCode int) (code int, message string, err os.Error) {
220 code, continued, message, err := r.readCodeLine(expectCode)
221 if err == nil && continued {
222 err = ProtocolError("unexpected multi-line response: " + message)
223 }
224 return
225 }
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244 func (r *Reader) ReadResponse(expectCode int) (code int, message string, err os.Error) {
245 code, continued, message, err := r.readCodeLine(expectCode)
246 for err == nil && continued {
247 var code2 int
248 var moreMessage string
249 code2, continued, moreMessage, err = r.readCodeLine(expectCode)
250 if code != code2 {
251 err = ProtocolError("status code mismatch: " + strconv.Itoa(code) + ", " + strconv.Itoa(code2))
252 }
253 message += "\n" + moreMessage
254 }
255 return
256 }
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274 func (r *Reader) DotReader() io.Reader {
275 r.closeDot()
276 r.dot = &dotReader{r: r}
277 return r.dot
278 }
279
280 type dotReader struct {
281 r *Reader
282 state int
283 }
284
285
286 func (d *dotReader) Read(b []byte) (n int, err os.Error) {
287
288
289
290 const (
291 stateBeginLine = iota
292 stateDot
293 stateDotCR
294 stateCR
295 stateData
296 stateEOF
297 )
298 br := d.r.R
299 for n < len(b) && d.state != stateEOF {
300 var c byte
301 c, err = br.ReadByte()
302 if err != nil {
303 if err == os.EOF {
304 err = io.ErrUnexpectedEOF
305 }
306 break
307 }
308 switch d.state {
309 case stateBeginLine:
310 if c == '.' {
311 d.state = stateDot
312 continue
313 }
314 if c == '\r' {
315 d.state = stateCR
316 continue
317 }
318 d.state = stateData
319
320 case stateDot:
321 if c == '\r' {
322 d.state = stateDotCR
323 continue
324 }
325 if c == '\n' {
326 d.state = stateEOF
327 continue
328 }
329 d.state = stateData
330
331 case stateDotCR:
332 if c == '\n' {
333 d.state = stateEOF
334 continue
335 }
336
337
338 br.UnreadByte()
339 c = '\r'
340 d.state = stateData
341
342 case stateCR:
343 if c == '\n' {
344 d.state = stateBeginLine
345 break
346 }
347
348 br.UnreadByte()
349 c = '\r'
350 d.state = stateData
351
352 case stateData:
353 if c == '\r' {
354 d.state = stateCR
355 continue
356 }
357 if c == '\n' {
358 d.state = stateBeginLine
359 }
360 }
361 b[n] = c
362 n++
363 }
364 if err == nil && d.state == stateEOF {
365 err = os.EOF
366 }
367 if err != nil && d.r.dot == d {
368 d.r.dot = nil
369 }
370 return
371 }
372
373
374
375 func (r *Reader) closeDot() {
376 if r.dot == nil {
377 return
378 }
379 buf := make([]byte, 128)
380 for r.dot != nil {
381
382
383 r.dot.Read(buf)
384 }
385 }
386
387
388
389
390 func (r *Reader) ReadDotBytes() ([]byte, os.Error) {
391 return ioutil.ReadAll(r.DotReader())
392 }
393
394
395
396
397
398 func (r *Reader) ReadDotLines() ([]string, os.Error) {
399
400
401
402 var v []string
403 var err os.Error
404 for {
405 var line string
406 line, err = r.ReadLine()
407 if err != nil {
408 if err == os.EOF {
409 err = io.ErrUnexpectedEOF
410 }
411 break
412 }
413
414
415 if len(line) > 0 && line[0] == '.' {
416 if len(line) == 1 {
417 break
418 }
419 line = line[1:]
420 }
421 v = append(v, line)
422 }
423 return v, err
424 }
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446 func (r *Reader) ReadMIMEHeader() (MIMEHeader, os.Error) {
447 m := make(MIMEHeader)
448 for {
449 kv, err := r.readContinuedLineSlice()
450 if len(kv) == 0 {
451 return m, err
452 }
453
454
455 i := bytes.IndexByte(kv, ':')
456 if i < 0 || bytes.IndexByte(kv[0:i], ' ') >= 0 {
457 return m, ProtocolError("malformed MIME header line: " + string(kv))
458 }
459 key := CanonicalMIMEHeaderKey(string(kv[0:i]))
460
461
462 i++
463 for i < len(kv) && (kv[i] == ' ' || kv[i] == '\t') {
464 i++
465 }
466 value := string(kv[i:])
467
468 m[key] = append(m[key], value)
469
470 if err != nil {
471 return m, err
472 }
473 }
474 panic("unreachable")
475 }
476
477
478
479
480
481
482 func CanonicalMIMEHeaderKey(s string) string {
483
484 needUpper := true
485 for i := 0; i < len(s); i++ {
486 c := s[i]
487 if needUpper && 'a' <= c && c <= 'z' {
488 goto MustRewrite
489 }
490 if !needUpper && 'A' <= c && c <= 'Z' {
491 goto MustRewrite
492 }
493 needUpper = c == '-'
494 }
495 return s
496
497 MustRewrite:
498
499
500
501
502 a := []byte(s)
503 upper := true
504 for i, v := range a {
505 if upper && 'a' <= v && v <= 'z' {
506 a[i] = v + 'A' - 'a'
507 }
508 if !upper && 'A' <= v && v <= 'Z' {
509 a[i] = v + 'a' - 'A'
510 }
511 upper = v == '-'
512 }
513 return string(a)
514 }