Skip to content

Commit e9de710

Browse files
committed
enhance email content handling: allow infinite line length in encoding
1 parent a210d49 commit e9de710

3 files changed

Lines changed: 37 additions & 4 deletions

File tree

Lib/email/contentmanager.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import sys
12
import binascii
23
import email.charset
34
import email.message
@@ -142,13 +143,15 @@ def _encode_base64(data, max_line_length):
142143

143144

144145
def _encode_text(string, charset, cte, policy):
146+
# max_line_length 0/None means no limit, ie: infinitely long.
147+
maxlen = policy.max_line_length or sys.maxsize
145148
lines = string.encode(charset).splitlines()
146149
linesep = policy.linesep.encode('ascii')
147150
def embedded_body(lines): return linesep.join(lines) + linesep
148151
def normal_body(lines): return b'\n'.join(lines) + b'\n'
149152
if cte is None:
150153
# Use heuristics to decide on the "best" encoding.
151-
if max((len(x) for x in lines), default=0) <= policy.max_line_length:
154+
if max((len(x) for x in lines), default=0) <= maxlen:
152155
try:
153156
return '7bit', normal_body(lines).decode('ascii')
154157
except UnicodeDecodeError:
@@ -157,7 +160,7 @@ def normal_body(lines): return b'\n'.join(lines) + b'\n'
157160
return '8bit', normal_body(lines).decode('ascii', 'surrogateescape')
158161
sniff = embedded_body(lines[:10])
159162
sniff_qp = quoprimime.body_encode(sniff.decode('latin-1'),
160-
policy.max_line_length)
163+
maxlen)
161164
sniff_base64 = binascii.b2a_base64(sniff)
162165
# This is a little unfair to qp; it includes lineseps, base64 doesn't.
163166
if len(sniff_qp) > len(sniff_base64):
@@ -172,9 +175,9 @@ def normal_body(lines): return b'\n'.join(lines) + b'\n'
172175
data = normal_body(lines).decode('ascii', 'surrogateescape')
173176
elif cte == 'quoted-printable':
174177
data = quoprimime.body_encode(normal_body(lines).decode('latin-1'),
175-
policy.max_line_length)
178+
maxlen)
176179
elif cte == 'base64':
177-
data = _encode_base64(embedded_body(lines), policy.max_line_length)
180+
data = _encode_base64(embedded_body(lines), maxlen)
178181
else:
179182
raise ValueError("Unknown content transfer encoding {}".format(cte))
180183
return cte, data

Lib/test/test_email/test_message.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1004,6 +1004,34 @@ def test_folding_with_long_nospace_http_policy_1(self):
10041004
parsed_msg = message_from_bytes(m.as_bytes(), policy=policy.default)
10051005
self.assertEqual(parsed_msg['Message-ID'], m['Message-ID'])
10061006

1007+
def test_no_wrapping_with_zero_max_line_length(self):
1008+
pol = policy.default.clone(max_line_length=0)
1009+
subj = "S" * 100
1010+
msg = EmailMessage(policy=pol)
1011+
msg["From"] = "a@ex.com"
1012+
msg["To"] = "b@ex.com"
1013+
msg["Subject"] = subj
1014+
1015+
raw = msg.as_bytes()
1016+
self.assertNotIn(b"\r\n ", raw, "Found fold indicator; wrapping not disabled")
1017+
1018+
parsed = message_from_bytes(raw, policy=policy.default)
1019+
self.assertEqual(parsed["Subject"], subj)
1020+
1021+
def test_no_wrapping_with_none_max_line_length(self):
1022+
pol = policy.default.clone(max_line_length=None)
1023+
subj = "S" * 100
1024+
body = "B" * 100
1025+
msg = EmailMessage(policy=pol)
1026+
msg["From"] = "a@ex.com"
1027+
msg["To"] = "b@ex.com"
1028+
msg["Subject"] = subj
1029+
msg.set_content(body)
1030+
1031+
parsed = message_from_bytes(msg.as_bytes(), policy=policy.default)
1032+
self.assertEqual(parsed["Subject"], subj)
1033+
self.assertEqual(parsed.get_body().get_content().rstrip('\n'), body)
1034+
10071035
def test_invalid_header_names(self):
10081036
invalid_headers = [
10091037
('Invalid Header', 'contains space'),
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
:mod:`email`: Ensure policy accepts unlimited line lengths by
2+
treating 0 or :const:`None` as :data:`sys.maxsize`

0 commit comments

Comments
 (0)