@@ -424,11 +424,75 @@ func ReadHeader(r *bufio.Reader) (Header, error) {
424
424
}
425
425
}
426
426
427
- const maxHeaderLen = 76
428
-
429
427
// Regexp that detects Quoted Printable (QP) characters
430
428
var qpReg = regexp .MustCompile ("(=[0-9A-Z]{2,2})+" )
431
429
430
+ func foldLine (v string , maxlen int ) (line , next string , ok bool ) {
431
+ ok = true
432
+
433
+ // We'll need to fold before maxlen
434
+ foldBefore := maxlen + 1
435
+ foldAt := len (v )
436
+
437
+ var folding string
438
+ if foldBefore > len (v ) {
439
+ // We reached the end of the string
440
+ if v [len (v )- 1 ] != '\n' {
441
+ // If there isn't already a trailing CRLF, insert one
442
+ folding = "\r \n "
443
+ }
444
+ } else {
445
+ // Find the last QP character before limit
446
+ foldAtQP := qpReg .FindAllStringIndex (v [:foldBefore ], - 1 )
447
+ // Find the closest whitespace before maxlen
448
+ foldAtEOL := strings .LastIndexAny (v [:foldBefore ], " \t \n " )
449
+
450
+ // Fold at the latest whitespace by default
451
+ foldAt = foldAtEOL
452
+
453
+ // if there are QP characters in the string
454
+ if len (foldAtQP ) > 0 {
455
+ // Get the start index of the last QP character
456
+ foldAtQPLastIndex := foldAtQP [len (foldAtQP )- 1 ][0 ]
457
+ if foldAtQPLastIndex > foldAt {
458
+ // Fold at the latest QP character if there are no whitespaces
459
+ // after it and before line length limit
460
+ foldAt = foldAtQPLastIndex
461
+ }
462
+ }
463
+
464
+ if foldAt == 0 {
465
+ // The whitespace we found was the previous folding WSP
466
+ foldAt = foldBefore - 1
467
+ } else if foldAt < 0 {
468
+ // We didn't find any whitespace, we have to insert one
469
+ foldAt = foldBefore - 2
470
+ }
471
+
472
+ switch v [foldAt ] {
473
+ case ' ' , '\t' :
474
+ if v [foldAt - 1 ] != '\n' {
475
+ folding = "\r \n " // The next char will be a WSP, don't need to insert one
476
+ }
477
+ case '\n' :
478
+ folding = "" // There is already a CRLF, nothing to do
479
+ default :
480
+ // Another char, we need to insert CRLF + WSP. This will insert an
481
+ // extra space in the string, so this should be avoided if
482
+ // possible.
483
+ folding = "\r \n "
484
+ ok = len (foldAtQP ) > 0
485
+ }
486
+ }
487
+
488
+ return v [:foldAt ] + folding , v [foldAt :], ok
489
+ }
490
+
491
+ const (
492
+ preferredHeaderLen = 76
493
+ maxHeaderLen = 998
494
+ )
495
+
432
496
// formatHeaderField formats a header field, ensuring each line is no longer
433
497
// than 76 characters. It tries to fold lines at whitespace characters if
434
498
// possible. If the header contains a word longer than this limit, it will be
@@ -442,63 +506,21 @@ func formatHeaderField(k, v string) string {
442
506
443
507
first := true
444
508
for len (v ) > 0 {
445
- maxlen := maxHeaderLen
509
+ // If this is the first line, substract the length of the key
510
+ keylen := 0
446
511
if first {
447
- maxlen - = len (s )
512
+ keylen = len (s )
448
513
}
449
514
450
- // We'll need to fold before i
451
- foldBefore := maxlen + 1
452
- foldAt := len (v )
453
-
454
- var folding string
455
- if foldBefore > len (v ) {
456
- // We reached the end of the string
457
- if v [len (v )- 1 ] != '\n' {
458
- // If there isn't already a trailing CRLF, insert one
459
- folding = "\r \n "
460
- }
461
- } else {
462
- // Find the last QP character before limit
463
- foldAtQP := qpReg .FindAllStringIndex (v [:foldBefore ], - 1 )
464
- // Find the closest whitespace before i
465
- foldAtEOL := strings .LastIndexAny (v [:foldBefore ], " \t \n " )
466
-
467
- // Fold at the latest whitespace by default
468
- foldAt = foldAtEOL
469
-
470
- // if there are QP characters in the string
471
- if len (foldAtQP ) > 0 {
472
- // Get the start index of the last QP character
473
- foldAtQPLastIndex := foldAtQP [len (foldAtQP )- 1 ][0 ]
474
- if foldAtQPLastIndex > foldAt {
475
- // Fold at the latest QP character if there are no whitespaces after it and before line hard limit
476
- foldAt = foldAtQPLastIndex
477
- }
478
- }
479
-
480
- if foldAt == 0 {
481
- // The whitespace we found was the previous folding WSP
482
- foldAt = foldBefore - 1
483
- } else if foldAt < 0 {
484
- // We didn't find any whitespace, we have to insert one
485
- foldAt = foldBefore - 2
486
- }
487
-
488
- switch v [foldAt ] {
489
- case ' ' , '\t' :
490
- if v [foldAt - 1 ] != '\n' {
491
- folding = "\r \n " // The next char will be a WSP, don't need to insert one
492
- }
493
- case '\n' :
494
- folding = "" // There is already a CRLF, nothing to do
495
- default :
496
- folding = "\r \n " // Another char, we need to insert CRLF + WSP
497
- }
515
+ // First try with a soft limit
516
+ l , next , ok := foldLine (v , preferredHeaderLen - keylen )
517
+ if ! ok {
518
+ // Folding failed to preserve the original header field value. Try
519
+ // with a larger, hard limit.
520
+ l , next , _ = foldLine (v , maxHeaderLen - keylen )
498
521
}
499
-
500
- s += v [:foldAt ] + folding
501
- v = v [foldAt :]
522
+ v = next
523
+ s += l
502
524
first = false
503
525
}
504
526
0 commit comments