From 458e2fbfcc0a06d7d55ff5a1dcd79c91c64e8138 Mon Sep 17 00:00:00 2001
From: Claude Paroz <claude@2xlibre.net>
Date: Tue, 11 Oct 2016 20:53:26 +0200
Subject: [PATCH] Fixed #27333 -- Prevented BASE64 encoding in
 message.as_string() on Python 3

Thanks Tim Graham for the review.
---
 django/core/mail/message.py | 29 +++++++++--------------------
 tests/mail/tests.py         |  4 ++++
 2 files changed, 13 insertions(+), 20 deletions(-)

diff --git a/django/core/mail/message.py b/django/core/mail/message.py
index 44178d2447..98f3cfd632 100644
--- a/django/core/mail/message.py
+++ b/django/core/mail/message.py
@@ -211,31 +211,20 @@ class SafeMIMEText(MIMEMixin, MIMEText):
 
     def __init__(self, _text, _subtype='plain', _charset=None):
         self.encoding = _charset
-        if _charset == 'utf-8':
-            # Unfortunately, Python doesn't yet pass a Charset instance as
-            # MIMEText init parameter to set_payload().
-            # http://bugs.python.org/issue27445
-            # We do it manually and trigger re-encoding of the payload.
-            if six.PY3 and isinstance(_text, bytes):
-                # Sniffing encoding would fail with bytes content in MIMEText.__init__.
-                _text = _text.decode('utf-8')
-            MIMEText.__init__(self, _text, _subtype, None)
-            del self['Content-Transfer-Encoding']
-            has_long_lines = any(len(l) > RFC5322_EMAIL_LINE_LENGTH_LIMIT for l in _text.splitlines())
-            # Quoted-Printable encoding has the side effect of shortening long
-            # lines, if any (#22561).
-            self.set_payload(_text, utf8_charset_qp if has_long_lines else utf8_charset)
-            self.replace_header('Content-Type', 'text/%s; charset="%s"' % (_subtype, _charset))
-        elif _charset is None:
-            # the default value of '_charset' is 'us-ascii' on Python 2
-            MIMEText.__init__(self, _text, _subtype)
-        else:
-            MIMEText.__init__(self, _text, _subtype, _charset)
+        MIMEText.__init__(self, _text, _subtype=_subtype, _charset=_charset)
 
     def __setitem__(self, name, val):
         name, val = forbid_multi_line_headers(name, val, self.encoding)
         MIMEText.__setitem__(self, name, val)
 
+    def set_payload(self, payload, charset=None):
+        if charset == 'utf-8':
+            has_long_lines = any(len(l) > RFC5322_EMAIL_LINE_LENGTH_LIMIT for l in payload.splitlines())
+            # Quoted-Printable encoding has the side effect of shortening long
+            # lines, if any (#22561).
+            charset = utf8_charset_qp if has_long_lines else utf8_charset
+        MIMEText.set_payload(self, payload, charset=charset)
+
 
 class SafeMIMEMultipart(MIMEMixin, MIMEMultipart):
 
diff --git a/tests/mail/tests.py b/tests/mail/tests.py
index bc0d8eabc9..662cd7c6d6 100644
--- a/tests/mail/tests.py
+++ b/tests/mail/tests.py
@@ -572,6 +572,8 @@ class MailTests(HeadersCheckMixin, SimpleTestCase):
         )
         s = msg.message().as_bytes()
         self.assertIn(b'Content-Transfer-Encoding: 8bit', s)
+        s = msg.message().as_string()
+        self.assertIn(str('Content-Transfer-Encoding: 8bit'), s)
 
         msg = EmailMessage(
             'Subject', 'Body with non latin characters: А Б В Г Д Е Ж Ѕ З И І К Л М Н О П.', 'bounce@example.com',
@@ -579,6 +581,8 @@ class MailTests(HeadersCheckMixin, SimpleTestCase):
         )
         s = msg.message().as_bytes()
         self.assertIn(b'Content-Transfer-Encoding: 8bit', s)
+        s = msg.message().as_string()
+        self.assertIn(str('Content-Transfer-Encoding: 8bit'), s)
 
     def test_dont_base64_encode_message_rfc822(self):
         # Ticket #18967