Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #6918, #12791: If an email message has an encoding, actually us…

…e 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
  • Loading branch information...
commit 68f4d1256a535e675aa202a2ee917c02371ae70a 1 parent 80545c3
Karen Tracey authored March 05, 2010
38  django/core/mail/message.py
@@ -54,7 +54,8 @@ def make_msgid(idstring=None):
54 54
     return msgid
55 55
 
56 56
 
57  
-def forbid_multi_line_headers(name, val):
  57
+def forbid_multi_line_headers(name, val, encoding):
  58
+    encoding = encoding or settings.DEFAULT_CHARSET
58 59
     """Forbids multi-line headers, to prevent header injection."""
59 60
     val = force_unicode(val)
60 61
     if '\n' in val or '\r' in val:
@@ -65,29 +66,36 @@ def forbid_multi_line_headers(name, val):
65 66
         if name.lower() in ('to', 'from', 'cc'):
66 67
             result = []
67 68
             for nm, addr in getaddresses((val,)):
68  
-                nm = str(Header(nm, settings.DEFAULT_CHARSET))
  69
+                nm = str(Header(nm.encode(encoding), encoding))
69 70
                 result.append(formataddr((nm, str(addr))))
70 71
             val = ', '.join(result)
71 72
         else:
72  
-            val = Header(val, settings.DEFAULT_CHARSET)
  73
+            val = Header(val.encode(encoding), encoding)
73 74
     else:
74 75
         if name.lower() == 'subject':
75 76
             val = Header(val)
76 77
     return name, val
77 78
 
78  
-
79 79
 class SafeMIMEText(MIMEText):
80  
-    def __setitem__(self, name, val):
81  
-        name, val = forbid_multi_line_headers(name, val)
  80
+    
  81
+    def __init__(self, text, subtype, charset):
  82
+        self.encoding = charset
  83
+        MIMEText.__init__(self, text, subtype, charset)
  84
+    
  85
+    def __setitem__(self, name, val):    
  86
+        name, val = forbid_multi_line_headers(name, val, self.encoding)
82 87
         MIMEText.__setitem__(self, name, val)
83 88
 
84  
-
85 89
 class SafeMIMEMultipart(MIMEMultipart):
  90
+    
  91
+    def __init__(self, _subtype='mixed', boundary=None, _subparts=None, encoding=None, **_params):
  92
+        self.encoding = encoding
  93
+        MIMEMultipart.__init__(self, _subtype, boundary, _subparts, **_params)
  94
+        
86 95
     def __setitem__(self, name, val):
87  
-        name, val = forbid_multi_line_headers(name, val)
  96
+        name, val = forbid_multi_line_headers(name, val, self.encoding)
88 97
         MIMEMultipart.__setitem__(self, name, val)
89 98
 
90  
-
91 99
 class EmailMessage(object):
92 100
     """
93 101
     A container for email information.
@@ -131,7 +139,7 @@ def get_connection(self, fail_silently=False):
131 139
 
132 140
     def message(self):
133 141
         encoding = self.encoding or settings.DEFAULT_CHARSET
134  
-        msg = SafeMIMEText(smart_str(self.body, settings.DEFAULT_CHARSET),
  142
+        msg = SafeMIMEText(smart_str(self.body, encoding),
135 143
                            self.content_subtype, encoding)
136 144
         msg = self._create_message(msg)
137 145
         msg['Subject'] = self.subject
@@ -190,8 +198,9 @@ def _create_message(self, msg):
190 198
 
191 199
     def _create_attachments(self, msg):
192 200
         if self.attachments:
  201
+            encoding = self.encoding or settings.DEFAULT_CHARSET
193 202
             body_msg = msg
194  
-            msg = SafeMIMEMultipart(_subtype=self.mixed_subtype)
  203
+            msg = SafeMIMEMultipart(_subtype=self.mixed_subtype, encoding=encoding)
195 204
             if self.body:
196 205
                 msg.attach(body_msg)
197 206
             for attachment in self.attachments:
@@ -207,8 +216,8 @@ def _create_mime_attachment(self, content, mimetype):
207 216
         """
208 217
         basetype, subtype = mimetype.split('/', 1)
209 218
         if basetype == 'text':
210  
-            attachment = SafeMIMEText(smart_str(content,
211  
-                settings.DEFAULT_CHARSET), subtype, settings.DEFAULT_CHARSET)
  219
+            encoding = self.encoding or settings.DEFAULT_CHARSET
  220
+            attachment = SafeMIMEText(smart_str(content, encoding), subtype, encoding)
212 221
         else:
213 222
             # Encode non-text attachments with base64.
214 223
             attachment = MIMEBase(basetype, subtype)
@@ -263,9 +272,10 @@ def _create_message(self, msg):
263 272
         return self._create_attachments(self._create_alternatives(msg))
264 273
 
265 274
     def _create_alternatives(self, msg):
  275
+        encoding = self.encoding or settings.DEFAULT_CHARSET
266 276
         if self.alternatives:
267 277
             body_msg = msg
268  
-            msg = SafeMIMEMultipart(_subtype=self.alternative_subtype)
  278
+            msg = SafeMIMEMultipart(_subtype=self.alternative_subtype, encoding=encoding)
269 279
             if self.body:
270 280
                 msg.attach(body_msg)
271 281
             for alternative in self.alternatives:
39  tests/regressiontests/mail/tests.py
@@ -112,10 +112,47 @@
112 112
 >>> email.message()['To']
113 113
 '=?utf-8?q?S=C3=BCrname=2C_Firstname?= <to@example.com>, other@example.com'
114 114
 
  115
+# Regression for #6918 - When a header contains unicode,
  116
+# make sure headers can be set with a different encoding than utf-8
  117
+>>> email = EmailMessage('Message from Firstname Sürname', 'Content', 'from@example.com', ['"Sürname, Firstname" <to@example.com>','other@example.com'])
  118
+>>> email.encoding = 'iso-8859-1'
  119
+>>> email.message()['To']
  120
+'=?iso-8859-1?q?S=FCrname=2C_Firstname?= <to@example.com>, other@example.com'
  121
+>>> email.message()['Subject'].encode()
  122
+u'=?iso-8859-1?q?Message_from_Firstname_S=FCrname?='
  123
+
  124
+# Make sure headers can be set with a different encoding than utf-8 in SafeMIMEMultipart as well
  125
+>>> headers = {"Date": "Fri, 09 Nov 2001 01:08:47 -0000", "Message-ID": "foo"}
  126
+>>> subject, from_email, to = 'hello', 'from@example.com', '"Sürname, Firstname" <to@example.com>'
  127
+>>> text_content = 'This is an important message.'
  128
+>>> html_content = '<p>This is an <strong>important</strong> message.</p>'
  129
+>>> msg = EmailMultiAlternatives('Message from Firstname Sürname', text_content, from_email, [to], headers=headers)
  130
+>>> msg.attach_alternative(html_content, "text/html")
  131
+>>> msg.encoding = 'iso-8859-1'
  132
+>>> msg.message()['To']
  133
+'=?iso-8859-1?q?S=FCrname=2C_Firstname?= <to@example.com>'
  134
+>>> msg.message()['Subject'].encode()
  135
+u'=?iso-8859-1?q?Message_from_Firstname_S=FCrname?='
  136
+
  137
+# Regression for #12791  - Encode body correctly with other encodings than utf-8
  138
+>>> email = EmailMessage('Subject', 'Firstname Sürname is a great guy.', 'from@example.com', ['other@example.com'])
  139
+>>> email.encoding = 'iso-8859-1'
  140
+>>> message = email.message()
  141
+>>> message.as_string()
  142
+'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.'
  143
+
  144
+# Make sure MIME attachments also works correctly with other encodings than utf-8
  145
+>>> text_content = 'Firstname Sürname is a great guy.'
  146
+>>> html_content = '<p>Firstname Sürname is a <strong>great</strong> guy.</p>'
  147
+>>> msg = EmailMultiAlternatives('Subject', text_content, 'from@example.com', ['to@example.com'])
  148
+>>> msg.encoding = 'iso-8859-1'
  149
+>>> msg.attach_alternative(html_content, "text/html")
  150
+>>> msg.message().as_string()
  151
+'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--===============...==--'
  152
+
115 153
 # Handle attachments within an multipart/alternative mail correctly (#9367)
116 154
 # (test is not as precise/clear as it could be w.r.t. email tree structure,
117 155
 #  but it's good enough.)
118  
-
119 156
 >>> headers = {"Date": "Fri, 09 Nov 2001 01:08:47 -0000", "Message-ID": "foo"}
120 157
 >>> subject, from_email, to = 'hello', 'from@example.com', 'to@example.com'
121 158
 >>> text_content = 'This is an important message.'

0 notes on commit 68f4d12

Please sign in to comment.
Something went wrong with that request. Please try again.