Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

[1.1.X] Fixed #6918, #12791: If an email message has an encoding, act…

…ually use that encoding to encode body and headers. Thanks for patch with tests oyvind.

Backport of r12683 and r12688 from trunk.



git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.1.X@12689 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit daec7345845ca3349237cee9da0f00ba99f7ce76 1 parent b3c2ae9
@kmtracey kmtracey authored
Showing with 63 additions and 11 deletions.
  1. +23 −10 django/core/mail.py
  2. +40 −1 tests/regressiontests/mail/tests.py
View
33 django/core/mail.py
@@ -69,8 +69,9 @@ def make_msgid(idstring=None):
class BadHeaderError(ValueError):
pass
-def forbid_multi_line_headers(name, val):
+def forbid_multi_line_headers(name, val, encoding):
"""Forbids multi-line headers, to prevent header injection."""
+ encoding = encoding or settings.DEFAULT_CHARSET
val = force_unicode(val)
if '\n' in val or '\r' in val:
raise BadHeaderError("Header values can't contain newlines (got %r for header %r)" % (val, name))
@@ -80,24 +81,34 @@ def forbid_multi_line_headers(name, val):
if name.lower() in ('to', 'from', 'cc'):
result = []
for nm, addr in getaddresses((val,)):
- nm = str(Header(nm, settings.DEFAULT_CHARSET))
+ nm = str(Header(nm.encode(encoding), encoding))
result.append(formataddr((nm, str(addr))))
val = ', '.join(result)
else:
- val = Header(val, settings.DEFAULT_CHARSET)
+ val = Header(val.encode(encoding), encoding)
else:
if name.lower() == 'subject':
val = Header(val)
return name, val
class SafeMIMEText(MIMEText):
+
+ def __init__(self, text, subtype, charset):
+ self.encoding = charset
+ MIMEText.__init__(self, text, subtype, charset)
+
def __setitem__(self, name, val):
- name, val = forbid_multi_line_headers(name, val)
+ name, val = forbid_multi_line_headers(name, val, self.encoding)
MIMEText.__setitem__(self, name, val)
class SafeMIMEMultipart(MIMEMultipart):
+
+ def __init__(self, _subtype='mixed', boundary=None, _subparts=None, encoding=None, **_params):
+ self.encoding = encoding
+ MIMEMultipart.__init__(self, _subtype, boundary, _subparts, **_params)
+
def __setitem__(self, name, val):
- name, val = forbid_multi_line_headers(name, val)
+ name, val = forbid_multi_line_headers(name, val, self.encoding)
MIMEMultipart.__setitem__(self, name, val)
class SMTPConnection(object):
@@ -234,7 +245,7 @@ def get_connection(self, fail_silently=False):
def message(self):
encoding = self.encoding or settings.DEFAULT_CHARSET
- msg = SafeMIMEText(smart_str(self.body, settings.DEFAULT_CHARSET),
+ msg = SafeMIMEText(smart_str(self.body, encoding),
self.content_subtype, encoding)
msg = self._create_message(msg)
msg['Subject'] = self.subject
@@ -293,8 +304,9 @@ def _create_message(self, msg):
def _create_attachments(self, msg):
if self.attachments:
+ encoding = self.encoding or settings.DEFAULT_CHARSET
body_msg = msg
- msg = SafeMIMEMultipart(_subtype=self.mixed_subtype)
+ msg = SafeMIMEMultipart(_subtype=self.mixed_subtype, encoding=encoding)
if self.body:
msg.attach(body_msg)
for attachment in self.attachments:
@@ -310,8 +322,8 @@ def _create_mime_attachment(self, content, mimetype):
"""
basetype, subtype = mimetype.split('/', 1)
if basetype == 'text':
- attachment = SafeMIMEText(smart_str(content,
- settings.DEFAULT_CHARSET), subtype, settings.DEFAULT_CHARSET)
+ encoding = self.encoding or settings.DEFAULT_CHARSET
+ attachment = SafeMIMEText(smart_str(content, encoding), subtype, encoding)
else:
# Encode non-text attachments with base64.
attachment = MIMEBase(basetype, subtype)
@@ -365,9 +377,10 @@ def _create_message(self, msg):
return self._create_attachments(self._create_alternatives(msg))
def _create_alternatives(self, msg):
+ encoding = self.encoding or settings.DEFAULT_CHARSET
if self.alternatives:
body_msg = msg
- msg = SafeMIMEMultipart(_subtype=self.alternative_subtype)
+ msg = SafeMIMEMultipart(_subtype=self.alternative_subtype, encoding=encoding)
if self.body:
msg.attach(body_msg)
for alternative in self.alternatives:
View
41 tests/regressiontests/mail/tests.py
@@ -106,10 +106,49 @@
>>> email.message()['To']
'=?utf-8?q?S=C3=BCrname=2C_Firstname?= <to@example.com>, other@example.com'
+# Regression for #6918 - When a header contains unicode,
+# make sure headers can be set with a different encoding than utf-8
+>>> email = EmailMessage('Message from Firstname Sürname', 'Content', 'from@example.com', ['"Sürname, Firstname" <to@example.com>','other@example.com'])
+>>> email.encoding = 'iso-8859-1'
+>>> email.message()['To']
+'=?iso-8859-1?q?S=FCrname=2C_Firstname?= <to@example.com>, other@example.com'
+>>> email.message()['Subject'].encode()
+u'=?iso-8859-1?q?Message_from_Firstname_S=FCrname?='
+
+# Make sure headers can be set with a different encoding than utf-8 in SafeMIMEMultipart as well
+>>> headers = {"Date": "Fri, 09 Nov 2001 01:08:47 -0000", "Message-ID": "foo"}
+>>> subject, from_email, to = 'hello', 'from@example.com', '"Sürname, Firstname" <to@example.com>'
+>>> text_content = 'This is an important message.'
+>>> html_content = '<p>This is an <strong>important</strong> message.</p>'
+>>> msg = EmailMultiAlternatives('Message from Firstname Sürname', text_content, from_email, [to], headers=headers)
+>>> msg.attach_alternative(html_content, "text/html")
+>>> msg.encoding = 'iso-8859-1'
+>>> msg.message()['To']
+'=?iso-8859-1?q?S=FCrname=2C_Firstname?= <to@example.com>'
+>>> msg.message()['Subject'].encode()
+u'=?iso-8859-1?q?Message_from_Firstname_S=FCrname?='
+
+# Regression for #12791 - Encode body correctly with other encodings than utf-8
+>>> email = EmailMessage('Subject', 'Firstname Sürname is a great guy.', 'from@example.com', ['other@example.com'])
+>>> email.encoding = 'iso-8859-1'
+>>> message = email.message()
+>>> message.as_string()
+'Content-Type: text/plain; charset="iso-8859-1"\nMIME-Version: 1.0\nContent-Transfer-Encoding: quoted-printable\nSubject: Subject\nFrom: from@example.com\nTo: other@example.com\nDate: ...\nMessage-ID: <...>\n\nFirstname S=FCrname is a great guy.'
+
+# Make sure MIME attachments also works correctly with other encodings than utf-8
+>>> text_content = 'Firstname Sürname is a great guy.'
+>>> html_content = '<p>Firstname Sürname is a <strong>great</strong> guy.</p>'
+>>> msg = EmailMultiAlternatives('Subject', text_content, 'from@example.com', ['to@example.com'])
+>>> msg.encoding = 'iso-8859-1'
+>>> msg.attach_alternative(html_content, "text/html")
+>>> msg.message().get_payload(0).as_string()
+'Content-Type: text/plain; charset="iso-8859-1"\nMIME-Version: 1.0\nContent-Transfer-Encoding: quoted-printable\n\nFirstname S=FCrname is a great guy.'
+>>> msg.message().get_payload(1).as_string()
+'Content-Type: text/html; charset="iso-8859-1"\nMIME-Version: 1.0\nContent-Transfer-Encoding: quoted-printable\n\n<p>Firstname S=FCrname is a <strong>great</strong> guy.</p>'
+
# Handle attachments within an multipart/alternative mail correctly (#9367)
# (test is not as precise/clear as it could be w.r.t. email tree structure,
# but it's good enough.)
-
>>> headers = {"Date": "Fri, 09 Nov 2001 01:08:47 -0000", "Message-ID": "foo"}
>>> subject, from_email, to = 'hello', 'from@example.com', 'to@example.com'
>>> text_content = 'This is an important message.'
Please sign in to comment.
Something went wrong with that request. Please try again.