From ff433a24f4c626bbf5ad0f3a7be93a24edf3a3be Mon Sep 17 00:00:00 2001 From: Adam Coddington Date: Tue, 7 Jul 2015 23:09:41 -0700 Subject: [PATCH] Always store payload parts having a Content-Disposition of 'attachment' as attachments, even if their Content-Type is text/plain. Fixes #52. Squashed commit of the following: commit 5bcbac33d6b8283d4943d9346f354e6dbe669a55 Author: Adam Coddington Date: Tue Jul 7 23:05:15 2015 -0700 Add handling for situations in which the message was deleted in another thread before processing. commit 6183f68b39f292c6c6fe489b8b08b0b4332f8e3a Author: Adam Coddington Date: Tue Jul 7 22:58:15 2015 -0700 Always store message as attachment if its content-disposition is marked as such. commit 4c16494b1a89e0c78edf41ef8c334c38289e62ac Author: Adam Coddington Date: Tue Jul 7 22:46:52 2015 -0700 Adding a (hopefully) failing test case for #52. --- django_mailbox/models.py | 5 ++- .../messages/message_with_text_attachment.eml | 33 +++++++++++++++++++ django_mailbox/tests/test_process_email.py | 9 +++++ django_mailbox/transports/imap.py | 8 ++++- 4 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 django_mailbox/tests/messages/message_with_text_attachment.eml diff --git a/django_mailbox/models.py b/django_mailbox/models.py index d227af5..4324cd8 100644 --- a/django_mailbox/models.py +++ b/django_mailbox/models.py @@ -298,7 +298,10 @@ class Mailbox(models.Model): ) ) new.set_payload('') - elif msg.get_content_type() not in TEXT_STORED_MIMETYPES: + elif ( + (msg.get_content_type() not in TEXT_STORED_MIMETYPES) or + ('attachment' in msg.get('Content-Disposition', '')) + ): filename = msg.get_filename() if not filename: extension = mimetypes.guess_extension(msg.get_content_type()) diff --git a/django_mailbox/tests/messages/message_with_text_attachment.eml b/django_mailbox/tests/messages/message_with_text_attachment.eml new file mode 100644 index 0000000..c329f55 --- /dev/null +++ b/django_mailbox/tests/messages/message_with_text_attachment.eml @@ -0,0 +1,33 @@ +MIME-Version: 1.0 +Received: by 10.76.173.10 with HTTP; Tue, 7 Jul 2015 19:50:29 -0700 (PDT) +X-Originating-IP: [71.63.222.120] +Date: Tue, 7 Jul 2015 19:50:29 -0700 +Delivered-To: me@adamcoddington.net +Message-ID: +Subject: Test e-mail message +From: Adam Coddington +To: Adam Coddington +Content-Type: multipart/mixed; boundary=089e0111d1089c45be051a5433d8 + +--089e0111d1089c45be051a5433d8 +Content-Type: multipart/alternative; boundary=089e0111d1089c45b4051a5433d6 + +--089e0111d1089c45b4051a5433d6 +Content-Type: text/plain; charset=UTF-8 + +Has an attached text document, too! + +--089e0111d1089c45b4051a5433d6 +Content-Type: text/html; charset=UTF-8 + +
Has an attached text document, too!
+ +--089e0111d1089c45b4051a5433d6-- +--089e0111d1089c45be051a5433d8 +Content-Type: text/plain; charset=US-ASCII; name="attachment.txt" +Content-Disposition: attachment; filename="attachment.txt" +Content-Transfer-Encoding: base64 +X-Attachment-Id: f_ibu64edo0 + +VGhpcyBpcyBhbiBhdHRhY2htZW50Lgo= +--089e0111d1089c45be051a5433d8-- diff --git a/django_mailbox/tests/test_process_email.py b/django_mailbox/tests/test_process_email.py index 320dcdd..47632cd 100644 --- a/django_mailbox/tests/test_process_email.py +++ b/django_mailbox/tests/test_process_email.py @@ -230,3 +230,12 @@ class TestProcessEmail(EmailMessageTestCase): second_reply_msg = msg.reply(reply_email_object) self.assertEqual(self.mailbox.from_email, second_reply_msg.from_header) + + def test_message_with_text_attachment(self): + email_object = self._get_email_object( + 'message_with_text_attachment.eml', + ) + + msg = self.mailbox.process_incoming_message(email_object) + + self.assertEqual(msg.attachments.all().count(), 1) diff --git a/django_mailbox/transports/imap.py b/django_mailbox/transports/imap.py index cef3eb4..82f7530 100644 --- a/django_mailbox/transports/imap.py +++ b/django_mailbox/transports/imap.py @@ -103,7 +103,13 @@ class ImapTransport(EmailTransport): typ, msg_contents = self.server.uid('fetch', uid, '(RFC822)') if not msg_contents: continue - message = self.get_email_from_bytes(msg_contents[0][1]) + try: + message = self.get_email_from_bytes(msg_contents[0][1]) + except TypeError: + # This happens if another thread/process deletes the + # message between our generating the ID list and our + # processing it here. + continue if condition and not condition(message): continue