Skip to content

Commit e98e5ec

Browse files
authored
Unicode encode errors (#49)
Fix unicode and encode errors * Sending queued emails * Sending non queued emails * In admin detail view Thanks to @xusy2k to find an trying to fix the bugs in PR #48.
1 parent 1921f3e commit e98e5ec

File tree

5 files changed

+81
-27
lines changed

5 files changed

+81
-27
lines changed

django_yubin/engine.py

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
1010
"""
1111
from django.core.mail import get_connection
12-
from django.utils.encoding import smart_str
12+
from django.utils.encoding import force_bytes, smart_str
1313
from django.utils.timezone import now
1414
from django_yubin import constants, models, settings
1515
from lockfile import FileLock, AlreadyLocked, LockTimeout
@@ -201,18 +201,23 @@ def send_queued_message(queued_message, smtp_connection=None, blacklist=None,
201201
(message.to_address.encode("utf-8"),
202202
message.subject.encode("utf-8")))
203203
opened_connection = smtp_connection.open()
204-
smtp_connection.connection.sendmail(
205-
message.from_address,
206-
[message.to_address],
207-
smart_str(message.encoded_message).encode('utf-8'))
204+
try:
205+
smtp_connection.connection.sendmail(
206+
message.from_address,
207+
[message.to_address],
208+
smart_str(message.encoded_message).encode('utf-8'))
209+
except UnicodeDecodeError:
210+
smtp_connection.connection.sendmail(
211+
message.from_address,
212+
[message.to_address],
213+
force_bytes(message.encoded_message))
208214
queued_message.message.date_sent = now()
209215
queued_message.message.save()
210216
queued_message.delete()
211217
result = constants.RESULT_SENT
212218
except (SocketError, smtplib.SMTPSenderRefused,
213-
smtplib.SMTPRecipientsRefused,
214-
smtplib.SMTPAuthenticationError,
215-
UnicodeEncodeError) as err:
219+
smtplib.SMTPRecipientsRefused, smtplib.SMTPAuthenticationError,
220+
UnicodeDecodeError, UnicodeEncodeError) as err:
216221
queued_message.defer()
217222
logger.warning("Message to %s deferred due to failure: %s" %
218223
(message.to_address.encode("utf-8"), err))
@@ -257,18 +262,30 @@ def send_message(email_message, smtp_connection=None):
257262

258263
try:
259264
opened_connection = smtp_connection.open()
260-
smtp_connection.connection.sendmail(
261-
email_message.from_email,
262-
email_message.recipients(),
263-
email_message.message().as_string())
265+
try:
266+
smtp_connection.connection.sendmail(
267+
email_message.from_email,
268+
email_message.recipients(),
269+
smart_str(email_message.message().as_string()).encode('utf-8'))
270+
except UnicodeDecodeError:
271+
message = email_message.message()
272+
charset = message.get_charset()
273+
if charset:
274+
charset = charset.get_output_charset()
275+
else:
276+
charset = 'utf-8'
277+
smtp_connection.connection.sendmail(
278+
email_message.from_email,
279+
email_message.recipients(),
280+
force_bytes(message.as_string(), charset))
264281
result = constants.RESULT_SENT
265282
except (SocketError, smtplib.SMTPSenderRefused,
266-
smtplib.SMTPRecipientsRefused,
267-
smtplib.SMTPAuthenticationError,
268-
UnicodeEncodeError) as err:
283+
smtplib.SMTPRecipientsRefused, smtplib.SMTPAuthenticationError,
284+
UnicodeDecodeError, UnicodeEncodeError) as err:
269285
result = constants.RESULT_FAILED
270286
logger.warning("Message from %s failed due to: %s" %
271287
(email_message.from_email, err))
288+
272289
if opened_connection:
273290
smtp_connection.close()
274291
return result

django_yubin/models.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# ----------------------------------------------------------------------------
44

55
from django.db import models
6-
from django.utils.encoding import python_2_unicode_compatible
6+
from django.utils.encoding import force_bytes, python_2_unicode_compatible
77
from django.utils.timezone import now
88
from django_yubin import constants, managers
99

@@ -52,6 +52,8 @@ def __str__(self):
5252
def get_pyz_message(self):
5353
try:
5454
msg = message_from_string(self.encoded_message)
55+
except UnicodeEncodeError:
56+
msg = message_from_string(force_bytes(self.encoded_message))
5557
except (TypeError, AttributeError):
5658
msg = message_from_bytes(self.encoded_message)
5759
return msg

django_yubin/smtp_queue.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,6 @@ def send_messages(self, email_messages):
3333

3434
num_sent = 0
3535
for email_message in email_messages:
36-
queue_email_message(email_message)
37-
num_sent += 1
36+
if queue_email_message(email_message):
37+
num_sent += 1
3838
return num_sent

humans.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,6 @@ Dave Peake (@davepeake)
1111
Grzegorz Bialy (@grzegorzbialy)
1212
Carlos Salom (@csalom)
1313
Juan Luis Garcia (@JuanLuisGarcia)
14+
Xus Zoyo (@xusy2k)
1415

1516
https://github.com/APSL/django-yubin/graphs/contributors

tests/tests/test_backend.py

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
from django.conf import settings as django_settings
88
from django.core import mail
9+
from django.core.management import call_command
10+
from django.utils.encoding import force_text
911

1012
from django_yubin import models, constants, settings, mail_admins, mail_managers
1113

@@ -95,7 +97,7 @@ def testQueuedMessagePriorities(self):
9597
constants.PRIORITY_NORMAL)
9698

9799
@skipIf(not RFC_6532_SUPPORT, 'RFC 6532 not supported')
98-
def testUnicodeQueuedMessage(self):
100+
def testUnicodeErrorQueuedMessage(self):
99101
"""
100102
Checks that we capture unicode errors on mail
101103
"""
@@ -111,22 +113,54 @@ def testUnicodeQueuedMessage(self):
111113
self.assertEqual(num_errors, 1)
112114

113115
@skipIf(not RFC_6532_SUPPORT, 'RFC 6532 not supported')
114-
def testUnicodePriorityMessage(self):
116+
def testUnicodeQueuedMessage(self):
115117
"""
116-
Checks that we capture unicode errors on mail on priority.
117-
It's hard to check as by definiton priority email does not Logs its
118-
contents.
118+
Checks that we capture unicode errors on mail
119119
"""
120120
from django.core.management import call_command
121-
msg = mail.EmailMessage(subject=u'á subject', body='body',
122-
from_email=u'juan.ló[email protected]', to=[u'únñ[email protected]'],
123-
headers={'X-Mail-Queue-Priority': 'now'})
121+
msg = mail.EmailMessage(subject=u'Chère maman',
122+
body='Je t\'aime très fort',
123+
from_email='[email protected]',
124+
124125
msg.send()
126+
125127
queued_messages = models.QueuedMessage.objects.all()
126128
self.assertEqual(queued_messages.count(), 1)
129+
127130
call_command('send_mail', verbosity='0')
131+
132+
queued_messages = models.QueuedMessage.objects.all()
133+
self.assertEqual(queued_messages.count(), 0)
134+
128135
num_errors = models.Log.objects.filter(result=constants.RESULT_FAILED).count()
129-
self.assertEqual(num_errors, 1)
136+
self.assertEqual(num_errors, 0)
137+
138+
message = msg.message()
139+
self.assertEqual(message['subject'], '=?utf-8?q?Ch=C3=A8re_maman?=')
140+
self.assertEqual(force_text(message.get_payload()), 'Je t\'aime très fort')
141+
142+
@skipIf(not RFC_6532_SUPPORT, 'RFC 6532 not supported')
143+
def testUnicodePriorityNowNotQueuedMessage(self):
144+
"""
145+
Checks that we capture unicode errors on mail on priority.
146+
It's hard to check as by definiton priority email does not Logs its
147+
contents.
148+
"""
149+
msg = mail.EmailMessage(subject=u'Chère maman',
150+
body='Je t\'aime très fort',
151+
from_email='[email protected]',
152+
153+
headers={'X-Mail-Queue-Priority': 'now-not-queued'})
154+
num_sent = mail.get_connection().send_messages([msg])
155+
self.assertEqual(num_sent, 1)
156+
queued_messages = models.QueuedMessage.objects.all()
157+
self.assertEqual(queued_messages.count(), 0)
158+
call_command('send_mail', verbosity='0')
159+
num_errors = models.Log.objects.filter(result=constants.RESULT_FAILED).count()
160+
self.assertEqual(num_errors, 0)
161+
message = msg.message()
162+
self.assertEqual(message['subject'], '=?utf-8?q?Ch=C3=A8re_maman?=')
163+
self.assertEqual(force_text(message.get_payload()), 'Je t\'aime très fort')
130164

131165
def testSendMessageNowPriority(self):
132166
# NOW priority message

0 commit comments

Comments
 (0)