Merge lp:~abompard/mailman/bug-1060951 into lp:mailman

Proposed by Aurélien Bompard
Status: Merged
Approved by: Barry Warsaw
Approved revision: 7271
Merged at revision: 7273
Proposed branch: lp:~abompard/mailman/bug-1060951
Merge into: lp:mailman
Diff against target: 268 lines (+68/-42)
13 files modified
src/mailman/app/bounces.py (+4/-1)
src/mailman/app/moderator.py (+3/-3)
src/mailman/archiving/mailarchive.py (+2/-0)
src/mailman/archiving/mhonarc.py (+2/-0)
src/mailman/archiving/prototype.py (+2/-0)
src/mailman/commands/docs/unshunt.rst (+1/-1)
src/mailman/commands/eml_membership.py (+2/-0)
src/mailman/email/message.py (+9/-35)
src/mailman/email/tests/test_message.py (+33/-1)
src/mailman/model/bounce.py (+4/-1)
src/mailman/model/messagestore.py (+2/-0)
src/mailman/rules/implicit_dest.py (+2/-0)
src/mailman/utilities/email.py (+2/-0)
To merge this branch: bzr merge lp:~abompard/mailman/bug-1060951
Reviewer Review Type Date Requested Status
Barry Warsaw Approve
Review via email: mp+243401@code.launchpad.net

Description of the change

This branch solves bug #1060951 by removing the auto-conversion of headers to unicode in the Message class.

To post a comment you must log in.
Revision history for this message
Barry Warsaw (barry) wrote :

Some comments and questions. Thanks for working on this!

review: Needs Fixing
lp:~abompard/mailman/bug-1060951 updated
7271. By Aurélien Bompard

Convert unicode instance testing to bytes instance testing

Revision history for this message
Aurélien Bompard (abompard) wrote :

I fixed the isinstance calls and the exception, but I don't understand your comment about Python's base email class.

Revision history for this message
Barry Warsaw (barry) :
review: Approve
Revision history for this message
Barry Warsaw (barry) wrote :

Committed, after fixing a few other minor style fixes. Thanks!

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/mailman/app/bounces.py'
--- src/mailman/app/bounces.py 2014-01-07 03:43:59 +0000
+++ src/mailman/app/bounces.py 2014-12-04 09:39:12 +0000
@@ -200,10 +200,13 @@
200 optionsurl=member.options_url,200 optionsurl=member.options_url,
201 owneraddr=mlist.owner_address,201 owneraddr=mlist.owner_address,
202 )202 )
203 message_id = msg['message-id']
204 if isinstance(message_id, bytes):
205 message_id = message_id.decode("ascii")
203 pendable = _ProbePendable(206 pendable = _ProbePendable(
204 # We can only pend unicodes.207 # We can only pend unicodes.
205 member_id=member.member_id.hex,208 member_id=member.member_id.hex,
206 message_id=msg['message-id'],209 message_id=message_id,
207 )210 )
208 token = getUtility(IPendings).add(pendable)211 token = getUtility(IPendings).add(pendable)
209 mailbox, domain_parts = split_email(mlist.bounces_address)212 mailbox, domain_parts = split_email(mlist.bounces_address)
210213
=== modified file 'src/mailman/app/moderator.py'
--- src/mailman/app/moderator.py 2014-04-28 15:23:35 +0000
+++ src/mailman/app/moderator.py 2014-12-04 09:39:12 +0000
@@ -86,9 +86,9 @@
86 # Message-ID header.86 # Message-ID header.
87 message_id = msg.get('message-id')87 message_id = msg.get('message-id')
88 if message_id is None:88 if message_id is None:
89 msg['Message-ID'] = message_id = unicode(make_msgid())89 msg['Message-ID'] = message_id = make_msgid().decode("ascii")
90 assert isinstance(message_id, unicode), (90 if isinstance(message_id, bytes):
91 'Message-ID is not a unicode: %s' % message_id)91 message_id = message_id.decode("ascii")
92 getUtility(IMessageStore).add(msg)92 getUtility(IMessageStore).add(msg)
93 # Prepare the message metadata with some extra information needed only by93 # Prepare the message metadata with some extra information needed only by
94 # the moderation interface.94 # the moderation interface.
9595
=== modified file 'src/mailman/archiving/mailarchive.py'
--- src/mailman/archiving/mailarchive.py 2014-01-01 14:59:42 +0000
+++ src/mailman/archiving/mailarchive.py 2014-12-04 09:39:12 +0000
@@ -68,6 +68,8 @@
68 message_id_hash = msg.get('x-message-id-hash')68 message_id_hash = msg.get('x-message-id-hash')
69 if message_id_hash is None:69 if message_id_hash is None:
70 return None70 return None
71 if isinstance(message_id_hash, bytes):
72 message_id_hash = message_id_hash.decode("ascii")
71 return urljoin(self.base_url, message_id_hash)73 return urljoin(self.base_url, message_id_hash)
7274
73 def archive_message(self, mlist, msg):75 def archive_message(self, mlist, msg):
7476
=== modified file 'src/mailman/archiving/mhonarc.py'
--- src/mailman/archiving/mhonarc.py 2014-01-01 14:59:42 +0000
+++ src/mailman/archiving/mhonarc.py 2014-12-04 09:39:12 +0000
@@ -73,6 +73,8 @@
73 message_id_hash = msg.get('x-message-id-hash')73 message_id_hash = msg.get('x-message-id-hash')
74 if message_id_hash is None:74 if message_id_hash is None:
75 return None75 return None
76 if isinstance(message_id_hash, bytes):
77 message_id_hash = message_id_hash.decode("ascii")
76 return urljoin(self.list_url(mlist), message_id_hash)78 return urljoin(self.list_url(mlist), message_id_hash)
7779
78 def archive_message(self, mlist, msg):80 def archive_message(self, mlist, msg):
7981
=== modified file 'src/mailman/archiving/prototype.py'
--- src/mailman/archiving/prototype.py 2014-03-02 20:59:30 +0000
+++ src/mailman/archiving/prototype.py 2014-12-04 09:39:12 +0000
@@ -68,6 +68,8 @@
68 message_id_hash = msg.get('x-message-id-hash')68 message_id_hash = msg.get('x-message-id-hash')
69 if message_id_hash is None:69 if message_id_hash is None:
70 return None70 return None
71 if isinstance(message_id_hash, bytes):
72 message_id_hash = message_id_hash.decode("ascii")
71 return urljoin(Prototype.list_url(mlist), message_id_hash)73 return urljoin(Prototype.list_url(mlist), message_id_hash)
7274
73 @staticmethod75 @staticmethod
7476
=== modified file 'src/mailman/commands/docs/unshunt.rst'
--- src/mailman/commands/docs/unshunt.rst 2014-04-28 15:23:35 +0000
+++ src/mailman/commands/docs/unshunt.rst 2014-12-04 09:39:12 +0000
@@ -83,7 +83,7 @@
83 283 2
8484
85 >>> sorted(item.msg['message-id'] for item in items)85 >>> sorted(item.msg['message-id'] for item in items)
86 [u'<badgers>', u'<crow>']86 ['<badgers>', '<crow>']
8787
8888
89Return to the original queue89Return to the original queue
9090
=== modified file 'src/mailman/commands/eml_membership.py'
--- src/mailman/commands/eml_membership.py 2014-01-01 14:59:42 +0000
+++ src/mailman/commands/eml_membership.py 2014-12-04 09:39:12 +0000
@@ -72,6 +72,8 @@
72 print(_('$self.name: No valid address found to subscribe'),72 print(_('$self.name: No valid address found to subscribe'),
73 file=results)73 file=results)
74 return ContinueProcessing.no74 return ContinueProcessing.no
75 if isinstance(address, bytes):
76 address = address.decode("ascii")
75 # Have we already seen one join request from this user during the77 # Have we already seen one join request from this user during the
76 # processing of this email?78 # processing of this email?
77 joins = getattr(results, 'joins', set())79 joins = getattr(results, 'joins', set())
7880
=== modified file 'src/mailman/email/message.py'
--- src/mailman/email/message.py 2014-04-28 15:23:35 +0000
+++ src/mailman/email/message.py 2014-12-04 09:39:12 +0000
@@ -53,29 +53,6 @@
53 self.__version__ = VERSION53 self.__version__ = VERSION
54 email.message.Message.__init__(self)54 email.message.Message.__init__(self)
5555
56 def __getitem__(self, key):
57 # Ensure that header values are unicodes.
58 value = email.message.Message.__getitem__(self, key)
59 if isinstance(value, str):
60 return unicode(value, 'ascii')
61 return value
62
63 def get(self, name, failobj=None):
64 # Ensure that header values are unicodes.
65 value = email.message.Message.get(self, name, failobj)
66 if isinstance(value, str):
67 return unicode(value, 'ascii')
68 return value
69
70 def get_all(self, name, failobj=None):
71 # Ensure all header values are unicodes.
72 missing = object()
73 all_values = email.message.Message.get_all(self, name, missing)
74 if all_values is missing:
75 return failobj
76 return [(unicode(value, 'ascii') if isinstance(value, str) else value)
77 for value in all_values]
78
79 # BAW: For debugging w/ bin/dumpdb. Apparently pprint uses repr.56 # BAW: For debugging w/ bin/dumpdb. Apparently pprint uses repr.
80 def __repr__(self):57 def __repr__(self):
81 return self.__str__()58 return self.__str__()
@@ -144,18 +121,15 @@
144 field_values = self.get_all(header, [])121 field_values = self.get_all(header, [])
145 senders.extend(address.lower() for (display_name, address)122 senders.extend(address.lower() for (display_name, address)
146 in email.utils.getaddresses(field_values))123 in email.utils.getaddresses(field_values))
147 # Filter out None and the empty string.124 # Filter out None and the empty string, and convert to unicode.
148 return [sender for sender in senders if sender]125 clean_senders = []
149126 for sender in senders:
150 def get_filename(self, failobj=None):127 if not sender:
151 """Some MUA have bugs in RFC2231 filename encoding and cause128 continue
152 Mailman to stop delivery in Scrubber.py (called from ToDigest.py).129 if isinstance(sender, bytes):
153 """130 sender = sender.decode("ascii")
154 try:131 clean_senders.append(sender)
155 filename = email.message.Message.get_filename(self, failobj)132 return clean_senders
156 return filename
157 except (UnicodeError, LookupError, ValueError):
158 return failobj
159133
160134
161135
162136
163137
=== modified file 'src/mailman/email/tests/test_message.py'
--- src/mailman/email/tests/test_message.py 2014-01-01 14:59:42 +0000
+++ src/mailman/email/tests/test_message.py 2014-12-04 09:39:12 +0000
@@ -26,9 +26,10 @@
2626
2727
28import unittest28import unittest
29from email.parser import FeedParser
2930
30from mailman.app.lifecycle import create_list31from mailman.app.lifecycle import create_list
31from mailman.email.message import UserNotification32from mailman.email.message import UserNotification, Message
32from mailman.testing.helpers import get_queue_messages33from mailman.testing.helpers import get_queue_messages
33from mailman.testing.layers import ConfigLayer34from mailman.testing.layers import ConfigLayer
3435
@@ -58,3 +59,34 @@
58 self.assertEqual(len(messages), 1)59 self.assertEqual(len(messages), 1)
59 self.assertEqual(messages[0].msg.get_all('precedence'), 60 self.assertEqual(messages[0].msg.get_all('precedence'),
60 ['omg wtf bbq'])61 ['omg wtf bbq'])
62
63
64
6165
66class TestMessageSubclass(unittest.TestCase):
67
68 def test_i18n_filenames(self):
69 parser = FeedParser(_factory=Message)
70 parser.feed(b"""Message-ID: <blah@example.com>
71Content-Type: multipart/mixed; boundary="------------050607040206050605060208"
72
73This is a multi-part message in MIME format.
74--------------050607040206050605060208
75Content-Type: text/plain; charset=UTF-8
76Content-Transfer-Encoding: quoted-printable
77
78Test message containing an attachment with an accented filename
79
80--------------050607040206050605060208
81Content-Disposition: attachment;
82 filename*=UTF-8''d%C3%A9jeuner.txt
83
84Test content
85--------------050607040206050605060208--
86""")
87 msg = parser.close()
88 attachment = msg.get_payload()[1]
89 try:
90 filename = attachment.get_filename()
91 except TypeError as e:
92 self.fail(e)
93 self.assertEqual(filename, u"d\xe9jeuner.txt")
6294
=== modified file 'src/mailman/model/bounce.py'
--- src/mailman/model/bounce.py 2014-09-22 18:47:02 +0000
+++ src/mailman/model/bounce.py 2014-12-04 09:39:12 +0000
@@ -57,7 +57,10 @@
57 self.list_id = list_id57 self.list_id = list_id
58 self.email = email58 self.email = email
59 self.timestamp = now()59 self.timestamp = now()
60 self.message_id = msg['message-id']60 msgid = msg['message-id']
61 if isinstance(msgid, bytes):
62 msgid = msgid.decode("ascii")
63 self.message_id = msgid
61 self.context = (BounceContext.normal if context is None else context)64 self.context = (BounceContext.normal if context is None else context)
62 self.processed = False65 self.processed = False
6366
6467
=== modified file 'src/mailman/model/messagestore.py'
--- src/mailman/model/messagestore.py 2014-09-28 00:17:05 +0000
+++ src/mailman/model/messagestore.py 2014-12-04 09:39:12 +0000
@@ -58,6 +58,8 @@
58 raise ValueError('Exactly one Message-ID header required')58 raise ValueError('Exactly one Message-ID header required')
59 # Calculate and insert the X-Message-ID-Hash.59 # Calculate and insert the X-Message-ID-Hash.
60 message_id = message_ids[0]60 message_id = message_ids[0]
61 if isinstance(message_id, bytes):
62 message_id = message_id.decode("ascii")
61 # Complain if the Message-ID already exists in the storage.63 # Complain if the Message-ID already exists in the storage.
62 existing = store.query(Message).filter(64 existing = store.query(Message).filter(
63 Message.message_id == message_id).first()65 Message.message_id == message_id).first()
6466
=== modified file 'src/mailman/rules/implicit_dest.py'
--- src/mailman/rules/implicit_dest.py 2014-01-01 14:59:42 +0000
+++ src/mailman/rules/implicit_dest.py 2014-12-04 09:39:12 +0000
@@ -73,6 +73,8 @@
73 recipients = set()73 recipients = set()
74 for header in ('to', 'cc', 'resent-to', 'resent-cc'):74 for header in ('to', 'cc', 'resent-to', 'resent-cc'):
75 for fullname, address in getaddresses(msg.get_all(header, [])):75 for fullname, address in getaddresses(msg.get_all(header, [])):
76 if isinstance(address, bytes):
77 address = address.decode("ascii")
76 address = address.lower()78 address = address.lower()
77 if address in aliases:79 if address in aliases:
78 return False80 return False
7981
=== modified file 'src/mailman/utilities/email.py'
--- src/mailman/utilities/email.py 2014-04-28 15:23:35 +0000
+++ src/mailman/utilities/email.py 2014-12-04 09:39:12 +0000
@@ -62,6 +62,8 @@
62 message_id = msg.get('message-id')62 message_id = msg.get('message-id')
63 if message_id is None:63 if message_id is None:
64 return64 return
65 if isinstance(message_id, bytes):
66 message_id = message_id.decode("ascii")
65 # The angle brackets are not part of the Message-ID. See RFC 282267 # The angle brackets are not part of the Message-ID. See RFC 2822
66 # and http://wiki.list.org/display/DEV/Stable+URLs68 # and http://wiki.list.org/display/DEV/Stable+URLs
67 if message_id.startswith('<') and message_id.endswith('>'):69 if message_id.startswith('<') and message_id.endswith('>'):