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