Home | History | Annotate | Download | only in mail
      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 (a] machine.example>
     26 To: Mary Smith <mary (a] example.net>
     27 Subject: Saying Hello
     28 Date: Fri, 21 Nov 1997 09:55:06 -0600
     29 Message-ID: <1234 (a] 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 (a] machine.example>"},
     36 			"To":         []string{"Mary Smith <mary (a] example.net>"},
     37 			"Subject":    []string{"Saying Hello"},
     38 			"Date":       []string{"Fri, 21 Nov 1997 09:55:06 -0600"},
     39 			"Message-Id": []string{"<1234 (a] 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 (a] gmail.com>", "charset not supported"},
    133 		1:  {"a (a] gmail.com b (a] gmail.com", "expected single address"},
    134 		2:  {string([]byte{0xed, 0xa0, 0x80}) + " <micro (a] example.net>", "invalid utf-8 in address"},
    135 		3:  {"\"" + string([]byte{0xed, 0xa0, 0x80}) + "\" <half-surrogate (a] example.com>", "invalid utf-8 in quoted-string"},
    136 		4:  {"\"\\" + string([]byte{0x80}) + "\" <escaped-invalid-unicode (a] example.net>", "invalid utf-8 in quoted-string"},
    137 		5:  {"\"\x00\" <null (a] example.net>", "bad character in quoted-string"},
    138 		6:  {"\"\\\x00\" <escaped-null (a] 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 (a] machine.example>`, "missing @ in addr-spec"},
    142 		10: {"cfws (a] example.com (", "misformatted parenthetical comment"},
    143 		11: {"empty group: ;", "empty group"},
    144 		12: {"root group: embed group: null (a] example.com;", "no angle-addr"},
    145 		13: {"group not closed: null (a] example.com", "expected comma"},
    146 		14: {"group: first (a] example.com, second (a] 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 (a] machine.example`,
    165 			[]*Address{{
    166 				Address: "jdoe (a] machine.example",
    167 			}},
    168 		},
    169 		// RFC 5322, Appendix A.1.1
    170 		{
    171 			`John Doe <jdoe (a] machine.example>`,
    172 			[]*Address{{
    173 				Name:    "John Doe",
    174 				Address: "jdoe (a] machine.example",
    175 			}},
    176 		},
    177 		// RFC 5322, Appendix A.1.2
    178 		{
    179 			`"Joe Q. Public" <john.q.public (a] example.com>`,
    180 			[]*Address{{
    181 				Name:    "Joe Q. Public",
    182 				Address: "john.q.public (a] example.com",
    183 			}},
    184 		},
    185 		{
    186 			`"John (middle) Doe" <jdoe (a] machine.example>`,
    187 			[]*Address{{
    188 				Name:    "John (middle) Doe",
    189 				Address: "jdoe (a] machine.example",
    190 			}},
    191 		},
    192 		{
    193 			`John (middle) Doe <jdoe (a] machine.example>`,
    194 			[]*Address{{
    195 				Name:    "John (middle) Doe",
    196 				Address: "jdoe (a] machine.example",
    197 			}},
    198 		},
    199 		{
    200 			`John !@M@! Doe <jdoe (a] machine.example>`,
    201 			[]*Address{{
    202 				Name:    "John !@M@! Doe",
    203 				Address: "jdoe (a] machine.example",
    204 			}},
    205 		},
    206 		{
    207 			`"John <middle> Doe" <jdoe (a] machine.example>`,
    208 			[]*Address{{
    209 				Name:    "John <middle> Doe",
    210 				Address: "jdoe (a] machine.example",
    211 			}},
    212 		},
    213 		{
    214 			`Mary Smith <mary (a] x.test>, jdoe (a] example.org, Who? <one (a] y.test>`,
    215 			[]*Address{
    216 				{
    217 					Name:    "Mary Smith",
    218 					Address: "mary (a] x.test",
    219 				},
    220 				{
    221 					Address: "jdoe (a] example.org",
    222 				},
    223 				{
    224 					Name:    "Who?",
    225 					Address: "one (a] y.test",
    226 				},
    227 			},
    228 		},
    229 		{
    230 			`<boss (a] nil.test>, "Giant; \"Big\" Box" <sysservices (a] example.net>`,
    231 			[]*Address{
    232 				{
    233 					Address: "boss (a] nil.test",
    234 				},
    235 				{
    236 					Name:    `Giant; "Big" Box`,
    237 					Address: "sysservices (a] example.net",
    238 				},
    239 			},
    240 		},
    241 		// RFC 5322, Appendix A.6.1
    242 		{
    243 			`Joe Q. Public <john.q.public (a] example.com>`,
    244 			[]*Address{{
    245 				Name:    "Joe Q. Public",
    246 				Address: "john.q.public (a] example.com",
    247 			}},
    248 		},
    249 		// RFC 5322, Appendix A.1.3
    250 		{
    251 			`group1: groupaddr1 (a] example.com;`,
    252 			[]*Address{
    253 				{
    254 					Name:    "",
    255 					Address: "groupaddr1 (a] example.com",
    256 				},
    257 			},
    258 		},
    259 		{
    260 			`empty group: ;`,
    261 			[]*Address(nil),
    262 		},
    263 		{
    264 			`A Group:Ed Jones <c (a] a.test>,joe (a] where.test,John <jdoe (a] one.test>;`,
    265 			[]*Address{
    266 				{
    267 					Name:    "Ed Jones",
    268 					Address: "c (a] a.test",
    269 				},
    270 				{
    271 					Name:    "",
    272 					Address: "joe (a] where.test",
    273 				},
    274 				{
    275 					Name:    "John",
    276 					Address: "jdoe (a] one.test",
    277 				},
    278 			},
    279 		},
    280 		{
    281 			`Group1: <addr1 (a] example.com>;, Group 2: addr2 (a] example.com;, John <addr3 (a] example.com>`,
    282 			[]*Address{
    283 				{
    284 					Name:    "",
    285 					Address: "addr1 (a] example.com",
    286 				},
    287 				{
    288 					Name:    "",
    289 					Address: "addr2 (a] example.com",
    290 				},
    291 				{
    292 					Name:    "John",
    293 					Address: "addr3 (a] example.com",
    294 				},
    295 			},
    296 		},
    297 		// RFC 2047 "Q"-encoded ISO-8859-1 address.
    298 		{
    299 			`=?iso-8859-1?q?J=F6rg_Doe?= <joerg (a] example.com>`,
    300 			[]*Address{
    301 				{
    302 					Name:    `Jrg Doe`,
    303 					Address: "joerg (a] 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 (a] example.com>`,
    310 			[]*Address{
    311 				{
    312 					Name:    `Jorg Doe`,
    313 					Address: "joerg (a] example.com",
    314 				},
    315 			},
    316 		},
    317 		// RFC 2047 "Q"-encoded UTF-8 address.
    318 		{
    319 			`=?utf-8?q?J=C3=B6rg_Doe?= <joerg (a] example.com>`,
    320 			[]*Address{
    321 				{
    322 					Name:    `Jrg Doe`,
    323 					Address: "joerg (a] 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 (a] example.com>`,
    330 			[]*Address{
    331 				{
    332 					Name:    `JrgDoe`,
    333 					Address: "joerg (a] example.com",
    334 				},
    335 			},
    336 		},
    337 		// RFC 2047, Section 8.
    338 		{
    339 			`=?ISO-8859-1?Q?Andr=E9?= Pirard <PIRARD (a] vm1.ulg.ac.be>`,
    340 			[]*Address{
    341 				{
    342 					Name:    `Andr Pirard`,
    343 					Address: "PIRARD (a] 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 (a] example.com>`,
    350 			[]*Address{
    351 				{
    352 					Name:    `Jrg`,
    353 					Address: "joerg (a] example.com",
    354 				},
    355 			},
    356 		},
    357 		// Custom example of RFC 2047 "B"-encoded UTF-8 address.
    358 		{
    359 			`=?UTF-8?B?SsO2cmc=?= <joerg (a] example.com>`,
    360 			[]*Address{
    361 				{
    362 					Name:    `Jrg`,
    363 					Address: "joerg (a] example.com",
    364 				},
    365 			},
    366 		},
    367 		// Custom example with "." in name. For issue 4938
    368 		{
    369 			`Asem H. <noreply (a] example.com>`,
    370 			[]*Address{
    371 				{
    372 					Name:    `Asem H.`,
    373 					Address: "noreply (a] example.com",
    374 				},
    375 			},
    376 		},
    377 		// RFC 6532 3.2.3, qtext /= UTF8-non-ascii
    378 		{
    379 			`"G Pher" <gopher (a] example.com>`,
    380 			[]*Address{
    381 				{
    382 					Name:    `G Pher`,
    383 					Address: "gopher (a] example.com",
    384 				},
    385 			},
    386 		},
    387 		// RFC 6532 3.2, atext /= UTF8-non-ascii
    388 		{
    389 			` <micro (a] example.com>`,
    390 			[]*Address{
    391 				{
    392 					Name:    ``,
    393 					Address: "micro (a] 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 (a] example.com>`,
    420 			[]*Address{
    421 				{
    422 					Name:    "",
    423 					Address: "emptystring (a] example.com",
    424 				},
    425 			},
    426 		},
    427 		// CFWS
    428 		{
    429 			`<cfws (a] example.com> (CFWS (cfws))  (another comment)`,
    430 			[]*Address{
    431 				{
    432 					Name:    "",
    433 					Address: "cfws (a] example.com",
    434 				},
    435 			},
    436 		},
    437 		{
    438 			`<cfws (a] example.com> ()  (another comment), <cfws2 (a] example.com> (another)`,
    439 			[]*Address{
    440 				{
    441 					Name:    "",
    442 					Address: "cfws (a] example.com",
    443 				},
    444 				{
    445 					Name:    "",
    446 					Address: "cfws2 (a] example.com",
    447 				},
    448 			},
    449 		},
    450 		// Comment as display name
    451 		{
    452 			`john (a] example.com (John Doe)`,
    453 			[]*Address{
    454 				{
    455 					Name:    "John Doe",
    456 					Address: "john (a] example.com",
    457 				},
    458 			},
    459 		},
    460 		// Comment and display name
    461 		{
    462 			`John Doe <john (a] example.com> (Joey)`,
    463 			[]*Address{
    464 				{
    465 					Name:    "John Doe",
    466 					Address: "john (a] example.com",
    467 				},
    468 			},
    469 		},
    470 		// Comment as display name, no space
    471 		{
    472 			`john (a] example.com(John Doe)`,
    473 			[]*Address{
    474 				{
    475 					Name:    "John Doe",
    476 					Address: "john (a] example.com",
    477 				},
    478 			},
    479 		},
    480 		// Comment as display name, Q-encoded
    481 		{
    482 			`asjo (a] example.com (Adam =?utf-8?Q?Sj=C3=B8gren?=)`,
    483 			[]*Address{
    484 				{
    485 					Name:    "Adam Sjgren",
    486 					Address: "asjo (a] example.com",
    487 				},
    488 			},
    489 		},
    490 		// Comment as display name, Q-encoded and tab-separated
    491 		{
    492 			`asjo (a] example.com (Adam	=?utf-8?Q?Sj=C3=B8gren?=)`,
    493 			[]*Address{
    494 				{
    495 					Name:    "Adam Sjgren",
    496 					Address: "asjo (a] example.com",
    497 				},
    498 			},
    499 		},
    500 		// Nested comment as display name, Q-encoded
    501 		{
    502 			`asjo (a] example.com (Adam =?utf-8?Q?Sj=C3=B8gren?= (Debian))`,
    503 			[]*Address{
    504 				{
    505 					Name:    "Adam Sjgren (Debian)",
    506 					Address: "asjo (a] 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 (a] machine.example`,
    542 			[]*Address{{
    543 				Address: "jdoe (a] machine.example",
    544 			}},
    545 		},
    546 		// RFC 5322, Appendix A.1.1
    547 		{
    548 			`John Doe <jdoe (a] machine.example>`,
    549 			[]*Address{{
    550 				Name:    "John Doe",
    551 				Address: "jdoe (a] machine.example",
    552 			}},
    553 		},
    554 		// RFC 5322, Appendix A.1.2
    555 		{
    556 			`"Joe Q. Public" <john.q.public (a] example.com>`,
    557 			[]*Address{{
    558 				Name:    "Joe Q. Public",
    559 				Address: "john.q.public (a] example.com",
    560 			}},
    561 		},
    562 		{
    563 			`Mary Smith <mary (a] x.test>, jdoe (a] example.org, Who? <one (a] y.test>`,
    564 			[]*Address{
    565 				{
    566 					Name:    "Mary Smith",
    567 					Address: "mary (a] x.test",
    568 				},
    569 				{
    570 					Address: "jdoe (a] example.org",
    571 				},
    572 				{
    573 					Name:    "Who?",
    574 					Address: "one (a] y.test",
    575 				},
    576 			},
    577 		},
    578 		{
    579 			`<boss (a] nil.test>, "Giant; \"Big\" Box" <sysservices (a] example.net>`,
    580 			[]*Address{
    581 				{
    582 					Address: "boss (a] nil.test",
    583 				},
    584 				{
    585 					Name:    `Giant; "Big" Box`,
    586 					Address: "sysservices (a] example.net",
    587 				},
    588 			},
    589 		},
    590 		// RFC 2047 "Q"-encoded ISO-8859-1 address.
    591 		{
    592 			`=?iso-8859-1?q?J=F6rg_Doe?= <joerg (a] example.com>`,
    593 			[]*Address{
    594 				{
    595 					Name:    `Jrg Doe`,
    596 					Address: "joerg (a] 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 (a] example.com>`,
    603 			[]*Address{
    604 				{
    605 					Name:    `Jorg Doe`,
    606 					Address: "joerg (a] example.com",
    607 				},
    608 			},
    609 		},
    610 		// RFC 2047 "Q"-encoded ISO-8859-15 address.
    611 		{
    612 			`=?ISO-8859-15?Q?J=F6rg_Doe?= <joerg (a] example.com>`,
    613 			[]*Address{
    614 				{
    615 					Name:    `Jrg Doe`,
    616 					Address: "joerg (a] example.com",
    617 				},
    618 			},
    619 		},
    620 		// RFC 2047 "B"-encoded windows-1252 address.
    621 		{
    622 			`=?windows-1252?q?Andr=E9?= Pirard <PIRARD (a] vm1.ulg.ac.be>`,
    623 			[]*Address{
    624 				{
    625 					Name:    `Andr Pirard`,
    626 					Address: "PIRARD (a] 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 (a] example.com>`,
    633 			[]*Address{
    634 				{
    635 					Name:    `Jrg`,
    636 					Address: "joerg (a] example.com",
    637 				},
    638 			},
    639 		},
    640 		// Custom example of RFC 2047 "B"-encoded UTF-8 address.
    641 		{
    642 			`=?UTF-8?B?SsO2cmc=?= <joerg (a] example.com>`,
    643 			[]*Address{
    644 				{
    645 					Name:    `Jrg`,
    646 					Address: "joerg (a] example.com",
    647 				},
    648 			},
    649 		},
    650 		// Custom example with "." in name. For issue 4938
    651 		{
    652 			`Asem H. <noreply (a] example.com>`,
    653 			[]*Address{
    654 				{
    655 					Name:    `Asem H.`,
    656 					Address: "noreply (a] 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.Replace(in, []byte("\xf6"), []byte(""), -1)
    672 			case "windows-1252":
    673 				in = bytes.Replace(in, []byte("\xe9"), []byte(""), -1)
    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 (a] example.com"},
    710 			"<bob (a] example.com>",
    711 		},
    712 		{ // quoted local parts: RFC 5322, 3.4.1. and 3.2.4.
    713 			&Address{Address: `my@idiot@address (a] 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 (a] example.com"},
    722 			`"Bob" <bob (a] example.com>`,
    723 		},
    724 		{
    725 			// note the  (o with an umlaut)
    726 			&Address{Name: "Bb", Address: "bob (a] example.com"},
    727 			`=?utf-8?q?B=C3=B6b?= <bob (a] example.com>`,
    728 		},
    729 		{
    730 			&Address{Name: "Bob Jane", Address: "bob (a] example.com"},
    731 			`"Bob Jane" <bob (a] example.com>`,
    732 		},
    733 		{
    734 			&Address{Name: "Bb Jacb", Address: "bob (a] example.com"},
    735 			`=?utf-8?q?B=C3=B6b_Jac=C3=B6b?= <bob (a] 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: "Bb, Jacb", Address: "bob (a] example.com"},
    747 			`=?utf-8?b?QsO2YiwgSmFjw7Zi?= <bob (a] example.com>`,
    748 		},
    749 		{
    750 			&Address{Name: "=??Q?x?=", Address: "hello (a] world.com"},
    751 			`"=??Q?x?=" <hello (a] world.com>`,
    752 		},
    753 		{
    754 			&Address{Name: "=?hello", Address: "hello (a] world.com"},
    755 			`"=?hello" <hello (a] world.com>`,
    756 		},
    757 		{
    758 			&Address{Name: "world?=", Address: "hello (a] world.com"},
    759 			`"world?=" <hello (a] world.com>`,
    760 		},
    761 		{
    762 			// should q-encode even for invalid utf-8.
    763 			&Address{Name: string([]byte{0xed, 0xa0, 0x80}), Address: "invalid-utf8 (a] example.net"},
    764 			"=?utf-8?q?=ED=A0=80?= <invalid-utf8 (a] 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 (a] example.com>`,
    794 		`<bob.bob (a] example.com>`,
    795 		`<".bob"@example.com>`,
    796 		`<" "@example.com>`,
    797 		`<some.mail-with-dash (a] example.com>`,
    798 		`<"dot.and space"@example.com>`,
    799 		`<"very.unusual. (a] .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 (a] example.com>`,
    808 		`<def!xyz%abc (a] example.com>`,
    809 		`<_somename (a] 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 (a] example.com>`,
    845 		`<a"b(c)d,e:f;g<h>i[j\k]l (a] example.com>`,
    846 		`<just"not"right (a] example.com>`,
    847 		`<this is"not\allowed (a] example.com>`,
    848 		`<this\ still\"not\\allowed (a] example.com>`,
    849 		`<john..doe (a] example.com>`,
    850 		`<john.doe (a] example..com>`,
    851 		`<john.doe (a] example..com>`,
    852 		`<john.doe. (a] example.com>`,
    853 		`<john.doe. (a] .example.com>`,
    854 		`<.john.doe (a] example.com>`,
    855 		`<@example.com>`,
    856 		`<. (a] 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: "@lce", Address: "alice (a] example.com"},
    877 		{Name: "Bb O'Connor", Address: "bob (a] example.com"},
    878 		{Name: "???", Address: "bob (a] example.com"},
    879 		{Name: "Bb ???", Address: "bob (a] example.com"},
    880 		{Name: "Bb (Jacb)", Address: "bob (a] example.com"},
    881 		{Name: "#$%&'(),.:;<>@[]^`{|}~'", Address: "bob (a] example.com"},
    882 		// https://golang.org/issue/11292
    883 		{Name: "\"\\\x1f,\"", Address: "0@0"},
    884 		// https://golang.org/issue/12782
    885 		{Name: "na, me", Address: "test.mail (a] 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 }
    902