Skip to content

Commit 7d74480

Browse files
spookylukeymedmunds
authored andcommitted
Fixed crasher when sending rfc822 messages as attachments. (#59)
1 parent 7b010ab commit 7d74480

3 files changed

Lines changed: 33 additions & 2 deletions

File tree

anymail/utils.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,12 @@ def __init__(self, attachment, encoding):
171171
if isinstance(attachment, MIMEBase):
172172
self.name = attachment.get_filename()
173173
self.content = attachment.get_payload(decode=True)
174+
if self.content is None:
175+
if hasattr(attachment, 'as_bytes'):
176+
self.content = attachment.as_bytes()
177+
else:
178+
# Python 2.7 fallback
179+
self.content = attachment.as_string().encode(self.encoding)
174180
self.mimetype = attachment.get_content_type()
175181

176182
if get_content_disposition(attachment) == 'inline':

tests/test_mailgun_backend.py

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
# -*- coding: utf-8 -*-
22

33
from datetime import date, datetime
4+
try:
5+
from email import message_from_bytes
6+
except ImportError:
7+
from email import message_from_string
8+
9+
def message_from_bytes(s):
10+
return message_from_string(s.decode('utf-8'))
11+
412
from email.mime.base import MIMEBase
513
from email.mime.image import MIMEImage
614

@@ -14,7 +22,7 @@
1422
from anymail.message import attach_inline_image_file
1523

1624
from .mock_requests_backend import RequestsBackendMockAPITestCase, SessionSharingTestCasesMixin
17-
from .utils import sample_image_content, sample_image_path, SAMPLE_IMAGE_FILENAME, AnymailTestMixin
25+
from .utils import sample_image_content, sample_image_path, SAMPLE_FORWARDED_EMAIL, SAMPLE_IMAGE_FILENAME, AnymailTestMixin
1826

1927

2028
@override_settings(EMAIL_BACKEND='anymail.backends.mailgun.EmailBackend',
@@ -131,13 +139,27 @@ def test_attachments(self):
131139
mimeattachment.set_payload(pdf_content)
132140
self.message.attach(mimeattachment)
133141

142+
# And also with an message/rfc822 attachment
143+
forwarded_email = message_from_bytes(SAMPLE_FORWARDED_EMAIL)
144+
rfcmessage = MIMEBase("message", "rfc822")
145+
rfcmessage.attach(forwarded_email)
146+
self.message.attach(rfcmessage)
147+
134148
self.message.send()
135149
files = self.get_api_call_files()
136150
attachments = [value for (field, value) in files if field == 'attachment']
137-
self.assertEqual(len(attachments), 3)
151+
self.assertEqual(len(attachments), 4)
138152
self.assertEqual(attachments[0], ('test.txt', text_content, 'text/plain'))
139153
self.assertEqual(attachments[1], ('test.png', png_content, 'image/png')) # type inferred from filename
140154
self.assertEqual(attachments[2], (None, pdf_content, 'application/pdf')) # no filename
155+
# Email messages can get a bit changed with respect to
156+
# whitespace characters in headers, without breaking the message, so we
157+
# tolerate that:
158+
self.assertEqual(attachments[3][0], None)
159+
self.assertEqual(attachments[3][1].replace(b'\n', b'').replace(b' ', b''),
160+
(b'Content-Type: message/rfc822\nMIME-Version: 1.0\n\n' + SAMPLE_FORWARDED_EMAIL).replace(b'\n', b'').replace(b' ', b''))
161+
self.assertEqual(attachments[3][2], 'message/rfc822')
162+
141163
# Make sure the image attachment is not treated as embedded:
142164
inlines = [value for (field, value) in files if field == 'inline']
143165
self.assertEqual(len(inlines), 0)

tests/utils.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ def sample_image_content(filename=SAMPLE_IMAGE_FILENAME):
5252
return f.read()
5353

5454

55+
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 <someone@example.com>\nTo: someoneelse@example.com\nReply-To: reply.to@example.com\nMessage-Id: <20130503182626.18666.16540@example.com>\nList-Unsubscribe: <mailto:u+na6tmy3ege4tgnldmyytqojqmfsdembyme3tmy3cha4wcndbgaydqyrgoi6wszdpovrhi5dinfzw63tfmv4gs43uomstimdhnvqws3bomnxw2jtuhusteqjgmq6tm@example.com>\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<html>\n <body>Hi Bob, This is a message. Thanks!\n <br>\n</body></html>\n--eb663d73ae0a4d6c9153cc0aec8b7520--\n'
56+
57+
5558
# noinspection PyUnresolvedReferences
5659
class AnymailTestMixin:
5760
"""Helpful additional methods for Anymail tests"""

0 commit comments

Comments
 (0)