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
1=== modified file 'src/mailman/app/bounces.py'
2--- src/mailman/app/bounces.py 2014-01-07 03:43:59 +0000
3+++ src/mailman/app/bounces.py 2014-12-04 09:39:12 +0000
4@@ -200,10 +200,13 @@
5 optionsurl=member.options_url,
6 owneraddr=mlist.owner_address,
7 )
8+ message_id = msg['message-id']
9+ if isinstance(message_id, bytes):
10+ message_id = message_id.decode("ascii")
11 pendable = _ProbePendable(
12 # We can only pend unicodes.
13 member_id=member.member_id.hex,
14- message_id=msg['message-id'],
15+ message_id=message_id,
16 )
17 token = getUtility(IPendings).add(pendable)
18 mailbox, domain_parts = split_email(mlist.bounces_address)
19
20=== modified file 'src/mailman/app/moderator.py'
21--- src/mailman/app/moderator.py 2014-04-28 15:23:35 +0000
22+++ src/mailman/app/moderator.py 2014-12-04 09:39:12 +0000
23@@ -86,9 +86,9 @@
24 # Message-ID header.
25 message_id = msg.get('message-id')
26 if message_id is None:
27- msg['Message-ID'] = message_id = unicode(make_msgid())
28- assert isinstance(message_id, unicode), (
29- 'Message-ID is not a unicode: %s' % message_id)
30+ msg['Message-ID'] = message_id = make_msgid().decode("ascii")
31+ if isinstance(message_id, bytes):
32+ message_id = message_id.decode("ascii")
33 getUtility(IMessageStore).add(msg)
34 # Prepare the message metadata with some extra information needed only by
35 # the moderation interface.
36
37=== modified file 'src/mailman/archiving/mailarchive.py'
38--- src/mailman/archiving/mailarchive.py 2014-01-01 14:59:42 +0000
39+++ src/mailman/archiving/mailarchive.py 2014-12-04 09:39:12 +0000
40@@ -68,6 +68,8 @@
41 message_id_hash = msg.get('x-message-id-hash')
42 if message_id_hash is None:
43 return None
44+ if isinstance(message_id_hash, bytes):
45+ message_id_hash = message_id_hash.decode("ascii")
46 return urljoin(self.base_url, message_id_hash)
47
48 def archive_message(self, mlist, msg):
49
50=== modified file 'src/mailman/archiving/mhonarc.py'
51--- src/mailman/archiving/mhonarc.py 2014-01-01 14:59:42 +0000
52+++ src/mailman/archiving/mhonarc.py 2014-12-04 09:39:12 +0000
53@@ -73,6 +73,8 @@
54 message_id_hash = msg.get('x-message-id-hash')
55 if message_id_hash is None:
56 return None
57+ if isinstance(message_id_hash, bytes):
58+ message_id_hash = message_id_hash.decode("ascii")
59 return urljoin(self.list_url(mlist), message_id_hash)
60
61 def archive_message(self, mlist, msg):
62
63=== modified file 'src/mailman/archiving/prototype.py'
64--- src/mailman/archiving/prototype.py 2014-03-02 20:59:30 +0000
65+++ src/mailman/archiving/prototype.py 2014-12-04 09:39:12 +0000
66@@ -68,6 +68,8 @@
67 message_id_hash = msg.get('x-message-id-hash')
68 if message_id_hash is None:
69 return None
70+ if isinstance(message_id_hash, bytes):
71+ message_id_hash = message_id_hash.decode("ascii")
72 return urljoin(Prototype.list_url(mlist), message_id_hash)
73
74 @staticmethod
75
76=== modified file 'src/mailman/commands/docs/unshunt.rst'
77--- src/mailman/commands/docs/unshunt.rst 2014-04-28 15:23:35 +0000
78+++ src/mailman/commands/docs/unshunt.rst 2014-12-04 09:39:12 +0000
79@@ -83,7 +83,7 @@
80 2
81
82 >>> sorted(item.msg['message-id'] for item in items)
83- [u'<badgers>', u'<crow>']
84+ ['<badgers>', '<crow>']
85
86
87 Return to the original queue
88
89=== modified file 'src/mailman/commands/eml_membership.py'
90--- src/mailman/commands/eml_membership.py 2014-01-01 14:59:42 +0000
91+++ src/mailman/commands/eml_membership.py 2014-12-04 09:39:12 +0000
92@@ -72,6 +72,8 @@
93 print(_('$self.name: No valid address found to subscribe'),
94 file=results)
95 return ContinueProcessing.no
96+ if isinstance(address, bytes):
97+ address = address.decode("ascii")
98 # Have we already seen one join request from this user during the
99 # processing of this email?
100 joins = getattr(results, 'joins', set())
101
102=== modified file 'src/mailman/email/message.py'
103--- src/mailman/email/message.py 2014-04-28 15:23:35 +0000
104+++ src/mailman/email/message.py 2014-12-04 09:39:12 +0000
105@@ -53,29 +53,6 @@
106 self.__version__ = VERSION
107 email.message.Message.__init__(self)
108
109- def __getitem__(self, key):
110- # Ensure that header values are unicodes.
111- value = email.message.Message.__getitem__(self, key)
112- if isinstance(value, str):
113- return unicode(value, 'ascii')
114- return value
115-
116- def get(self, name, failobj=None):
117- # Ensure that header values are unicodes.
118- value = email.message.Message.get(self, name, failobj)
119- if isinstance(value, str):
120- return unicode(value, 'ascii')
121- return value
122-
123- def get_all(self, name, failobj=None):
124- # Ensure all header values are unicodes.
125- missing = object()
126- all_values = email.message.Message.get_all(self, name, missing)
127- if all_values is missing:
128- return failobj
129- return [(unicode(value, 'ascii') if isinstance(value, str) else value)
130- for value in all_values]
131-
132 # BAW: For debugging w/ bin/dumpdb. Apparently pprint uses repr.
133 def __repr__(self):
134 return self.__str__()
135@@ -144,18 +121,15 @@
136 field_values = self.get_all(header, [])
137 senders.extend(address.lower() for (display_name, address)
138 in email.utils.getaddresses(field_values))
139- # Filter out None and the empty string.
140- return [sender for sender in senders if sender]
141-
142- def get_filename(self, failobj=None):
143- """Some MUA have bugs in RFC2231 filename encoding and cause
144- Mailman to stop delivery in Scrubber.py (called from ToDigest.py).
145- """
146- try:
147- filename = email.message.Message.get_filename(self, failobj)
148- return filename
149- except (UnicodeError, LookupError, ValueError):
150- return failobj
151+ # Filter out None and the empty string, and convert to unicode.
152+ clean_senders = []
153+ for sender in senders:
154+ if not sender:
155+ continue
156+ if isinstance(sender, bytes):
157+ sender = sender.decode("ascii")
158+ clean_senders.append(sender)
159+ return clean_senders
160
161
162
163
164
165=== modified file 'src/mailman/email/tests/test_message.py'
166--- src/mailman/email/tests/test_message.py 2014-01-01 14:59:42 +0000
167+++ src/mailman/email/tests/test_message.py 2014-12-04 09:39:12 +0000
168@@ -26,9 +26,10 @@
169
170
171 import unittest
172+from email.parser import FeedParser
173
174 from mailman.app.lifecycle import create_list
175-from mailman.email.message import UserNotification
176+from mailman.email.message import UserNotification, Message
177 from mailman.testing.helpers import get_queue_messages
178 from mailman.testing.layers import ConfigLayer
179
180@@ -58,3 +59,34 @@
181 self.assertEqual(len(messages), 1)
182 self.assertEqual(messages[0].msg.get_all('precedence'),
183 ['omg wtf bbq'])
184+
185+
186+
187
188+class TestMessageSubclass(unittest.TestCase):
189+
190+ def test_i18n_filenames(self):
191+ parser = FeedParser(_factory=Message)
192+ parser.feed(b"""Message-ID: <blah@example.com>
193+Content-Type: multipart/mixed; boundary="------------050607040206050605060208"
194+
195+This is a multi-part message in MIME format.
196+--------------050607040206050605060208
197+Content-Type: text/plain; charset=UTF-8
198+Content-Transfer-Encoding: quoted-printable
199+
200+Test message containing an attachment with an accented filename
201+
202+--------------050607040206050605060208
203+Content-Disposition: attachment;
204+ filename*=UTF-8''d%C3%A9jeuner.txt
205+
206+Test content
207+--------------050607040206050605060208--
208+""")
209+ msg = parser.close()
210+ attachment = msg.get_payload()[1]
211+ try:
212+ filename = attachment.get_filename()
213+ except TypeError as e:
214+ self.fail(e)
215+ self.assertEqual(filename, u"d\xe9jeuner.txt")
216
217=== modified file 'src/mailman/model/bounce.py'
218--- src/mailman/model/bounce.py 2014-09-22 18:47:02 +0000
219+++ src/mailman/model/bounce.py 2014-12-04 09:39:12 +0000
220@@ -57,7 +57,10 @@
221 self.list_id = list_id
222 self.email = email
223 self.timestamp = now()
224- self.message_id = msg['message-id']
225+ msgid = msg['message-id']
226+ if isinstance(msgid, bytes):
227+ msgid = msgid.decode("ascii")
228+ self.message_id = msgid
229 self.context = (BounceContext.normal if context is None else context)
230 self.processed = False
231
232
233=== modified file 'src/mailman/model/messagestore.py'
234--- src/mailman/model/messagestore.py 2014-09-28 00:17:05 +0000
235+++ src/mailman/model/messagestore.py 2014-12-04 09:39:12 +0000
236@@ -58,6 +58,8 @@
237 raise ValueError('Exactly one Message-ID header required')
238 # Calculate and insert the X-Message-ID-Hash.
239 message_id = message_ids[0]
240+ if isinstance(message_id, bytes):
241+ message_id = message_id.decode("ascii")
242 # Complain if the Message-ID already exists in the storage.
243 existing = store.query(Message).filter(
244 Message.message_id == message_id).first()
245
246=== modified file 'src/mailman/rules/implicit_dest.py'
247--- src/mailman/rules/implicit_dest.py 2014-01-01 14:59:42 +0000
248+++ src/mailman/rules/implicit_dest.py 2014-12-04 09:39:12 +0000
249@@ -73,6 +73,8 @@
250 recipients = set()
251 for header in ('to', 'cc', 'resent-to', 'resent-cc'):
252 for fullname, address in getaddresses(msg.get_all(header, [])):
253+ if isinstance(address, bytes):
254+ address = address.decode("ascii")
255 address = address.lower()
256 if address in aliases:
257 return False
258
259=== modified file 'src/mailman/utilities/email.py'
260--- src/mailman/utilities/email.py 2014-04-28 15:23:35 +0000
261+++ src/mailman/utilities/email.py 2014-12-04 09:39:12 +0000
262@@ -62,6 +62,8 @@
263 message_id = msg.get('message-id')
264 if message_id is None:
265 return
266+ if isinstance(message_id, bytes):
267+ message_id = message_id.decode("ascii")
268 # The angle brackets are not part of the Message-ID. See RFC 2822
269 # and http://wiki.list.org/display/DEV/Stable+URLs
270 if message_id.startswith('<') and message_id.endswith('>'):