From 05f559296f757f356b17ccfeb238c38359f9ab16 Mon Sep 17 00:00:00 2001 From: Ankit Goel Date: Sun, 1 Sep 2024 20:10:58 +0100 Subject: [PATCH 1/2] gh-78981: Update contentmanager to handle 0/None max_line_length Fixed a bug where `email.contentmanager.set_content` raised error when `email.policy.Policy.max_line_length` is 0 or `None` --- Lib/email/contentmanager.py | 7 +++- Lib/email/quoprimime.py | 4 ++ Lib/test/test_email/test_contentmanager.py | 39 +++++++++++++++++++ ...4-09-01-20-20-49.gh-issue-78981.nNRvSX.rst | 2 + 4 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2024-09-01-20-20-49.gh-issue-78981.nNRvSX.rst diff --git a/Lib/email/contentmanager.py b/Lib/email/contentmanager.py index b4f5830beada4a..99073183e169d2 100644 --- a/Lib/email/contentmanager.py +++ b/Lib/email/contentmanager.py @@ -133,6 +133,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): @@ -148,7 +150,10 @@ def embedded_body(lines): return linesep.join(lines) + linesep def normal_body(lines): return b'\n'.join(lines) + b'\n' if cte is None: # Use heuristics to decide on the "best" encoding. - if max((len(x) for x in lines), default=0) <= policy.max_line_length: + if ( + not policy.max_line_length or + max((len(x) for x in lines), default=0) <= policy.max_line_length + ): try: return '7bit', normal_body(lines).decode('ascii') except UnicodeDecodeError: diff --git a/Lib/email/quoprimime.py b/Lib/email/quoprimime.py index 500bbc5151769d..1743b982b9b90a 100644 --- a/Lib/email/quoprimime.py +++ b/Lib/email/quoprimime.py @@ -40,6 +40,7 @@ ] import re +import sys from string import ascii_letters, digits, hexdigits @@ -169,6 +170,9 @@ def body_encode(body, maxlinelen=76, eol=NL): """ + if not maxlinelen: + maxlinelen = sys.maxsize + if maxlinelen < 4: raise ValueError("maxlinelen must be at least 4") if not body: diff --git a/Lib/test/test_email/test_contentmanager.py b/Lib/test/test_email/test_contentmanager.py index 694cef4ba7e413..bf29e0a430aff7 100644 --- a/Lib/test/test_email/test_contentmanager.py +++ b/Lib/test/test_email/test_contentmanager.py @@ -481,6 +481,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): diff --git a/Misc/NEWS.d/next/Library/2024-09-01-20-20-49.gh-issue-78981.nNRvSX.rst b/Misc/NEWS.d/next/Library/2024-09-01-20-20-49.gh-issue-78981.nNRvSX.rst new file mode 100644 index 00000000000000..b61fde967a168f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-09-01-20-20-49.gh-issue-78981.nNRvSX.rst @@ -0,0 +1,2 @@ +Fixed a bug where ``email.contentmanager.set_content`` raised error when +``email.policy.Policy.max_line_length`` is 0 or ``None``. Patch by silane. From eef77407177f44d6df853db636cb0cc9d9949d84 Mon Sep 17 00:00:00 2001 From: Ankit Goel Date: Thu, 10 Oct 2024 19:20:52 +0100 Subject: [PATCH 2/2] Fix formatting and news item based on pr comments --- Lib/test/test_email/test_contentmanager.py | 4 ++-- .../Library/2024-09-01-20-20-49.gh-issue-78981.nNRvSX.rst | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Lib/test/test_email/test_contentmanager.py b/Lib/test/test_email/test_contentmanager.py index bf29e0a430aff7..92484a527c4574 100644 --- a/Lib/test/test_email/test_contentmanager.py +++ b/Lib/test/test_email/test_contentmanager.py @@ -487,8 +487,8 @@ def test_set_text_non_ascii_with_policy_max_line_length_none_or_0(self): policy = self.policy.clone(max_line_length=max_line_length) m = self.message(policy=policy) content = ("áàäéèęöőáàäéèęöőáàäéèęöőáàäéèęöő" - "áàäéèęöőáàäéèęöőáàäéèęöőáàäéèęöő" - "áàäéèęöőáàäéèęöőáàäéèęöőáàäéèęöő.\n") + "áàäéèęöőáàäéèęöőáàäéèęöőáàäéèęöő" + "áàäéèęöőáàäéèęöőáàäéèęöőáàäéèęöő.\n") raw_data_manager.set_content(m, content) self.assertEqual(bytes(m), (textwrap.dedent("""\ Content-Type: text/plain; charset="utf-8" diff --git a/Misc/NEWS.d/next/Library/2024-09-01-20-20-49.gh-issue-78981.nNRvSX.rst b/Misc/NEWS.d/next/Library/2024-09-01-20-20-49.gh-issue-78981.nNRvSX.rst index b61fde967a168f..964c17da406c30 100644 --- a/Misc/NEWS.d/next/Library/2024-09-01-20-20-49.gh-issue-78981.nNRvSX.rst +++ b/Misc/NEWS.d/next/Library/2024-09-01-20-20-49.gh-issue-78981.nNRvSX.rst @@ -1,2 +1,2 @@ -Fixed a bug where ``email.contentmanager.set_content`` raised error when +Fixed a bug where :meth:`email.contentmanager.set_content` raised error when ``email.policy.Policy.max_line_length`` is 0 or ``None``. Patch by silane.