From 84fb618ceee5c912ce563034186987c0cfc673e7 Mon Sep 17 00:00:00 2001 From: Mark Sapiro Date: Sun, 19 Jan 2020 18:43:07 -0800 Subject: [PATCH 1/3] bpo-27321 Fix email.generator.py to not attempt to replace a non-existent header. --- Lib/email/generator.py | 5 ++- Lib/test/test_email/test_email.py | 33 +++++++++++++++++++ .../2020-01-19-18-40-26.bpo-27321.8e6SpM.rst | 2 ++ 3 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2020-01-19-18-40-26.bpo-27321.8e6SpM.rst diff --git a/Lib/email/generator.py b/Lib/email/generator.py index ae670c2353c858a..9e084d98d34e093 100644 --- a/Lib/email/generator.py +++ b/Lib/email/generator.py @@ -186,7 +186,10 @@ def _write(self, msg): # If we munged the cte, copy the message again and re-fix the CTE. if munge_cte: msg = deepcopy(msg) - msg.replace_header('content-transfer-encoding', munge_cte[0]) + if msg.get('content-transfer-encoding') is not None: + msg.replace_header('content-transfer-encoding', munge_cte[0]) + else: + msg['Content-Transfer-Encoding'] = munge_cte[0] msg.replace_header('content-type', munge_cte[1]) # Write the headers. First we see if the message object wants to # handle that itself. If not, we'll do it generically. diff --git a/Lib/test/test_email/test_email.py b/Lib/test/test_email/test_email.py index 8ec39190ea8da4c..1474e99e33a097a 100644 --- a/Lib/test/test_email/test_email.py +++ b/Lib/test/test_email/test_email.py @@ -311,6 +311,39 @@ def test_as_string_policy(self): g.flatten(msg) self.assertEqual(fullrepr, s.getvalue()) + def test_nonascii_as_string_without_cte(self): + m = textwrap.dedent("""\ + Content-type: text/plain; charset="iso-8859-1" + + Test if non-ascii messages with no Content-Transfer-Encoding set + can be as_string'd: + Föö bär + """) + source = m.encode('iso-8859-1') + expected = textwrap.dedent("""\ + Content-type: text/plain; charset="iso-8859-1" + Content-Transfer-Encoding: quoted-printable + + Test if non-ascii messages with no Content-Transfer-Encoding set + can be as_string'd: + F=F6=F6 b=E4r + """) + msg = email.message_from_bytes(source) + self.assertEqual(msg.as_string(), expected) + + def test_nonascii_as_string_without_content_type_and_cte(self): + m = textwrap.dedent("""\ + MIME-Version: 1.0 + + Test if non-ascii messages with no Content-Type nor + Content-Transfer-Encoding set can be as_string'd: + Föö bär + """) + source = m.encode('iso-8859-1') + expected = source.decode('ascii', 'replace') + msg = email.message_from_bytes(source) + self.assertEqual(msg.as_string(), expected) + def test_as_bytes(self): msg = self._msgobj('msg_01.txt') with openfile('msg_01.txt') as fp: diff --git a/Misc/NEWS.d/next/Library/2020-01-19-18-40-26.bpo-27321.8e6SpM.rst b/Misc/NEWS.d/next/Library/2020-01-19-18-40-26.bpo-27321.8e6SpM.rst new file mode 100644 index 000000000000000..28acf7f6ef919c4 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2020-01-19-18-40-26.bpo-27321.8e6SpM.rst @@ -0,0 +1,2 @@ +Fixed KeyError exception when flattening an email to a string attempts to +replace a non-existent Content-Transfer-Encoding header. From cf284919184b3cd7a18d4bf5a44f1e604f17f1ac Mon Sep 17 00:00:00 2001 From: Mark Sapiro Date: Sun, 19 Jan 2020 20:12:34 -0800 Subject: [PATCH 2/3] Added a MIME-Version: header to the test. --- Lib/test/test_email/test_email.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Lib/test/test_email/test_email.py b/Lib/test/test_email/test_email.py index 1474e99e33a097a..9ea85eee8946087 100644 --- a/Lib/test/test_email/test_email.py +++ b/Lib/test/test_email/test_email.py @@ -313,6 +313,7 @@ def test_as_string_policy(self): def test_nonascii_as_string_without_cte(self): m = textwrap.dedent("""\ + MIME-Version: 1.0 Content-type: text/plain; charset="iso-8859-1" Test if non-ascii messages with no Content-Transfer-Encoding set @@ -321,6 +322,7 @@ def test_nonascii_as_string_without_cte(self): """) source = m.encode('iso-8859-1') expected = textwrap.dedent("""\ + MIME-Version: 1.0 Content-type: text/plain; charset="iso-8859-1" Content-Transfer-Encoding: quoted-printable From b3f3157c18b524c2e104550eec5998eea56aae56 Mon Sep 17 00:00:00 2001 From: Barry Warsaw Date: Mon, 19 Oct 2020 15:06:02 -0700 Subject: [PATCH 3/3] Avoid a "double negative" conditional --- Lib/email/generator.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Lib/email/generator.py b/Lib/email/generator.py index 9e084d98d34e093..c9b121624e08d5e 100644 --- a/Lib/email/generator.py +++ b/Lib/email/generator.py @@ -186,10 +186,11 @@ def _write(self, msg): # If we munged the cte, copy the message again and re-fix the CTE. if munge_cte: msg = deepcopy(msg) - if msg.get('content-transfer-encoding') is not None: - msg.replace_header('content-transfer-encoding', munge_cte[0]) - else: + # Preserve the header order if the CTE header already exists. + if msg.get('content-transfer-encoding') is None: msg['Content-Transfer-Encoding'] = munge_cte[0] + else: + msg.replace_header('content-transfer-encoding', munge_cte[0]) msg.replace_header('content-type', munge_cte[1]) # Write the headers. First we see if the message object wants to # handle that itself. If not, we'll do it generically.