From 7d7448011b961b742ab053f29e265a554eff8297 Mon Sep 17 00:00:00 2001 From: Luke Plant Date: Sat, 15 Apr 2017 21:45:32 +0300 Subject: [PATCH] Fixed crasher when sending rfc822 messages as attachments. (#59) --- anymail/utils.py | 6 ++++++ tests/test_mailgun_backend.py | 26 ++++++++++++++++++++++++-- tests/utils.py | 3 +++ 3 files changed, 33 insertions(+), 2 deletions(-) diff --git a/anymail/utils.py b/anymail/utils.py index 66c307a..f9e71ad 100644 --- a/anymail/utils.py +++ b/anymail/utils.py @@ -171,6 +171,12 @@ class Attachment(object): if isinstance(attachment, MIMEBase): self.name = attachment.get_filename() self.content = attachment.get_payload(decode=True) + if self.content is None: + if hasattr(attachment, 'as_bytes'): + self.content = attachment.as_bytes() + else: + # Python 2.7 fallback + self.content = attachment.as_string().encode(self.encoding) self.mimetype = attachment.get_content_type() if get_content_disposition(attachment) == 'inline': diff --git a/tests/test_mailgun_backend.py b/tests/test_mailgun_backend.py index 7c6da1f..ed96bbf 100644 --- a/tests/test_mailgun_backend.py +++ b/tests/test_mailgun_backend.py @@ -1,6 +1,14 @@ # -*- coding: utf-8 -*- from datetime import date, datetime +try: + from email import message_from_bytes +except ImportError: + from email import message_from_string + + def message_from_bytes(s): + return message_from_string(s.decode('utf-8')) + from email.mime.base import MIMEBase from email.mime.image import MIMEImage @@ -14,7 +22,7 @@ from anymail.exceptions import AnymailAPIError, AnymailRequestsAPIError, Anymail from anymail.message import attach_inline_image_file from .mock_requests_backend import RequestsBackendMockAPITestCase, SessionSharingTestCasesMixin -from .utils import sample_image_content, sample_image_path, SAMPLE_IMAGE_FILENAME, AnymailTestMixin +from .utils import sample_image_content, sample_image_path, SAMPLE_FORWARDED_EMAIL, SAMPLE_IMAGE_FILENAME, AnymailTestMixin @override_settings(EMAIL_BACKEND='anymail.backends.mailgun.EmailBackend', @@ -131,13 +139,27 @@ class MailgunBackendStandardEmailTests(MailgunBackendMockAPITestCase): mimeattachment.set_payload(pdf_content) self.message.attach(mimeattachment) + # And also with an message/rfc822 attachment + forwarded_email = message_from_bytes(SAMPLE_FORWARDED_EMAIL) + rfcmessage = MIMEBase("message", "rfc822") + rfcmessage.attach(forwarded_email) + self.message.attach(rfcmessage) + self.message.send() files = self.get_api_call_files() attachments = [value for (field, value) in files if field == 'attachment'] - self.assertEqual(len(attachments), 3) + self.assertEqual(len(attachments), 4) self.assertEqual(attachments[0], ('test.txt', text_content, 'text/plain')) self.assertEqual(attachments[1], ('test.png', png_content, 'image/png')) # type inferred from filename self.assertEqual(attachments[2], (None, pdf_content, 'application/pdf')) # no filename + # Email messages can get a bit changed with respect to + # whitespace characters in headers, without breaking the message, so we + # tolerate that: + self.assertEqual(attachments[3][0], None) + self.assertEqual(attachments[3][1].replace(b'\n', b'').replace(b' ', b''), + (b'Content-Type: message/rfc822\nMIME-Version: 1.0\n\n' + SAMPLE_FORWARDED_EMAIL).replace(b'\n', b'').replace(b' ', b'')) + self.assertEqual(attachments[3][2], 'message/rfc822') + # Make sure the image attachment is not treated as embedded: inlines = [value for (field, value) in files if field == 'inline'] self.assertEqual(len(inlines), 0) diff --git a/tests/utils.py b/tests/utils.py index 79cbdb4..e341797 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -52,6 +52,9 @@ def sample_image_content(filename=SAMPLE_IMAGE_FILENAME): return f.read() +SAMPLE_FORWARDED_EMAIL = b'Received: by luna.mailgun.net with SMTP mgrt 8734663311733; Fri, 03 May 2013\n 18:26:27 +0000\nContent-Type: multipart/alternative; boundary="eb663d73ae0a4d6c9153cc0aec8b7520"\nMime-Version: 1.0\nSubject: Test email\nFrom: Someone \nTo: someoneelse@example.com\nReply-To: reply.to@example.com\nMessage-Id: <20130503182626.18666.16540@example.com>\nList-Unsubscribe: \nX-Mailgun-Sid: WyIwNzI5MCIsICJhbGljZUBleGFtcGxlLmNvbSIsICI2Il0=\nX-Mailgun-Variables: {"my_var_1": "Mailgun Variable #1", "my-var-2": "awesome"}\nDate: Fri, 03 May 2013 18:26:27 +0000\nSender: someone@example.com\n\n--eb663d73ae0a4d6c9153cc0aec8b7520\nMime-Version: 1.0\nContent-Type: text/plain; charset="ascii"\nContent-Transfer-Encoding: 7bit\n\nHi Bob, This is a message. Thanks!\n\n--eb663d73ae0a4d6c9153cc0aec8b7520\nMime-Version: 1.0\nContent-Type: text/html; charset="ascii"\nContent-Transfer-Encoding: 7bit\n\n\n Hi Bob, This is a message. Thanks!\n
\n\n--eb663d73ae0a4d6c9153cc0aec8b7520--\n' + + # noinspection PyUnresolvedReferences class AnymailTestMixin: """Helpful additional methods for Anymail tests"""