]> git.ipfire.org Git - thirdparty/gcc.git/blob - libgo/go/net/mail/message_test.go
14ac9192a4af0569e3ff3b5ce3c1a2e248dc9bd2
[thirdparty/gcc.git] / libgo / go / net / mail / message_test.go
1 // Copyright 2011 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4
5 package mail
6
7 import (
8 "bytes"
9 "io"
10 "io/ioutil"
11 "mime"
12 "reflect"
13 "strings"
14 "testing"
15 "time"
16 )
17
18 var parseTests = []struct {
19 in string
20 header Header
21 body string
22 }{
23 {
24 // RFC 5322, Appendix A.1.1
25 in: `From: John Doe <jdoe@machine.example>
26 To: Mary Smith <mary@example.net>
27 Subject: Saying Hello
28 Date: Fri, 21 Nov 1997 09:55:06 -0600
29 Message-ID: <1234@local.machine.example>
30
31 This is a message just to say hello.
32 So, "Hello".
33 `,
34 header: Header{
35 "From": []string{"John Doe <jdoe@machine.example>"},
36 "To": []string{"Mary Smith <mary@example.net>"},
37 "Subject": []string{"Saying Hello"},
38 "Date": []string{"Fri, 21 Nov 1997 09:55:06 -0600"},
39 "Message-Id": []string{"<1234@local.machine.example>"},
40 },
41 body: "This is a message just to say hello.\nSo, \"Hello\".\n",
42 },
43 }
44
45 func TestParsing(t *testing.T) {
46 for i, test := range parseTests {
47 msg, err := ReadMessage(bytes.NewBuffer([]byte(test.in)))
48 if err != nil {
49 t.Errorf("test #%d: Failed parsing message: %v", i, err)
50 continue
51 }
52 if !headerEq(msg.Header, test.header) {
53 t.Errorf("test #%d: Incorrectly parsed message header.\nGot:\n%+v\nWant:\n%+v",
54 i, msg.Header, test.header)
55 }
56 body, err := ioutil.ReadAll(msg.Body)
57 if err != nil {
58 t.Errorf("test #%d: Failed reading body: %v", i, err)
59 continue
60 }
61 bodyStr := string(body)
62 if bodyStr != test.body {
63 t.Errorf("test #%d: Incorrectly parsed message body.\nGot:\n%+v\nWant:\n%+v",
64 i, bodyStr, test.body)
65 }
66 }
67 }
68
69 func headerEq(a, b Header) bool {
70 if len(a) != len(b) {
71 return false
72 }
73 for k, as := range a {
74 bs, ok := b[k]
75 if !ok {
76 return false
77 }
78 if !reflect.DeepEqual(as, bs) {
79 return false
80 }
81 }
82 return true
83 }
84
85 func TestDateParsing(t *testing.T) {
86 tests := []struct {
87 dateStr string
88 exp time.Time
89 }{
90 // RFC 5322, Appendix A.1.1
91 {
92 "Fri, 21 Nov 1997 09:55:06 -0600",
93 time.Date(1997, 11, 21, 9, 55, 6, 0, time.FixedZone("", -6*60*60)),
94 },
95 // RFC 5322, Appendix A.6.2
96 // Obsolete date.
97 {
98 "21 Nov 97 09:55:06 GMT",
99 time.Date(1997, 11, 21, 9, 55, 6, 0, time.FixedZone("GMT", 0)),
100 },
101 // Commonly found format not specified by RFC 5322.
102 {
103 "Fri, 21 Nov 1997 09:55:06 -0600 (MDT)",
104 time.Date(1997, 11, 21, 9, 55, 6, 0, time.FixedZone("", -6*60*60)),
105 },
106 }
107 for _, test := range tests {
108 hdr := Header{
109 "Date": []string{test.dateStr},
110 }
111 date, err := hdr.Date()
112 if err != nil {
113 t.Errorf("Header(Date: %s).Date(): %v", test.dateStr, err)
114 } else if !date.Equal(test.exp) {
115 t.Errorf("Header(Date: %s).Date() = %+v, want %+v", test.dateStr, date, test.exp)
116 }
117
118 date, err = ParseDate(test.dateStr)
119 if err != nil {
120 t.Errorf("ParseDate(%s): %v", test.dateStr, err)
121 } else if !date.Equal(test.exp) {
122 t.Errorf("ParseDate(%s) = %+v, want %+v", test.dateStr, date, test.exp)
123 }
124 }
125 }
126
127 func TestAddressParsingError(t *testing.T) {
128 mustErrTestCases := [...]struct {
129 text string
130 wantErrText string
131 }{
132 0: {"=?iso-8859-2?Q?Bogl=E1rka_Tak=E1cs?= <unknown@gmail.com>", "charset not supported"},
133 1: {"a@gmail.com b@gmail.com", "expected single address"},
134 2: {string([]byte{0xed, 0xa0, 0x80}) + " <micro@example.net>", "invalid utf-8 in address"},
135 3: {"\"" + string([]byte{0xed, 0xa0, 0x80}) + "\" <half-surrogate@example.com>", "invalid utf-8 in quoted-string"},
136 4: {"\"\\" + string([]byte{0x80}) + "\" <escaped-invalid-unicode@example.net>", "invalid utf-8 in quoted-string"},
137 5: {"\"\x00\" <null@example.net>", "bad character in quoted-string"},
138 6: {"\"\\\x00\" <escaped-null@example.net>", "bad character in quoted-string"},
139 7: {"John Doe", "no angle-addr"},
140 8: {`<jdoe#machine.example>`, "missing @ in addr-spec"},
141 9: {`John <middle> Doe <jdoe@machine.example>`, "missing @ in addr-spec"},
142 10: {"cfws@example.com (", "misformatted parenthetical comment"},
143 11: {"empty group: ;", "empty group"},
144 12: {"root group: embed group: null@example.com;", "no angle-addr"},
145 13: {"group not closed: null@example.com", "expected comma"},
146 14: {"group: first@example.com, second@example.com;", "group with multiple addresses"},
147 }
148
149 for i, tc := range mustErrTestCases {
150 _, err := ParseAddress(tc.text)
151 if err == nil || !strings.Contains(err.Error(), tc.wantErrText) {
152 t.Errorf(`mail.ParseAddress(%q) #%d want %q, got %v`, tc.text, i, tc.wantErrText, err)
153 }
154 }
155 }
156
157 func TestAddressParsing(t *testing.T) {
158 tests := []struct {
159 addrsStr string
160 exp []*Address
161 }{
162 // Bare address
163 {
164 `jdoe@machine.example`,
165 []*Address{{
166 Address: "jdoe@machine.example",
167 }},
168 },
169 // RFC 5322, Appendix A.1.1
170 {
171 `John Doe <jdoe@machine.example>`,
172 []*Address{{
173 Name: "John Doe",
174 Address: "jdoe@machine.example",
175 }},
176 },
177 // RFC 5322, Appendix A.1.2
178 {
179 `"Joe Q. Public" <john.q.public@example.com>`,
180 []*Address{{
181 Name: "Joe Q. Public",
182 Address: "john.q.public@example.com",
183 }},
184 },
185 {
186 `"John (middle) Doe" <jdoe@machine.example>`,
187 []*Address{{
188 Name: "John (middle) Doe",
189 Address: "jdoe@machine.example",
190 }},
191 },
192 {
193 `John (middle) Doe <jdoe@machine.example>`,
194 []*Address{{
195 Name: "John (middle) Doe",
196 Address: "jdoe@machine.example",
197 }},
198 },
199 {
200 `John !@M@! Doe <jdoe@machine.example>`,
201 []*Address{{
202 Name: "John !@M@! Doe",
203 Address: "jdoe@machine.example",
204 }},
205 },
206 {
207 `"John <middle> Doe" <jdoe@machine.example>`,
208 []*Address{{
209 Name: "John <middle> Doe",
210 Address: "jdoe@machine.example",
211 }},
212 },
213 {
214 `Mary Smith <mary@x.test>, jdoe@example.org, Who? <one@y.test>`,
215 []*Address{
216 {
217 Name: "Mary Smith",
218 Address: "mary@x.test",
219 },
220 {
221 Address: "jdoe@example.org",
222 },
223 {
224 Name: "Who?",
225 Address: "one@y.test",
226 },
227 },
228 },
229 {
230 `<boss@nil.test>, "Giant; \"Big\" Box" <sysservices@example.net>`,
231 []*Address{
232 {
233 Address: "boss@nil.test",
234 },
235 {
236 Name: `Giant; "Big" Box`,
237 Address: "sysservices@example.net",
238 },
239 },
240 },
241 // RFC 5322, Appendix A.6.1
242 {
243 `Joe Q. Public <john.q.public@example.com>`,
244 []*Address{{
245 Name: "Joe Q. Public",
246 Address: "john.q.public@example.com",
247 }},
248 },
249 // RFC 5322, Appendix A.1.3
250 {
251 `group1: groupaddr1@example.com;`,
252 []*Address{
253 {
254 Name: "",
255 Address: "groupaddr1@example.com",
256 },
257 },
258 },
259 {
260 `empty group: ;`,
261 []*Address(nil),
262 },
263 {
264 `A Group:Ed Jones <c@a.test>,joe@where.test,John <jdoe@one.test>;`,
265 []*Address{
266 {
267 Name: "Ed Jones",
268 Address: "c@a.test",
269 },
270 {
271 Name: "",
272 Address: "joe@where.test",
273 },
274 {
275 Name: "John",
276 Address: "jdoe@one.test",
277 },
278 },
279 },
280 {
281 `Group1: <addr1@example.com>;, Group 2: addr2@example.com;, John <addr3@example.com>`,
282 []*Address{
283 {
284 Name: "",
285 Address: "addr1@example.com",
286 },
287 {
288 Name: "",
289 Address: "addr2@example.com",
290 },
291 {
292 Name: "John",
293 Address: "addr3@example.com",
294 },
295 },
296 },
297 // RFC 2047 "Q"-encoded ISO-8859-1 address.
298 {
299 `=?iso-8859-1?q?J=F6rg_Doe?= <joerg@example.com>`,
300 []*Address{
301 {
302 Name: `Jörg Doe`,
303 Address: "joerg@example.com",
304 },
305 },
306 },
307 // RFC 2047 "Q"-encoded US-ASCII address. Dumb but legal.
308 {
309 `=?us-ascii?q?J=6Frg_Doe?= <joerg@example.com>`,
310 []*Address{
311 {
312 Name: `Jorg Doe`,
313 Address: "joerg@example.com",
314 },
315 },
316 },
317 // RFC 2047 "Q"-encoded UTF-8 address.
318 {
319 `=?utf-8?q?J=C3=B6rg_Doe?= <joerg@example.com>`,
320 []*Address{
321 {
322 Name: `Jörg Doe`,
323 Address: "joerg@example.com",
324 },
325 },
326 },
327 // RFC 2047 "Q"-encoded UTF-8 address with multiple encoded-words.
328 {
329 `=?utf-8?q?J=C3=B6rg?= =?utf-8?q?Doe?= <joerg@example.com>`,
330 []*Address{
331 {
332 Name: `JörgDoe`,
333 Address: "joerg@example.com",
334 },
335 },
336 },
337 // RFC 2047, Section 8.
338 {
339 `=?ISO-8859-1?Q?Andr=E9?= Pirard <PIRARD@vm1.ulg.ac.be>`,
340 []*Address{
341 {
342 Name: `André Pirard`,
343 Address: "PIRARD@vm1.ulg.ac.be",
344 },
345 },
346 },
347 // Custom example of RFC 2047 "B"-encoded ISO-8859-1 address.
348 {
349 `=?ISO-8859-1?B?SvZyZw==?= <joerg@example.com>`,
350 []*Address{
351 {
352 Name: `Jörg`,
353 Address: "joerg@example.com",
354 },
355 },
356 },
357 // Custom example of RFC 2047 "B"-encoded UTF-8 address.
358 {
359 `=?UTF-8?B?SsO2cmc=?= <joerg@example.com>`,
360 []*Address{
361 {
362 Name: `Jörg`,
363 Address: "joerg@example.com",
364 },
365 },
366 },
367 // Custom example with "." in name. For issue 4938
368 {
369 `Asem H. <noreply@example.com>`,
370 []*Address{
371 {
372 Name: `Asem H.`,
373 Address: "noreply@example.com",
374 },
375 },
376 },
377 // RFC 6532 3.2.3, qtext /= UTF8-non-ascii
378 {
379 `"Gø Pher" <gopher@example.com>`,
380 []*Address{
381 {
382 Name: `Gø Pher`,
383 Address: "gopher@example.com",
384 },
385 },
386 },
387 // RFC 6532 3.2, atext /= UTF8-non-ascii
388 {
389 `µ <micro@example.com>`,
390 []*Address{
391 {
392 Name: `µ`,
393 Address: "micro@example.com",
394 },
395 },
396 },
397 // RFC 6532 3.2.2, local address parts allow UTF-8
398 {
399 `Micro <µ@example.com>`,
400 []*Address{
401 {
402 Name: `Micro`,
403 Address: "µ@example.com",
404 },
405 },
406 },
407 // RFC 6532 3.2.4, domains parts allow UTF-8
408 {
409 `Micro <micro@µ.example.com>`,
410 []*Address{
411 {
412 Name: `Micro`,
413 Address: "micro@µ.example.com",
414 },
415 },
416 },
417 // Issue 14866
418 {
419 `"" <emptystring@example.com>`,
420 []*Address{
421 {
422 Name: "",
423 Address: "emptystring@example.com",
424 },
425 },
426 },
427 // CFWS
428 {
429 `<cfws@example.com> (CFWS (cfws)) (another comment)`,
430 []*Address{
431 {
432 Name: "",
433 Address: "cfws@example.com",
434 },
435 },
436 },
437 {
438 `<cfws@example.com> () (another comment), <cfws2@example.com> (another)`,
439 []*Address{
440 {
441 Name: "",
442 Address: "cfws@example.com",
443 },
444 {
445 Name: "",
446 Address: "cfws2@example.com",
447 },
448 },
449 },
450 // Comment as display name
451 {
452 `john@example.com (John Doe)`,
453 []*Address{
454 {
455 Name: "John Doe",
456 Address: "john@example.com",
457 },
458 },
459 },
460 // Comment and display name
461 {
462 `John Doe <john@example.com> (Joey)`,
463 []*Address{
464 {
465 Name: "John Doe",
466 Address: "john@example.com",
467 },
468 },
469 },
470 // Comment as display name, no space
471 {
472 `john@example.com(John Doe)`,
473 []*Address{
474 {
475 Name: "John Doe",
476 Address: "john@example.com",
477 },
478 },
479 },
480 // Comment as display name, Q-encoded
481 {
482 `asjo@example.com (Adam =?utf-8?Q?Sj=C3=B8gren?=)`,
483 []*Address{
484 {
485 Name: "Adam Sjøgren",
486 Address: "asjo@example.com",
487 },
488 },
489 },
490 // Comment as display name, Q-encoded and tab-separated
491 {
492 `asjo@example.com (Adam =?utf-8?Q?Sj=C3=B8gren?=)`,
493 []*Address{
494 {
495 Name: "Adam Sjøgren",
496 Address: "asjo@example.com",
497 },
498 },
499 },
500 // Nested comment as display name, Q-encoded
501 {
502 `asjo@example.com (Adam =?utf-8?Q?Sj=C3=B8gren?= (Debian))`,
503 []*Address{
504 {
505 Name: "Adam Sjøgren (Debian)",
506 Address: "asjo@example.com",
507 },
508 },
509 },
510 }
511 for _, test := range tests {
512 if len(test.exp) == 1 {
513 addr, err := ParseAddress(test.addrsStr)
514 if err != nil {
515 t.Errorf("Failed parsing (single) %q: %v", test.addrsStr, err)
516 continue
517 }
518 if !reflect.DeepEqual([]*Address{addr}, test.exp) {
519 t.Errorf("Parse (single) of %q: got %+v, want %+v", test.addrsStr, addr, test.exp)
520 }
521 }
522
523 addrs, err := ParseAddressList(test.addrsStr)
524 if err != nil {
525 t.Errorf("Failed parsing (list) %q: %v", test.addrsStr, err)
526 continue
527 }
528 if !reflect.DeepEqual(addrs, test.exp) {
529 t.Errorf("Parse (list) of %q: got %+v, want %+v", test.addrsStr, addrs, test.exp)
530 }
531 }
532 }
533
534 func TestAddressParser(t *testing.T) {
535 tests := []struct {
536 addrsStr string
537 exp []*Address
538 }{
539 // Bare address
540 {
541 `jdoe@machine.example`,
542 []*Address{{
543 Address: "jdoe@machine.example",
544 }},
545 },
546 // RFC 5322, Appendix A.1.1
547 {
548 `John Doe <jdoe@machine.example>`,
549 []*Address{{
550 Name: "John Doe",
551 Address: "jdoe@machine.example",
552 }},
553 },
554 // RFC 5322, Appendix A.1.2
555 {
556 `"Joe Q. Public" <john.q.public@example.com>`,
557 []*Address{{
558 Name: "Joe Q. Public",
559 Address: "john.q.public@example.com",
560 }},
561 },
562 {
563 `Mary Smith <mary@x.test>, jdoe@example.org, Who? <one@y.test>`,
564 []*Address{
565 {
566 Name: "Mary Smith",
567 Address: "mary@x.test",
568 },
569 {
570 Address: "jdoe@example.org",
571 },
572 {
573 Name: "Who?",
574 Address: "one@y.test",
575 },
576 },
577 },
578 {
579 `<boss@nil.test>, "Giant; \"Big\" Box" <sysservices@example.net>`,
580 []*Address{
581 {
582 Address: "boss@nil.test",
583 },
584 {
585 Name: `Giant; "Big" Box`,
586 Address: "sysservices@example.net",
587 },
588 },
589 },
590 // RFC 2047 "Q"-encoded ISO-8859-1 address.
591 {
592 `=?iso-8859-1?q?J=F6rg_Doe?= <joerg@example.com>`,
593 []*Address{
594 {
595 Name: `Jörg Doe`,
596 Address: "joerg@example.com",
597 },
598 },
599 },
600 // RFC 2047 "Q"-encoded US-ASCII address. Dumb but legal.
601 {
602 `=?us-ascii?q?J=6Frg_Doe?= <joerg@example.com>`,
603 []*Address{
604 {
605 Name: `Jorg Doe`,
606 Address: "joerg@example.com",
607 },
608 },
609 },
610 // RFC 2047 "Q"-encoded ISO-8859-15 address.
611 {
612 `=?ISO-8859-15?Q?J=F6rg_Doe?= <joerg@example.com>`,
613 []*Address{
614 {
615 Name: `Jörg Doe`,
616 Address: "joerg@example.com",
617 },
618 },
619 },
620 // RFC 2047 "B"-encoded windows-1252 address.
621 {
622 `=?windows-1252?q?Andr=E9?= Pirard <PIRARD@vm1.ulg.ac.be>`,
623 []*Address{
624 {
625 Name: `André Pirard`,
626 Address: "PIRARD@vm1.ulg.ac.be",
627 },
628 },
629 },
630 // Custom example of RFC 2047 "B"-encoded ISO-8859-15 address.
631 {
632 `=?ISO-8859-15?B?SvZyZw==?= <joerg@example.com>`,
633 []*Address{
634 {
635 Name: `Jörg`,
636 Address: "joerg@example.com",
637 },
638 },
639 },
640 // Custom example of RFC 2047 "B"-encoded UTF-8 address.
641 {
642 `=?UTF-8?B?SsO2cmc=?= <joerg@example.com>`,
643 []*Address{
644 {
645 Name: `Jörg`,
646 Address: "joerg@example.com",
647 },
648 },
649 },
650 // Custom example with "." in name. For issue 4938
651 {
652 `Asem H. <noreply@example.com>`,
653 []*Address{
654 {
655 Name: `Asem H.`,
656 Address: "noreply@example.com",
657 },
658 },
659 },
660 }
661
662 ap := AddressParser{WordDecoder: &mime.WordDecoder{
663 CharsetReader: func(charset string, input io.Reader) (io.Reader, error) {
664 in, err := ioutil.ReadAll(input)
665 if err != nil {
666 return nil, err
667 }
668
669 switch charset {
670 case "iso-8859-15":
671 in = bytes.ReplaceAll(in, []byte("\xf6"), []byte("ö"))
672 case "windows-1252":
673 in = bytes.ReplaceAll(in, []byte("\xe9"), []byte("é"))
674 }
675
676 return bytes.NewReader(in), nil
677 },
678 }}
679
680 for _, test := range tests {
681 if len(test.exp) == 1 {
682 addr, err := ap.Parse(test.addrsStr)
683 if err != nil {
684 t.Errorf("Failed parsing (single) %q: %v", test.addrsStr, err)
685 continue
686 }
687 if !reflect.DeepEqual([]*Address{addr}, test.exp) {
688 t.Errorf("Parse (single) of %q: got %+v, want %+v", test.addrsStr, addr, test.exp)
689 }
690 }
691
692 addrs, err := ap.ParseList(test.addrsStr)
693 if err != nil {
694 t.Errorf("Failed parsing (list) %q: %v", test.addrsStr, err)
695 continue
696 }
697 if !reflect.DeepEqual(addrs, test.exp) {
698 t.Errorf("Parse (list) of %q: got %+v, want %+v", test.addrsStr, addrs, test.exp)
699 }
700 }
701 }
702
703 func TestAddressString(t *testing.T) {
704 tests := []struct {
705 addr *Address
706 exp string
707 }{
708 {
709 &Address{Address: "bob@example.com"},
710 "<bob@example.com>",
711 },
712 { // quoted local parts: RFC 5322, 3.4.1. and 3.2.4.
713 &Address{Address: `my@idiot@address@example.com`},
714 `<"my@idiot@address"@example.com>`,
715 },
716 { // quoted local parts
717 &Address{Address: ` @example.com`},
718 `<" "@example.com>`,
719 },
720 {
721 &Address{Name: "Bob", Address: "bob@example.com"},
722 `"Bob" <bob@example.com>`,
723 },
724 {
725 // note the ö (o with an umlaut)
726 &Address{Name: "Böb", Address: "bob@example.com"},
727 `=?utf-8?q?B=C3=B6b?= <bob@example.com>`,
728 },
729 {
730 &Address{Name: "Bob Jane", Address: "bob@example.com"},
731 `"Bob Jane" <bob@example.com>`,
732 },
733 {
734 &Address{Name: "Böb Jacöb", Address: "bob@example.com"},
735 `=?utf-8?q?B=C3=B6b_Jac=C3=B6b?= <bob@example.com>`,
736 },
737 { // https://golang.org/issue/12098
738 &Address{Name: "Rob", Address: ""},
739 `"Rob" <@>`,
740 },
741 { // https://golang.org/issue/12098
742 &Address{Name: "Rob", Address: "@"},
743 `"Rob" <@>`,
744 },
745 {
746 &Address{Name: "Böb, Jacöb", Address: "bob@example.com"},
747 `=?utf-8?b?QsO2YiwgSmFjw7Zi?= <bob@example.com>`,
748 },
749 {
750 &Address{Name: "=??Q?x?=", Address: "hello@world.com"},
751 `"=??Q?x?=" <hello@world.com>`,
752 },
753 {
754 &Address{Name: "=?hello", Address: "hello@world.com"},
755 `"=?hello" <hello@world.com>`,
756 },
757 {
758 &Address{Name: "world?=", Address: "hello@world.com"},
759 `"world?=" <hello@world.com>`,
760 },
761 {
762 // should q-encode even for invalid utf-8.
763 &Address{Name: string([]byte{0xed, 0xa0, 0x80}), Address: "invalid-utf8@example.net"},
764 "=?utf-8?q?=ED=A0=80?= <invalid-utf8@example.net>",
765 },
766 }
767 for _, test := range tests {
768 s := test.addr.String()
769 if s != test.exp {
770 t.Errorf("Address%+v.String() = %v, want %v", *test.addr, s, test.exp)
771 continue
772 }
773
774 // Check round-trip.
775 if test.addr.Address != "" && test.addr.Address != "@" {
776 a, err := ParseAddress(test.exp)
777 if err != nil {
778 t.Errorf("ParseAddress(%#q): %v", test.exp, err)
779 continue
780 }
781 if a.Name != test.addr.Name || a.Address != test.addr.Address {
782 t.Errorf("ParseAddress(%#q) = %#v, want %#v", test.exp, a, test.addr)
783 }
784 }
785 }
786 }
787
788 // Check if all valid addresses can be parsed, formatted and parsed again
789 func TestAddressParsingAndFormatting(t *testing.T) {
790
791 // Should pass
792 tests := []string{
793 `<Bob@example.com>`,
794 `<bob.bob@example.com>`,
795 `<".bob"@example.com>`,
796 `<" "@example.com>`,
797 `<some.mail-with-dash@example.com>`,
798 `<"dot.and space"@example.com>`,
799 `<"very.unusual.@.unusual.com"@example.com>`,
800 `<admin@mailserver1>`,
801 `<postmaster@localhost>`,
802 "<#!$%&'*+-/=?^_`{}|~@example.org>",
803 `<"very.(),:;<>[]\".VERY.\"very@\\ \"very\".unusual"@strange.example.com>`, // escaped quotes
804 `<"()<>[]:,;@\\\"!#$%&'*+-/=?^_{}| ~.a"@example.org>`, // escaped backslashes
805 `<"Abc\\@def"@example.com>`,
806 `<"Joe\\Blow"@example.com>`,
807 `<test1/test2=test3@example.com>`,
808 `<def!xyz%abc@example.com>`,
809 `<_somename@example.com>`,
810 `<joe@uk>`,
811 `<~@example.com>`,
812 `<"..."@test.com>`,
813 `<"john..doe"@example.com>`,
814 `<"john.doe."@example.com>`,
815 `<".john.doe"@example.com>`,
816 `<"."@example.com>`,
817 `<".."@example.com>`,
818 `<"0:"@0>`,
819 }
820
821 for _, test := range tests {
822 addr, err := ParseAddress(test)
823 if err != nil {
824 t.Errorf("Couldn't parse address %s: %s", test, err.Error())
825 continue
826 }
827 str := addr.String()
828 addr, err = ParseAddress(str)
829 if err != nil {
830 t.Errorf("ParseAddr(%q) error: %v", test, err)
831 continue
832 }
833
834 if addr.String() != test {
835 t.Errorf("String() round-trip = %q; want %q", addr, test)
836 continue
837 }
838
839 }
840
841 // Should fail
842 badTests := []string{
843 `<Abc.example.com>`,
844 `<A@b@c@example.com>`,
845 `<a"b(c)d,e:f;g<h>i[j\k]l@example.com>`,
846 `<just"not"right@example.com>`,
847 `<this is"not\allowed@example.com>`,
848 `<this\ still\"not\\allowed@example.com>`,
849 `<john..doe@example.com>`,
850 `<john.doe@example..com>`,
851 `<john.doe@example..com>`,
852 `<john.doe.@example.com>`,
853 `<john.doe.@.example.com>`,
854 `<.john.doe@example.com>`,
855 `<@example.com>`,
856 `<.@example.com>`,
857 `<test@.>`,
858 `< @example.com>`,
859 `<""test""blah""@example.com>`,
860 `<""@0>`,
861 }
862
863 for _, test := range badTests {
864 _, err := ParseAddress(test)
865 if err == nil {
866 t.Errorf("Should have failed to parse address: %s", test)
867 continue
868 }
869
870 }
871
872 }
873
874 func TestAddressFormattingAndParsing(t *testing.T) {
875 tests := []*Address{
876 {Name: "@lïce", Address: "alice@example.com"},
877 {Name: "Böb O'Connor", Address: "bob@example.com"},
878 {Name: "???", Address: "bob@example.com"},
879 {Name: "Böb ???", Address: "bob@example.com"},
880 {Name: "Böb (Jacöb)", Address: "bob@example.com"},
881 {Name: "à#$%&'(),.:;<>@[]^`{|}~'", Address: "bob@example.com"},
882 // https://golang.org/issue/11292
883 {Name: "\"\\\x1f,\"", Address: "0@0"},
884 // https://golang.org/issue/12782
885 {Name: "naé, mée", Address: "test.mail@gmail.com"},
886 }
887
888 for i, test := range tests {
889 parsed, err := ParseAddress(test.String())
890 if err != nil {
891 t.Errorf("test #%d: ParseAddr(%q) error: %v", i, test.String(), err)
892 continue
893 }
894 if parsed.Name != test.Name {
895 t.Errorf("test #%d: Parsed name = %q; want %q", i, parsed.Name, test.Name)
896 }
897 if parsed.Address != test.Address {
898 t.Errorf("test #%d: Parsed address = %q; want %q", i, parsed.Address, test.Address)
899 }
900 }
901 }