mirror of https://github.com/django/django.git
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.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@12683 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
80545c3685
commit
68f4d1256a
|
@ -54,7 +54,8 @@ def make_msgid(idstring=None):
|
||||||
return msgid
|
return msgid
|
||||||
|
|
||||||
|
|
||||||
def forbid_multi_line_headers(name, val):
|
def forbid_multi_line_headers(name, val, encoding):
|
||||||
|
encoding = encoding or settings.DEFAULT_CHARSET
|
||||||
"""Forbids multi-line headers, to prevent header injection."""
|
"""Forbids multi-line headers, to prevent header injection."""
|
||||||
val = force_unicode(val)
|
val = force_unicode(val)
|
||||||
if '\n' in val or '\r' in val:
|
if '\n' in val or '\r' in val:
|
||||||
|
@ -65,29 +66,36 @@ def forbid_multi_line_headers(name, val):
|
||||||
if name.lower() in ('to', 'from', 'cc'):
|
if name.lower() in ('to', 'from', 'cc'):
|
||||||
result = []
|
result = []
|
||||||
for nm, addr in getaddresses((val,)):
|
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))))
|
result.append(formataddr((nm, str(addr))))
|
||||||
val = ', '.join(result)
|
val = ', '.join(result)
|
||||||
else:
|
else:
|
||||||
val = Header(val, settings.DEFAULT_CHARSET)
|
val = Header(val.encode(encoding), encoding)
|
||||||
else:
|
else:
|
||||||
if name.lower() == 'subject':
|
if name.lower() == 'subject':
|
||||||
val = Header(val)
|
val = Header(val)
|
||||||
return name, val
|
return name, val
|
||||||
|
|
||||||
|
|
||||||
class SafeMIMEText(MIMEText):
|
class SafeMIMEText(MIMEText):
|
||||||
def __setitem__(self, name, val):
|
|
||||||
name, val = forbid_multi_line_headers(name, val)
|
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, self.encoding)
|
||||||
MIMEText.__setitem__(self, name, val)
|
MIMEText.__setitem__(self, name, val)
|
||||||
|
|
||||||
|
|
||||||
class SafeMIMEMultipart(MIMEMultipart):
|
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):
|
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)
|
MIMEMultipart.__setitem__(self, name, val)
|
||||||
|
|
||||||
|
|
||||||
class EmailMessage(object):
|
class EmailMessage(object):
|
||||||
"""
|
"""
|
||||||
A container for email information.
|
A container for email information.
|
||||||
|
@ -131,7 +139,7 @@ class EmailMessage(object):
|
||||||
|
|
||||||
def message(self):
|
def message(self):
|
||||||
encoding = self.encoding or settings.DEFAULT_CHARSET
|
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)
|
self.content_subtype, encoding)
|
||||||
msg = self._create_message(msg)
|
msg = self._create_message(msg)
|
||||||
msg['Subject'] = self.subject
|
msg['Subject'] = self.subject
|
||||||
|
@ -190,8 +198,9 @@ class EmailMessage(object):
|
||||||
|
|
||||||
def _create_attachments(self, msg):
|
def _create_attachments(self, msg):
|
||||||
if self.attachments:
|
if self.attachments:
|
||||||
|
encoding = self.encoding or settings.DEFAULT_CHARSET
|
||||||
body_msg = msg
|
body_msg = msg
|
||||||
msg = SafeMIMEMultipart(_subtype=self.mixed_subtype)
|
msg = SafeMIMEMultipart(_subtype=self.mixed_subtype, encoding=encoding)
|
||||||
if self.body:
|
if self.body:
|
||||||
msg.attach(body_msg)
|
msg.attach(body_msg)
|
||||||
for attachment in self.attachments:
|
for attachment in self.attachments:
|
||||||
|
@ -207,8 +216,8 @@ class EmailMessage(object):
|
||||||
"""
|
"""
|
||||||
basetype, subtype = mimetype.split('/', 1)
|
basetype, subtype = mimetype.split('/', 1)
|
||||||
if basetype == 'text':
|
if basetype == 'text':
|
||||||
attachment = SafeMIMEText(smart_str(content,
|
encoding = self.encoding or settings.DEFAULT_CHARSET
|
||||||
settings.DEFAULT_CHARSET), subtype, settings.DEFAULT_CHARSET)
|
attachment = SafeMIMEText(smart_str(content, encoding), subtype, encoding)
|
||||||
else:
|
else:
|
||||||
# Encode non-text attachments with base64.
|
# Encode non-text attachments with base64.
|
||||||
attachment = MIMEBase(basetype, subtype)
|
attachment = MIMEBase(basetype, subtype)
|
||||||
|
@ -263,9 +272,10 @@ class EmailMultiAlternatives(EmailMessage):
|
||||||
return self._create_attachments(self._create_alternatives(msg))
|
return self._create_attachments(self._create_alternatives(msg))
|
||||||
|
|
||||||
def _create_alternatives(self, msg):
|
def _create_alternatives(self, msg):
|
||||||
|
encoding = self.encoding or settings.DEFAULT_CHARSET
|
||||||
if self.alternatives:
|
if self.alternatives:
|
||||||
body_msg = msg
|
body_msg = msg
|
||||||
msg = SafeMIMEMultipart(_subtype=self.alternative_subtype)
|
msg = SafeMIMEMultipart(_subtype=self.alternative_subtype, encoding=encoding)
|
||||||
if self.body:
|
if self.body:
|
||||||
msg.attach(body_msg)
|
msg.attach(body_msg)
|
||||||
for alternative in self.alternatives:
|
for alternative in self.alternatives:
|
||||||
|
|
|
@ -112,10 +112,47 @@ BadHeaderError: Header values can't contain newlines (got u'Subject\nInjection T
|
||||||
>>> email.message()['To']
|
>>> email.message()['To']
|
||||||
'=?utf-8?q?S=C3=BCrname=2C_Firstname?= <to@example.com>, other@example.com'
|
'=?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().as_string()
|
||||||
|
'Content-Type: multipart/alternative; boundary="===============...=="\nMIME-Version: 1.0\nSubject: Subject\nFrom: from@example.com\nTo: to@example.com\nDate: ...\nMessage-ID: <...>\n\n--===============...==\nContent-Type: text/plain; charset="iso-8859-1"\nMIME-Version: 1.0\nContent-Transfer-Encoding: quoted-printable\n\nFirstname S=FCrname is a great guy.\n--===============...==\nContent-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>\n--===============...==--'
|
||||||
|
|
||||||
# Handle attachments within an multipart/alternative mail correctly (#9367)
|
# 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,
|
# (test is not as precise/clear as it could be w.r.t. email tree structure,
|
||||||
# but it's good enough.)
|
# but it's good enough.)
|
||||||
|
|
||||||
>>> headers = {"Date": "Fri, 09 Nov 2001 01:08:47 -0000", "Message-ID": "foo"}
|
>>> headers = {"Date": "Fri, 09 Nov 2001 01:08:47 -0000", "Message-ID": "foo"}
|
||||||
>>> subject, from_email, to = 'hello', 'from@example.com', 'to@example.com'
|
>>> subject, from_email, to = 'hello', 'from@example.com', 'to@example.com'
|
||||||
>>> text_content = 'This is an important message.'
|
>>> text_content = 'This is an important message.'
|
||||||
|
|
Loading…
Reference in New Issue