Source file
src/net/mail/message_test.go
1
2
3
4
5 package mail
6
7 import (
8 "bytes"
9 "io"
10 "mime"
11 "reflect"
12 "strings"
13 "testing"
14 "time"
15 )
16
17 var parseTests = []struct {
18 in string
19 header Header
20 body string
21 }{
22 {
23
24 in: `From: John Doe <[email protected]>
25 To: Mary Smith <[email protected]>
26 Subject: Saying Hello
27 Date: Fri, 21 Nov 1997 09:55:06 -0600
28 Message-ID: <[email protected]>
29
30 This is a message just to say hello.
31 So, "Hello".
32 `,
33 header: Header{
34 "From": []string{"John Doe <[email protected]>"},
35 "To": []string{"Mary Smith <[email protected]>"},
36 "Subject": []string{"Saying Hello"},
37 "Date": []string{"Fri, 21 Nov 1997 09:55:06 -0600"},
38 "Message-Id": []string{"<[email protected]>"},
39 },
40 body: "This is a message just to say hello.\nSo, \"Hello\".\n",
41 },
42 {
43
44 in: `Feedback-Type: abuse
45 User-Agent: SomeGenerator/1.0
46 Version: 1
47 `,
48 header: Header{
49 "Feedback-Type": []string{"abuse"},
50 "User-Agent": []string{"SomeGenerator/1.0"},
51 "Version": []string{"1"},
52 },
53 body: "",
54 },
55 {
56
57
58 in: `From: [email protected]
59 Custom/Header: v
60
61 Body
62 `,
63 header: Header{
64 "From": []string{"[email protected]"},
65 "Custom/Header": []string{"v"},
66 },
67 body: "Body\n",
68 },
69 {
70
71
72 in: `From [email protected] Mon Jun 19 00:00:00 2023
73 From: [email protected]
74
75 Hello, gophers!
76 `,
77 header: Header{
78 "From": []string{"[email protected]"},
79 "From [email protected] Mon Jun 19 00": []string{"00:00 2023"},
80 },
81 body: "Hello, gophers!\n",
82 },
83 }
84
85 func TestParsing(t *testing.T) {
86 for i, test := range parseTests {
87 msg, err := ReadMessage(bytes.NewBuffer([]byte(test.in)))
88 if err != nil {
89 t.Errorf("test #%d: Failed parsing message: %v", i, err)
90 continue
91 }
92 if !headerEq(msg.Header, test.header) {
93 t.Errorf("test #%d: Incorrectly parsed message header.\nGot:\n%+v\nWant:\n%+v",
94 i, msg.Header, test.header)
95 }
96 body, err := io.ReadAll(msg.Body)
97 if err != nil {
98 t.Errorf("test #%d: Failed reading body: %v", i, err)
99 continue
100 }
101 bodyStr := string(body)
102 if bodyStr != test.body {
103 t.Errorf("test #%d: Incorrectly parsed message body.\nGot:\n%+v\nWant:\n%+v",
104 i, bodyStr, test.body)
105 }
106 }
107 }
108
109 func headerEq(a, b Header) bool {
110 if len(a) != len(b) {
111 return false
112 }
113 for k, as := range a {
114 bs, ok := b[k]
115 if !ok {
116 return false
117 }
118 if !reflect.DeepEqual(as, bs) {
119 return false
120 }
121 }
122 return true
123 }
124
125 func TestDateParsing(t *testing.T) {
126 tests := []struct {
127 dateStr string
128 exp time.Time
129 }{
130
131 {
132 "Fri, 21 Nov 1997 09:55:06 -0600",
133 time.Date(1997, 11, 21, 9, 55, 6, 0, time.FixedZone("", -6*60*60)),
134 },
135
136
137 {
138 "21 Nov 97 09:55:06 GMT",
139 time.Date(1997, 11, 21, 9, 55, 6, 0, time.FixedZone("GMT", 0)),
140 },
141
142 {
143 "Fri, 21 Nov 1997 09:55:06 -0600 (MDT)",
144 time.Date(1997, 11, 21, 9, 55, 6, 0, time.FixedZone("", -6*60*60)),
145 },
146 {
147 "Thu, 20 Nov 1997 09:55:06 -0600 (MDT)",
148 time.Date(1997, 11, 20, 9, 55, 6, 0, time.FixedZone("", -6*60*60)),
149 },
150 {
151 "Thu, 20 Nov 1997 09:55:06 GMT (GMT)",
152 time.Date(1997, 11, 20, 9, 55, 6, 0, time.UTC),
153 },
154 {
155 "Fri, 21 Nov 1997 09:55:06 +1300 (TOT)",
156 time.Date(1997, 11, 21, 9, 55, 6, 0, time.FixedZone("", +13*60*60)),
157 },
158 }
159 for _, test := range tests {
160 hdr := Header{
161 "Date": []string{test.dateStr},
162 }
163 date, err := hdr.Date()
164 if err != nil {
165 t.Errorf("Header(Date: %s).Date(): %v", test.dateStr, err)
166 } else if !date.Equal(test.exp) {
167 t.Errorf("Header(Date: %s).Date() = %+v, want %+v", test.dateStr, date, test.exp)
168 }
169
170 date, err = ParseDate(test.dateStr)
171 if err != nil {
172 t.Errorf("ParseDate(%s): %v", test.dateStr, err)
173 } else if !date.Equal(test.exp) {
174 t.Errorf("ParseDate(%s) = %+v, want %+v", test.dateStr, date, test.exp)
175 }
176 }
177 }
178
179 func TestDateParsingCFWS(t *testing.T) {
180 tests := []struct {
181 dateStr string
182 exp time.Time
183 valid bool
184 }{
185
186 {
187 " ",
188
189 time.Date(1997, 11, 21, 9, 55, 6, 0, time.FixedZone("", -6*60*60)),
190 false,
191 },
192
193 {
194 " Fri, 21 Nov 1997 09:55:06 -0600",
195 time.Date(1997, 11, 21, 9, 55, 6, 0, time.FixedZone("", -6*60*60)),
196 true,
197 },
198 {
199 "21 Nov 1997 09:55:06 -0600",
200 time.Date(1997, 11, 21, 9, 55, 6, 0, time.FixedZone("", -6*60*60)),
201 true,
202 },
203 {
204 "Fri 21 Nov 1997 09:55:06 -0600",
205 time.Date(1997, 11, 21, 9, 55, 6, 0, time.FixedZone("", -6*60*60)),
206 false,
207 },
208
209 {
210 "Fri, 21 Nov 1997 09:55:06 -0600",
211 time.Date(1997, 11, 21, 9, 55, 6, 0, time.FixedZone("", -6*60*60)),
212 true,
213 },
214
215 {
216 "Fri, 21 Nov 1997 09:55:06 -0600",
217 time.Date(1997, 11, 21, 9, 55, 6, 0, time.FixedZone("", -6*60*60)),
218 true,
219 },
220
221 {
222 "Fri, 21 Nov 1997 09:55:06 CST",
223 time.Time{},
224 true,
225 },
226
227 {
228 "Fri, 21 Nov 1997 09:55:06 CST (no leading FWS and a trailing CRLF) \r\n",
229 time.Time{},
230 true,
231 },
232
233 {
234 "Fri, 21 Nov 1997 09:55:06 -0600 (MDT and non-US-ASCII signs éèç )",
235 time.Date(1997, 11, 21, 9, 55, 6, 0, time.FixedZone("", -6*60*60)),
236 true,
237 },
238
239
240 {
241 "Fri, 21 Nov 1997 09:55:06 -0600 \r\n (thisisa(valid)cfws) \t ",
242 time.Date(1997, 11, 21, 9, 55, 6, 0, time.FixedZone("", -6*60*60)),
243 true,
244 },
245
246 {
247 "Fri, 21 Nov 1997 \r 09:55:06 -0600 \r\n (thisisa(valid)cfws) \t ",
248 time.Date(1997, 11, 21, 9, 55, 6, 0, time.FixedZone("", -6*60*60)),
249 false,
250 },
251
252 {
253 "Fri, 21 Nov 199\r\n7 09:55:06 -0600 \r\n (thisisa(valid)cfws) \t ",
254 time.Date(1997, 11, 21, 9, 55, 6, 0, time.FixedZone("", -6*60*60)),
255 true,
256 },
257
258 {
259 "Fri, 21 Nov 1997 ù 09:55:06 -0600 \r\n (thisisa(valid)cfws) \t ",
260 time.Date(1997, 11, 21, 9, 55, 6, 0, time.FixedZone("", -6*60*60)),
261 false,
262 },
263
264 {
265 "Fri, 21 Nov () 1997 09:55:06 -0600 \r\n (thisisa(valid)cfws) \t ",
266 time.Date(1997, 11, 21, 9, 55, 6, 0, time.FixedZone("", -6*60*60)),
267 false,
268 },
269
270 {
271 "Fri, 21 Nov 1997 09:55:06 -060 \r\n (Thisisa(valid)cfws) \t ",
272 time.Date(1997, 11, 21, 9, 55, 6, 0, time.FixedZone("", -6*60*60)),
273 false,
274 },
275
276 {
277 "Fri, 21 1997 09:55:06 -0600",
278 time.Date(1997, 11, 21, 9, 55, 6, 0, time.FixedZone("", -6*60*60)),
279 false,
280 },
281
282 {
283 "Fri, 21 OCT 1997 09:55:06 CST",
284 time.Time{},
285 false,
286 },
287
288 {
289 "Fri, 21 Nov 1997 09:55:06 -060",
290 time.Date(1997, 11, 21, 9, 55, 6, 0, time.FixedZone("", -6*60*60)),
291 false,
292 },
293
294 {
295 "Fri, 21 1997 09:55:06 GT",
296 time.Date(1997, 11, 21, 9, 55, 6, 0, time.FixedZone("", -6*60*60)),
297 false,
298 },
299
300
301 {
302 "Tue, 26 May 2020 14:04:40 GMT",
303 time.Date(2020, 05, 26, 14, 04, 40, 0, time.UTC),
304 true,
305 },
306 {
307 "Tue, 26 May 2020 14:04:40 UT",
308 time.Date(2020, 05, 26, 14, 04, 40, 0, time.UTC),
309 true,
310 },
311 {
312 "Thu, 21 May 2020 14:04:40 UT",
313 time.Date(2020, 05, 21, 14, 04, 40, 0, time.UTC),
314 true,
315 },
316 {
317 "Tue, 26 May 2020 14:04:40 XT",
318 time.Date(2020, 05, 26, 14, 04, 40, 0, time.UTC),
319 false,
320 },
321 {
322 "Thu, 21 May 2020 14:04:40 XT",
323 time.Date(2020, 05, 21, 14, 04, 40, 0, time.UTC),
324 false,
325 },
326 {
327 "Thu, 21 May 2020 14:04:40 UTC",
328 time.Date(2020, 05, 21, 14, 04, 40, 0, time.UTC),
329 true,
330 },
331 {
332 "Fri, 21 Nov 1997 09:55:06 GMT (GMT)",
333 time.Date(1997, 11, 21, 9, 55, 6, 0, time.UTC),
334 true,
335 },
336 }
337 for _, test := range tests {
338 hdr := Header{
339 "Date": []string{test.dateStr},
340 }
341 date, err := hdr.Date()
342 if err != nil && test.valid {
343 t.Errorf("Header(Date: %s).Date(): %v", test.dateStr, err)
344 } else if err == nil && test.exp.IsZero() {
345
346
347 } else if err == nil && !date.Equal(test.exp) && test.valid {
348 t.Errorf("Header(Date: %s).Date() = %+v, want %+v", test.dateStr, date, test.exp)
349 } else if err == nil && !test.valid {
350 t.Errorf("Header(Date: %s).Date() did not return an error but %v", test.dateStr, date)
351 }
352
353 date, err = ParseDate(test.dateStr)
354 if err != nil && test.valid {
355 t.Errorf("ParseDate(%s): %v", test.dateStr, err)
356 } else if err == nil && test.exp.IsZero() {
357
358
359 } else if err == nil && !test.valid {
360 t.Errorf("ParseDate(%s) did not return an error but %v", test.dateStr, date)
361 } else if err == nil && test.valid && !date.Equal(test.exp) {
362 t.Errorf("ParseDate(%s) = %+v, want %+v", test.dateStr, date, test.exp)
363 }
364 }
365 }
366
367 func TestAddressParsingError(t *testing.T) {
368 mustErrTestCases := [...]struct {
369 text string
370 wantErrText string
371 }{
372 0: {"=?iso-8859-2?Q?Bogl=E1rka_Tak=E1cs?= <[email protected]>", "charset not supported"},
373 1: {"[email protected] [email protected]", "expected single address"},
374 2: {string([]byte{0xed, 0xa0, 0x80}) + " <[email protected]>", "invalid utf-8 in address"},
375 3: {"\"" + string([]byte{0xed, 0xa0, 0x80}) + "\" <[email protected]>", "invalid utf-8 in quoted-string"},
376 4: {"\"\\" + string([]byte{0x80}) + "\" <[email protected]>", "invalid utf-8 in quoted-string"},
377 5: {"\"\x00\" <[email protected]>", "bad character in quoted-string"},
378 6: {"\"\\\x00\" <[email protected]>", "bad character in quoted-string"},
379 7: {"John Doe", "no angle-addr"},
380 8: {`<jdoe#machine.example>`, "missing @ in addr-spec"},
381 9: {`John <middle> Doe <[email protected]>`, "missing @ in addr-spec"},
382 10: {"[email protected] (", "misformatted parenthetical comment"},
383 11: {"empty group: ;", "empty group"},
384 12: {"root group: embed group: [email protected];", "no angle-addr"},
385 13: {"group not closed: [email protected]", "expected comma"},
386 14: {"group: [email protected], [email protected];", "group with multiple addresses"},
387 15: {"john.doe", "missing '@' or angle-addr"},
388 16: {"john.doe@", "missing '@' or angle-addr"},
389 17: {"John [email protected]", "no angle-addr"},
390 18: {" group: [email protected]; (asd", "misformatted parenthetical comment"},
391 19: {" group: ; (asd", "misformatted parenthetical comment"},
392 20: {`(John) Doe <[email protected]>`, "missing word in phrase:"},
393 }
394
395 for i, tc := range mustErrTestCases {
396 _, err := ParseAddress(tc.text)
397 if err == nil || !strings.Contains(err.Error(), tc.wantErrText) {
398 t.Errorf(`mail.ParseAddress(%q) #%d want %q, got %v`, tc.text, i, tc.wantErrText, err)
399 }
400 }
401
402 t.Run("CustomWordDecoder", func(t *testing.T) {
403 p := &AddressParser{WordDecoder: &mime.WordDecoder{}}
404 for i, tc := range mustErrTestCases {
405 _, err := p.Parse(tc.text)
406 if err == nil || !strings.Contains(err.Error(), tc.wantErrText) {
407 t.Errorf(`p.Parse(%q) #%d want %q, got %v`, tc.text, i, tc.wantErrText, err)
408 }
409 }
410 })
411
412 }
413
414 func TestAddressParsing(t *testing.T) {
415 tests := []struct {
416 addrsStr string
417 exp []*Address
418 }{
419
420 {
421 `[email protected]`,
422 []*Address{{
423 Address: "[email protected]",
424 }},
425 },
426
427 {
428 `John Doe <[email protected]>`,
429 []*Address{{
430 Name: "John Doe",
431 Address: "[email protected]",
432 }},
433 },
434
435 {
436 `"Joe Q. Public" <[email protected]>`,
437 []*Address{{
438 Name: "Joe Q. Public",
439 Address: "[email protected]",
440 }},
441 },
442
443 {
444 `John (middle) Doe <[email protected]>`,
445 []*Address{{
446 Name: "John Doe",
447 Address: "[email protected]",
448 }},
449 },
450
451 {
452 `"John (middle) Doe" <[email protected]>`,
453 []*Address{{
454 Name: "John (middle) Doe",
455 Address: "[email protected]",
456 }},
457 },
458 {
459 `"John <middle> Doe" <[email protected]>`,
460 []*Address{{
461 Name: "John <middle> Doe",
462 Address: "[email protected]",
463 }},
464 },
465 {
466 `Mary Smith <[email protected]>, [email protected], Who? <[email protected]>`,
467 []*Address{
468 {
469 Name: "Mary Smith",
470 Address: "[email protected]",
471 },
472 {
473 Address: "[email protected]",
474 },
475 {
476 Name: "Who?",
477 Address: "[email protected]",
478 },
479 },
480 },
481 {
482 `<[email protected]>, "Giant; \"Big\" Box" <[email protected]>`,
483 []*Address{
484 {
485 Address: "[email protected]",
486 },
487 {
488 Name: `Giant; "Big" Box`,
489 Address: "[email protected]",
490 },
491 },
492 },
493
494 {
495 `Joe Q. Public <[email protected]>`,
496 []*Address{{
497 Name: "Joe Q. Public",
498 Address: "[email protected]",
499 }},
500 },
501
502 {
503 `group1: [email protected];`,
504 []*Address{
505 {
506 Name: "",
507 Address: "[email protected]",
508 },
509 },
510 },
511 {
512 `empty group: ;`,
513 []*Address(nil),
514 },
515 {
516 `A Group:Ed Jones <[email protected]>,[email protected],John <[email protected]>;`,
517 []*Address{
518 {
519 Name: "Ed Jones",
520 Address: "[email protected]",
521 },
522 {
523 Name: "",
524 Address: "[email protected]",
525 },
526 {
527 Name: "John",
528 Address: "[email protected]",
529 },
530 },
531 },
532
533 {
534 ` , [email protected],,John <[email protected]>,`,
535 []*Address{
536 {
537 Name: "",
538 Address: "[email protected]",
539 },
540 {
541 Name: "John",
542 Address: "[email protected]",
543 },
544 },
545 },
546 {
547 ` , [email protected],,John <[email protected]>,,`,
548 []*Address{
549 {
550 Name: "",
551 Address: "[email protected]",
552 },
553 {
554 Name: "John",
555 Address: "[email protected]",
556 },
557 },
558 },
559 {
560 `Group1: <[email protected]>;, Group 2: [email protected];, John <[email protected]>`,
561 []*Address{
562 {
563 Name: "",
564 Address: "[email protected]",
565 },
566 {
567 Name: "",
568 Address: "[email protected]",
569 },
570 {
571 Name: "John",
572 Address: "[email protected]",
573 },
574 },
575 },
576
577 {
578 `=?iso-8859-1?q?J=F6rg_Doe?= <[email protected]>`,
579 []*Address{
580 {
581 Name: `Jörg Doe`,
582 Address: "[email protected]",
583 },
584 },
585 },
586
587 {
588 `=?us-ascii?q?J=6Frg_Doe?= <[email protected]>`,
589 []*Address{
590 {
591 Name: `Jorg Doe`,
592 Address: "[email protected]",
593 },
594 },
595 },
596
597 {
598 `=?utf-8?q?J=C3=B6rg_Doe?= <[email protected]>`,
599 []*Address{
600 {
601 Name: `Jörg Doe`,
602 Address: "[email protected]",
603 },
604 },
605 },
606
607 {
608 `=?utf-8?q?J=C3=B6rg?= =?utf-8?q?Doe?= <[email protected]>`,
609 []*Address{
610 {
611 Name: `JörgDoe`,
612 Address: "[email protected]",
613 },
614 },
615 },
616
617 {
618 `=?ISO-8859-1?Q?Andr=E9?= Pirard <[email protected]>`,
619 []*Address{
620 {
621 Name: `André Pirard`,
622 Address: "[email protected]",
623 },
624 },
625 },
626
627 {
628 `=?ISO-8859-1?B?SvZyZw==?= <[email protected]>`,
629 []*Address{
630 {
631 Name: `Jörg`,
632 Address: "[email protected]",
633 },
634 },
635 },
636
637 {
638 `=?UTF-8?B?SsO2cmc=?= <[email protected]>`,
639 []*Address{
640 {
641 Name: `Jörg`,
642 Address: "[email protected]",
643 },
644 },
645 },
646
647 {
648 `Asem H. <[email protected]>`,
649 []*Address{
650 {
651 Name: `Asem H.`,
652 Address: "[email protected]",
653 },
654 },
655 },
656
657 {
658 `"Gø Pher" <[email protected]>`,
659 []*Address{
660 {
661 Name: `Gø Pher`,
662 Address: "[email protected]",
663 },
664 },
665 },
666
667 {
668 `µ <[email protected]>`,
669 []*Address{
670 {
671 Name: `µ`,
672 Address: "[email protected]",
673 },
674 },
675 },
676
677 {
678 `Micro <µ@example.com>`,
679 []*Address{
680 {
681 Name: `Micro`,
682 Address: "µ@example.com",
683 },
684 },
685 },
686
687 {
688 `Micro <micro@µ.example.com>`,
689 []*Address{
690 {
691 Name: `Micro`,
692 Address: "micro@µ.example.com",
693 },
694 },
695 },
696
697 {
698 `"" <[email protected]>`,
699 []*Address{
700 {
701 Name: "",
702 Address: "[email protected]",
703 },
704 },
705 },
706
707 {
708 `<[email protected]> (CFWS (cfws)) (another comment)`,
709 []*Address{
710 {
711 Name: "",
712 Address: "[email protected]",
713 },
714 },
715 },
716 {
717 `<[email protected]> () (another comment), <[email protected]> (another)`,
718 []*Address{
719 {
720 Name: "",
721 Address: "[email protected]",
722 },
723 {
724 Name: "",
725 Address: "[email protected]",
726 },
727 },
728 },
729
730 {
731 `[email protected] (John Doe)`,
732 []*Address{
733 {
734 Name: "John Doe",
735 Address: "[email protected]",
736 },
737 },
738 },
739
740 {
741 `John Doe <[email protected]> (Joey)`,
742 []*Address{
743 {
744 Name: "John Doe",
745 Address: "[email protected]",
746 },
747 },
748 },
749
750 {
751 `[email protected](John Doe)`,
752 []*Address{
753 {
754 Name: "John Doe",
755 Address: "[email protected]",
756 },
757 },
758 },
759
760 {
761 `[email protected] (Adam =?utf-8?Q?Sj=C3=B8gren?=)`,
762 []*Address{
763 {
764 Name: "Adam Sjøgren",
765 Address: "[email protected]",
766 },
767 },
768 },
769
770 {
771 `[email protected] (Adam =?utf-8?Q?Sj=C3=B8gren?=)`,
772 []*Address{
773 {
774 Name: "Adam Sjøgren",
775 Address: "[email protected]",
776 },
777 },
778 },
779
780 {
781 `[email protected] (Adam =?utf-8?Q?Sj=C3=B8gren?= (Debian))`,
782 []*Address{
783 {
784 Name: "Adam Sjøgren (Debian)",
785 Address: "[email protected]",
786 },
787 },
788 },
789
790 {
791 `group (comment:): [email protected], [email protected];`,
792 []*Address{
793 {
794 Address: "[email protected]",
795 },
796 {
797 Address: "[email protected]",
798 },
799 },
800 },
801 {
802 `x(:"):"@a.example;("@b.example;`,
803 []*Address{
804 {
805 Address: `@a.example;(@b.example`,
806 },
807 },
808 },
809 }
810 for _, test := range tests {
811 if len(test.exp) == 1 {
812 addr, err := ParseAddress(test.addrsStr)
813 if err != nil {
814 t.Errorf("Failed parsing (single) %q: %v", test.addrsStr, err)
815 continue
816 }
817 if !reflect.DeepEqual([]*Address{addr}, test.exp) {
818 t.Errorf("Parse (single) of %q: got %+v, want %+v", test.addrsStr, addr, test.exp)
819 }
820 }
821
822 addrs, err := ParseAddressList(test.addrsStr)
823 if err != nil {
824 t.Errorf("Failed parsing (list) %q: %v", test.addrsStr, err)
825 continue
826 }
827 if !reflect.DeepEqual(addrs, test.exp) {
828 t.Errorf("Parse (list) of %q: got %+v, want %+v", test.addrsStr, addrs, test.exp)
829 }
830 }
831 }
832
833 func TestAddressParser(t *testing.T) {
834 tests := []struct {
835 addrsStr string
836 exp []*Address
837 }{
838
839 {
840 `[email protected]`,
841 []*Address{{
842 Address: "[email protected]",
843 }},
844 },
845
846 {
847 `John Doe <[email protected]>`,
848 []*Address{{
849 Name: "John Doe",
850 Address: "[email protected]",
851 }},
852 },
853
854 {
855 `"Joe Q. Public" <[email protected]>`,
856 []*Address{{
857 Name: "Joe Q. Public",
858 Address: "[email protected]",
859 }},
860 },
861 {
862 `Mary Smith <[email protected]>, [email protected], Who? <[email protected]>`,
863 []*Address{
864 {
865 Name: "Mary Smith",
866 Address: "[email protected]",
867 },
868 {
869 Address: "[email protected]",
870 },
871 {
872 Name: "Who?",
873 Address: "[email protected]",
874 },
875 },
876 },
877 {
878 `<[email protected]>, "Giant; \"Big\" Box" <[email protected]>`,
879 []*Address{
880 {
881 Address: "[email protected]",
882 },
883 {
884 Name: `Giant; "Big" Box`,
885 Address: "[email protected]",
886 },
887 },
888 },
889
890 {
891 `=?iso-8859-1?q?J=F6rg_Doe?= <[email protected]>`,
892 []*Address{
893 {
894 Name: `Jörg Doe`,
895 Address: "[email protected]",
896 },
897 },
898 },
899
900 {
901 `=?us-ascii?q?J=6Frg_Doe?= <[email protected]>`,
902 []*Address{
903 {
904 Name: `Jorg Doe`,
905 Address: "[email protected]",
906 },
907 },
908 },
909
910 {
911 `=?ISO-8859-15?Q?J=F6rg_Doe?= <[email protected]>`,
912 []*Address{
913 {
914 Name: `Jörg Doe`,
915 Address: "[email protected]",
916 },
917 },
918 },
919
920 {
921 `=?windows-1252?q?Andr=E9?= Pirard <[email protected]>`,
922 []*Address{
923 {
924 Name: `André Pirard`,
925 Address: "[email protected]",
926 },
927 },
928 },
929
930 {
931 `=?ISO-8859-15?B?SvZyZw==?= <[email protected]>`,
932 []*Address{
933 {
934 Name: `Jörg`,
935 Address: "[email protected]",
936 },
937 },
938 },
939
940 {
941 `=?UTF-8?B?SsO2cmc=?= <[email protected]>`,
942 []*Address{
943 {
944 Name: `Jörg`,
945 Address: "[email protected]",
946 },
947 },
948 },
949
950 {
951 `Asem H. <[email protected]>`,
952 []*Address{
953 {
954 Name: `Asem H.`,
955 Address: "[email protected]",
956 },
957 },
958 },
959 }
960
961 ap := AddressParser{WordDecoder: &mime.WordDecoder{
962 CharsetReader: func(charset string, input io.Reader) (io.Reader, error) {
963 in, err := io.ReadAll(input)
964 if err != nil {
965 return nil, err
966 }
967
968 switch charset {
969 case "iso-8859-15":
970 in = bytes.ReplaceAll(in, []byte("\xf6"), []byte("ö"))
971 case "windows-1252":
972 in = bytes.ReplaceAll(in, []byte("\xe9"), []byte("é"))
973 }
974
975 return bytes.NewReader(in), nil
976 },
977 }}
978
979 for _, test := range tests {
980 if len(test.exp) == 1 {
981 addr, err := ap.Parse(test.addrsStr)
982 if err != nil {
983 t.Errorf("Failed parsing (single) %q: %v", test.addrsStr, err)
984 continue
985 }
986 if !reflect.DeepEqual([]*Address{addr}, test.exp) {
987 t.Errorf("Parse (single) of %q: got %+v, want %+v", test.addrsStr, addr, test.exp)
988 }
989 }
990
991 addrs, err := ap.ParseList(test.addrsStr)
992 if err != nil {
993 t.Errorf("Failed parsing (list) %q: %v", test.addrsStr, err)
994 continue
995 }
996 if !reflect.DeepEqual(addrs, test.exp) {
997 t.Errorf("Parse (list) of %q: got %+v, want %+v", test.addrsStr, addrs, test.exp)
998 }
999 }
1000 }
1001
1002 func TestAddressString(t *testing.T) {
1003 tests := []struct {
1004 addr *Address
1005 exp string
1006 }{
1007 {
1008 &Address{Address: "[email protected]"},
1009 "<[email protected]>",
1010 },
1011 {
1012 &Address{Address: `my@idiot@[email protected]`},
1013 `<"my@idiot@address"@example.com>`,
1014 },
1015 {
1016 &Address{Address: ` @example.com`},
1017 `<" "@example.com>`,
1018 },
1019 {
1020 &Address{Name: "Bob", Address: "[email protected]"},
1021 `"Bob" <[email protected]>`,
1022 },
1023 {
1024
1025 &Address{Name: "Böb", Address: "[email protected]"},
1026 `=?utf-8?q?B=C3=B6b?= <[email protected]>`,
1027 },
1028 {
1029 &Address{Name: "Bob Jane", Address: "[email protected]"},
1030 `"Bob Jane" <[email protected]>`,
1031 },
1032 {
1033 &Address{Name: "Böb Jacöb", Address: "[email protected]"},
1034 `=?utf-8?q?B=C3=B6b_Jac=C3=B6b?= <[email protected]>`,
1035 },
1036 {
1037 &Address{Name: "Rob", Address: ""},
1038 `"Rob" <@>`,
1039 },
1040 {
1041 &Address{Name: "Rob", Address: "@"},
1042 `"Rob" <@>`,
1043 },
1044 {
1045 &Address{Name: "Böb, Jacöb", Address: "[email protected]"},
1046 `=?utf-8?b?QsO2YiwgSmFjw7Zi?= <[email protected]>`,
1047 },
1048 {
1049 &Address{Name: "=??Q?x?=", Address: "[email protected]"},
1050 `"=??Q?x?=" <[email protected]>`,
1051 },
1052 {
1053 &Address{Name: "=?hello", Address: "[email protected]"},
1054 `"=?hello" <[email protected]>`,
1055 },
1056 {
1057 &Address{Name: "world?=", Address: "[email protected]"},
1058 `"world?=" <[email protected]>`,
1059 },
1060 {
1061
1062 &Address{Name: string([]byte{0xed, 0xa0, 0x80}), Address: "[email protected]"},
1063 "=?utf-8?q?=ED=A0=80?= <[email protected]>",
1064 },
1065 }
1066 for _, test := range tests {
1067 s := test.addr.String()
1068 if s != test.exp {
1069 t.Errorf("Address%+v.String() = %v, want %v", *test.addr, s, test.exp)
1070 continue
1071 }
1072
1073
1074 if test.addr.Address != "" && test.addr.Address != "@" {
1075 a, err := ParseAddress(test.exp)
1076 if err != nil {
1077 t.Errorf("ParseAddress(%#q): %v", test.exp, err)
1078 continue
1079 }
1080 if a.Name != test.addr.Name || a.Address != test.addr.Address {
1081 t.Errorf("ParseAddress(%#q) = %#v, want %#v", test.exp, a, test.addr)
1082 }
1083 }
1084 }
1085 }
1086
1087
1088 func TestAddressParsingAndFormatting(t *testing.T) {
1089
1090
1091 tests := []string{
1092 `<[email protected]>`,
1093 `<[email protected]>`,
1094 `<".bob"@example.com>`,
1095 `<" "@example.com>`,
1096 `<[email protected]>`,
1097 `<"dot.and space"@example.com>`,
1098 `<"[email protected]"@example.com>`,
1099 `<admin@mailserver1>`,
1100 `<postmaster@localhost>`,
1101 "<#!$%&'*+-/=?^_`{}|[email protected]>",
1102 `<"very.(),:;<>[]\".VERY.\"very@\\ \"very\".unusual"@strange.example.com>`,
1103 `<"()<>[]:,;@\\\"!#$%&'*+-/=?^_{}| ~.a"@example.org>`,
1104 `<"Abc\\@def"@example.com>`,
1105 `<"Joe\\Blow"@example.com>`,
1106 `<test1/[email protected]>`,
1107 `<def!xyz%[email protected]>`,
1108 `<[email protected]>`,
1109 `<joe@uk>`,
1110 `<[email protected]>`,
1111 `<"..."@test.com>`,
1112 `<"john..doe"@example.com>`,
1113 `<"john.doe."@example.com>`,
1114 `<".john.doe"@example.com>`,
1115 `<"."@example.com>`,
1116 `<".."@example.com>`,
1117 `<"0:"@0>`,
1118 }
1119
1120 for _, test := range tests {
1121 addr, err := ParseAddress(test)
1122 if err != nil {
1123 t.Errorf("Couldn't parse address %s: %s", test, err.Error())
1124 continue
1125 }
1126 str := addr.String()
1127 addr, err = ParseAddress(str)
1128 if err != nil {
1129 t.Errorf("ParseAddr(%q) error: %v", test, err)
1130 continue
1131 }
1132
1133 if addr.String() != test {
1134 t.Errorf("String() round-trip = %q; want %q", addr, test)
1135 continue
1136 }
1137
1138 }
1139
1140
1141 badTests := []string{
1142 `<Abc.example.com>`,
1143 `<A@b@[email protected]>`,
1144 `<a"b(c)d,e:f;g<h>i[j\k][email protected]>`,
1145 `<just"not"[email protected]>`,
1146 `<this is"not\[email protected]>`,
1147 `<this\ still\"not\\[email protected]>`,
1148 `<[email protected]>`,
1149 `<[email protected]>`,
1150 `<[email protected]>`,
1151 `<[email protected]>`,
1152 `<[email protected]>`,
1153 `<[email protected]>`,
1154 `<@example.com>`,
1155 `<[email protected]>`,
1156 `<test@.>`,
1157 `< @example.com>`,
1158 `<""test""blah""@example.com>`,
1159 `<""@0>`,
1160 }
1161
1162 for _, test := range badTests {
1163 _, err := ParseAddress(test)
1164 if err == nil {
1165 t.Errorf("Should have failed to parse address: %s", test)
1166 continue
1167 }
1168
1169 }
1170
1171 }
1172
1173 func TestAddressFormattingAndParsing(t *testing.T) {
1174 tests := []*Address{
1175 {Name: "@lïce", Address: "[email protected]"},
1176 {Name: "Böb O'Connor", Address: "[email protected]"},
1177 {Name: "???", Address: "[email protected]"},
1178 {Name: "Böb ???", Address: "[email protected]"},
1179 {Name: "Böb (Jacöb)", Address: "[email protected]"},
1180 {Name: "à#$%&'(),.:;<>@[]^`{|}~'", Address: "[email protected]"},
1181
1182 {Name: "\"\\\x1f,\"", Address: "0@0"},
1183
1184 {Name: "naé, mée", Address: "[email protected]"},
1185 }
1186
1187 for i, test := range tests {
1188 parsed, err := ParseAddress(test.String())
1189 if err != nil {
1190 t.Errorf("test #%d: ParseAddr(%q) error: %v", i, test.String(), err)
1191 continue
1192 }
1193 if parsed.Name != test.Name {
1194 t.Errorf("test #%d: Parsed name = %q; want %q", i, parsed.Name, test.Name)
1195 }
1196 if parsed.Address != test.Address {
1197 t.Errorf("test #%d: Parsed address = %q; want %q", i, parsed.Address, test.Address)
1198 }
1199 }
1200 }
1201
1202 func TestEmptyAddress(t *testing.T) {
1203 parsed, err := ParseAddress("")
1204 if parsed != nil || err == nil {
1205 t.Errorf(`ParseAddress("") = %v, %v, want nil, error`, parsed, err)
1206 }
1207 list, err := ParseAddressList("")
1208 if len(list) > 0 || err == nil {
1209 t.Errorf(`ParseAddressList("") = %v, %v, want nil, error`, list, err)
1210 }
1211 list, err = ParseAddressList(",")
1212 if len(list) > 0 || err == nil {
1213 t.Errorf(`ParseAddressList("") = %v, %v, want nil, error`, list, err)
1214 }
1215 list, err = ParseAddressList("a@b c@d")
1216 if len(list) > 0 || err == nil {
1217 t.Errorf(`ParseAddressList("") = %v, %v, want nil, error`, list, err)
1218 }
1219 }
1220
View as plain text