summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClaude Paroz <claude@2xlibre.net>2016-04-17 21:03:15 +0200
committerClaude Paroz <claude@2xlibre.net>2016-04-19 09:35:24 +0200
commit836d475afefecd643d5e7f44027d7209df3ac690 (patch)
tree868ff9939012e6d29dc911ab75fff7bed2ade24c
parentf43da05cc5c76102c04fd2e76b4bada880265dbe (diff)
Fixed #22561 -- Prevented too long lines in email messages
Thanks NotSqrt for the excellent report and Tim Graham for the review.
-rw-r--r--django/core/mail/message.py9
-rw-r--r--tests/mail/tests.py16
2 files changed, 24 insertions, 1 deletions
diff --git a/django/core/mail/message.py b/django/core/mail/message.py
index 546b895352..8dbc6b935c 100644
--- a/django/core/mail/message.py
+++ b/django/core/mail/message.py
@@ -25,11 +25,15 @@ from django.utils.encoding import force_text
# some spam filters.
utf8_charset = Charset.Charset('utf-8')
utf8_charset.body_encoding = None # Python defaults to BASE64
+utf8_charset_qp = Charset.Charset('utf-8')
+utf8_charset_qp.body_encoding = Charset.QP
# Default MIME type to use on attachments (if it is not explicitly given
# and cannot be guessed).
DEFAULT_ATTACHMENT_MIME_TYPE = 'application/octet-stream'
+RFC5322_EMAIL_LINE_LENGTH_LIMIT = 998
+
class BadHeaderError(ValueError):
pass
@@ -169,7 +173,10 @@ class SafeMIMEText(MIMEMixin, MIMEText):
# We do it manually and trigger re-encoding of the payload.
MIMEText.__init__(self, _text, _subtype, None)
del self['Content-Transfer-Encoding']
- self.set_payload(_text, utf8_charset)
+ 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
diff --git a/tests/mail/tests.py b/tests/mail/tests.py
index 36e3e3399e..4e7075e695 100644
--- a/tests/mail/tests.py
+++ b/tests/mail/tests.py
@@ -639,6 +639,22 @@ class BaseEmailBackendTests(HeadersCheckMixin, object):
self.assertEqual(message["subject"], '=?utf-8?q?Ch=C3=A8re_maman?=')
self.assertEqual(force_text(message.get_payload(decode=True)), 'Je t\'aime très fort')
+ def test_send_long_lines(self):
+ """
+ Email line length is limited to 998 chars by the RFC:
+ https://tools.ietf.org/html/rfc5322#section-2.1.1
+ Message body containing longer lines are converted to Quoted-Printable
+ to avoid having to insert newlines, which could be hairy to do properly.
+ """
+ email = EmailMessage('Subject', "Comment ça va? " * 100, 'from@example.com', ['to@example.com'])
+ email.send()
+ message = self.get_the_message()
+ self.assertMessageHasHeaders(message, {
+ ('MIME-Version', '1.0'),
+ ('Content-Type', 'text/plain; charset="utf-8"'),
+ ('Content-Transfer-Encoding', 'quoted-printable'),
+ })
+
def test_send_many(self):
email1 = EmailMessage('Subject', 'Content1', 'from@example.com', ['to@example.com'])
email2 = EmailMessage('Subject', 'Content2', 'from@example.com', ['to@example.com'])