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