Skip to content

Commit 2a2becc

Browse files
committed
Merge with 3.5
2 parents 06aed90 + 6ceda63 commit 2a2becc

5 files changed

Lines changed: 59 additions & 10 deletions

File tree

Lib/email/contentmanager.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -126,12 +126,13 @@ def _finalize_set(msg, disposition, filename, cid, params):
126126
msg.set_param(key, value)
127127

128128

129-
# XXX: This is a cleaned-up version of base64mime.body_encode. It would
130-
# be nice to drop both this and quoprimime.body_encode in favor of
131-
# enhanced binascii routines that accepted a max_line_length parameter.
129+
# XXX: This is a cleaned-up version of base64mime.body_encode (including a bug
130+
# fix in the calculation of unencoded_bytes_per_line). It would be nice to
131+
# drop both this and quoprimime.body_encode in favor of enhanced binascii
132+
# routines that accepted a max_line_length parameter.
132133
def _encode_base64(data, max_line_length):
133134
encoded_lines = []
134-
unencoded_bytes_per_line = max_line_length * 3 // 4
135+
unencoded_bytes_per_line = max_line_length // 4 * 3
135136
for i in range(0, len(data), unencoded_bytes_per_line):
136137
thisline = data[i:i+unencoded_bytes_per_line]
137138
encoded_lines.append(binascii.b2a_base64(thisline).decode('ascii'))

Lib/test/test_email/__init__.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,10 @@ def example_as_myfunc_input(self, name, count):
120120
Note: if and only if the generated test name is a valid identifier can it
121121
be used to select the test individually from the unittest command line.
122122
123+
The values in the params dict can be a single value, a tuple, or a
124+
dict. If a single value of a tuple, it is passed to the test function
125+
as positional arguments. If a dict, it is a passed via **kw.
126+
123127
"""
124128
paramdicts = {}
125129
testers = collections.defaultdict(list)
@@ -148,8 +152,12 @@ def example_as_myfunc_input(self, name, count):
148152
if name.startswith(paramsname):
149153
testnameroot = 'test_' + name[len(paramsname):]
150154
for paramname, params in paramsdict.items():
151-
test = (lambda self, name=name, params=params:
152-
getattr(self, name)(*params))
155+
if hasattr(params, 'keys'):
156+
test = (lambda self, name=name, params=params:
157+
getattr(self, name)(**params))
158+
else:
159+
test = (lambda self, name=name, params=params:
160+
getattr(self, name)(*params))
153161
testname = testnameroot + '_' + paramname
154162
test.__name__ = testname
155163
testfuncs[testname] = test

Lib/test/test_email/test_inversion.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import io
88
import unittest
99
from email import policy, message_from_bytes
10+
from email.message import EmailMessage
1011
from email.generator import BytesGenerator
1112
from test.test_email import TestEmailBase, parameterize
1213

@@ -23,7 +24,10 @@ def dedent(bstr):
2324

2425

2526
@parameterize
26-
class TestInversion(TestEmailBase, unittest.TestCase):
27+
class TestInversion(TestEmailBase):
28+
29+
policy = policy.default
30+
message = EmailMessage
2731

2832
def msg_as_input(self, msg):
2933
m = message_from_bytes(msg, policy=policy.SMTP)
@@ -44,6 +48,23 @@ def msg_as_input(self, msg):
4448

4549
}
4650

51+
payload_params = {
52+
'plain_text': dict(payload='This is a test\n'*20),
53+
'base64_text': dict(payload=(('xy a'*40+'\n')*5), cte='base64'),
54+
'qp_text': dict(payload=(('xy a'*40+'\n')*5), cte='quoted-printable'),
55+
}
56+
57+
def payload_as_body(self, payload, **kw):
58+
msg = self._make_message()
59+
msg['From'] = 'foo'
60+
msg['To'] = 'bar'
61+
msg['Subject'] = 'payload round trip test'
62+
msg.set_content(payload, **kw)
63+
b = bytes(msg)
64+
msg2 = message_from_bytes(b, policy=self.policy)
65+
self.assertEqual(bytes(msg2), b)
66+
self.assertEqual(msg2.get_content(), payload)
67+
4768

4869
if __name__ == '__main__':
4970
unittest.main()

Misc/NEWS

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,11 @@ Core and Builtins
113113
Library
114114
-------
115115

116+
- Issue #24594: Validates persist parameter when opening MSI database
117+
118+
- Issue #28047: Fixed calculation of line length used for the base64 CTE
119+
in the new email policies.
120+
116121
- Issue #27576: Fix call order in OrderedDict.__init__().
117122

118123
- email.generator.DecodedGenerator now supports the policy keyword.

PC/_msi.c

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -955,18 +955,32 @@ static PyTypeObject msidb_Type = {
955955
0, /*tp_is_gc*/
956956
};
957957

958+
#define Py_NOT_PERSIST(x, flag) \
959+
(x != (int)(flag) && \
960+
x != ((int)(flag) | MSIDBOPEN_PATCHFILE))
961+
962+
#define Py_INVALID_PERSIST(x) \
963+
(Py_NOT_PERSIST(x, MSIDBOPEN_READONLY) && \
964+
Py_NOT_PERSIST(x, MSIDBOPEN_TRANSACT) && \
965+
Py_NOT_PERSIST(x, MSIDBOPEN_DIRECT) && \
966+
Py_NOT_PERSIST(x, MSIDBOPEN_CREATE) && \
967+
Py_NOT_PERSIST(x, MSIDBOPEN_CREATEDIRECT))
968+
958969
static PyObject* msiopendb(PyObject *obj, PyObject *args)
959970
{
960971
int status;
961972
char *path;
962973
int persist;
963974
MSIHANDLE h;
964975
msiobj *result;
965-
966976
if (!PyArg_ParseTuple(args, "si:MSIOpenDatabase", &path, &persist))
967977
return NULL;
968-
969-
status = MsiOpenDatabase(path, (LPCSTR)persist, &h);
978+
/* We need to validate that persist is a valid MSIDBOPEN_* value. Otherwise,
979+
MsiOpenDatabase may treat the value as a pointer, leading to unexpected
980+
behavior. */
981+
if (Py_INVALID_PERSIST(persist))
982+
return msierror(ERROR_INVALID_PARAMETER);
983+
status = MsiOpenDatabase(path, (LPCSTR)persist, &h);
970984
if (status != ERROR_SUCCESS)
971985
return msierror(status);
972986

0 commit comments

Comments
 (0)