Skip to content

Commit daec734

Browse files
committed
[1.1.X] Fixed #6918, #12791: If an email message has an encoding, actually 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
1 parent b3c2ae9 commit daec734

File tree

2 files changed

+63
-11
lines changed

2 files changed

+63
-11
lines changed

django/core/mail.py

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,9 @@ def make_msgid(idstring=None):
6969
class BadHeaderError(ValueError):
7070
pass
7171

72-
def forbid_multi_line_headers(name, val):
72+
def forbid_multi_line_headers(name, val, encoding):
7373
"""Forbids multi-line headers, to prevent header injection."""
74+
encoding = encoding or settings.DEFAULT_CHARSET
7475
val = force_unicode(val)
7576
if '\n' in val or '\r' in val:
7677
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):
8081
if name.lower() in ('to', 'from', 'cc'):
8182
result = []
8283
for nm, addr in getaddresses((val,)):
83-
nm = str(Header(nm, settings.DEFAULT_CHARSET))
84+
nm = str(Header(nm.encode(encoding), encoding))
8485
result.append(formataddr((nm, str(addr))))
8586
val = ', '.join(result)
8687
else:
87-
val = Header(val, settings.DEFAULT_CHARSET)
88+
val = Header(val.encode(encoding), encoding)
8889
else:
8990
if name.lower() == 'subject':
9091
val = Header(val)
9192
return name, val
9293

9394
class SafeMIMEText(MIMEText):
95+
96+
def __init__(self, text, subtype, charset):
97+
self.encoding = charset
98+
MIMEText.__init__(self, text, subtype, charset)
99+
94100
def __setitem__(self, name, val):
95-
name, val = forbid_multi_line_headers(name, val)
101+
name, val = forbid_multi_line_headers(name, val, self.encoding)
96102
MIMEText.__setitem__(self, name, val)
97103

98104
class SafeMIMEMultipart(MIMEMultipart):
105+
106+
def __init__(self, _subtype='mixed', boundary=None, _subparts=None, encoding=None, **_params):
107+
self.encoding = encoding
108+
MIMEMultipart.__init__(self, _subtype, boundary, _subparts, **_params)
109+
99110
def __setitem__(self, name, val):
100-
name, val = forbid_multi_line_headers(name, val)
111+
name, val = forbid_multi_line_headers(name, val, self.encoding)
101112
MIMEMultipart.__setitem__(self, name, val)
102113

103114
class SMTPConnection(object):
@@ -234,7 +245,7 @@ def get_connection(self, fail_silently=False):
234245

235246
def message(self):
236247
encoding = self.encoding or settings.DEFAULT_CHARSET
237-
msg = SafeMIMEText(smart_str(self.body, settings.DEFAULT_CHARSET),
248+
msg = SafeMIMEText(smart_str(self.body, encoding),
238249
self.content_subtype, encoding)
239250
msg = self._create_message(msg)
240251
msg['Subject'] = self.subject
@@ -293,8 +304,9 @@ def _create_message(self, msg):
293304

294305
def _create_attachments(self, msg):
295306
if self.attachments:
307+
encoding = self.encoding or settings.DEFAULT_CHARSET
296308
body_msg = msg
297-
msg = SafeMIMEMultipart(_subtype=self.mixed_subtype)
309+
msg = SafeMIMEMultipart(_subtype=self.mixed_subtype, encoding=encoding)
298310
if self.body:
299311
msg.attach(body_msg)
300312
for attachment in self.attachments:
@@ -310,8 +322,8 @@ def _create_mime_attachment(self, content, mimetype):
310322
"""
311323
basetype, subtype = mimetype.split('/', 1)
312324
if basetype == 'text':
313-
attachment = SafeMIMEText(smart_str(content,
314-
settings.DEFAULT_CHARSET), subtype, settings.DEFAULT_CHARSET)
325+
encoding = self.encoding or settings.DEFAULT_CHARSET
326+
attachment = SafeMIMEText(smart_str(content, encoding), subtype, encoding)
315327
else:
316328
# Encode non-text attachments with base64.
317329
attachment = MIMEBase(basetype, subtype)
@@ -365,9 +377,10 @@ def _create_message(self, msg):
365377
return self._create_attachments(self._create_alternatives(msg))
366378

367379
def _create_alternatives(self, msg):
380+
encoding = self.encoding or settings.DEFAULT_CHARSET
368381
if self.alternatives:
369382
body_msg = msg
370-
msg = SafeMIMEMultipart(_subtype=self.alternative_subtype)
383+
msg = SafeMIMEMultipart(_subtype=self.alternative_subtype, encoding=encoding)
371384
if self.body:
372385
msg.attach(body_msg)
373386
for alternative in self.alternatives:

tests/regressiontests/mail/tests.py

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,10 +106,49 @@
106106
>>> email.message()['To']
107107
'=?utf-8?q?S=C3=BCrname=2C_Firstname?= <[email protected]>, [email protected]'
108108
109+
# Regression for #6918 - When a header contains unicode,
110+
# make sure headers can be set with a different encoding than utf-8
111+
>>> email = EmailMessage('Message from Firstname Sürname', 'Content', '[email protected]', ['"Sürname, Firstname" <[email protected]>','[email protected]'])
112+
>>> email.encoding = 'iso-8859-1'
113+
>>> email.message()['To']
114+
'=?iso-8859-1?q?S=FCrname=2C_Firstname?= <[email protected]>, [email protected]'
115+
>>> email.message()['Subject'].encode()
116+
u'=?iso-8859-1?q?Message_from_Firstname_S=FCrname?='
117+
118+
# Make sure headers can be set with a different encoding than utf-8 in SafeMIMEMultipart as well
119+
>>> headers = {"Date": "Fri, 09 Nov 2001 01:08:47 -0000", "Message-ID": "foo"}
120+
>>> subject, from_email, to = 'hello', '[email protected]', '"Sürname, Firstname" <[email protected]>'
121+
>>> text_content = 'This is an important message.'
122+
>>> html_content = '<p>This is an <strong>important</strong> message.</p>'
123+
>>> msg = EmailMultiAlternatives('Message from Firstname Sürname', text_content, from_email, [to], headers=headers)
124+
>>> msg.attach_alternative(html_content, "text/html")
125+
>>> msg.encoding = 'iso-8859-1'
126+
>>> msg.message()['To']
127+
'=?iso-8859-1?q?S=FCrname=2C_Firstname?= <[email protected]>'
128+
>>> msg.message()['Subject'].encode()
129+
u'=?iso-8859-1?q?Message_from_Firstname_S=FCrname?='
130+
131+
# Regression for #12791 - Encode body correctly with other encodings than utf-8
132+
>>> email = EmailMessage('Subject', 'Firstname Sürname is a great guy.', '[email protected]', ['[email protected]'])
133+
>>> email.encoding = 'iso-8859-1'
134+
>>> message = email.message()
135+
>>> message.as_string()
136+
'Content-Type: text/plain; charset="iso-8859-1"\nMIME-Version: 1.0\nContent-Transfer-Encoding: quoted-printable\nSubject: Subject\nFrom: [email protected]\nTo: [email protected]\nDate: ...\nMessage-ID: <...>\n\nFirstname S=FCrname is a great guy.'
137+
138+
# Make sure MIME attachments also works correctly with other encodings than utf-8
139+
>>> text_content = 'Firstname Sürname is a great guy.'
140+
>>> html_content = '<p>Firstname Sürname is a <strong>great</strong> guy.</p>'
141+
>>> msg = EmailMultiAlternatives('Subject', text_content, '[email protected]', ['[email protected]'])
142+
>>> msg.encoding = 'iso-8859-1'
143+
>>> msg.attach_alternative(html_content, "text/html")
144+
>>> msg.message().get_payload(0).as_string()
145+
'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.'
146+
>>> msg.message().get_payload(1).as_string()
147+
'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>'
148+
109149
# Handle attachments within an multipart/alternative mail correctly (#9367)
110150
# (test is not as precise/clear as it could be w.r.t. email tree structure,
111151
# but it's good enough.)
112-
113152
>>> headers = {"Date": "Fri, 09 Nov 2001 01:08:47 -0000", "Message-ID": "foo"}
114153
>>> subject, from_email, to = 'hello', '[email protected]', '[email protected]'
115154
>>> text_content = 'This is an important message.'

0 commit comments

Comments
 (0)