Skip to content

Commit 6f64300

Browse files
committed
fix: redundant \n and WSP when using policy.compat32
1 parent c02bc47 commit 6f64300

File tree

3 files changed

+40
-19
lines changed

3 files changed

+40
-19
lines changed

Lib/email/_policybase.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,29 @@ def header_source_parse(self, sourcelines):
307307
308308
"""
309309
name, value = sourcelines[0].split(':', 1)
310-
value = value.lstrip(' \t') + ''.join(sourcelines[1:])
310+
311+
# Fixed: https://github.com/python/cpython/issues/124452
312+
#
313+
# Root cause: The function '_refold_parse_tree' in '_header_value_parse.py'.
314+
# If there is no WSP, it can't figure out how to wrap the text.
315+
# Therefore, it places the entire value directly after '\n', and because
316+
# there is a WSP after '<HeaderName>:', the WSP will be moved to the front
317+
# of the value according to RFC5322, section 2.2.3.
318+
#
319+
# However, the WSP is not part of the value; therefore, we must
320+
# remove it.
321+
322+
no_first_value = value.strip() == '' and len(sourcelines) > 1
323+
324+
# When using the compat32 policy, the value is '\n'. Therefore,
325+
# use an empty string if there is no value (without WSP and CRLF)
326+
# on the first line
327+
value = '' if no_first_value else value.lstrip(' \t')
328+
329+
if no_first_value and sourcelines[1][0] in ' \t':
330+
sourcelines[1] = sourcelines[1][1:]
331+
332+
value += ''.join(sourcelines[1:])
311333
return (name, value.rstrip('\r\n'))
312334

313335
def header_store_parse(self, name, value):

Lib/email/policy.py

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -125,24 +125,11 @@ def header_source_parse(self, sourcelines):
125125
126126
"""
127127
name, value = sourcelines[0].split(':', 1)
128+
no_first_value = value.strip() == '' and len(sourcelines) > 1
128129

129-
# Fixed: https://github.com/python/cpython/issues/124452
130-
#
131-
# Root cause: The function '_refold_parse_tree' in '_header_value_parse.py'.
132-
# If there is no WSP, it can't figure out how to wrap the text.
133-
# Therefore, it places the entire value directly after '\n', and because
134-
# there is a WSP after '<HeaderName>:', the WSP will be moved to the front
135-
# of the value according to RFC5322, section 2.2.3.
136-
#
137-
# However, the WSP is not part of the value; therefore, we must
138-
# remove it.
139-
140-
# Remove leading WSP in the first line only if there no value in the
141-
# first line, and has values after that
142-
remove_wsp = not value.strip() and len(sourcelines) > 1
143-
144-
value = value.lstrip(' \t')
145-
if remove_wsp and sourcelines[1][0] in ' \t':
130+
value = '' if no_first_value else value.lstrip(' \t')
131+
132+
if no_first_value and sourcelines[1][0] in ' \t':
146133
sourcelines[1] = sourcelines[1][1:]
147134

148135
value += ''.join(sourcelines[1:])

Lib/test/test_email/test_message.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -970,7 +970,7 @@ def test_folding_with_short_nospace_1(self):
970970
parsed_msg = message_from_bytes(m.as_bytes(), policy=policy.default)
971971
self.assertEqual(parsed_msg['Message-ID'], m['Message-ID'])
972972

973-
def test_folding_with_long_nospace_1(self):
973+
def test_folding_with_long_nospace_default_policy_1(self):
974974
# Fixed: https://github.com/python/cpython/issues/124452
975975
#
976976
# When the value is too long, it should be converted back
@@ -986,6 +986,18 @@ def test_folding_with_long_nospace_1(self):
986986
parsed_msg = message_from_bytes(m.as_bytes(), policy=policy.default)
987987
self.assertEqual(parsed_msg['Message-ID'], m['Message-ID'])
988988

989+
def test_folding_with_long_nospace_compat32_policy_1(self):
990+
# Fixed: https://github.com/python/cpython/issues/124452
991+
#
992+
# When the value is too long, it should be converted back
993+
# to its original form without any modifications.
994+
995+
m = EmailMessage(policy.compat32)
996+
m['Message-ID'] = '12345678912345678123456789123456789123456789'\
997+
'12345678912345678123456789123456789123456789'
998+
parsed_msg = message_from_bytes(m.as_bytes(), policy=policy.default)
999+
self.assertEqual(parsed_msg['Message-ID'], m['Message-ID'])
1000+
9891001

9901002
def test_get_body_malformed(self):
9911003
"""test for bpo-42892"""

0 commit comments

Comments
 (0)