Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge pull request #10 from rpatterson/master

Recursively cleanup multipart payloads for encoding.
  • Loading branch information...
commit 83dfc5f20d98be2c2a3c2e7bd545b5bc4e85a20f 2 parents 4821855 + ef2d6d5
@mcdonc mcdonc authored
View
2  repoze/sendmail/delivery.py
@@ -26,6 +26,7 @@
from zope.interface import implementer
from repoze.sendmail.interfaces import IMailDelivery
from repoze.sendmail.maildir import Maildir
+from repoze.sendmail import encoding
from transaction.interfaces import IDataManager
import transaction
@@ -79,6 +80,7 @@ class AbstractMailDelivery(object):
def send(self, fromaddr, toaddrs, message):
assert isinstance(message, Message), \
'Message must be instance of email.message.Message'
+ encoding.cleanup_message(message)
messageid = message['Message-Id']
if messageid is None:
messageid = message['Message-Id'] = make_msgid('repoze.sendmail')
View
31 repoze/sendmail/encoding.py
@@ -21,10 +21,10 @@
'content-disposition')
-def encode_message(message,
+def cleanup_message(message,
addr_headers=ADDR_HEADERS, param_headers=PARAM_HEADERS):
"""
- Encode a `Message` handling headers and payloads.
+ Cleanup a `Message` handling header and payload charsets.
Headers are handled in the most sane way possible. Address names
are left in `ascii` if possible or encoded to `latin_1` or `utf-8`
@@ -36,7 +36,8 @@ def encode_message(message,
encoding. Finally, all other header are left in `ascii` if
possible or encoded to `latin_1` or `utf-8` as a whole.
- The return is a byte string of the whole message.
+ The message is modified in place and is also returned in such a
+ state that it can be safely encoded to ascii.
"""
for key, value in message.items():
if key.lower() in addr_headers:
@@ -72,7 +73,31 @@ def encode_message(message,
if PY_2:
payload = encoded
message.set_payload(payload, charset=best)
+ elif isinstance(payload, list):
+ for part in payload:
+ cleanup_message(part)
+
+ return message
+
+
+def encode_message(message,
+ addr_headers=ADDR_HEADERS, param_headers=PARAM_HEADERS):
+ """
+ Encode a `Message` handling headers and payloads.
+
+ Headers are handled in the most sane way possible. Address names
+ are left in `ascii` if possible or encoded to `latin_1` or `utf-8`
+ and finally encoded according to RFC 2047 without encoding the
+ address, something the `email` stdlib package doesn't do.
+ Parameterized headers such as `filename` in the
+ `Content-Disposition` header, have their values encoded properly
+ while leaving the rest of the header to be handled without
+ encoding. Finally, all other header are left in `ascii` if
+ possible or encoded to `latin_1` or `utf-8` as a whole.
+ The return is a byte string of the whole message.
+ """
+ cleanup_message(message)
return message.as_string().encode('ascii')
View
6 repoze/sendmail/tests/test_delivery.py
@@ -187,7 +187,7 @@ def tearDown(self):
def testNonASCIIAddrs(self):
import os
- from email.message import Message
+ from email.mime import base
import transaction
from repoze.sendmail.delivery import QueuedMailDelivery
from repoze.sendmail._compat import b
@@ -197,7 +197,9 @@ def testNonASCIIAddrs(self):
non_ascii = b('LaPe\xc3\xb1a').decode('utf-8')
fromaddr = non_ascii+' <jim@example.com>'
toaddrs = (non_ascii+' <guido@recip.com>',)
- message = Message()
+ message = base.MIMEBase('text', 'plain')
+ message['From'] = fromaddr
+ message['To'] = ','.join(toaddrs)
delivery.send(fromaddr, toaddrs, message)
self.assertTrue(os.listdir(os.path.join(self.maildir_path, 'tmp')))
View
26 repoze/sendmail/tests/test_encoding.py
@@ -207,3 +207,29 @@ def test_binary_body(self):
encoded = self._callFUT(message)
self.assertTrue(encodestring(body) in encoded)
+
+ def test_encoding_multipart(self):
+ from email.mime import multipart
+ from email.mime import nonmultipart
+ from repoze.sendmail._compat import encodestring
+ from repoze.sendmail._compat import b
+
+ message = multipart.MIMEMultipart('alternative')
+
+ utf_8_encoded = b('mo \xe2\x82\xac')
+ utf_8 = utf_8_encoded.decode('utf_8')
+
+ plain_string = utf_8
+ plain_part = nonmultipart.MIMENonMultipart('plain', 'plain')
+ plain_part.set_payload(plain_string)
+ message.attach(plain_part)
+
+ html_string = '<p>'+utf_8+'</p>'
+ html_part = nonmultipart.MIMENonMultipart('text', 'html')
+ html_part.set_payload(html_string)
+ message.attach(html_part)
+
+ encoded = self._callFUT(message)
+
+ self.assertTrue(encodestring(plain_string.encode('utf_8')) in encoded)
+ self.assertTrue(encodestring(html_string.encode('utf_8')) in encoded)
Please sign in to comment.
Something went wrong with that request. Please try again.