Commit bc13a08f authored by Florian Apolloner's avatar Florian Apolloner
Browse files

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

Backport of 8d789449 from master.
parent d2a2af67
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -112,7 +112,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
@@ -114,3 +114,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
@@ -984,3 +984,37 @@ class SMTPBackendTests(BaseEmailBackendTests, SimpleTestCase):
        self.assertEqual(myemailbackend.timeout, 42)
        self.assertEqual(myemailbackend.connection.timeout, 42)
        myemailbackend.close()

    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