From 783773680baa511a8e321c9f0b7015772e6eab8a Mon Sep 17 00:00:00 2001 From: silane <18604696+silane@users.noreply.github.com> Date: Wed, 26 Sep 2018 04:31:23 +0900 Subject: [PATCH 1/6] Fix contentmanager raise error when policy.max_line_length is 0 or None --- Lib/email/contentmanager.py | 6 ++++-- Lib/email/quoprimime.py | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Lib/email/contentmanager.py b/Lib/email/contentmanager.py index b904ded94c92ef..ef7cec515fb05c 100644 --- a/Lib/email/contentmanager.py +++ b/Lib/email/contentmanager.py @@ -131,6 +131,8 @@ def _finalize_set(msg, disposition, filename, cid, params): # drop both this and quoprimime.body_encode in favor of enhanced binascii # routines that accepted a max_line_length parameter. def _encode_base64(data, max_line_length): + if not max_line_length: + return binascii.b2a_base64(data).decode('ascii') encoded_lines = [] unencoded_bytes_per_line = max_line_length // 4 * 3 for i in range(0, len(data), unencoded_bytes_per_line): @@ -150,8 +152,8 @@ def normal_body(lines): return b'\n'.join(lines) + b'\n' return '7bit', normal_body(lines).decode('ascii') except UnicodeDecodeError: pass - if (policy.cte_type == '8bit' and - max(len(x) for x in lines) <= policy.max_line_length): + if (policy.cte_type == '8bit' and (not policy.max_line_length or + max(len(x) for x in lines) <= policy.max_line_length)): return '8bit', normal_body(lines).decode('ascii', 'surrogateescape') sniff = embedded_body(lines[:10]) sniff_qp = quoprimime.body_encode(sniff.decode('latin-1'), diff --git a/Lib/email/quoprimime.py b/Lib/email/quoprimime.py index 94534f7ee1e33e..43014512617c1c 100644 --- a/Lib/email/quoprimime.py +++ b/Lib/email/quoprimime.py @@ -168,6 +168,8 @@ def body_encode(body, maxlinelen=76, eol=NL): """ + if not maxlinelen: + maxlinelen=float('+inf') if maxlinelen < 4: raise ValueError("maxlinelen must be at least 4") if not body: From 215c02f895f67f0b9fc5edad6f5b50ccc09a1a73 Mon Sep 17 00:00:00 2001 From: silane <18604696+silane@users.noreply.github.com> Date: Wed, 26 Sep 2018 15:27:12 +0900 Subject: [PATCH 2/6] Add tests --- Lib/test/test_email/test_contentmanager.py | 39 ++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/Lib/test/test_email/test_contentmanager.py b/Lib/test/test_email/test_contentmanager.py index 169058eac83da3..34ef9845a7d36a 100644 --- a/Lib/test/test_email/test_contentmanager.py +++ b/Lib/test/test_email/test_contentmanager.py @@ -453,6 +453,45 @@ def test_set_text_11_lines_long_line_maximal_non_ascii_heuristics(self): self.assertEqual(m.get_payload(decode=True).decode('utf-8'), content) self.assertEqual(m.get_content(), content) + def test_set_text_non_ascii_with_policy_max_line_length_none_or_0(self): + for max_line_length in (0, None): + with self.subTest(max_line_length=max_line_length): + policy = self.policy.clone(max_line_length=max_line_length) + m = self.message(policy=policy) + content = ("áàäéèęöőáàäéèęöőáàäéèęöőáàäéèęöő" + "áàäéèęöőáàäéèęöőáàäéèęöőáàäéèęöő" + "áàäéèęöőáàäéèęöőáàäéèęöőáàäéèęöő.\n") + raw_data_manager.set_content(m,content) + self.assertEqual(bytes(m), (textwrap.dedent("""\ + Content-Type: text/plain; charset="utf-8" + Content-Transfer-Encoding: 8bit + """) + "\n" + \ + "áàäéèęöőáàäéèęöőáàäéèęöőáàäéèęöő" + "áàäéèęöőáàäéèęöőáàäéèęöőáàäéèęöő" + "áàäéèęöőáàäéèęöőáàäéèęöőáàäéèęöő.\n" + ).encode('utf8')) + self.assertEqual( + m.get_payload(decode=True).decode('utf-8'), content) + self.assertEqual(m.get_content(), content) + + def test_set_bytes_with_policy_max_line_length_none_or_0(self): + for max_line_length in (0, None): + with self.subTest(max_line_length=max_line_length): + policy = self.policy.clone(max_line_length=max_line_length) + m = self.message(policy=policy) + content = b'b\xFFgus\tcon\nt\rent ' + b'z'*80 + raw_data_manager.set_content( + m,content, maintype='application', subtype='octet-stream') + self.assertEqual(bytes(m), textwrap.dedent("""\ + Content-Type: application/octet-stream + Content-Transfer-Encoding: base64 + """).encode('ascii') + b"\n" + + b"Yv9ndXMJY29uCnQNZW50IHp6enp6enp6enp6enp6enp6enp6enp6enp6" + b"enp6enp6enp6enp6enp6enp6enp6enp6enp6enp6enp6enp6enp6enp6" + b"enp6enp6enp6enp6\n") + self.assertEqual(m.get_payload(decode=True), content) + self.assertEqual(m.get_content(), content) + def test_set_text_non_ascii_with_cte_7bit_raises(self): m = self._make_message() with self.assertRaises(UnicodeError): From b4843545838979981933c6ed5e13def7b7eafa28 Mon Sep 17 00:00:00 2001 From: silane <18604696+silane@users.noreply.github.com> Date: Wed, 26 Sep 2018 15:28:03 +0900 Subject: [PATCH 3/6] Add NEWS entry --- .../next/Library/2018-09-26-15-18-18.bpo-34800.auD5YK.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Library/2018-09-26-15-18-18.bpo-34800.auD5YK.rst diff --git a/Misc/NEWS.d/next/Library/2018-09-26-15-18-18.bpo-34800.auD5YK.rst b/Misc/NEWS.d/next/Library/2018-09-26-15-18-18.bpo-34800.auD5YK.rst new file mode 100644 index 00000000000000..1cc87b7ec7755f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-09-26-15-18-18.bpo-34800.auD5YK.rst @@ -0,0 +1,2 @@ +Fix `email.contentmanager.set_content` raise error when +`email.policy.Policy.max_line_length` is 0 or None. Patch by silane. From 229a556260588eceab191b6971bf48cc9293d657 Mon Sep 17 00:00:00 2001 From: silane <18604696+silane@users.noreply.github.com> Date: Wed, 26 Sep 2018 10:04:10 +0200 Subject: [PATCH 4/6] Fix spaces --- Lib/test/test_email/test_contentmanager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_email/test_contentmanager.py b/Lib/test/test_email/test_contentmanager.py index 34ef9845a7d36a..0458cb07a9f6ec 100644 --- a/Lib/test/test_email/test_contentmanager.py +++ b/Lib/test/test_email/test_contentmanager.py @@ -473,7 +473,7 @@ def test_set_text_non_ascii_with_policy_max_line_length_none_or_0(self): self.assertEqual( m.get_payload(decode=True).decode('utf-8'), content) self.assertEqual(m.get_content(), content) - + def test_set_bytes_with_policy_max_line_length_none_or_0(self): for max_line_length in (0, None): with self.subTest(max_line_length=max_line_length): @@ -485,7 +485,7 @@ def test_set_bytes_with_policy_max_line_length_none_or_0(self): self.assertEqual(bytes(m), textwrap.dedent("""\ Content-Type: application/octet-stream Content-Transfer-Encoding: base64 - """).encode('ascii') + b"\n" + + """).encode('ascii') + b"\n" + b"Yv9ndXMJY29uCnQNZW50IHp6enp6enp6enp6enp6enp6enp6enp6enp6" b"enp6enp6enp6enp6enp6enp6enp6enp6enp6enp6enp6enp6enp6enp6" b"enp6enp6enp6enp6\n") From c1d2d2d0b5e964e647b274c60ff2c707b906205d Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Sat, 23 Apr 2022 08:51:44 -0700 Subject: [PATCH 5/6] Update Lib/test/test_email/test_contentmanager.py Co-authored-by: Abhilash Raj --- Lib/test/test_email/test_contentmanager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_email/test_contentmanager.py b/Lib/test/test_email/test_contentmanager.py index 0458cb07a9f6ec..c8c2dd2149ba98 100644 --- a/Lib/test/test_email/test_contentmanager.py +++ b/Lib/test/test_email/test_contentmanager.py @@ -461,7 +461,7 @@ def test_set_text_non_ascii_with_policy_max_line_length_none_or_0(self): content = ("áàäéèęöőáàäéèęöőáàäéèęöőáàäéèęöő" "áàäéèęöőáàäéèęöőáàäéèęöőáàäéèęöő" "áàäéèęöőáàäéèęöőáàäéèęöőáàäéèęöő.\n") - raw_data_manager.set_content(m,content) + raw_data_manager.set_content(m, content) self.assertEqual(bytes(m), (textwrap.dedent("""\ Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 8bit From 9beb4f52598cd35cd71766c534cdc1f3a4b57f9e Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Sat, 23 Apr 2022 08:52:10 -0700 Subject: [PATCH 6/6] Update Lib/test/test_email/test_contentmanager.py Co-authored-by: Abhilash Raj --- Lib/test/test_email/test_contentmanager.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_email/test_contentmanager.py b/Lib/test/test_email/test_contentmanager.py index c8c2dd2149ba98..572360bd133c4a 100644 --- a/Lib/test/test_email/test_contentmanager.py +++ b/Lib/test/test_email/test_contentmanager.py @@ -481,7 +481,7 @@ def test_set_bytes_with_policy_max_line_length_none_or_0(self): m = self.message(policy=policy) content = b'b\xFFgus\tcon\nt\rent ' + b'z'*80 raw_data_manager.set_content( - m,content, maintype='application', subtype='octet-stream') + m, content, maintype='application', subtype='octet-stream') self.assertEqual(bytes(m), textwrap.dedent("""\ Content-Type: application/octet-stream Content-Transfer-Encoding: base64