Commit 8d789449 authored by Florian Apolloner's avatar Florian Apolloner
Browse files

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

parent 6448dd83
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -120,7 +120,7 @@ class EmailBackend(BaseEmailBackend):
                      for addr in email_message.recipients()]
        message = email_message.message()
        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:
            if not self.fail_silently:
                raise
+7 −4
Original line number Diff line number Diff line
@@ -123,7 +123,7 @@ def sanitize_address(addr, encoding):


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

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


+2 −0
Original line number Diff line number Diff line
@@ -115,3 +115,5 @@ Bugfixes
  (:ticket:`23609`).

* Fixed generic relations in ``ModelAdmin.list_filter`` (:ticket:`23616`).

* Restored RFC compliance for the SMTP backend on Python 3 (:ticket:`23063`).
+35 −1
Original line number Diff line number Diff line
@@ -9,7 +9,7 @@ import smtpd
import sys
import tempfile
import threading
from smtplib import SMTPException
from smtplib import SMTPException, SMTP
from ssl import SSLError

from django.core import mail
@@ -1038,3 +1038,37 @@ class SMTPBackendTests(BaseEmailBackendTests, SimpleTestCase):
    def test_email_timeout_override_settings(self):
        backend = smtp.EmailBackend()
        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