summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: 23fb31a)
raw | patch | inline | side by side (parent: 23fb31a)
author | schlatterbeck <schlatterbeck@57a73879-2fb5-44c3-a270-3262357dd7e2> | |
Tue, 5 Oct 2010 14:24:25 +0000 (14:24 +0000) | ||
committer | schlatterbeck <schlatterbeck@57a73879-2fb5-44c3-a270-3262357dd7e2> | |
Tue, 5 Oct 2010 14:24:25 +0000 (14:24 +0000) |
of type message/rfc822 and attaches the individual parts instead of
attaching the whole message/rfc822 attachment to the roundup issue.
- Fix handling of incoming message/rfc822 attachments. These resulted in
a weird mail usage error because the email module threw a TypeError
which roundup interprets as a Reject exception. Fixes issue2550667.
Added regression tests for message/rfc822 attachments with and without
configured unpacking (mailgw unpack_rfc822, see Features above)
Thanks to Benni Bärmann for reporting.
git-svn-id: http://svn.roundup-tracker.org/svnroot/roundup/roundup/trunk@4530 57a73879-2fb5-44c3-a270-3262357dd7e2
attaching the whole message/rfc822 attachment to the roundup issue.
- Fix handling of incoming message/rfc822 attachments. These resulted in
a weird mail usage error because the email module threw a TypeError
which roundup interprets as a Reject exception. Fixes issue2550667.
Added regression tests for message/rfc822 attachments with and without
configured unpacking (mailgw unpack_rfc822, see Features above)
Thanks to Benni Bärmann for reporting.
git-svn-id: http://svn.roundup-tracker.org/svnroot/roundup/roundup/trunk@4530 57a73879-2fb5-44c3-a270-3262357dd7e2
diff --git a/CHANGES.txt b/CHANGES.txt
index 55bcab3771a859a9baacd93dd6052d51992821e6..2347b09f1f26d06f5301c39df0dbb7df650e8e41 100644 (file)
--- a/CHANGES.txt
+++ b/CHANGES.txt
timeout of 30 seconds configurable. This is the time a client waits
for the locked database to become free before giving up. Used only for
SQLite backend.
+- new mailgw config item unpack_rfc822 that unpacks message attachments
+ of type message/rfc822 and attaches the individual parts instead of
+ attaching the whole message/rfc822 attachment to the roundup issue.
Fixed:
- Fix charset of first text-part of outgoing multipart messages, thanks Dirk
Geschke for reporting, see
http://thread.gmane.org/gmane.comp.bug-tracking.roundup.user/10223
+- Fix handling of incoming message/rfc822 attachments. These resulted in
+ a weird mail usage error because the email module threw a TypeError
+ which roundup interprets as a Reject exception. Fixes issue2550667.
+ Added regression tests for message/rfc822 attachments with and without
+ configured unpacking (mailgw unpack_rfc822, see Features above)
+ Thanks to Benni Bärmann for reporting.
2010-07-12 1.4.15
index 60b4190b1f4f357dc695ff015070a876687544dc..8e42cef1418840d171bb0753449acaed89c17d7b 100644 (file)
--- a/roundup/configuration.py
+++ b/roundup/configuration.py
"Regular expression matching end of line."),
(RegExpOption, "blankline_re", r"[\r\n]+\s*[\r\n]+",
"Regular expression matching a blank line."),
+ (BooleanOption, "unpack_rfc822", "no",
+ "Unpack attached messages (encoded as message/rfc822 in MIME)\n"
+ "as multiple parts attached as files to the issue, if not\n"
+ "set we handle message/rfc822 attachments as a single file."),
(BooleanOption, "ignore_alternatives", "no",
"When parsing incoming mails, roundup uses the first\n"
"text/plain part it finds. If this part is inside a\n"
diff --git a/roundup/mailgw.py b/roundup/mailgw.py
index d96d5edaaba08cc65563173a4e7a82db2feff63b..67cf6a3739e10ad27d22d5f4913e58b7f4d92432 100644 (file)
--- a/roundup/mailgw.py
+++ b/roundup/mailgw.py
and given "file" class nodes that are linked to the "msg" node.
. In a multipart/alternative message or part, we look for a text/plain
subpart and ignore the other parts.
+ . A message/rfc822 is treated similar tomultipart/mixed (except for
+ special handling of the first text part) if unpack_rfc822 is set in
+ the mailgw config section.
Summary
-------
def getname(self):
"""Find an appropriate name for this message."""
+ name = None
if self.gettype() == 'message/rfc822':
# handle message/rfc822 specially - the name should be
# the subject of the actual e-mail embedded here
+ # we add a '.eml' extension like other email software does it
self.fp.seek(0)
- name = Message(self.fp).getheader('subject')
- else:
+ s = cStringIO.StringIO(self.getbody())
+ name = Message(s).getheader('subject')
+ if name:
+ name = name + '.eml'
+ if not name:
# try name on Content-Type
name = self.getparam('name')
if not name:
# flagging.
# multipart/form-data:
# For web forms only.
+ # message/rfc822:
+ # Only if configured in [mailgw] unpack_rfc822
- def extract_content(self, parent_type=None, ignore_alternatives = False):
+ def extract_content(self, parent_type=None, ignore_alternatives=False,
+ unpack_rfc822=False):
"""Extract the body and the attachments recursively.
If the content is hidden inside a multipart/alternative part,
ig = ignore_alternatives and not content_found
for part in self.getparts():
new_content, new_attach = part.extract_content(content_type,
- not content and ig)
+ not content and ig, unpack_rfc822)
# If we haven't found a text/plain part yet, take this one,
# otherwise make it an attachment.
attachments.extend(new_attach)
if ig and content_type == 'multipart/alternative' and content:
attachments = []
+ elif unpack_rfc822 and content_type == 'message/rfc822':
+ s = cStringIO.StringIO(self.getbody())
+ m = Message(s)
+ ig = ignore_alternatives and not content
+ new_content, attachments = m.extract_content(m.gettype(), ig,
+ unpack_rfc822)
+ attachments.insert(0, m.text_as_attachment())
elif (parent_type == 'multipart/signed' and
content_type == 'application/pgp-signature'):
# ignore it so it won't be saved as an attachment
encrypted.""")
# now handle the body - find the message
ig = self.instance.config.MAILGW_IGNORE_ALTERNATIVES
- content, attachments = message.extract_content(ignore_alternatives = ig)
+ content, attachments = message.extract_content(ignore_alternatives=ig,
+ unpack_rfc822 = self.instance.config.MAILGW_UNPACK_RFC822)
if content is None:
raise MailUsageError, _("""
Roundup requires the submission to be plain text. The message parser could
diff --git a/roundup/roundupdb.py b/roundup/roundupdb.py
index d3576b1732e19e56febb738db917800dec5ef269..eb716022397268261121779d51588907c5d77013 100644 (file)
--- a/roundup/roundupdb.py
+++ b/roundup/roundupdb.py
from email.Header import Header
from email.MIMEText import MIMEText
from email.MIMEBase import MIMEBase
+from email.parser import FeedParser
from roundup import password, date, hyperdb
from roundup.i18n import _
else:
part = MIMEText(content)
part['Content-Transfer-Encoding'] = '7bit'
+ elif mime_type == 'message/rfc822':
+ main, sub = mime_type.split('/')
+ p = FeedParser()
+ p.feed(content)
+ part = MIMEBase(main, sub)
+ part.set_payload([p.close()])
else:
# some other type, so encode it
if not mime_type:
part = MIMEBase(main, sub)
part.set_payload(content)
Encoders.encode_base64(part)
- part['Content-Disposition'] = 'attachment;\n filename="%s"'%name
+ cd = 'Content-Disposition'
+ part[cd] = 'attachment;\n filename="%s"'%name
message.attach(part)
else:
diff --git a/test/test_mailgw.py b/test/test_mailgw.py
index 47b8eeb77607bb1f67ed1db103c203c9e0b3f651..29d063a4d3ea3b4bdba85023ac55d2c65366ed49 100644 (file)
--- a/test/test_mailgw.py
+++ b/test/test_mailgw.py
<html>umlaut =E4=F6=FC=C4=D6=DC=DF</html>
+--001485f339f8f361fb049188dbba--
+'''
+
+ multipart_msg_rfc822 = '''From: mary <mary@test.test>
+To: issue_tracker@your.tracker.email.domain.example
+Message-Id: <followup_dummy_id>
+In-Reply-To: <dummy_test_message_id>
+Subject: [issue1] Testing...
+Content-Type: multipart/mixed; boundary=001485f339f8f361fb049188dbba
+
+This is a multi-part message in MIME format.
+--001485f339f8f361fb049188dbba
+Content-Type: text/plain; charset=ISO-8859-15
+Content-Transfer-Encoding: 7bit
+
+First part: Text
+
+--001485f339f8f361fb049188dbba
+Content-Type: message/rfc822; name="Fwd: Original email subject.eml"
+Content-Transfer-Encoding: 7bit
+Content-Disposition: attachment; filename="Fwd: Original email subject.eml"
+
+Message-Id: <followup_dummy_id_2>
+In-Reply-To: <dummy_test_message_id_2>
+MIME-Version: 1.0
+Subject: Fwd: Original email subject
+Date: Mon, 23 Aug 2010 08:23:33 +0200
+Content-Type: multipart/alternative; boundary="090500050101020406060002"
+
+This is a multi-part message in MIME format.
+--090500050101020406060002
+Content-Type: text/plain; charset=ISO-8859-15; format=flowed
+Content-Transfer-Encoding: 7bit
+
+some text in inner email
+========================
+
+--090500050101020406060002
+Content-Type: text/html; charset=ISO-8859-15
+Content-Transfer-Encoding: 7bit
+
+<html>
+some text in inner email
+========================
+</html>
+
+--090500050101020406060002--
+
--001485f339f8f361fb049188dbba--
'''
--utf-8--
''')
+ def testMultipartRFC822(self):
+ self.doNewIssue()
+ self._handle_mail(self.multipart_msg_rfc822)
+ messages = self.db.issue.get('1', 'messages')
+ messages.sort()
+ msg = self.db.msg.getnode (messages[-1])
+ assert(len(msg.files) == 1)
+ name = "Fwd: Original email subject.eml"
+ for n, id in enumerate (msg.files):
+ f = self.db.file.getnode (id)
+ self.assertEqual(f.name, name)
+ self.assertEqual(msg.content, 'First part: Text')
+ self.compareMessages(self._get_mail(),
+'''TO: chef@bork.bork.bork, richard@test.test
+Content-Type: text/plain; charset="utf-8"
+Subject: [issue1] Testing...
+To: chef@bork.bork.bork, richard@test.test
+From: "Contrary, Mary" <issue_tracker@your.tracker.email.domain.example>
+Reply-To: Roundup issue tracker
+ <issue_tracker@your.tracker.email.domain.example>
+MIME-Version: 1.0
+Message-Id: <followup_dummy_id>
+In-Reply-To: <dummy_test_message_id>
+X-Roundup-Name: Roundup issue tracker
+X-Roundup-Loop: hello
+X-Roundup-Issue-Status: chatting
+X-Roundup-Issue-Files: Fwd: Original email subject.eml
+Content-Transfer-Encoding: quoted-printable
+
+
+--utf-8
+MIME-Version: 1.0
+Content-Type: text/plain; charset="utf-8"
+Content-Transfer-Encoding: quoted-printable
+
+
+Contrary, Mary <mary@test.test> added the comment:
+
+First part: Text
+
+----------
+status: unread -> chatting
+
+_______________________________________________________________________
+Roundup issue tracker <issue_tracker@your.tracker.email.domain.example>
+<http://tracker.example/cgi-bin/roundup.cgi/bugs/issue1>
+_______________________________________________________________________
+--utf-8
+Content-Type: message/rfc822
+MIME-Version: 1.0
+Content-Disposition: attachment;
+ filename="Fwd: Original email subject.eml"
+
+Message-Id: <followup_dummy_id_2>
+In-Reply-To: <dummy_test_message_id_2>
+MIME-Version: 1.0
+Subject: Fwd: Original email subject
+Date: Mon, 23 Aug 2010 08:23:33 +0200
+Content-Type: multipart/alternative; boundary="090500050101020406060002"
+
+This is a multi-part message in MIME format.
+--090500050101020406060002
+Content-Type: text/plain; charset=ISO-8859-15; format=flowed
+Content-Transfer-Encoding: 7bit
+
+some text in inner email
+========================
+
+--090500050101020406060002
+Content-Type: text/html; charset=ISO-8859-15
+Content-Transfer-Encoding: 7bit
+
+<html>
+some text in inner email
+========================
+</html>
+
+--090500050101020406060002--
+
+--utf-8--
+''')
+
+ def testMultipartRFC822Unpack(self):
+ self.doNewIssue()
+ self.db.config.MAILGW_UNPACK_RFC822 = True
+ self._handle_mail(self.multipart_msg_rfc822)
+ messages = self.db.issue.get('1', 'messages')
+ messages.sort()
+ msg = self.db.msg.getnode (messages[-1])
+ self.assertEqual(len(msg.files), 2)
+ t = 'some text in inner email\n========================\n'
+ content = {0 : t, 1 : '<html>\n' + t + '</html>\n'}
+ for n, id in enumerate (msg.files):
+ f = self.db.file.getnode (id)
+ self.assertEqual(f.name, 'unnamed')
+ if n in content :
+ self.assertEqual(f.content, content [n])
+ self.assertEqual(msg.content, 'First part: Text')
+
def testSimpleFollowup(self):
self.doNewIssue()
self._handle_mail('''Content-Type: text/plain;