David Symonds | 32e3644 | 2011-06-01 14:10:21 +1000 | [diff] [blame] | 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" |
Alexandre Cesaro | 1defd22 | 2015-05-25 18:58:19 +0200 | [diff] [blame] | 9 | "io" |
David Symonds | 32e3644 | 2011-06-01 14:10:21 +1000 | [diff] [blame] | 10 | "io/ioutil" |
Alexandre Cesaro | 1defd22 | 2015-05-25 18:58:19 +0200 | [diff] [blame] | 11 | "mime" |
David Symonds | 32e3644 | 2011-06-01 14:10:21 +1000 | [diff] [blame] | 12 | "reflect" |
David Crawshaw | 8bc1bfb | 2014-05-07 05:58:36 -0400 | [diff] [blame] | 13 | "strings" |
David Symonds | 32e3644 | 2011-06-01 14:10:21 +1000 | [diff] [blame] | 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 |
Russ Cox | 03823b8 | 2011-11-30 12:01:46 -0500 | [diff] [blame] | 88 | exp time.Time |
David Symonds | 32e3644 | 2011-06-01 14:10:21 +1000 | [diff] [blame] | 89 | }{ |
| 90 | // RFC 5322, Appendix A.1.1 |
| 91 | { |
| 92 | "Fri, 21 Nov 1997 09:55:06 -0600", |
Russ Cox | 03823b8 | 2011-11-30 12:01:46 -0500 | [diff] [blame] | 93 | time.Date(1997, 11, 21, 9, 55, 6, 0, time.FixedZone("", -6*60*60)), |
David Symonds | 32e3644 | 2011-06-01 14:10:21 +1000 | [diff] [blame] | 94 | }, |
Dan Peterson | 022548c | 2016-04-12 16:58:56 -0300 | [diff] [blame] | 95 | // RFC 5322, Appendix A.6.2 |
David Symonds | 32e3644 | 2011-06-01 14:10:21 +1000 | [diff] [blame] | 96 | // Obsolete date. |
| 97 | { |
| 98 | "21 Nov 97 09:55:06 GMT", |
Russ Cox | 03823b8 | 2011-11-30 12:01:46 -0500 | [diff] [blame] | 99 | time.Date(1997, 11, 21, 9, 55, 6, 0, time.FixedZone("GMT", 0)), |
David Symonds | 32e3644 | 2011-06-01 14:10:21 +1000 | [diff] [blame] | 100 | }, |
Bill Thiede | 132dbb6 | 2012-05-25 09:19:21 +1000 | [diff] [blame] | 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 | }, |
David Symonds | 32e3644 | 2011-06-01 14:10:21 +1000 | [diff] [blame] | 106 | } |
| 107 | for _, test := range tests { |
| 108 | hdr := Header{ |
| 109 | "Date": []string{test.dateStr}, |
| 110 | } |
| 111 | date, err := hdr.Date() |
| 112 | if err != nil { |
Russ Cox | fc88a0f | 2016-10-20 16:43:09 -0400 | [diff] [blame] | 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) |
David Symonds | 32e3644 | 2011-06-01 14:10:21 +1000 | [diff] [blame] | 116 | } |
Russ Cox | fc88a0f | 2016-10-20 16:43:09 -0400 | [diff] [blame] | 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) |
David Symonds | 32e3644 | 2011-06-01 14:10:21 +1000 | [diff] [blame] | 123 | } |
| 124 | } |
| 125 | } |
David Symonds | ff0198b | 2011-06-06 16:46:14 +1000 | [diff] [blame] | 126 | |
David Crawshaw | 8bc1bfb | 2014-05-07 05:58:36 -0400 | [diff] [blame] | 127 | func TestAddressParsingError(t *testing.T) { |
Hiroshi Ioka | e7538df | 2016-03-04 02:54:51 +0900 | [diff] [blame] | 128 | mustErrTestCases := [...]struct { |
| 129 | text string |
| 130 | wantErrText string |
| 131 | }{ |
Minaev Mike | 8598396 | 2017-08-07 08:22:21 +0000 | [diff] [blame] | 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"}, |
Mihail Minaev | 6f2e5f9 | 2017-09-26 11:47:49 +0000 | [diff] [blame] | 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"}, |
David Crawshaw | 8bc1bfb | 2014-05-07 05:58:36 -0400 | [diff] [blame] | 147 | } |
David Crawshaw | 8bc1bfb | 2014-05-07 05:58:36 -0400 | [diff] [blame] | 148 | |
Hiroshi Ioka | e7538df | 2016-03-04 02:54:51 +0900 | [diff] [blame] | 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 | } |
Ingo Oeser | a4f057b | 2015-09-04 18:22:56 +0200 | [diff] [blame] | 154 | } |
| 155 | } |
| 156 | |
David Symonds | ff0198b | 2011-06-06 16:46:14 +1000 | [diff] [blame] | 157 | func TestAddressParsing(t *testing.T) { |
| 158 | tests := []struct { |
| 159 | addrsStr string |
| 160 | exp []*Address |
| 161 | }{ |
| 162 | // Bare address |
| 163 | { |
| 164 | `jdoe@machine.example`, |
Russ Cox | dcf1d7b | 2011-12-02 14:14:25 -0500 | [diff] [blame] | 165 | []*Address{{ |
David Symonds | ff0198b | 2011-06-06 16:46:14 +1000 | [diff] [blame] | 166 | Address: "jdoe@machine.example", |
| 167 | }}, |
| 168 | }, |
| 169 | // RFC 5322, Appendix A.1.1 |
| 170 | { |
| 171 | `John Doe <jdoe@machine.example>`, |
Russ Cox | dcf1d7b | 2011-12-02 14:14:25 -0500 | [diff] [blame] | 172 | []*Address{{ |
David Symonds | ff0198b | 2011-06-06 16:46:14 +1000 | [diff] [blame] | 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>`, |
Russ Cox | dcf1d7b | 2011-12-02 14:14:25 -0500 | [diff] [blame] | 180 | []*Address{{ |
David Symonds | ff0198b | 2011-06-06 16:46:14 +1000 | [diff] [blame] | 181 | Name: "Joe Q. Public", |
| 182 | Address: "john.q.public@example.com", |
| 183 | }}, |
| 184 | }, |
| 185 | { |
Guilherme Rezende | 738acbc | 2017-07-24 17:04:02 -0300 | [diff] [blame] | 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 | { |
David Symonds | ff0198b | 2011-06-06 16:46:14 +1000 | [diff] [blame] | 214 | `Mary Smith <mary@x.test>, jdoe@example.org, Who? <one@y.test>`, |
| 215 | []*Address{ |
Russ Cox | dcf1d7b | 2011-12-02 14:14:25 -0500 | [diff] [blame] | 216 | { |
David Symonds | ff0198b | 2011-06-06 16:46:14 +1000 | [diff] [blame] | 217 | Name: "Mary Smith", |
| 218 | Address: "mary@x.test", |
| 219 | }, |
Russ Cox | dcf1d7b | 2011-12-02 14:14:25 -0500 | [diff] [blame] | 220 | { |
David Symonds | ff0198b | 2011-06-06 16:46:14 +1000 | [diff] [blame] | 221 | Address: "jdoe@example.org", |
| 222 | }, |
Russ Cox | dcf1d7b | 2011-12-02 14:14:25 -0500 | [diff] [blame] | 223 | { |
David Symonds | ff0198b | 2011-06-06 16:46:14 +1000 | [diff] [blame] | 224 | Name: "Who?", |
| 225 | Address: "one@y.test", |
| 226 | }, |
| 227 | }, |
| 228 | }, |
| 229 | { |
| 230 | `<boss@nil.test>, "Giant; \"Big\" Box" <sysservices@example.net>`, |
| 231 | []*Address{ |
Russ Cox | dcf1d7b | 2011-12-02 14:14:25 -0500 | [diff] [blame] | 232 | { |
David Symonds | ff0198b | 2011-06-06 16:46:14 +1000 | [diff] [blame] | 233 | Address: "boss@nil.test", |
| 234 | }, |
Russ Cox | dcf1d7b | 2011-12-02 14:14:25 -0500 | [diff] [blame] | 235 | { |
David Symonds | ff0198b | 2011-06-06 16:46:14 +1000 | [diff] [blame] | 236 | Name: `Giant; "Big" Box`, |
| 237 | Address: "sysservices@example.net", |
| 238 | }, |
| 239 | }, |
| 240 | }, |
Guilherme Rezende | 738acbc | 2017-07-24 17:04:02 -0300 | [diff] [blame] | 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 | }, |
David Symonds | ff0198b | 2011-06-06 16:46:14 +1000 | [diff] [blame] | 249 | // RFC 5322, Appendix A.1.3 |
Mihail Minaev | 6f2e5f9 | 2017-09-26 11:47:49 +0000 | [diff] [blame] | 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 | }, |
David Symonds | ffd01f2 | 2011-06-09 10:18:36 +1000 | [diff] [blame] | 297 | // RFC 2047 "Q"-encoded ISO-8859-1 address. |
| 298 | { |
| 299 | `=?iso-8859-1?q?J=F6rg_Doe?= <joerg@example.com>`, |
| 300 | []*Address{ |
Russ Cox | dcf1d7b | 2011-12-02 14:14:25 -0500 | [diff] [blame] | 301 | { |
David Symonds | ffd01f2 | 2011-06-09 10:18:36 +1000 | [diff] [blame] | 302 | Name: `Jörg Doe`, |
| 303 | Address: "joerg@example.com", |
| 304 | }, |
| 305 | }, |
| 306 | }, |
Russ Cox | 06e4b06 | 2014-09-16 17:40:33 -0400 | [diff] [blame] | 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 | }, |
David Symonds | ffd01f2 | 2011-06-09 10:18:36 +1000 | [diff] [blame] | 317 | // RFC 2047 "Q"-encoded UTF-8 address. |
| 318 | { |
| 319 | `=?utf-8?q?J=C3=B6rg_Doe?= <joerg@example.com>`, |
| 320 | []*Address{ |
Russ Cox | dcf1d7b | 2011-12-02 14:14:25 -0500 | [diff] [blame] | 321 | { |
David Symonds | ffd01f2 | 2011-06-09 10:18:36 +1000 | [diff] [blame] | 322 | Name: `Jörg Doe`, |
| 323 | Address: "joerg@example.com", |
| 324 | }, |
| 325 | }, |
| 326 | }, |
Hiroshi Ioka | 6491496 | 2017-03-04 19:47:19 +0900 | [diff] [blame] | 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 | }, |
David Symonds | 63639dd | 2011-06-10 08:47:27 +1000 | [diff] [blame] | 337 | // RFC 2047, Section 8. |
| 338 | { |
| 339 | `=?ISO-8859-1?Q?Andr=E9?= Pirard <PIRARD@vm1.ulg.ac.be>`, |
| 340 | []*Address{ |
Russ Cox | dcf1d7b | 2011-12-02 14:14:25 -0500 | [diff] [blame] | 341 | { |
David Symonds | 63639dd | 2011-06-10 08:47:27 +1000 | [diff] [blame] | 342 | Name: `André Pirard`, |
| 343 | Address: "PIRARD@vm1.ulg.ac.be", |
| 344 | }, |
| 345 | }, |
| 346 | }, |
David Symonds | 371aa14 | 2011-06-14 17:32:47 +1000 | [diff] [blame] | 347 | // Custom example of RFC 2047 "B"-encoded ISO-8859-1 address. |
| 348 | { |
| 349 | `=?ISO-8859-1?B?SvZyZw==?= <joerg@example.com>`, |
| 350 | []*Address{ |
Russ Cox | dcf1d7b | 2011-12-02 14:14:25 -0500 | [diff] [blame] | 351 | { |
David Symonds | 371aa14 | 2011-06-14 17:32:47 +1000 | [diff] [blame] | 352 | Name: `Jörg`, |
| 353 | Address: "joerg@example.com", |
| 354 | }, |
| 355 | }, |
| 356 | }, |
| 357 | // Custom example of RFC 2047 "B"-encoded UTF-8 address. |
| 358 | { |
David Symonds | 371aa14 | 2011-06-14 17:32:47 +1000 | [diff] [blame] | 359 | `=?UTF-8?B?SsO2cmc=?= <joerg@example.com>`, |
| 360 | []*Address{ |
Russ Cox | dcf1d7b | 2011-12-02 14:14:25 -0500 | [diff] [blame] | 361 | { |
David Symonds | 371aa14 | 2011-06-14 17:32:47 +1000 | [diff] [blame] | 362 | Name: `Jörg`, |
| 363 | Address: "joerg@example.com", |
| 364 | }, |
| 365 | }, |
| 366 | }, |
Ryan Slade | 73b8baa | 2013-08-08 10:00:24 -0700 | [diff] [blame] | 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 | }, |
Conrad Irwin | 7f52b43 | 2016-02-19 10:12:44 -0800 | [diff] [blame] | 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 | }, |
Russ Cox | 2bafbe1 | 2016-10-26 16:48:53 -0400 | [diff] [blame] | 417 | // Issue 14866 |
| 418 | { |
| 419 | `"" <emptystring@example.com>`, |
| 420 | []*Address{ |
| 421 | { |
| 422 | Name: "", |
| 423 | Address: "emptystring@example.com", |
| 424 | }, |
| 425 | }, |
| 426 | }, |
Minaev Mike | 8598396 | 2017-08-07 08:22:21 +0000 | [diff] [blame] | 427 | // CFWS |
| 428 | { |
Michael Stapelberg | fcee189 | 2017-11-14 04:46:03 -0800 | [diff] [blame] | 429 | `<cfws@example.com> (CFWS (cfws)) (another comment)`, |
Minaev Mike | 8598396 | 2017-08-07 08:22:21 +0000 | [diff] [blame] | 430 | []*Address{ |
| 431 | { |
| 432 | Name: "", |
| 433 | Address: "cfws@example.com", |
| 434 | }, |
| 435 | }, |
| 436 | }, |
| 437 | { |
Michael Stapelberg | fcee189 | 2017-11-14 04:46:03 -0800 | [diff] [blame] | 438 | `<cfws@example.com> () (another comment), <cfws2@example.com> (another)`, |
Minaev Mike | 8598396 | 2017-08-07 08:22:21 +0000 | [diff] [blame] | 439 | []*Address{ |
| 440 | { |
| 441 | Name: "", |
| 442 | Address: "cfws@example.com", |
| 443 | }, |
| 444 | { |
| 445 | Name: "", |
| 446 | Address: "cfws2@example.com", |
| 447 | }, |
| 448 | }, |
| 449 | }, |
Michael Stapelberg | fcee189 | 2017-11-14 04:46:03 -0800 | [diff] [blame] | 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 | }, |
David Symonds | ff0198b | 2011-06-06 16:46:14 +1000 | [diff] [blame] | 510 | } |
| 511 | for _, test := range tests { |
Graham Miller | 9f807fc | 2012-10-05 10:08:54 +1000 | [diff] [blame] | 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) |
David Symonds | ff0198b | 2011-06-06 16:46:14 +1000 | [diff] [blame] | 524 | if err != nil { |
Graham Miller | 9f807fc | 2012-10-05 10:08:54 +1000 | [diff] [blame] | 525 | t.Errorf("Failed parsing (list) %q: %v", test.addrsStr, err) |
David Symonds | ff0198b | 2011-06-06 16:46:14 +1000 | [diff] [blame] | 526 | continue |
| 527 | } |
| 528 | if !reflect.DeepEqual(addrs, test.exp) { |
Graham Miller | 9f807fc | 2012-10-05 10:08:54 +1000 | [diff] [blame] | 529 | t.Errorf("Parse (list) of %q: got %+v, want %+v", test.addrsStr, addrs, test.exp) |
David Symonds | ff0198b | 2011-06-06 16:46:14 +1000 | [diff] [blame] | 530 | } |
| 531 | } |
| 532 | } |
David Symonds | 5c32c96 | 2011-06-08 13:32:47 +1000 | [diff] [blame] | 533 | |
Alexandre Cesaro | 1defd22 | 2015-05-25 18:58:19 +0200 | [diff] [blame] | 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.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 | |
Russ Cox | e8cc083 | 2015-12-03 16:00:04 -0500 | [diff] [blame] | 703 | func TestAddressString(t *testing.T) { |
David Symonds | 5c32c96 | 2011-06-08 13:32:47 +1000 | [diff] [blame] | 704 | tests := []struct { |
| 705 | addr *Address |
| 706 | exp string |
| 707 | }{ |
| 708 | { |
| 709 | &Address{Address: "bob@example.com"}, |
| 710 | "<bob@example.com>", |
| 711 | }, |
MathiasB | daaa450 | 2015-04-10 12:14:16 +0200 | [diff] [blame] | 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 | }, |
David Symonds | 5c32c96 | 2011-06-08 13:32:47 +1000 | [diff] [blame] | 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 | }, |
Jakub Ryszard Czarnowicz | d3b9567 | 2014-02-07 10:49:10 +1100 | [diff] [blame] | 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 | }, |
David Symonds | 1d75b40 | 2015-08-11 15:05:12 +1000 | [diff] [blame] | 737 | { // https://golang.org/issue/12098 |
| 738 | &Address{Name: "Rob", Address: ""}, |
David Symonds | 1052b43 | 2015-08-11 16:36:40 +1000 | [diff] [blame] | 739 | `"Rob" <@>`, |
David Symonds | 1d75b40 | 2015-08-11 15:05:12 +1000 | [diff] [blame] | 740 | }, |
| 741 | { // https://golang.org/issue/12098 |
| 742 | &Address{Name: "Rob", Address: "@"}, |
| 743 | `"Rob" <@>`, |
| 744 | }, |
Alexandre Cesaro | 2cb265d | 2015-10-20 17:30:21 +0200 | [diff] [blame] | 745 | { |
| 746 | &Address{Name: "Böb, Jacöb", Address: "bob@example.com"}, |
| 747 | `=?utf-8?b?QsO2YiwgSmFjw7Zi?= <bob@example.com>`, |
| 748 | }, |
Russ Cox | e8cc083 | 2015-12-03 16:00:04 -0500 | [diff] [blame] | 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 | }, |
Conrad Irwin | 7f52b43 | 2016-02-19 10:12:44 -0800 | [diff] [blame] | 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 | }, |
David Symonds | 5c32c96 | 2011-06-08 13:32:47 +1000 | [diff] [blame] | 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) |
Russ Cox | e8cc083 | 2015-12-03 16:00:04 -0500 | [diff] [blame] | 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 | } |
David Symonds | 5c32c96 | 2011-06-08 13:32:47 +1000 | [diff] [blame] | 784 | } |
| 785 | } |
| 786 | } |
MathiasB | daaa450 | 2015-04-10 12:14:16 +0200 | [diff] [blame] | 787 | |
| 788 | // Check if all valid addresses can be parsed, formatted and parsed again |
| 789 | func TestAddressParsingAndFormatting(t *testing.T) { |
Joe Tsai | b53088a | 2017-12-01 00:59:45 +0000 | [diff] [blame] | 790 | |
MathiasB | daaa450 | 2015-04-10 12:14:16 +0200 | [diff] [blame] | 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>`, |
MathiasB | bd1efd5 | 2015-07-31 12:25:06 +0200 | [diff] [blame] | 818 | `<"0:"@0>`, |
MathiasB | daaa450 | 2015-04-10 12:14:16 +0200 | [diff] [blame] | 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>`, |
MathiasB | bd1efd5 | 2015-07-31 12:25:06 +0200 | [diff] [blame] | 860 | `<""@0>`, |
MathiasB | daaa450 | 2015-04-10 12:14:16 +0200 | [diff] [blame] | 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 | } |
Alexandre Cesaro | 2cb265d | 2015-10-20 17:30:21 +0200 | [diff] [blame] | 873 | |
| 874 | func TestAddressFormattingAndParsing(t *testing.T) { |
| 875 | tests := []*Address{ |
Mikio Hara | d41d473 | 2015-12-04 10:58:13 +0900 | [diff] [blame] | 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"}, |
Alexandre Cesaro | 2cb265d | 2015-10-20 17:30:21 +0200 | [diff] [blame] | 882 | // https://golang.org/issue/11292 |
Mikio Hara | d41d473 | 2015-12-04 10:58:13 +0900 | [diff] [blame] | 883 | {Name: "\"\\\x1f,\"", Address: "0@0"}, |
Alexandre Cesaro | 2cb265d | 2015-10-20 17:30:21 +0200 | [diff] [blame] | 884 | // https://golang.org/issue/12782 |
Mikio Hara | d41d473 | 2015-12-04 10:58:13 +0900 | [diff] [blame] | 885 | {Name: "naé, mée", Address: "test.mail@gmail.com"}, |
Alexandre Cesaro | 2cb265d | 2015-10-20 17:30:21 +0200 | [diff] [blame] | 886 | } |
| 887 | |
Alexandre Cesaro | 69c09af | 2015-12-02 23:28:49 +0100 | [diff] [blame] | 888 | for i, test := range tests { |
Alexandre Cesaro | 2cb265d | 2015-10-20 17:30:21 +0200 | [diff] [blame] | 889 | parsed, err := ParseAddress(test.String()) |
| 890 | if err != nil { |
Alexandre Cesaro | 69c09af | 2015-12-02 23:28:49 +0100 | [diff] [blame] | 891 | t.Errorf("test #%d: ParseAddr(%q) error: %v", i, test.String(), err) |
Alexandre Cesaro | 2cb265d | 2015-10-20 17:30:21 +0200 | [diff] [blame] | 892 | continue |
| 893 | } |
| 894 | if parsed.Name != test.Name { |
Alexandre Cesaro | 69c09af | 2015-12-02 23:28:49 +0100 | [diff] [blame] | 895 | t.Errorf("test #%d: Parsed name = %q; want %q", i, parsed.Name, test.Name) |
Alexandre Cesaro | 2cb265d | 2015-10-20 17:30:21 +0200 | [diff] [blame] | 896 | } |
| 897 | if parsed.Address != test.Address { |
Alexandre Cesaro | 69c09af | 2015-12-02 23:28:49 +0100 | [diff] [blame] | 898 | t.Errorf("test #%d: Parsed address = %q; want %q", i, parsed.Address, test.Address) |
Alexandre Cesaro | 2cb265d | 2015-10-20 17:30:21 +0200 | [diff] [blame] | 899 | } |
| 900 | } |
| 901 | } |