Fixed #23063 -- Convert \n and \r to \r\n when using the SMTP backend as per RFC.

This commit is contained in:
Florian Apolloner 2014-10-09 14:18:31 +02:00
parent 6448dd8335
commit 8d789449c7
4 changed files with 45 additions and 6 deletions

View File

@ -120,7 +120,7 @@ class EmailBackend(BaseEmailBackend):
for addr in email_message.recipients()] for addr in email_message.recipients()]
message = email_message.message() message = email_message.message()
try: try:
self.connection.sendmail(from_email, recipients, message.as_bytes()) self.connection.sendmail(from_email, recipients, message.as_bytes(linesep='\r\n'))
except smtplib.SMTPException: except smtplib.SMTPException:
if not self.fail_silently: if not self.fail_silently:
raise raise

View File

@ -123,7 +123,7 @@ def sanitize_address(addr, encoding):
class MIMEMixin(): class MIMEMixin():
def as_string(self, unixfrom=False): def as_string(self, unixfrom=False, linesep='\n'):
"""Return the entire formatted message as a string. """Return the entire formatted message as a string.
Optional `unixfrom' when True, means include the Unix From_ envelope Optional `unixfrom' when True, means include the Unix From_ envelope
header. header.
@ -133,13 +133,16 @@ class MIMEMixin():
""" """
fp = six.StringIO() fp = six.StringIO()
g = generator.Generator(fp, mangle_from_=False) g = generator.Generator(fp, mangle_from_=False)
if six.PY2:
g.flatten(self, unixfrom=unixfrom) g.flatten(self, unixfrom=unixfrom)
else:
g.flatten(self, unixfrom=unixfrom, linesep=linesep)
return fp.getvalue() return fp.getvalue()
if six.PY2: if six.PY2:
as_bytes = as_string as_bytes = as_string
else: else:
def as_bytes(self, unixfrom=False): def as_bytes(self, unixfrom=False, linesep='\n'):
"""Return the entire formatted message as bytes. """Return the entire formatted message as bytes.
Optional `unixfrom' when True, means include the Unix From_ envelope Optional `unixfrom' when True, means include the Unix From_ envelope
header. header.
@ -149,7 +152,7 @@ class MIMEMixin():
""" """
fp = six.BytesIO() fp = six.BytesIO()
g = generator.BytesGenerator(fp, mangle_from_=False) g = generator.BytesGenerator(fp, mangle_from_=False)
g.flatten(self, unixfrom=unixfrom) g.flatten(self, unixfrom=unixfrom, linesep=linesep)
return fp.getvalue() return fp.getvalue()

View File

@ -115,3 +115,5 @@ Bugfixes
(:ticket:`23609`). (:ticket:`23609`).
* Fixed generic relations in ``ModelAdmin.list_filter`` (:ticket:`23616`). * Fixed generic relations in ``ModelAdmin.list_filter`` (:ticket:`23616`).
* Restored RFC compliance for the SMTP backend on Python 3 (:ticket:`23063`).

View File

@ -9,7 +9,7 @@ import smtpd
import sys import sys
import tempfile import tempfile
import threading import threading
from smtplib import SMTPException from smtplib import SMTPException, SMTP
from ssl import SSLError from ssl import SSLError
from django.core import mail from django.core import mail
@ -1038,3 +1038,37 @@ class SMTPBackendTests(BaseEmailBackendTests, SimpleTestCase):
def test_email_timeout_override_settings(self): def test_email_timeout_override_settings(self):
backend = smtp.EmailBackend() backend = smtp.EmailBackend()
self.assertEqual(backend.timeout, 10) self.assertEqual(backend.timeout, 10)
def test_email_msg_uses_crlf(self):
"""#23063 -- Test that RFC-compliant messages are sent over SMTP."""
send = SMTP.send
try:
smtp_messages = []
def mock_send(self, s):
smtp_messages.append(s)
return send(self, s)
SMTP.send = mock_send
email = EmailMessage('Subject', 'Content', 'from@example.com', ['to@example.com'])
mail.get_connection().send_messages([email])
# Find the actual message
msg = None
for i, m in enumerate(smtp_messages):
if m[:4] == 'data':
msg = smtp_messages[i+1]
break
self.assertTrue(msg)
if PY3:
msg = msg.decode('utf-8')
# Ensure that the message only contains CRLF and not combinations of CRLF, LF, and CR.
msg = msg.replace('\r\n', '')
self.assertNotIn('\r', msg)
self.assertNotIn('\n', msg)
finally:
SMTP.send = send