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