1
2
3
4
5 package textproto
6
7 import (
8 "bufio"
9 "bytes"
10 "fmt"
11 "io"
12 "strconv"
13 "strings"
14 "sync"
15 )
16
17
18
19 type Reader struct {
20 R *bufio.Reader
21 dot *dotReader
22 buf []byte
23 }
24
25
26
27
28
29
30 func NewReader(r *bufio.Reader) *Reader {
31 return &Reader{R: r}
32 }
33
34
35
36 func (r *Reader) ReadLine() (string, error) {
37 line, err := r.readLineSlice()
38 return string(line), err
39 }
40
41
42 func (r *Reader) ReadLineBytes() ([]byte, error) {
43 line, err := r.readLineSlice()
44 if line != nil {
45 buf := make([]byte, len(line))
46 copy(buf, line)
47 line = buf
48 }
49 return line, err
50 }
51
52 func (r *Reader) readLineSlice() ([]byte, error) {
53 r.closeDot()
54 var line []byte
55 for {
56 l, more, err := r.R.ReadLine()
57 if err != nil {
58 return nil, err
59 }
60
61 if line == nil && !more {
62 return l, nil
63 }
64 line = append(line, l...)
65 if !more {
66 break
67 }
68 }
69 return line, nil
70 }
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90 func (r *Reader) ReadContinuedLine() (string, error) {
91 line, err := r.readContinuedLineSlice(noValidation)
92 return string(line), err
93 }
94
95
96
97 func trim(s []byte) []byte {
98 i := 0
99 for i < len(s) && (s[i] == ' ' || s[i] == '\t') {
100 i++
101 }
102 n := len(s)
103 for n > i && (s[n-1] == ' ' || s[n-1] == '\t') {
104 n--
105 }
106 return s[i:n]
107 }
108
109
110
111 func (r *Reader) ReadContinuedLineBytes() ([]byte, error) {
112 line, err := r.readContinuedLineSlice(noValidation)
113 if line != nil {
114 buf := make([]byte, len(line))
115 copy(buf, line)
116 line = buf
117 }
118 return line, err
119 }
120
121
122
123
124
125 func (r *Reader) readContinuedLineSlice(validateFirstLine func([]byte) error) ([]byte, error) {
126 if validateFirstLine == nil {
127 return nil, fmt.Errorf("missing validateFirstLine func")
128 }
129
130
131 line, err := r.readLineSlice()
132 if err != nil {
133 return nil, err
134 }
135 if len(line) == 0 {
136 return line, nil
137 }
138
139 if err := validateFirstLine(line); err != nil {
140 return nil, err
141 }
142
143
144
145
146
147 if r.R.Buffered() > 1 {
148 peek, _ := r.R.Peek(2)
149 if len(peek) > 0 && (isASCIILetter(peek[0]) || peek[0] == '\n') ||
150 len(peek) == 2 && peek[0] == '\r' && peek[1] == '\n' {
151 return trim(line), nil
152 }
153 }
154
155
156
157 r.buf = append(r.buf[:0], trim(line)...)
158
159
160 for r.skipSpace() > 0 {
161 line, err := r.readLineSlice()
162 if err != nil {
163 break
164 }
165 r.buf = append(r.buf, ' ')
166 r.buf = append(r.buf, trim(line)...)
167 }
168 return r.buf, nil
169 }
170
171
172 func (r *Reader) skipSpace() int {
173 n := 0
174 for {
175 c, err := r.R.ReadByte()
176 if err != nil {
177
178 break
179 }
180 if c != ' ' && c != '\t' {
181 r.R.UnreadByte()
182 break
183 }
184 n++
185 }
186 return n
187 }
188
189 func (r *Reader) readCodeLine(expectCode int) (code int, continued bool, message string, err error) {
190 line, err := r.ReadLine()
191 if err != nil {
192 return
193 }
194 return parseCodeLine(line, expectCode)
195 }
196
197 func parseCodeLine(line string, expectCode int) (code int, continued bool, message string, err error) {
198 if len(line) < 4 || line[3] != ' ' && line[3] != '-' {
199 err = ProtocolError("short response: " + line)
200 return
201 }
202 continued = line[3] == '-'
203 code, err = strconv.Atoi(line[0:3])
204 if err != nil || code < 100 {
205 err = ProtocolError("invalid response code: " + line)
206 return
207 }
208 message = line[4:]
209 if 1 <= expectCode && expectCode < 10 && code/100 != expectCode ||
210 10 <= expectCode && expectCode < 100 && code/10 != expectCode ||
211 100 <= expectCode && expectCode < 1000 && code != expectCode {
212 err = &Error{code, message}
213 }
214 return
215 }
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234 func (r *Reader) ReadCodeLine(expectCode int) (code int, message string, err error) {
235 code, continued, message, err := r.readCodeLine(expectCode)
236 if err == nil && continued {
237 err = ProtocolError("unexpected multi-line response: " + message)
238 }
239 return
240 }
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268 func (r *Reader) ReadResponse(expectCode int) (code int, message string, err error) {
269 code, continued, message, err := r.readCodeLine(expectCode)
270 multi := continued
271 for continued {
272 line, err := r.ReadLine()
273 if err != nil {
274 return 0, "", err
275 }
276
277 var code2 int
278 var moreMessage string
279 code2, continued, moreMessage, err = parseCodeLine(line, 0)
280 if err != nil || code2 != code {
281 message += "\n" + strings.TrimRight(line, "\r\n")
282 continued = true
283 continue
284 }
285 message += "\n" + moreMessage
286 }
287 if err != nil && multi && message != "" {
288
289 err = &Error{code, message}
290 }
291 return
292 }
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310 func (r *Reader) DotReader() io.Reader {
311 r.closeDot()
312 r.dot = &dotReader{r: r}
313 return r.dot
314 }
315
316 type dotReader struct {
317 r *Reader
318 state int
319 }
320
321
322 func (d *dotReader) Read(b []byte) (n int, err error) {
323
324
325
326 const (
327 stateBeginLine = iota
328 stateDot
329 stateDotCR
330 stateCR
331 stateData
332 stateEOF
333 )
334 br := d.r.R
335 for n < len(b) && d.state != stateEOF {
336 var c byte
337 c, err = br.ReadByte()
338 if err != nil {
339 if err == io.EOF {
340 err = io.ErrUnexpectedEOF
341 }
342 break
343 }
344 switch d.state {
345 case stateBeginLine:
346 if c == '.' {
347 d.state = stateDot
348 continue
349 }
350 if c == '\r' {
351 d.state = stateCR
352 continue
353 }
354 d.state = stateData
355
356 case stateDot:
357 if c == '\r' {
358 d.state = stateDotCR
359 continue
360 }
361 if c == '\n' {
362 d.state = stateEOF
363 continue
364 }
365 d.state = stateData
366
367 case stateDotCR:
368 if c == '\n' {
369 d.state = stateEOF
370 continue
371 }
372
373
374 br.UnreadByte()
375 c = '\r'
376 d.state = stateData
377
378 case stateCR:
379 if c == '\n' {
380 d.state = stateBeginLine
381 break
382 }
383
384 br.UnreadByte()
385 c = '\r'
386 d.state = stateData
387
388 case stateData:
389 if c == '\r' {
390 d.state = stateCR
391 continue
392 }
393 if c == '\n' {
394 d.state = stateBeginLine
395 }
396 }
397 b[n] = c
398 n++
399 }
400 if err == nil && d.state == stateEOF {
401 err = io.EOF
402 }
403 if err != nil && d.r.dot == d {
404 d.r.dot = nil
405 }
406 return
407 }
408
409
410
411 func (r *Reader) closeDot() {
412 if r.dot == nil {
413 return
414 }
415 buf := make([]byte, 128)
416 for r.dot != nil {
417
418
419 r.dot.Read(buf)
420 }
421 }
422
423
424
425
426 func (r *Reader) ReadDotBytes() ([]byte, error) {
427 return io.ReadAll(r.DotReader())
428 }
429
430
431
432
433
434 func (r *Reader) ReadDotLines() ([]string, error) {
435
436
437
438 var v []string
439 var err error
440 for {
441 var line string
442 line, err = r.ReadLine()
443 if err != nil {
444 if err == io.EOF {
445 err = io.ErrUnexpectedEOF
446 }
447 break
448 }
449
450
451 if len(line) > 0 && line[0] == '.' {
452 if len(line) == 1 {
453 break
454 }
455 line = line[1:]
456 }
457 v = append(v, line)
458 }
459 return v, err
460 }
461
462 var colon = []byte(":")
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483 func (r *Reader) ReadMIMEHeader() (MIMEHeader, error) {
484
485
486
487 var strs []string
488 hint := r.upcomingHeaderNewlines()
489 if hint > 0 {
490 strs = make([]string, hint)
491 }
492
493 m := make(MIMEHeader, hint)
494
495
496 if buf, err := r.R.Peek(1); err == nil && (buf[0] == ' ' || buf[0] == '\t') {
497 line, err := r.readLineSlice()
498 if err != nil {
499 return m, err
500 }
501 return m, ProtocolError("malformed MIME header initial line: " + string(line))
502 }
503
504 for {
505 kv, err := r.readContinuedLineSlice(mustHaveFieldNameColon)
506 if len(kv) == 0 {
507 return m, err
508 }
509
510
511 k, v, ok := bytes.Cut(kv, colon)
512 if !ok {
513 return m, ProtocolError("malformed MIME header line: " + string(kv))
514 }
515 key := canonicalMIMEHeaderKey(k)
516
517
518
519
520 if key == "" {
521 continue
522 }
523
524
525 value := strings.TrimLeft(string(v), " \t")
526
527 vv := m[key]
528 if vv == nil && len(strs) > 0 {
529
530
531
532
533 vv, strs = strs[:1:1], strs[1:]
534 vv[0] = value
535 m[key] = vv
536 } else {
537 m[key] = append(vv, value)
538 }
539
540 if err != nil {
541 return m, err
542 }
543 }
544 }
545
546
547
548 func noValidation(_ []byte) error { return nil }
549
550
551
552
553 func mustHaveFieldNameColon(line []byte) error {
554 if bytes.IndexByte(line, ':') < 0 {
555 return ProtocolError(fmt.Sprintf("malformed MIME header: missing colon: %q", line))
556 }
557 return nil
558 }
559
560 var nl = []byte("\n")
561
562
563
564 func (r *Reader) upcomingHeaderNewlines() (n int) {
565
566 r.R.Peek(1)
567 s := r.R.Buffered()
568 if s == 0 {
569 return
570 }
571 peek, _ := r.R.Peek(s)
572 return bytes.Count(peek, nl)
573 }
574
575
576
577
578
579
580
581
582
583 func CanonicalMIMEHeaderKey(s string) string {
584
585 upper := true
586 for i := 0; i < len(s); i++ {
587 c := s[i]
588 if !validHeaderFieldByte(c) {
589 return s
590 }
591 if upper && 'a' <= c && c <= 'z' {
592 return canonicalMIMEHeaderKey([]byte(s))
593 }
594 if !upper && 'A' <= c && c <= 'Z' {
595 return canonicalMIMEHeaderKey([]byte(s))
596 }
597 upper = c == '-'
598 }
599 return s
600 }
601
602 const toLower = 'a' - 'A'
603
604
605
606
607
608
609
610
611
612 func validHeaderFieldByte(b byte) bool {
613 return int(b) < len(isTokenTable) && isTokenTable[b]
614 }
615
616
617
618
619
620
621
622 func canonicalMIMEHeaderKey(a []byte) string {
623
624 for _, c := range a {
625 if validHeaderFieldByte(c) {
626 continue
627 }
628
629 return string(a)
630 }
631
632 upper := true
633 for i, c := range a {
634
635
636
637
638 if upper && 'a' <= c && c <= 'z' {
639 c -= toLower
640 } else if !upper && 'A' <= c && c <= 'Z' {
641 c += toLower
642 }
643 a[i] = c
644 upper = c == '-'
645 }
646 commonHeaderOnce.Do(initCommonHeader)
647
648
649
650 if v := commonHeader[string(a)]; v != "" {
651 return v
652 }
653 return string(a)
654 }
655
656
657 var commonHeader map[string]string
658
659 var commonHeaderOnce sync.Once
660
661 func initCommonHeader() {
662 commonHeader = make(map[string]string)
663 for _, v := range []string{
664 "Accept",
665 "Accept-Charset",
666 "Accept-Encoding",
667 "Accept-Language",
668 "Accept-Ranges",
669 "Cache-Control",
670 "Cc",
671 "Connection",
672 "Content-Id",
673 "Content-Language",
674 "Content-Length",
675 "Content-Transfer-Encoding",
676 "Content-Type",
677 "Cookie",
678 "Date",
679 "Dkim-Signature",
680 "Etag",
681 "Expires",
682 "From",
683 "Host",
684 "If-Modified-Since",
685 "If-None-Match",
686 "In-Reply-To",
687 "Last-Modified",
688 "Location",
689 "Message-Id",
690 "Mime-Version",
691 "Pragma",
692 "Received",
693 "Return-Path",
694 "Server",
695 "Set-Cookie",
696 "Subject",
697 "To",
698 "User-Agent",
699 "Via",
700 "X-Forwarded-For",
701 "X-Imforwards",
702 "X-Powered-By",
703 } {
704 commonHeader[v] = v
705 }
706 }
707
708
709
710 var isTokenTable = [127]bool{
711 '!': true,
712 '#': true,
713 '$': true,
714 '%': true,
715 '&': true,
716 '\'': true,
717 '*': true,
718 '+': true,
719 '-': true,
720 '.': true,
721 '0': true,
722 '1': true,
723 '2': true,
724 '3': true,
725 '4': true,
726 '5': true,
727 '6': true,
728 '7': true,
729 '8': true,
730 '9': true,
731 'A': true,
732 'B': true,
733 'C': true,
734 'D': true,
735 'E': true,
736 'F': true,
737 'G': true,
738 'H': true,
739 'I': true,
740 'J': true,
741 'K': true,
742 'L': true,
743 'M': true,
744 'N': true,
745 'O': true,
746 'P': true,
747 'Q': true,
748 'R': true,
749 'S': true,
750 'T': true,
751 'U': true,
752 'W': true,
753 'V': true,
754 'X': true,
755 'Y': true,
756 'Z': true,
757 '^': true,
758 '_': true,
759 '`': true,
760 'a': true,
761 'b': true,
762 'c': true,
763 'd': true,
764 'e': true,
765 'f': true,
766 'g': true,
767 'h': true,
768 'i': true,
769 'j': true,
770 'k': true,
771 'l': true,
772 'm': true,
773 'n': true,
774 'o': true,
775 'p': true,
776 'q': true,
777 'r': true,
778 's': true,
779 't': true,
780 'u': true,
781 'v': true,
782 'w': true,
783 'x': true,
784 'y': true,
785 'z': true,
786 '|': true,
787 '~': true,
788 }
789
View as plain text