17db96d56Sopenharmony_ciimport io 27db96d56Sopenharmony_ciimport textwrap 37db96d56Sopenharmony_ciimport unittest 47db96d56Sopenharmony_cifrom email import message_from_string, message_from_bytes 57db96d56Sopenharmony_cifrom email.message import EmailMessage 67db96d56Sopenharmony_cifrom email.generator import Generator, BytesGenerator 77db96d56Sopenharmony_cifrom email.headerregistry import Address 87db96d56Sopenharmony_cifrom email import policy 97db96d56Sopenharmony_cifrom test.test_email import TestEmailBase, parameterize 107db96d56Sopenharmony_ci 117db96d56Sopenharmony_ci 127db96d56Sopenharmony_ci@parameterize 137db96d56Sopenharmony_ciclass TestGeneratorBase: 147db96d56Sopenharmony_ci 157db96d56Sopenharmony_ci policy = policy.default 167db96d56Sopenharmony_ci 177db96d56Sopenharmony_ci def msgmaker(self, msg, policy=None): 187db96d56Sopenharmony_ci policy = self.policy if policy is None else policy 197db96d56Sopenharmony_ci return self.msgfunc(msg, policy=policy) 207db96d56Sopenharmony_ci 217db96d56Sopenharmony_ci refold_long_expected = { 227db96d56Sopenharmony_ci 0: textwrap.dedent("""\ 237db96d56Sopenharmony_ci To: whom_it_may_concern@example.com 247db96d56Sopenharmony_ci From: nobody_you_want_to_know@example.com 257db96d56Sopenharmony_ci Subject: We the willing led by the unknowing are doing the 267db96d56Sopenharmony_ci impossible for the ungrateful. We have done so much for so long with so little 277db96d56Sopenharmony_ci we are now qualified to do anything with nothing. 287db96d56Sopenharmony_ci 297db96d56Sopenharmony_ci None 307db96d56Sopenharmony_ci """), 317db96d56Sopenharmony_ci 40: textwrap.dedent("""\ 327db96d56Sopenharmony_ci To: whom_it_may_concern@example.com 337db96d56Sopenharmony_ci From: 347db96d56Sopenharmony_ci nobody_you_want_to_know@example.com 357db96d56Sopenharmony_ci Subject: We the willing led by the 367db96d56Sopenharmony_ci unknowing are doing the impossible for 377db96d56Sopenharmony_ci the ungrateful. We have done so much 387db96d56Sopenharmony_ci for so long with so little we are now 397db96d56Sopenharmony_ci qualified to do anything with nothing. 407db96d56Sopenharmony_ci 417db96d56Sopenharmony_ci None 427db96d56Sopenharmony_ci """), 437db96d56Sopenharmony_ci 20: textwrap.dedent("""\ 447db96d56Sopenharmony_ci To: 457db96d56Sopenharmony_ci whom_it_may_concern@example.com 467db96d56Sopenharmony_ci From: 477db96d56Sopenharmony_ci nobody_you_want_to_know@example.com 487db96d56Sopenharmony_ci Subject: We the 497db96d56Sopenharmony_ci willing led by the 507db96d56Sopenharmony_ci unknowing are doing 517db96d56Sopenharmony_ci the impossible for 527db96d56Sopenharmony_ci the ungrateful. We 537db96d56Sopenharmony_ci have done so much 547db96d56Sopenharmony_ci for so long with so 557db96d56Sopenharmony_ci little we are now 567db96d56Sopenharmony_ci qualified to do 577db96d56Sopenharmony_ci anything with 587db96d56Sopenharmony_ci nothing. 597db96d56Sopenharmony_ci 607db96d56Sopenharmony_ci None 617db96d56Sopenharmony_ci """), 627db96d56Sopenharmony_ci } 637db96d56Sopenharmony_ci refold_long_expected[100] = refold_long_expected[0] 647db96d56Sopenharmony_ci 657db96d56Sopenharmony_ci refold_all_expected = refold_long_expected.copy() 667db96d56Sopenharmony_ci refold_all_expected[0] = ( 677db96d56Sopenharmony_ci "To: whom_it_may_concern@example.com\n" 687db96d56Sopenharmony_ci "From: nobody_you_want_to_know@example.com\n" 697db96d56Sopenharmony_ci "Subject: We the willing led by the unknowing are doing the " 707db96d56Sopenharmony_ci "impossible for the ungrateful. We have done so much for " 717db96d56Sopenharmony_ci "so long with so little we are now qualified to do anything " 727db96d56Sopenharmony_ci "with nothing.\n" 737db96d56Sopenharmony_ci "\n" 747db96d56Sopenharmony_ci "None\n") 757db96d56Sopenharmony_ci refold_all_expected[100] = ( 767db96d56Sopenharmony_ci "To: whom_it_may_concern@example.com\n" 777db96d56Sopenharmony_ci "From: nobody_you_want_to_know@example.com\n" 787db96d56Sopenharmony_ci "Subject: We the willing led by the unknowing are doing the " 797db96d56Sopenharmony_ci "impossible for the ungrateful. We have\n" 807db96d56Sopenharmony_ci " done so much for so long with so little we are now qualified " 817db96d56Sopenharmony_ci "to do anything with nothing.\n" 827db96d56Sopenharmony_ci "\n" 837db96d56Sopenharmony_ci "None\n") 847db96d56Sopenharmony_ci 857db96d56Sopenharmony_ci length_params = [n for n in refold_long_expected] 867db96d56Sopenharmony_ci 877db96d56Sopenharmony_ci def length_as_maxheaderlen_parameter(self, n): 887db96d56Sopenharmony_ci msg = self.msgmaker(self.typ(self.refold_long_expected[0])) 897db96d56Sopenharmony_ci s = self.ioclass() 907db96d56Sopenharmony_ci g = self.genclass(s, maxheaderlen=n, policy=self.policy) 917db96d56Sopenharmony_ci g.flatten(msg) 927db96d56Sopenharmony_ci self.assertEqual(s.getvalue(), self.typ(self.refold_long_expected[n])) 937db96d56Sopenharmony_ci 947db96d56Sopenharmony_ci def length_as_max_line_length_policy(self, n): 957db96d56Sopenharmony_ci msg = self.msgmaker(self.typ(self.refold_long_expected[0])) 967db96d56Sopenharmony_ci s = self.ioclass() 977db96d56Sopenharmony_ci g = self.genclass(s, policy=self.policy.clone(max_line_length=n)) 987db96d56Sopenharmony_ci g.flatten(msg) 997db96d56Sopenharmony_ci self.assertEqual(s.getvalue(), self.typ(self.refold_long_expected[n])) 1007db96d56Sopenharmony_ci 1017db96d56Sopenharmony_ci def length_as_maxheaderlen_parm_overrides_policy(self, n): 1027db96d56Sopenharmony_ci msg = self.msgmaker(self.typ(self.refold_long_expected[0])) 1037db96d56Sopenharmony_ci s = self.ioclass() 1047db96d56Sopenharmony_ci g = self.genclass(s, maxheaderlen=n, 1057db96d56Sopenharmony_ci policy=self.policy.clone(max_line_length=10)) 1067db96d56Sopenharmony_ci g.flatten(msg) 1077db96d56Sopenharmony_ci self.assertEqual(s.getvalue(), self.typ(self.refold_long_expected[n])) 1087db96d56Sopenharmony_ci 1097db96d56Sopenharmony_ci def length_as_max_line_length_with_refold_none_does_not_fold(self, n): 1107db96d56Sopenharmony_ci msg = self.msgmaker(self.typ(self.refold_long_expected[0])) 1117db96d56Sopenharmony_ci s = self.ioclass() 1127db96d56Sopenharmony_ci g = self.genclass(s, policy=self.policy.clone(refold_source='none', 1137db96d56Sopenharmony_ci max_line_length=n)) 1147db96d56Sopenharmony_ci g.flatten(msg) 1157db96d56Sopenharmony_ci self.assertEqual(s.getvalue(), self.typ(self.refold_long_expected[0])) 1167db96d56Sopenharmony_ci 1177db96d56Sopenharmony_ci def length_as_max_line_length_with_refold_all_folds(self, n): 1187db96d56Sopenharmony_ci msg = self.msgmaker(self.typ(self.refold_long_expected[0])) 1197db96d56Sopenharmony_ci s = self.ioclass() 1207db96d56Sopenharmony_ci g = self.genclass(s, policy=self.policy.clone(refold_source='all', 1217db96d56Sopenharmony_ci max_line_length=n)) 1227db96d56Sopenharmony_ci g.flatten(msg) 1237db96d56Sopenharmony_ci self.assertEqual(s.getvalue(), self.typ(self.refold_all_expected[n])) 1247db96d56Sopenharmony_ci 1257db96d56Sopenharmony_ci def test_crlf_control_via_policy(self): 1267db96d56Sopenharmony_ci source = "Subject: test\r\n\r\ntest body\r\n" 1277db96d56Sopenharmony_ci expected = source 1287db96d56Sopenharmony_ci msg = self.msgmaker(self.typ(source)) 1297db96d56Sopenharmony_ci s = self.ioclass() 1307db96d56Sopenharmony_ci g = self.genclass(s, policy=policy.SMTP) 1317db96d56Sopenharmony_ci g.flatten(msg) 1327db96d56Sopenharmony_ci self.assertEqual(s.getvalue(), self.typ(expected)) 1337db96d56Sopenharmony_ci 1347db96d56Sopenharmony_ci def test_flatten_linesep_overrides_policy(self): 1357db96d56Sopenharmony_ci source = "Subject: test\n\ntest body\n" 1367db96d56Sopenharmony_ci expected = source 1377db96d56Sopenharmony_ci msg = self.msgmaker(self.typ(source)) 1387db96d56Sopenharmony_ci s = self.ioclass() 1397db96d56Sopenharmony_ci g = self.genclass(s, policy=policy.SMTP) 1407db96d56Sopenharmony_ci g.flatten(msg, linesep='\n') 1417db96d56Sopenharmony_ci self.assertEqual(s.getvalue(), self.typ(expected)) 1427db96d56Sopenharmony_ci 1437db96d56Sopenharmony_ci def test_set_mangle_from_via_policy(self): 1447db96d56Sopenharmony_ci source = textwrap.dedent("""\ 1457db96d56Sopenharmony_ci Subject: test that 1467db96d56Sopenharmony_ci from is mangled in the body! 1477db96d56Sopenharmony_ci 1487db96d56Sopenharmony_ci From time to time I write a rhyme. 1497db96d56Sopenharmony_ci """) 1507db96d56Sopenharmony_ci variants = ( 1517db96d56Sopenharmony_ci (None, True), 1527db96d56Sopenharmony_ci (policy.compat32, True), 1537db96d56Sopenharmony_ci (policy.default, False), 1547db96d56Sopenharmony_ci (policy.default.clone(mangle_from_=True), True), 1557db96d56Sopenharmony_ci ) 1567db96d56Sopenharmony_ci for p, mangle in variants: 1577db96d56Sopenharmony_ci expected = source.replace('From ', '>From ') if mangle else source 1587db96d56Sopenharmony_ci with self.subTest(policy=p, mangle_from_=mangle): 1597db96d56Sopenharmony_ci msg = self.msgmaker(self.typ(source)) 1607db96d56Sopenharmony_ci s = self.ioclass() 1617db96d56Sopenharmony_ci g = self.genclass(s, policy=p) 1627db96d56Sopenharmony_ci g.flatten(msg) 1637db96d56Sopenharmony_ci self.assertEqual(s.getvalue(), self.typ(expected)) 1647db96d56Sopenharmony_ci 1657db96d56Sopenharmony_ci def test_compat32_max_line_length_does_not_fold_when_none(self): 1667db96d56Sopenharmony_ci msg = self.msgmaker(self.typ(self.refold_long_expected[0])) 1677db96d56Sopenharmony_ci s = self.ioclass() 1687db96d56Sopenharmony_ci g = self.genclass(s, policy=policy.compat32.clone(max_line_length=None)) 1697db96d56Sopenharmony_ci g.flatten(msg) 1707db96d56Sopenharmony_ci self.assertEqual(s.getvalue(), self.typ(self.refold_long_expected[0])) 1717db96d56Sopenharmony_ci 1727db96d56Sopenharmony_ci def test_rfc2231_wrapping(self): 1737db96d56Sopenharmony_ci # This is pretty much just to make sure we don't have an infinite 1747db96d56Sopenharmony_ci # loop; I don't expect anyone to hit this in the field. 1757db96d56Sopenharmony_ci msg = self.msgmaker(self.typ(textwrap.dedent("""\ 1767db96d56Sopenharmony_ci To: nobody 1777db96d56Sopenharmony_ci Content-Disposition: attachment; 1787db96d56Sopenharmony_ci filename="afilenamelongenoghtowraphere" 1797db96d56Sopenharmony_ci 1807db96d56Sopenharmony_ci None 1817db96d56Sopenharmony_ci """))) 1827db96d56Sopenharmony_ci expected = textwrap.dedent("""\ 1837db96d56Sopenharmony_ci To: nobody 1847db96d56Sopenharmony_ci Content-Disposition: attachment; 1857db96d56Sopenharmony_ci filename*0*=us-ascii''afilename; 1867db96d56Sopenharmony_ci filename*1*=longenoghtowraphere 1877db96d56Sopenharmony_ci 1887db96d56Sopenharmony_ci None 1897db96d56Sopenharmony_ci """) 1907db96d56Sopenharmony_ci s = self.ioclass() 1917db96d56Sopenharmony_ci g = self.genclass(s, policy=self.policy.clone(max_line_length=33)) 1927db96d56Sopenharmony_ci g.flatten(msg) 1937db96d56Sopenharmony_ci self.assertEqual(s.getvalue(), self.typ(expected)) 1947db96d56Sopenharmony_ci 1957db96d56Sopenharmony_ci def test_rfc2231_wrapping_switches_to_default_len_if_too_narrow(self): 1967db96d56Sopenharmony_ci # This is just to make sure we don't have an infinite loop; I don't 1977db96d56Sopenharmony_ci # expect anyone to hit this in the field, so I'm not bothering to make 1987db96d56Sopenharmony_ci # the result optimal (the encoding isn't needed). 1997db96d56Sopenharmony_ci msg = self.msgmaker(self.typ(textwrap.dedent("""\ 2007db96d56Sopenharmony_ci To: nobody 2017db96d56Sopenharmony_ci Content-Disposition: attachment; 2027db96d56Sopenharmony_ci filename="afilenamelongenoghtowraphere" 2037db96d56Sopenharmony_ci 2047db96d56Sopenharmony_ci None 2057db96d56Sopenharmony_ci """))) 2067db96d56Sopenharmony_ci expected = textwrap.dedent("""\ 2077db96d56Sopenharmony_ci To: nobody 2087db96d56Sopenharmony_ci Content-Disposition: 2097db96d56Sopenharmony_ci attachment; 2107db96d56Sopenharmony_ci filename*0*=us-ascii''afilenamelongenoghtowraphere 2117db96d56Sopenharmony_ci 2127db96d56Sopenharmony_ci None 2137db96d56Sopenharmony_ci """) 2147db96d56Sopenharmony_ci s = self.ioclass() 2157db96d56Sopenharmony_ci g = self.genclass(s, policy=self.policy.clone(max_line_length=20)) 2167db96d56Sopenharmony_ci g.flatten(msg) 2177db96d56Sopenharmony_ci self.assertEqual(s.getvalue(), self.typ(expected)) 2187db96d56Sopenharmony_ci 2197db96d56Sopenharmony_ci 2207db96d56Sopenharmony_ciclass TestGenerator(TestGeneratorBase, TestEmailBase): 2217db96d56Sopenharmony_ci 2227db96d56Sopenharmony_ci msgfunc = staticmethod(message_from_string) 2237db96d56Sopenharmony_ci genclass = Generator 2247db96d56Sopenharmony_ci ioclass = io.StringIO 2257db96d56Sopenharmony_ci typ = str 2267db96d56Sopenharmony_ci 2277db96d56Sopenharmony_ci 2287db96d56Sopenharmony_ciclass TestBytesGenerator(TestGeneratorBase, TestEmailBase): 2297db96d56Sopenharmony_ci 2307db96d56Sopenharmony_ci msgfunc = staticmethod(message_from_bytes) 2317db96d56Sopenharmony_ci genclass = BytesGenerator 2327db96d56Sopenharmony_ci ioclass = io.BytesIO 2337db96d56Sopenharmony_ci typ = lambda self, x: x.encode('ascii') 2347db96d56Sopenharmony_ci 2357db96d56Sopenharmony_ci def test_cte_type_7bit_handles_unknown_8bit(self): 2367db96d56Sopenharmony_ci source = ("Subject: Maintenant je vous présente mon " 2377db96d56Sopenharmony_ci "collègue\n\n").encode('utf-8') 2387db96d56Sopenharmony_ci expected = ('Subject: Maintenant je vous =?unknown-8bit?q?' 2397db96d56Sopenharmony_ci 'pr=C3=A9sente_mon_coll=C3=A8gue?=\n\n').encode('ascii') 2407db96d56Sopenharmony_ci msg = message_from_bytes(source) 2417db96d56Sopenharmony_ci s = io.BytesIO() 2427db96d56Sopenharmony_ci g = BytesGenerator(s, policy=self.policy.clone(cte_type='7bit')) 2437db96d56Sopenharmony_ci g.flatten(msg) 2447db96d56Sopenharmony_ci self.assertEqual(s.getvalue(), expected) 2457db96d56Sopenharmony_ci 2467db96d56Sopenharmony_ci def test_cte_type_7bit_transforms_8bit_cte(self): 2477db96d56Sopenharmony_ci source = textwrap.dedent("""\ 2487db96d56Sopenharmony_ci From: foo@bar.com 2497db96d56Sopenharmony_ci To: Dinsdale 2507db96d56Sopenharmony_ci Subject: Nudge nudge, wink, wink 2517db96d56Sopenharmony_ci Mime-Version: 1.0 2527db96d56Sopenharmony_ci Content-Type: text/plain; charset="latin-1" 2537db96d56Sopenharmony_ci Content-Transfer-Encoding: 8bit 2547db96d56Sopenharmony_ci 2557db96d56Sopenharmony_ci oh là là, know what I mean, know what I mean? 2567db96d56Sopenharmony_ci """).encode('latin1') 2577db96d56Sopenharmony_ci msg = message_from_bytes(source) 2587db96d56Sopenharmony_ci expected = textwrap.dedent("""\ 2597db96d56Sopenharmony_ci From: foo@bar.com 2607db96d56Sopenharmony_ci To: Dinsdale 2617db96d56Sopenharmony_ci Subject: Nudge nudge, wink, wink 2627db96d56Sopenharmony_ci Mime-Version: 1.0 2637db96d56Sopenharmony_ci Content-Type: text/plain; charset="iso-8859-1" 2647db96d56Sopenharmony_ci Content-Transfer-Encoding: quoted-printable 2657db96d56Sopenharmony_ci 2667db96d56Sopenharmony_ci oh l=E0 l=E0, know what I mean, know what I mean? 2677db96d56Sopenharmony_ci """).encode('ascii') 2687db96d56Sopenharmony_ci s = io.BytesIO() 2697db96d56Sopenharmony_ci g = BytesGenerator(s, policy=self.policy.clone(cte_type='7bit', 2707db96d56Sopenharmony_ci linesep='\n')) 2717db96d56Sopenharmony_ci g.flatten(msg) 2727db96d56Sopenharmony_ci self.assertEqual(s.getvalue(), expected) 2737db96d56Sopenharmony_ci 2747db96d56Sopenharmony_ci def test_smtputf8_policy(self): 2757db96d56Sopenharmony_ci msg = EmailMessage() 2767db96d56Sopenharmony_ci msg['From'] = "Páolo <főo@bar.com>" 2777db96d56Sopenharmony_ci msg['To'] = 'Dinsdale' 2787db96d56Sopenharmony_ci msg['Subject'] = 'Nudge nudge, wink, wink \u1F609' 2797db96d56Sopenharmony_ci msg.set_content("oh là là, know what I mean, know what I mean?") 2807db96d56Sopenharmony_ci expected = textwrap.dedent("""\ 2817db96d56Sopenharmony_ci From: Páolo <főo@bar.com> 2827db96d56Sopenharmony_ci To: Dinsdale 2837db96d56Sopenharmony_ci Subject: Nudge nudge, wink, wink \u1F609 2847db96d56Sopenharmony_ci Content-Type: text/plain; charset="utf-8" 2857db96d56Sopenharmony_ci Content-Transfer-Encoding: 8bit 2867db96d56Sopenharmony_ci MIME-Version: 1.0 2877db96d56Sopenharmony_ci 2887db96d56Sopenharmony_ci oh là là, know what I mean, know what I mean? 2897db96d56Sopenharmony_ci """).encode('utf-8').replace(b'\n', b'\r\n') 2907db96d56Sopenharmony_ci s = io.BytesIO() 2917db96d56Sopenharmony_ci g = BytesGenerator(s, policy=policy.SMTPUTF8) 2927db96d56Sopenharmony_ci g.flatten(msg) 2937db96d56Sopenharmony_ci self.assertEqual(s.getvalue(), expected) 2947db96d56Sopenharmony_ci 2957db96d56Sopenharmony_ci def test_smtp_policy(self): 2967db96d56Sopenharmony_ci msg = EmailMessage() 2977db96d56Sopenharmony_ci msg["From"] = Address(addr_spec="foo@bar.com", display_name="Páolo") 2987db96d56Sopenharmony_ci msg["To"] = Address(addr_spec="bar@foo.com", display_name="Dinsdale") 2997db96d56Sopenharmony_ci msg["Subject"] = "Nudge nudge, wink, wink" 3007db96d56Sopenharmony_ci msg.set_content("oh boy, know what I mean, know what I mean?") 3017db96d56Sopenharmony_ci expected = textwrap.dedent("""\ 3027db96d56Sopenharmony_ci From: =?utf-8?q?P=C3=A1olo?= <foo@bar.com> 3037db96d56Sopenharmony_ci To: Dinsdale <bar@foo.com> 3047db96d56Sopenharmony_ci Subject: Nudge nudge, wink, wink 3057db96d56Sopenharmony_ci Content-Type: text/plain; charset="utf-8" 3067db96d56Sopenharmony_ci Content-Transfer-Encoding: 7bit 3077db96d56Sopenharmony_ci MIME-Version: 1.0 3087db96d56Sopenharmony_ci 3097db96d56Sopenharmony_ci oh boy, know what I mean, know what I mean? 3107db96d56Sopenharmony_ci """).encode().replace(b"\n", b"\r\n") 3117db96d56Sopenharmony_ci s = io.BytesIO() 3127db96d56Sopenharmony_ci g = BytesGenerator(s, policy=policy.SMTP) 3137db96d56Sopenharmony_ci g.flatten(msg) 3147db96d56Sopenharmony_ci self.assertEqual(s.getvalue(), expected) 3157db96d56Sopenharmony_ci 3167db96d56Sopenharmony_ci 3177db96d56Sopenharmony_ciif __name__ == '__main__': 3187db96d56Sopenharmony_ci unittest.main() 319