Mailjet: fix TypeError in sanitize_address

Fix TypeError when sending to or from addresses with
display names containing commas. Rewrite Anymail's
workaround for Mailjet's problem with commas in display
names, to avoid calling Django's internal sanitize_address
in an unsupported way.

The TypeError results from Django changes that will be
introduced in Django 2.2.15, 3.0.9, and 3.1.
This commit is contained in:
medmunds
2020-07-23 13:06:25 -07:00
committed by Mike Edmunds
parent 448d5ec8e5
commit c4ed6660b3
3 changed files with 21 additions and 5 deletions

View File

@@ -39,6 +39,10 @@ Fixes
* **Amazon SES:** Fix bcc, which wasn't working at all on non-template sends. * **Amazon SES:** Fix bcc, which wasn't working at all on non-template sends.
(Thanks to `@mwheels`_ for reporting the issue.) (Thanks to `@mwheels`_ for reporting the issue.)
* **Mailjet:** Fix TypeError when sending to or from addresses with display names
containing commas (introduced in Django 2.2.15, 3.0.9, and 3.1).
Features Features
~~~~~~~~ ~~~~~~~~

View File

@@ -1,3 +1,5 @@
from email.header import Header
from six.moves.urllib.parse import quote from six.moves.urllib.parse import quote
from ..exceptions import AnymailRequestsAPIError from ..exceptions import AnymailRequestsAPIError
@@ -158,11 +160,15 @@ class MailjetPayload(RequestsPayload):
def _format_email_for_mailjet(self, email): def _format_email_for_mailjet(self, email):
"""Return EmailAddress email converted to a string that Mailjet can parse properly""" """Return EmailAddress email converted to a string that Mailjet can parse properly"""
# Workaround Mailjet 3.0 bug parsing display-name with commas # Workaround Mailjet 3.0 bug parsing RFC-2822 quoted display-name with commas
# (see test_comma_in_display_name in test_mailjet_backend for details) # (see test_comma_in_display_name in test_mailjet_backend for details)
if "," in email.display_name: if "," in email.display_name:
return EmailAddress(email.display_name.encode('utf-8'), email.addr_spec).formataddr('utf-8') # Force MIME "encoded-word" encoding on name, to hide comma from Mailjet.
else: # We just want the RFC-2047 quoting, not the header wrapping (which will
# be Mailjet's responsibility), so set a huge maxlinelen.
encoded_name = Header(email.display_name.encode('utf-8'),
charset='utf-8', maxlinelen=1000000).encode()
email = EmailAddress(encoded_name, email.addr_spec)
return email.address return email.address
# #

View File

@@ -106,13 +106,19 @@ class MailjetBackendStandardEmailTests(MailjetBackendMockAPITestCase):
# (This shouldn't be necessary in Mailjet 3.1, where Name becomes a separate json field for Cc/Bcc.) # (This shouldn't be necessary in Mailjet 3.1, where Name becomes a separate json field for Cc/Bcc.)
msg = mail.EmailMessage( msg = mail.EmailMessage(
'Subject', 'Message', '"Example, Inc." <from@example.com>', 'Subject', 'Message', '"Example, Inc." <from@example.com>',
['"Recipient, Ltd." <to@example.com>']) ['"Recipient, Ltd." <to@example.com>'],
cc=['"This is a very long display name, intended to test our workaround does not insert carriage returns'
' or newlines into the encoded value, which would cause other problems" <long@example.com']
)
msg.send() msg.send()
data = self.get_api_call_json() data = self.get_api_call_json()
self.assertEqual(data['FromName'], 'Example, Inc.') self.assertEqual(data['FromName'], 'Example, Inc.')
self.assertEqual(data['FromEmail'], 'from@example.com') self.assertEqual(data['FromEmail'], 'from@example.com')
# self.assertEqual(data['To'], '"Recipient, Ltd." <to@example.com>') # this doesn't work # self.assertEqual(data['To'], '"Recipient, Ltd." <to@example.com>') # this doesn't work
self.assertEqual(data['To'], '=?utf-8?q?Recipient=2C_Ltd=2E?= <to@example.com>') # workaround self.assertEqual(data['To'], '=?utf-8?q?Recipient=2C_Ltd=2E?= <to@example.com>') # workaround
self.assertEqual(data['Cc'], '=?utf-8?q?This_is_a_very_long_display_name=2C_intended_to_test_our_workaround'
'_does_not_insert_carriage_returns_or_newlines_into_the_encoded_value=2C_which'
'_would_cause_other_problems?= <long@example.com>')
def test_email_message(self): def test_email_message(self):
email = mail.EmailMessage( email = mail.EmailMessage(