#28047: Fix calculation of base64 line length.

This is buggy in the old email code as well, but it doesn't affect anything
there because only the default line length is ever used there.
This commit is contained in:
R David Murray 2016-09-09 15:00:09 -04:00
parent de02b084e6
commit 94a7927cc6
4 changed files with 40 additions and 7 deletions

View File

@ -126,12 +126,13 @@ def _finalize_set(msg, disposition, filename, cid, params):
msg.set_param(key, value)
# XXX: This is a cleaned-up version of base64mime.body_encode. It would
# be nice to drop both this and quoprimime.body_encode in favor of
# enhanced binascii routines that accepted a max_line_length parameter.
# XXX: This is a cleaned-up version of base64mime.body_encode (including a bug
# fix in the calculation of unencoded_bytes_per_line). It would be nice to
# 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):
encoded_lines = []
unencoded_bytes_per_line = max_line_length * 3 // 4
unencoded_bytes_per_line = max_line_length // 4 * 3
for i in range(0, len(data), unencoded_bytes_per_line):
thisline = data[i:i+unencoded_bytes_per_line]
encoded_lines.append(binascii.b2a_base64(thisline).decode('ascii'))

View File

@ -121,6 +121,10 @@ def parameterize(cls):
Note: if and only if the generated test name is a valid identifier can it
be used to select the test individually from the unittest command line.
The values in the params dict can be a single value, a tuple, or a
dict. If a single value of a tuple, it is passed to the test function
as positional arguments. If a dict, it is a passed via **kw.
"""
paramdicts = {}
testers = collections.defaultdict(list)
@ -149,8 +153,12 @@ def parameterize(cls):
if name.startswith(paramsname):
testnameroot = 'test_' + name[len(paramsname):]
for paramname, params in paramsdict.items():
test = (lambda self, name=name, params=params:
getattr(self, name)(*params))
if hasattr(params, 'keys'):
test = (lambda self, name=name, params=params:
getattr(self, name)(**params))
else:
test = (lambda self, name=name, params=params:
getattr(self, name)(*params))
testname = testnameroot + '_' + paramname
test.__name__ = testname
testfuncs[testname] = test

View File

@ -7,6 +7,7 @@ producing RFC valid messages.
import io
import unittest
from email import policy, message_from_bytes
from email.message import EmailMessage
from email.generator import BytesGenerator
from test.test_email import TestEmailBase, parameterize
@ -23,7 +24,10 @@ def dedent(bstr):
@parameterize
class TestInversion(TestEmailBase, unittest.TestCase):
class TestInversion(TestEmailBase):
policy = policy.default
message = EmailMessage
def msg_as_input(self, msg):
m = message_from_bytes(msg, policy=policy.SMTP)
@ -44,6 +48,23 @@ class TestInversion(TestEmailBase, unittest.TestCase):
}
payload_params = {
'plain_text': dict(payload='This is a test\n'*20),
'base64_text': dict(payload=(('xy a'*40+'\n')*5), cte='base64'),
'qp_text': dict(payload=(('xy a'*40+'\n')*5), cte='quoted-printable'),
}
def payload_as_body(self, payload, **kw):
msg = self._make_message()
msg['From'] = 'foo'
msg['To'] = 'bar'
msg['Subject'] = 'payload round trip test'
msg.set_content(payload, **kw)
b = bytes(msg)
msg2 = message_from_bytes(b, policy=self.policy)
self.assertEqual(bytes(msg2), b)
self.assertEqual(msg2.get_content(), payload)
if __name__ == '__main__':
unittest.main()

View File

@ -62,6 +62,9 @@ Core and Builtins
Library
-------
- Issue #28047: Fixed calculation of line length used for the base64 CTE
in the new email policies.
- Issue #27445: Don't pass str(_charset) to MIMEText.set_payload().
Patch by Claude Paroz.