Mercurial > p > roundup > code
diff roundup/mailgw.py @ 3945:1dd64778bc45
Mail improvements:
- Implement new config option in mail-section "ignore_alternatives" to
ignore alternatives in a multipart/alternative mail. The *last*
text/plain part of the *first* multipart/alternative is used as the
message, if ignore_alternatives is set all other alternative parts of
the first multipart/alternative that contained a text/plain part are
ignored. Other multipart/alternative or other multipart are attached
as before. This fixes [SF#959811] "Multipart/alternative handling
considered bad". Note that this also changes which text/plain part is
attached as the message if there are several text/plain parts in a
multipart: Previously the *first* text/plain would be attached. Now we
attach the *last* one, this is more in line with rfc 2046, sec. 5.1.4.
according to Philipp Gortan.
- Fix bug in attachment of text parts: If there are multiple text/plain
parts in a nested multipart, the previous code would attach the
multipart serialisation instead of the text/plain serialisation as a
file to the issue in some cases.
- Add regression tests for the new config-option and bug-fixes above.
| author | Ralf Schlatterbeck <schlatterbeck@users.sourceforge.net> |
|---|---|
| date | Wed, 14 Nov 2007 14:57:47 +0000 |
| parents | 586679a314f7 |
| children | 81531a2aed59 |
line wrap: on
line diff
--- a/roundup/mailgw.py Wed Nov 14 05:53:20 2007 +0000 +++ b/roundup/mailgw.py Wed Nov 14 14:57:47 2007 +0000 @@ -73,7 +73,7 @@ an exception, the original message is bounced back to the sender with the explanatory message given in the exception. -$Id: mailgw.py,v 1.192 2007-09-26 03:20:21 jpend Exp $ +$Id: mailgw.py,v 1.193 2007-11-14 14:57:47 schlatterbeck Exp $ """ __docformat__ = 'restructuredtext' @@ -346,8 +346,13 @@ # multipart/form-data: # For web forms only. - def extract_content(self, parent_type=None): - """Extract the body and the attachments recursively.""" + def extract_content(self, parent_type=None, ignore_alternatives = False): + """Extract the body and the attachments recursively. + + If the content is hidden inside a multipart/alternative part, + we use the *last* text/plain part of the *first* + multipart/alternative in the whole message. + """ content_type = self.gettype() content = None attachments = [] @@ -355,17 +360,35 @@ if content_type == 'text/plain': content = self.getbody() elif content_type[:10] == 'multipart/': + content_found = bool (content) + ig = ignore_alternatives and not content_found for part in self.getparts(): - new_content, new_attach = part.extract_content(content_type) + new_content, new_attach = part.extract_content(content_type, + not content and ig) # If we haven't found a text/plain part yet, take this one, # otherwise make it an attachment. if not content: content = new_content + cpart = part elif new_content: - attachments.append(part.as_attachment()) + if content_found or content_type != 'multipart/alternative': + attachments.append(part.text_as_attachment()) + else: + # if we have found a text/plain in the current + # multipart/alternative and find another one, we + # use the first as an attachment (if configured) + # and use the second one because rfc 2046, sec. + # 5.1.4. specifies that later parts are better + # (thanks to Philipp Gortan for pointing this + # out) + attachments.append(cpart.text_as_attachment()) + content = new_content + cpart = part attachments.extend(new_attach) + if ig and content_type == 'multipart/alternative' and content: + attachments = [] elif (parent_type == 'multipart/signed' and content_type == 'application/pgp-signature'): # ignore it so it won't be saved as an attachment @@ -374,6 +397,20 @@ attachments.append(self.as_attachment()) return content, attachments + def text_as_attachment(self): + """Return first text/plain part as Message""" + if not self.gettype().startswith ('multipart/'): + return self.as_attachment() + for part in self.getparts(): + content_type = part.gettype() + if content_type == 'text/plain': + return part.as_attachment() + elif content_type.startswith ('multipart/'): + p = part.text_as_attachment() + if p: + return p + return None + def as_attachment(self): """Return this message as an attachment.""" return (self.getname(), self.gettype(), self.getbody()) @@ -1204,7 +1241,8 @@ This tracker has been configured to require all email be PGP signed or encrypted.""") # now handle the body - find the message - content, attachments = message.extract_content() + ig = self.instance.config.MAILGW_IGNORE_ALTERNATIVES + content, attachments = message.extract_content(ignore_alternatives = ig) if content is None: raise MailUsageError, _(""" Roundup requires the submission to be plain text. The message parser could
