Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion Lib/email/contentmanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -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:
Expand Down
4 changes: 4 additions & 0 deletions Lib/email/quoprimime.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
]

import re
import sys

from string import ascii_letters, digits, hexdigits

Expand Down Expand Up @@ -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:
Expand Down
39 changes: 39 additions & 0 deletions Lib/test/test_email/test_contentmanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
Loading