github.com/zebozhuang/go@v0.0.0-20200207033046-f8a98f6f5c5d/src/net/mail/message_test.go (about) 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 } 141 142 for i, tc := range mustErrTestCases { 143 _, err := ParseAddress(tc.text) 144 if err == nil || !strings.Contains(err.Error(), tc.wantErrText) { 145 t.Errorf(`mail.ParseAddress(%q) #%d want %q, got %v`, tc.text, i, tc.wantErrText, err) 146 } 147 } 148 } 149 150 func TestAddressParsing(t *testing.T) { 151 tests := []struct { 152 addrsStr string 153 exp []*Address 154 }{ 155 // Bare address 156 { 157 `jdoe@machine.example`, 158 []*Address{{ 159 Address: "jdoe@machine.example", 160 }}, 161 }, 162 // RFC 5322, Appendix A.1.1 163 { 164 `John Doe <jdoe@machine.example>`, 165 []*Address{{ 166 Name: "John Doe", 167 Address: "jdoe@machine.example", 168 }}, 169 }, 170 // RFC 5322, Appendix A.1.2 171 { 172 `"Joe Q. Public" <john.q.public@example.com>`, 173 []*Address{{ 174 Name: "Joe Q. Public", 175 Address: "john.q.public@example.com", 176 }}, 177 }, 178 { 179 `Mary Smith <mary@x.test>, jdoe@example.org, Who? <one@y.test>`, 180 []*Address{ 181 { 182 Name: "Mary Smith", 183 Address: "mary@x.test", 184 }, 185 { 186 Address: "jdoe@example.org", 187 }, 188 { 189 Name: "Who?", 190 Address: "one@y.test", 191 }, 192 }, 193 }, 194 { 195 `<boss@nil.test>, "Giant; \"Big\" Box" <sysservices@example.net>`, 196 []*Address{ 197 { 198 Address: "boss@nil.test", 199 }, 200 { 201 Name: `Giant; "Big" Box`, 202 Address: "sysservices@example.net", 203 }, 204 }, 205 }, 206 // RFC 5322, Appendix A.1.3 207 // TODO(dsymonds): Group addresses. 208 209 // RFC 2047 "Q"-encoded ISO-8859-1 address. 210 { 211 `=?iso-8859-1?q?J=F6rg_Doe?= <joerg@example.com>`, 212 []*Address{ 213 { 214 Name: `Jörg Doe`, 215 Address: "joerg@example.com", 216 }, 217 }, 218 }, 219 // RFC 2047 "Q"-encoded US-ASCII address. Dumb but legal. 220 { 221 `=?us-ascii?q?J=6Frg_Doe?= <joerg@example.com>`, 222 []*Address{ 223 { 224 Name: `Jorg Doe`, 225 Address: "joerg@example.com", 226 }, 227 }, 228 }, 229 // RFC 2047 "Q"-encoded UTF-8 address. 230 { 231 `=?utf-8?q?J=C3=B6rg_Doe?= <joerg@example.com>`, 232 []*Address{ 233 { 234 Name: `Jörg Doe`, 235 Address: "joerg@example.com", 236 }, 237 }, 238 }, 239 // RFC 2047 "Q"-encoded UTF-8 address with multiple encoded-words. 240 { 241 `=?utf-8?q?J=C3=B6rg?= =?utf-8?q?Doe?= <joerg@example.com>`, 242 []*Address{ 243 { 244 Name: `JörgDoe`, 245 Address: "joerg@example.com", 246 }, 247 }, 248 }, 249 // RFC 2047, Section 8. 250 { 251 `=?ISO-8859-1?Q?Andr=E9?= Pirard <PIRARD@vm1.ulg.ac.be>`, 252 []*Address{ 253 { 254 Name: `André Pirard`, 255 Address: "PIRARD@vm1.ulg.ac.be", 256 }, 257 }, 258 }, 259 // Custom example of RFC 2047 "B"-encoded ISO-8859-1 address. 260 { 261 `=?ISO-8859-1?B?SvZyZw==?= <joerg@example.com>`, 262 []*Address{ 263 { 264 Name: `Jörg`, 265 Address: "joerg@example.com", 266 }, 267 }, 268 }, 269 // Custom example of RFC 2047 "B"-encoded UTF-8 address. 270 { 271 `=?UTF-8?B?SsO2cmc=?= <joerg@example.com>`, 272 []*Address{ 273 { 274 Name: `Jörg`, 275 Address: "joerg@example.com", 276 }, 277 }, 278 }, 279 // Custom example with "." in name. For issue 4938 280 { 281 `Asem H. <noreply@example.com>`, 282 []*Address{ 283 { 284 Name: `Asem H.`, 285 Address: "noreply@example.com", 286 }, 287 }, 288 }, 289 // RFC 6532 3.2.3, qtext /= UTF8-non-ascii 290 { 291 `"Gø Pher" <gopher@example.com>`, 292 []*Address{ 293 { 294 Name: `Gø Pher`, 295 Address: "gopher@example.com", 296 }, 297 }, 298 }, 299 // RFC 6532 3.2, atext /= UTF8-non-ascii 300 { 301 `µ <micro@example.com>`, 302 []*Address{ 303 { 304 Name: `µ`, 305 Address: "micro@example.com", 306 }, 307 }, 308 }, 309 // RFC 6532 3.2.2, local address parts allow UTF-8 310 { 311 `Micro <µ@example.com>`, 312 []*Address{ 313 { 314 Name: `Micro`, 315 Address: "µ@example.com", 316 }, 317 }, 318 }, 319 // RFC 6532 3.2.4, domains parts allow UTF-8 320 { 321 `Micro <micro@µ.example.com>`, 322 []*Address{ 323 { 324 Name: `Micro`, 325 Address: "micro@µ.example.com", 326 }, 327 }, 328 }, 329 // Issue 14866 330 { 331 `"" <emptystring@example.com>`, 332 []*Address{ 333 { 334 Name: "", 335 Address: "emptystring@example.com", 336 }, 337 }, 338 }, 339 } 340 for _, test := range tests { 341 if len(test.exp) == 1 { 342 addr, err := ParseAddress(test.addrsStr) 343 if err != nil { 344 t.Errorf("Failed parsing (single) %q: %v", test.addrsStr, err) 345 continue 346 } 347 if !reflect.DeepEqual([]*Address{addr}, test.exp) { 348 t.Errorf("Parse (single) of %q: got %+v, want %+v", test.addrsStr, addr, test.exp) 349 } 350 } 351 352 addrs, err := ParseAddressList(test.addrsStr) 353 if err != nil { 354 t.Errorf("Failed parsing (list) %q: %v", test.addrsStr, err) 355 continue 356 } 357 if !reflect.DeepEqual(addrs, test.exp) { 358 t.Errorf("Parse (list) of %q: got %+v, want %+v", test.addrsStr, addrs, test.exp) 359 } 360 } 361 } 362 363 func TestAddressParser(t *testing.T) { 364 tests := []struct { 365 addrsStr string 366 exp []*Address 367 }{ 368 // Bare address 369 { 370 `jdoe@machine.example`, 371 []*Address{{ 372 Address: "jdoe@machine.example", 373 }}, 374 }, 375 // RFC 5322, Appendix A.1.1 376 { 377 `John Doe <jdoe@machine.example>`, 378 []*Address{{ 379 Name: "John Doe", 380 Address: "jdoe@machine.example", 381 }}, 382 }, 383 // RFC 5322, Appendix A.1.2 384 { 385 `"Joe Q. Public" <john.q.public@example.com>`, 386 []*Address{{ 387 Name: "Joe Q. Public", 388 Address: "john.q.public@example.com", 389 }}, 390 }, 391 { 392 `Mary Smith <mary@x.test>, jdoe@example.org, Who? <one@y.test>`, 393 []*Address{ 394 { 395 Name: "Mary Smith", 396 Address: "mary@x.test", 397 }, 398 { 399 Address: "jdoe@example.org", 400 }, 401 { 402 Name: "Who?", 403 Address: "one@y.test", 404 }, 405 }, 406 }, 407 { 408 `<boss@nil.test>, "Giant; \"Big\" Box" <sysservices@example.net>`, 409 []*Address{ 410 { 411 Address: "boss@nil.test", 412 }, 413 { 414 Name: `Giant; "Big" Box`, 415 Address: "sysservices@example.net", 416 }, 417 }, 418 }, 419 // RFC 2047 "Q"-encoded ISO-8859-1 address. 420 { 421 `=?iso-8859-1?q?J=F6rg_Doe?= <joerg@example.com>`, 422 []*Address{ 423 { 424 Name: `Jörg Doe`, 425 Address: "joerg@example.com", 426 }, 427 }, 428 }, 429 // RFC 2047 "Q"-encoded US-ASCII address. Dumb but legal. 430 { 431 `=?us-ascii?q?J=6Frg_Doe?= <joerg@example.com>`, 432 []*Address{ 433 { 434 Name: `Jorg Doe`, 435 Address: "joerg@example.com", 436 }, 437 }, 438 }, 439 // RFC 2047 "Q"-encoded ISO-8859-15 address. 440 { 441 `=?ISO-8859-15?Q?J=F6rg_Doe?= <joerg@example.com>`, 442 []*Address{ 443 { 444 Name: `Jörg Doe`, 445 Address: "joerg@example.com", 446 }, 447 }, 448 }, 449 // RFC 2047 "B"-encoded windows-1252 address. 450 { 451 `=?windows-1252?q?Andr=E9?= Pirard <PIRARD@vm1.ulg.ac.be>`, 452 []*Address{ 453 { 454 Name: `André Pirard`, 455 Address: "PIRARD@vm1.ulg.ac.be", 456 }, 457 }, 458 }, 459 // Custom example of RFC 2047 "B"-encoded ISO-8859-15 address. 460 { 461 `=?ISO-8859-15?B?SvZyZw==?= <joerg@example.com>`, 462 []*Address{ 463 { 464 Name: `Jörg`, 465 Address: "joerg@example.com", 466 }, 467 }, 468 }, 469 // Custom example of RFC 2047 "B"-encoded UTF-8 address. 470 { 471 `=?UTF-8?B?SsO2cmc=?= <joerg@example.com>`, 472 []*Address{ 473 { 474 Name: `Jörg`, 475 Address: "joerg@example.com", 476 }, 477 }, 478 }, 479 // Custom example with "." in name. For issue 4938 480 { 481 `Asem H. <noreply@example.com>`, 482 []*Address{ 483 { 484 Name: `Asem H.`, 485 Address: "noreply@example.com", 486 }, 487 }, 488 }, 489 } 490 491 ap := AddressParser{WordDecoder: &mime.WordDecoder{ 492 CharsetReader: func(charset string, input io.Reader) (io.Reader, error) { 493 in, err := ioutil.ReadAll(input) 494 if err != nil { 495 return nil, err 496 } 497 498 switch charset { 499 case "iso-8859-15": 500 in = bytes.Replace(in, []byte("\xf6"), []byte("ö"), -1) 501 case "windows-1252": 502 in = bytes.Replace(in, []byte("\xe9"), []byte("é"), -1) 503 } 504 505 return bytes.NewReader(in), nil 506 }, 507 }} 508 509 for _, test := range tests { 510 if len(test.exp) == 1 { 511 addr, err := ap.Parse(test.addrsStr) 512 if err != nil { 513 t.Errorf("Failed parsing (single) %q: %v", test.addrsStr, err) 514 continue 515 } 516 if !reflect.DeepEqual([]*Address{addr}, test.exp) { 517 t.Errorf("Parse (single) of %q: got %+v, want %+v", test.addrsStr, addr, test.exp) 518 } 519 } 520 521 addrs, err := ap.ParseList(test.addrsStr) 522 if err != nil { 523 t.Errorf("Failed parsing (list) %q: %v", test.addrsStr, err) 524 continue 525 } 526 if !reflect.DeepEqual(addrs, test.exp) { 527 t.Errorf("Parse (list) of %q: got %+v, want %+v", test.addrsStr, addrs, test.exp) 528 } 529 } 530 } 531 532 func TestAddressString(t *testing.T) { 533 tests := []struct { 534 addr *Address 535 exp string 536 }{ 537 { 538 &Address{Address: "bob@example.com"}, 539 "<bob@example.com>", 540 }, 541 { // quoted local parts: RFC 5322, 3.4.1. and 3.2.4. 542 &Address{Address: `my@idiot@address@example.com`}, 543 `<"my@idiot@address"@example.com>`, 544 }, 545 { // quoted local parts 546 &Address{Address: ` @example.com`}, 547 `<" "@example.com>`, 548 }, 549 { 550 &Address{Name: "Bob", Address: "bob@example.com"}, 551 `"Bob" <bob@example.com>`, 552 }, 553 { 554 // note the ö (o with an umlaut) 555 &Address{Name: "Böb", Address: "bob@example.com"}, 556 `=?utf-8?q?B=C3=B6b?= <bob@example.com>`, 557 }, 558 { 559 &Address{Name: "Bob Jane", Address: "bob@example.com"}, 560 `"Bob Jane" <bob@example.com>`, 561 }, 562 { 563 &Address{Name: "Böb Jacöb", Address: "bob@example.com"}, 564 `=?utf-8?q?B=C3=B6b_Jac=C3=B6b?= <bob@example.com>`, 565 }, 566 { // https://golang.org/issue/12098 567 &Address{Name: "Rob", Address: ""}, 568 `"Rob" <@>`, 569 }, 570 { // https://golang.org/issue/12098 571 &Address{Name: "Rob", Address: "@"}, 572 `"Rob" <@>`, 573 }, 574 { 575 &Address{Name: "Böb, Jacöb", Address: "bob@example.com"}, 576 `=?utf-8?b?QsO2YiwgSmFjw7Zi?= <bob@example.com>`, 577 }, 578 { 579 &Address{Name: "=??Q?x?=", Address: "hello@world.com"}, 580 `"=??Q?x?=" <hello@world.com>`, 581 }, 582 { 583 &Address{Name: "=?hello", Address: "hello@world.com"}, 584 `"=?hello" <hello@world.com>`, 585 }, 586 { 587 &Address{Name: "world?=", Address: "hello@world.com"}, 588 `"world?=" <hello@world.com>`, 589 }, 590 { 591 // should q-encode even for invalid utf-8. 592 &Address{Name: string([]byte{0xed, 0xa0, 0x80}), Address: "invalid-utf8@example.net"}, 593 "=?utf-8?q?=ED=A0=80?= <invalid-utf8@example.net>", 594 }, 595 } 596 for _, test := range tests { 597 s := test.addr.String() 598 if s != test.exp { 599 t.Errorf("Address%+v.String() = %v, want %v", *test.addr, s, test.exp) 600 continue 601 } 602 603 // Check round-trip. 604 if test.addr.Address != "" && test.addr.Address != "@" { 605 a, err := ParseAddress(test.exp) 606 if err != nil { 607 t.Errorf("ParseAddress(%#q): %v", test.exp, err) 608 continue 609 } 610 if a.Name != test.addr.Name || a.Address != test.addr.Address { 611 t.Errorf("ParseAddress(%#q) = %#v, want %#v", test.exp, a, test.addr) 612 } 613 } 614 } 615 } 616 617 // Check if all valid addresses can be parsed, formatted and parsed again 618 func TestAddressParsingAndFormatting(t *testing.T) { 619 620 // Should pass 621 tests := []string{ 622 `<Bob@example.com>`, 623 `<bob.bob@example.com>`, 624 `<".bob"@example.com>`, 625 `<" "@example.com>`, 626 `<some.mail-with-dash@example.com>`, 627 `<"dot.and space"@example.com>`, 628 `<"very.unusual.@.unusual.com"@example.com>`, 629 `<admin@mailserver1>`, 630 `<postmaster@localhost>`, 631 "<#!$%&'*+-/=?^_`{}|~@example.org>", 632 `<"very.(),:;<>[]\".VERY.\"very@\\ \"very\".unusual"@strange.example.com>`, // escaped quotes 633 `<"()<>[]:,;@\\\"!#$%&'*+-/=?^_{}| ~.a"@example.org>`, // escaped backslashes 634 `<"Abc\\@def"@example.com>`, 635 `<"Joe\\Blow"@example.com>`, 636 `<test1/test2=test3@example.com>`, 637 `<def!xyz%abc@example.com>`, 638 `<_somename@example.com>`, 639 `<joe@uk>`, 640 `<~@example.com>`, 641 `<"..."@test.com>`, 642 `<"john..doe"@example.com>`, 643 `<"john.doe."@example.com>`, 644 `<".john.doe"@example.com>`, 645 `<"."@example.com>`, 646 `<".."@example.com>`, 647 `<"0:"@0>`, 648 } 649 650 for _, test := range tests { 651 addr, err := ParseAddress(test) 652 if err != nil { 653 t.Errorf("Couldn't parse address %s: %s", test, err.Error()) 654 continue 655 } 656 str := addr.String() 657 addr, err = ParseAddress(str) 658 if err != nil { 659 t.Errorf("ParseAddr(%q) error: %v", test, err) 660 continue 661 } 662 663 if addr.String() != test { 664 t.Errorf("String() round-trip = %q; want %q", addr, test) 665 continue 666 } 667 668 } 669 670 // Should fail 671 badTests := []string{ 672 `<Abc.example.com>`, 673 `<A@b@c@example.com>`, 674 `<a"b(c)d,e:f;g<h>i[j\k]l@example.com>`, 675 `<just"not"right@example.com>`, 676 `<this is"not\allowed@example.com>`, 677 `<this\ still\"not\\allowed@example.com>`, 678 `<john..doe@example.com>`, 679 `<john.doe@example..com>`, 680 `<john.doe@example..com>`, 681 `<john.doe.@example.com>`, 682 `<john.doe.@.example.com>`, 683 `<.john.doe@example.com>`, 684 `<@example.com>`, 685 `<.@example.com>`, 686 `<test@.>`, 687 `< @example.com>`, 688 `<""test""blah""@example.com>`, 689 `<""@0>`, 690 } 691 692 for _, test := range badTests { 693 _, err := ParseAddress(test) 694 if err == nil { 695 t.Errorf("Should have failed to parse address: %s", test) 696 continue 697 } 698 699 } 700 701 } 702 703 func TestAddressFormattingAndParsing(t *testing.T) { 704 tests := []*Address{ 705 {Name: "@lïce", Address: "alice@example.com"}, 706 {Name: "Böb O'Connor", Address: "bob@example.com"}, 707 {Name: "???", Address: "bob@example.com"}, 708 {Name: "Böb ???", Address: "bob@example.com"}, 709 {Name: "Böb (Jacöb)", Address: "bob@example.com"}, 710 {Name: "à#$%&'(),.:;<>@[]^`{|}~'", Address: "bob@example.com"}, 711 // https://golang.org/issue/11292 712 {Name: "\"\\\x1f,\"", Address: "0@0"}, 713 // https://golang.org/issue/12782 714 {Name: "naé, mée", Address: "test.mail@gmail.com"}, 715 } 716 717 for i, test := range tests { 718 parsed, err := ParseAddress(test.String()) 719 if err != nil { 720 t.Errorf("test #%d: ParseAddr(%q) error: %v", i, test.String(), err) 721 continue 722 } 723 if parsed.Name != test.Name { 724 t.Errorf("test #%d: Parsed name = %q; want %q", i, parsed.Name, test.Name) 725 } 726 if parsed.Address != test.Address { 727 t.Errorf("test #%d: Parsed address = %q; want %q", i, parsed.Address, test.Address) 728 } 729 } 730 }