summaryrefslogtreecommitdiffstats
path: root/Lib/email
diff options
context:
space:
mode:
authorR. David Murray <rdmurray@bitdance.com>2010-01-16 05:15:17 (GMT)
committerR. David Murray <rdmurray@bitdance.com>2010-01-16 05:15:17 (GMT)
commited44dfa4c7dd03488da6e23c9901af7185af8302 (patch)
tree190b9cba0b769cd8cc063e68dee9a1ddbbbe423e /Lib/email
parent57cd8ebc2034d0e2a1ca51b84267346b5d6ded34 (diff)
downloadcpython-ed44dfa4c7dd03488da6e23c9901af7185af8302.zip
cpython-ed44dfa4c7dd03488da6e23c9901af7185af8302.tar.gz
cpython-ed44dfa4c7dd03488da6e23c9901af7185af8302.tar.bz2
Issue #1670765: Prevent email.generator.Generator from re-wrapping
headers in multipart/signed MIME parts, which fixes one of the sources of invalid modifications to such parts by Generator. Patch and tests by Martin von Gagern.
Diffstat (limited to 'Lib/email')
-rw-r--r--Lib/email/generator.py11
-rw-r--r--Lib/email/test/data/msg_45.txt33
-rw-r--r--Lib/email/test/test_email.py36
3 files changed, 80 insertions, 0 deletions
diff --git a/Lib/email/generator.py b/Lib/email/generator.py
index 77d44be..bcb0a49 100644
--- a/Lib/email/generator.py
+++ b/Lib/email/generator.py
@@ -237,6 +237,17 @@ class Generator:
print >> self._fp
self._fp.write(msg.epilogue)
+ def _handle_multipart_signed(self, msg):
+ # The contents of signed parts has to stay unmodified in order to keep
+ # the signature intact per RFC1847 2.1, so we disable header wrapping.
+ # RDM: This isn't enough to completely preserve the part, but it helps.
+ old_maxheaderlen = self._maxheaderlen
+ try:
+ self._maxheaderlen = 0
+ self._handle_multipart(msg)
+ finally:
+ self._maxheaderlen = old_maxheaderlen
+
def _handle_message_delivery_status(self, msg):
# We can't just write the headers directly to self's file object
# because this will leave an extra newline between the last header
diff --git a/Lib/email/test/data/msg_45.txt b/Lib/email/test/data/msg_45.txt
new file mode 100644
index 0000000..58fde95
--- /dev/null
+++ b/Lib/email/test/data/msg_45.txt
@@ -0,0 +1,33 @@
+From: <foo@bar.baz>
+To: <baz@bar.foo>
+Subject: test
+X-Long-Line: Some really long line contains a lot of text and thus has to be rewrapped because it is some
+ really long
+ line
+MIME-Version: 1.0
+Content-Type: multipart/signed; boundary="borderline";
+ protocol="application/pgp-signature"; micalg=pgp-sha1
+
+This is an OpenPGP/MIME signed message (RFC 2440 and 3156)
+--borderline
+Content-Type: text/plain
+X-Long-Line: Another really long line contains a lot of text and thus has to be rewrapped because it is another
+ really long
+ line
+
+This is the signed contents.
+
+--borderline
+Content-Type: application/pgp-signature; name="signature.asc"
+Content-Description: OpenPGP digital signature
+Content-Disposition: attachment; filename="signature.asc"
+
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v2.0.6 (GNU/Linux)
+
+iD8DBQFG03voRhp6o4m9dFsRApSZAKCCAN3IkJlVRg6NvAiMHlvvIuMGPQCeLZtj
+FGwfnRHFBFO/S4/DKysm0lI=
+=t7+s
+-----END PGP SIGNATURE-----
+
+--borderline--
diff --git a/Lib/email/test/test_email.py b/Lib/email/test/test_email.py
index c25635b..c38a426 100644
--- a/Lib/email/test/test_email.py
+++ b/Lib/email/test/test_email.py
@@ -3276,6 +3276,42 @@ Content-Type: application/x-foo;
+# Tests to ensure that signed parts of an email are completely preserved, as
+# required by RFC1847 section 2.1. Note that these are incomplete, because the
+# email package does not currently always preserve the body. See issue 96843.
+class TestSigned(TestEmailBase):
+
+ def _msg_and_obj(self, filename):
+ fp = openfile(findfile(filename))
+ try:
+ original = fp.read()
+ msg = email.message_from_string(original)
+ finally:
+ fp.close()
+ return original, msg
+
+ def _signed_parts_eq(self, original, result):
+ # Extract the first mime part of each message
+ import re
+ repart = re.compile(r'^--([^\n]+)\n(.*?)\n--\1$', re.S | re.M)
+ inpart = repart.search(original).group(2)
+ outpart = repart.search(result).group(2)
+ self.assertEqual(outpart, inpart)
+
+ def test_long_headers_as_string(self):
+ original, msg = self._msg_and_obj('msg_45.txt')
+ result = msg.as_string()
+ self._signed_parts_eq(original, result)
+
+ def test_long_headers_flatten(self):
+ original, msg = self._msg_and_obj('msg_45.txt')
+ fp = StringIO()
+ Generator(fp).flatten(msg)
+ result = fp.getvalue()
+ self._signed_parts_eq(original, result)
+
+
+
def _testclasses():
mod = sys.modules[__name__]
return [getattr(mod, name) for name in dir(mod) if name.startswith('Test')]