|
1 | 1 | import io
|
| 2 | +import re |
2 | 3 | import textwrap
|
3 | 4 | import unittest
|
4 | 5 | from email import message_from_string, message_from_bytes
|
@@ -288,6 +289,28 @@ def test_keep_long_encoded_newlines(self):
|
288 | 289 | g.flatten(msg)
|
289 | 290 | self.assertEqual(s.getvalue(), self.typ(expected))
|
290 | 291 |
|
| 292 | + def test_non_ascii_addr_spec_raises(self): |
| 293 | + # RFC2047 encoded-word is not permitted in any part of an addr-spec. |
| 294 | + # (See also test_non_ascii_addr_spec_preserved below.) |
| 295 | + g = self.genclass(self.ioclass(), policy=self.policy.clone(utf8=False)) |
| 296 | + cases = [ |
| 297 | + 'wők@example.com', |
| 298 | + 'wok@exàmple.com', |
| 299 | + 'wők@exàmple.com', |
| 300 | + '"Name, for display" <wők@example.com>', |
| 301 | + 'Näyttönimi <wők@example.com>', |
| 302 | + ] |
| 303 | + for address in cases: |
| 304 | + with self.subTest(address=address): |
| 305 | + msg = EmailMessage() |
| 306 | + msg['To'] = address |
| 307 | + expected_error = re.escape( |
| 308 | + "Non-ASCII address requires policy with utf8=True:" |
| 309 | + " '{}'".format(msg['To'].addresses[0].addr_spec) |
| 310 | + ) |
| 311 | + with self.assertRaisesRegex(ValueError, expected_error): |
| 312 | + g.flatten(msg) |
| 313 | + |
291 | 314 |
|
292 | 315 | class TestGenerator(TestGeneratorBase, TestEmailBase):
|
293 | 316 |
|
@@ -432,12 +455,12 @@ def test_cte_type_7bit_transforms_8bit_cte(self):
|
432 | 455 |
|
433 | 456 | def test_smtputf8_policy(self):
|
434 | 457 | msg = EmailMessage()
|
435 |
| - msg['From'] = "Páolo <főo@bar.com>" |
| 458 | + msg['From'] = "Páolo <főo@bàr.com>" |
436 | 459 | msg['To'] = 'Dinsdale'
|
437 | 460 | msg['Subject'] = 'Nudge nudge, wink, wink \u1F609'
|
438 | 461 | msg.set_content("oh là là, know what I mean, know what I mean?")
|
439 | 462 | expected = textwrap.dedent("""\
|
440 |
| - From: Páolo <főo@bar.com> |
| 463 | + From: Páolo <főo@bàr.com> |
441 | 464 | To: Dinsdale
|
442 | 465 | Subject: Nudge nudge, wink, wink \u1F609
|
443 | 466 | Content-Type: text/plain; charset="utf-8"
|
@@ -472,6 +495,37 @@ def test_smtp_policy(self):
|
472 | 495 | g.flatten(msg)
|
473 | 496 | self.assertEqual(s.getvalue(), expected)
|
474 | 497 |
|
| 498 | + def test_non_ascii_addr_spec_preserved(self): |
| 499 | + # A defective non-ASCII addr-spec parsed from the original |
| 500 | + # message is left unchanged when flattening. |
| 501 | + # (See also test_non_ascii_addr_spec_raises above.) |
| 502 | + source = ( |
| 503 | + 'To: jörg@example.com, "But a long name still works with refold_source" <jörg@example.com>' |
| 504 | + ).encode() |
| 505 | + expected = ( |
| 506 | + b'To: j\xc3\xb6rg@example.com,\n' |
| 507 | + b' "But a long name still works with refold_source" <j\xc3\xb6rg@example.com>\n' |
| 508 | + b'\n' |
| 509 | + ) |
| 510 | + msg = message_from_bytes(source, policy=policy.default) |
| 511 | + s = io.BytesIO() |
| 512 | + g = BytesGenerator(s, policy=policy.default) |
| 513 | + g.flatten(msg) |
| 514 | + self.assertEqual(s.getvalue(), expected) |
| 515 | + |
| 516 | + def test_idna_encoding_preserved(self): |
| 517 | + # Nothing tries to decode a pre-encoded IDNA domain. |
| 518 | + msg = EmailMessage() |
| 519 | + msg["To"] = Address( |
| 520 | + username='jörg', |
| 521 | + domain='☕.example'.encode('idna').decode() # IDNA 2003 |
| 522 | + ) |
| 523 | + expected = 'To: jörg@xn--53h.example\n\n'.encode() |
| 524 | + s = io.BytesIO() |
| 525 | + g = BytesGenerator(s, policy=policy.default.clone(utf8=True)) |
| 526 | + g.flatten(msg) |
| 527 | + self.assertEqual(s.getvalue(), expected) |
| 528 | + |
475 | 529 |
|
476 | 530 | if __name__ == '__main__':
|
477 | 531 | unittest.main()
|
0 commit comments