2 | | |
3 | | r""" |
4 | | # Tests for the django.core.mail. |
5 | | |
6 | | >>> import os |
7 | | >>> import shutil |
8 | | >>> import tempfile |
9 | | >>> from StringIO import StringIO |
10 | | >>> from django.conf import settings |
11 | | >>> from django.core import mail |
12 | | >>> from django.core.mail import EmailMessage, mail_admins, mail_managers, EmailMultiAlternatives |
13 | | >>> from django.core.mail import send_mail, send_mass_mail |
14 | | >>> from django.core.mail.backends.base import BaseEmailBackend |
15 | | >>> from django.core.mail.backends import console, dummy, locmem, filebased, smtp |
16 | | >>> from django.utils.translation import ugettext_lazy |
17 | | |
18 | | # Test normal ascii character case: |
19 | | |
20 | | >>> email = EmailMessage('Subject', 'Content', 'from@example.com', ['to@example.com']) |
21 | | >>> message = email.message() |
22 | | >>> message['Subject'].encode() |
23 | | 'Subject' |
24 | | >>> message.get_payload() |
25 | | 'Content' |
26 | | >>> message['From'] |
27 | | 'from@example.com' |
28 | | >>> message['To'] |
29 | | 'to@example.com' |
30 | | |
31 | | # Test multiple-recipient case |
32 | | |
33 | | >>> email = EmailMessage('Subject', 'Content', 'from@example.com', ['to@example.com','other@example.com']) |
34 | | >>> message = email.message() |
35 | | >>> message['Subject'].encode() |
36 | | 'Subject' |
37 | | >>> message.get_payload() |
38 | | 'Content' |
39 | | >>> message['From'] |
40 | | 'from@example.com' |
41 | | >>> message['To'] |
42 | | 'to@example.com, other@example.com' |
43 | | |
44 | | # Test for header injection |
45 | | |
46 | | >>> email = EmailMessage('Subject\nInjection Test', 'Content', 'from@example.com', ['to@example.com']) |
47 | | >>> message = email.message() |
48 | | Traceback (most recent call last): |
49 | | ... |
50 | | BadHeaderError: Header values can't contain newlines (got u'Subject\nInjection Test' for header 'Subject') |
51 | | |
52 | | >>> email = EmailMessage(ugettext_lazy('Subject\nInjection Test'), 'Content', 'from@example.com', ['to@example.com']) |
53 | | >>> message = email.message() |
54 | | Traceback (most recent call last): |
55 | | ... |
56 | | BadHeaderError: Header values can't contain newlines (got u'Subject\nInjection Test' for header 'Subject') |
57 | | |
58 | | # Test for space continuation character in long (ascii) subject headers (#7747) |
59 | | |
60 | | >>> email = EmailMessage('Long subject lines that get wrapped should use a space continuation character to get expected behaviour in Outlook and Thunderbird', 'Content', 'from@example.com', ['to@example.com']) |
61 | | >>> message = email.message() |
62 | | >>> message.as_string() |
63 | | 'Content-Type: text/plain; charset="utf-8"\nMIME-Version: 1.0\nContent-Transfer-Encoding: quoted-printable\nSubject: Long subject lines that get wrapped should use a space continuation\n character to get expected behaviour in Outlook and Thunderbird\nFrom: from@example.com\nTo: to@example.com\nDate: ...\nMessage-ID: <...>\n\nContent' |
64 | | |
65 | | # Specifying dates or message-ids in the extra headers overrides the defaul |
66 | | # values (#9233). |
67 | | |
68 | | >>> headers = {"date": "Fri, 09 Nov 2001 01:08:47 -0000", "Message-ID": "foo"} |
69 | | >>> email = EmailMessage('subject', 'content', 'from@example.com', ['to@example.com'], headers=headers) |
70 | | >>> email.message().as_string() |
71 | | 'Content-Type: text/plain; charset="utf-8"\nMIME-Version: 1.0\nContent-Transfer-Encoding: quoted-printable\nSubject: subject\nFrom: from@example.com\nTo: to@example.com\ndate: Fri, 09 Nov 2001 01:08:47 -0000\nMessage-ID: foo\n\ncontent' |
72 | | |
73 | | # Test that mail_admins/mail_managers doesn't connect to the mail server if there are no recipients (#9383) |
74 | | |
75 | | >>> old_admins = settings.ADMINS |
76 | | >>> old_managers = settings.MANAGERS |
77 | | >>> settings.ADMINS = [] |
78 | | >>> settings.MANAGERS = [] |
79 | | >>> mail.outbox = [] |
80 | | >>> mail_admins('hi','there') |
81 | | >>> len(mail.outbox) |
82 | | 0 |
83 | | >>> mail.outbox = [] |
84 | | >>> mail_managers('hi','there') |
85 | | >>> len(mail.outbox) |
86 | | 0 |
87 | | >>> settings.ADMINS = settings.MANAGERS = [('nobody','nobody@example.com')] |
88 | | >>> mail.outbox = [] |
89 | | >>> mail_admins('hi','there') |
90 | | >>> len(mail.outbox) |
91 | | 1 |
92 | | >>> mail.outbox = [] |
93 | | >>> mail_managers('hi','there') |
94 | | >>> len(mail.outbox) |
95 | | 1 |
96 | | |
97 | | # Make sure we can manually set the From header (#9214) |
98 | | |
99 | | >>> email = EmailMessage('Subject', 'Content', 'bounce@example.com', ['to@example.com'], headers={'From': 'from@example.com'}) |
100 | | >>> message = email.message() |
101 | | >>> message['From'] |
102 | | 'from@example.com' |
103 | | |
104 | | # Regression for #13259 - Make sure that headers are not changed |
105 | | # when calling EmailMessage.message() |
106 | | >>> email = EmailMessage('Subject', 'Content', 'bounce@example.com', ['to@example.com'], headers={'From': 'from@example.com'}) |
107 | | >>> message = email.message() |
108 | | >>> message['From'] |
109 | | 'from@example.com' |
110 | | >>> message = email.message() |
111 | | >>> message['From'] |
112 | | 'from@example.com' |
113 | | |
114 | | # Regression for #11144 - When a to/from/cc header contains unicode, |
115 | | # make sure the email addresses are parsed correctly (especially |
116 | | # with regards to commas) |
117 | | >>> email = EmailMessage('Subject', 'Content', 'from@example.com', ['"Firstname Sürname" <to@example.com>','other@example.com']) |
118 | | >>> email.message()['To'] |
119 | | '=?utf-8?q?Firstname_S=C3=BCrname?= <to@example.com>, other@example.com' |
120 | | |
121 | | >>> email = EmailMessage('Subject', 'Content', 'from@example.com', ['"Sürname, Firstname" <to@example.com>','other@example.com']) |
122 | | >>> email.message()['To'] |
123 | | '=?utf-8?q?S=C3=BCrname=2C_Firstname?= <to@example.com>, other@example.com' |
124 | | |
125 | | # Regression for #6918 - When a header contains unicode, |
126 | | # make sure headers can be set with a different encoding than utf-8 |
127 | | >>> email = EmailMessage('Message from Firstname Sürname', 'Content', 'from@example.com', ['"Sürname, Firstname" <to@example.com>','other@example.com']) |
128 | | >>> email.encoding = 'iso-8859-1' |
129 | | >>> email.message()['To'] |
130 | | '=?iso-8859-1?q?S=FCrname=2C_Firstname?= <to@example.com>, other@example.com' |
131 | | >>> email.message()['Subject'].encode() |
132 | | u'=?iso-8859-1?q?Message_from_Firstname_S=FCrname?=' |
133 | | |
134 | | # Make sure headers can be set with a different encoding than utf-8 in SafeMIMEMultipart as well |
135 | | >>> headers = {"Date": "Fri, 09 Nov 2001 01:08:47 -0000", "Message-ID": "foo"} |
136 | | >>> subject, from_email, to = 'hello', 'from@example.com', '"Sürname, Firstname" <to@example.com>' |
137 | | >>> text_content = 'This is an important message.' |
138 | | >>> html_content = '<p>This is an <strong>important</strong> message.</p>' |
139 | | >>> msg = EmailMultiAlternatives('Message from Firstname Sürname', text_content, from_email, [to], headers=headers) |
140 | | >>> msg.attach_alternative(html_content, "text/html") |
141 | | >>> msg.encoding = 'iso-8859-1' |
142 | | >>> msg.message()['To'] |
143 | | '=?iso-8859-1?q?S=FCrname=2C_Firstname?= <to@example.com>' |
144 | | >>> msg.message()['Subject'].encode() |
145 | | u'=?iso-8859-1?q?Message_from_Firstname_S=FCrname?=' |
146 | | |
147 | | # Regression for #12791 - Encode body correctly with other encodings than utf-8 |
148 | | >>> email = EmailMessage('Subject', 'Firstname Sürname is a great guy.', 'from@example.com', ['other@example.com']) |
149 | | >>> email.encoding = 'iso-8859-1' |
150 | | >>> message = email.message() |
151 | | >>> message.as_string() |
152 | | '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.' |
153 | | |
154 | | # Make sure MIME attachments also works correctly with other encodings than utf-8 |
155 | | >>> text_content = 'Firstname Sürname is a great guy.' |
156 | | >>> html_content = '<p>Firstname Sürname is a <strong>great</strong> guy.</p>' |
157 | | >>> msg = EmailMultiAlternatives('Subject', text_content, 'from@example.com', ['to@example.com']) |
158 | | >>> msg.encoding = 'iso-8859-1' |
159 | | >>> msg.attach_alternative(html_content, "text/html") |
160 | | >>> msg.message().get_payload(0).as_string() |
161 | | '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.' |
162 | | >>> msg.message().get_payload(1).as_string() |
163 | | '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>' |
164 | | |
165 | | # Handle attachments within an multipart/alternative mail correctly (#9367) |
166 | | # (test is not as precise/clear as it could be w.r.t. email tree structure, |
167 | | # but it's good enough.) |
168 | | >>> headers = {"Date": "Fri, 09 Nov 2001 01:08:47 -0000", "Message-ID": "foo"} |
169 | | >>> subject, from_email, to = 'hello', 'from@example.com', 'to@example.com' |
170 | | >>> text_content = 'This is an important message.' |
171 | | >>> html_content = '<p>This is an <strong>important</strong> message.</p>' |
172 | | >>> msg = EmailMultiAlternatives(subject, text_content, from_email, [to], headers=headers) |
173 | | >>> msg.attach_alternative(html_content, "text/html") |
174 | | >>> msg.attach("an attachment.pdf", "%PDF-1.4.%...", mimetype="application/pdf") |
175 | | >>> print msg.message().as_string() |
176 | | Content-Type: multipart/mixed; boundary="..." |
177 | | MIME-Version: 1.0 |
178 | | Subject: hello |
179 | | From: from@example.com |
180 | | To: to@example.com |
181 | | Date: Fri, 09 Nov 2001 01:08:47 -0000 |
182 | | Message-ID: foo |
183 | | ... |
184 | | Content-Type: multipart/alternative;... |
185 | | ... |
186 | | Content-Type: text/plain; charset="utf-8" |
187 | | MIME-Version: 1.0 |
188 | | Content-Transfer-Encoding: quoted-printable |
189 | | ... |
190 | | This is an important message. |
191 | | ... |
192 | | Content-Type: text/html; charset="utf-8" |
193 | | MIME-Version: 1.0 |
194 | | Content-Transfer-Encoding: quoted-printable |
195 | | ... |
196 | | <p>This is an <strong>important</strong> message.</p> |
197 | | ... |
198 | | ... |
199 | | Content-Type: application/pdf |
200 | | MIME-Version: 1.0 |
201 | | Content-Transfer-Encoding: base64 |
202 | | Content-Disposition: attachment; filename="an attachment.pdf" |
203 | | ... |
204 | | JVBERi0xLjQuJS4uLg== |
205 | | ... |
206 | | |
207 | | # Make sure that the console backend writes to stdout by default |
208 | | >>> connection = console.EmailBackend() |
209 | | >>> email = EmailMessage('Subject', 'Content', 'bounce@example.com', ['to@example.com'], headers={'From': 'from@example.com'}) |
210 | | >>> connection.send_messages([email]) |
211 | | Content-Type: text/plain; charset="utf-8" |
212 | | MIME-Version: 1.0 |
213 | | Content-Transfer-Encoding: quoted-printable |
214 | | Subject: Subject |
215 | | From: from@example.com |
216 | | To: to@example.com |
217 | | Date: ... |
218 | | Message-ID: ... |
219 | | |
220 | | Content |
221 | | ------------------------------------------------------------------------------- |
222 | | 1 |
223 | | |
224 | | # Test that the console backend can be pointed at an arbitrary stream |
225 | | >>> s = StringIO() |
226 | | >>> connection = mail.get_connection('django.core.mail.backends.console.EmailBackend', stream=s) |
227 | | >>> send_mail('Subject', 'Content', 'from@example.com', ['to@example.com'], connection=connection) |
228 | | 1 |
229 | | >>> print s.getvalue() |
230 | | Content-Type: text/plain; charset="utf-8" |
231 | | MIME-Version: 1.0 |
232 | | Content-Transfer-Encoding: quoted-printable |
233 | | Subject: Subject |
234 | | From: from@example.com |
235 | | To: to@example.com |
236 | | Date: ... |
237 | | Message-ID: ... |
238 | | |
239 | | Content |
240 | | ------------------------------------------------------------------------------- |
241 | | |
242 | | # Make sure that dummy backends returns correct number of sent messages |
243 | | >>> connection = dummy.EmailBackend() |
244 | | >>> email = EmailMessage('Subject', 'Content', 'bounce@example.com', ['to@example.com'], headers={'From': 'from@example.com'}) |
245 | | >>> connection.send_messages([email, email, email]) |
246 | | 3 |
247 | | |
248 | | # Make sure that locmen backend populates the outbox |
249 | | >>> mail.outbox = [] |
250 | | >>> connection = locmem.EmailBackend() |
251 | | >>> email1 = EmailMessage('Subject', 'Content', 'bounce@example.com', ['to@example.com'], headers={'From': 'from@example.com'}) |
252 | | >>> email2 = EmailMessage('Subject 2', 'Content', 'bounce@example.com', ['to@example.com'], headers={'From': 'from@example.com'}) |
253 | | >>> connection.send_messages([email1, email2]) |
254 | | 2 |
255 | | >>> len(mail.outbox) |
256 | | 2 |
257 | | >>> mail.outbox[0].subject |
258 | | 'Subject' |
259 | | >>> mail.outbox[1].subject |
260 | | 'Subject 2' |
261 | | |
262 | | # Make sure that multiple locmem connections share mail.outbox |
263 | | >>> mail.outbox = [] |
264 | | >>> connection1 = locmem.EmailBackend() |
265 | | >>> connection2 = locmem.EmailBackend() |
266 | | >>> email = EmailMessage('Subject', 'Content', 'bounce@example.com', ['to@example.com'], headers={'From': 'from@example.com'}) |
267 | | >>> connection1.send_messages([email]) |
268 | | 1 |
269 | | >>> connection2.send_messages([email]) |
270 | | 1 |
271 | | >>> len(mail.outbox) |
272 | | 2 |
273 | | |
274 | | # Make sure that the file backend write to the right location |
275 | | >>> tmp_dir = tempfile.mkdtemp() |
276 | | >>> connection = filebased.EmailBackend(file_path=tmp_dir) |
277 | | >>> email = EmailMessage('Subject', 'Content', 'bounce@example.com', ['to@example.com'], headers={'From': 'from@example.com'}) |
278 | | >>> connection.send_messages([email]) |
279 | | 1 |
280 | | >>> len(os.listdir(tmp_dir)) |
281 | | 1 |
282 | | >>> print open(os.path.join(tmp_dir, os.listdir(tmp_dir)[0])).read() |
283 | | Content-Type: text/plain; charset="utf-8" |
284 | | MIME-Version: 1.0 |
285 | | Content-Transfer-Encoding: quoted-printable |
286 | | Subject: Subject |
287 | | From: from@example.com |
288 | | To: to@example.com |
289 | | Date: ... |
290 | | Message-ID: ... |
291 | | |
292 | | Content |
293 | | ------------------------------------------------------------------------------- |
294 | | |
295 | | >>> connection2 = filebased.EmailBackend(file_path=tmp_dir) |
296 | | >>> connection2.send_messages([email]) |
297 | | 1 |
298 | | >>> len(os.listdir(tmp_dir)) |
299 | | 2 |
300 | | >>> connection.send_messages([email]) |
301 | | 1 |
302 | | >>> len(os.listdir(tmp_dir)) |
303 | | 2 |
304 | | >>> email.connection = filebased.EmailBackend(file_path=tmp_dir) |
305 | | >>> connection_created = connection.open() |
306 | | >>> num_sent = email.send() |
307 | | >>> len(os.listdir(tmp_dir)) |
308 | | 3 |
309 | | >>> num_sent = email.send() |
310 | | >>> len(os.listdir(tmp_dir)) |
311 | | 3 |
312 | | >>> connection.close() |
313 | | >>> shutil.rmtree(tmp_dir) |
314 | | |
315 | | # Make sure that get_connection() accepts arbitrary keyword that might be |
316 | | # used with custom backends. |
317 | | >>> c = mail.get_connection(fail_silently=True, foo='bar') |
318 | | >>> c.fail_silently |
319 | | True |
320 | | |
321 | | # Test custom backend defined in this suite. |
322 | | >>> conn = mail.get_connection('regressiontests.mail.custombackend.EmailBackend') |
323 | | >>> hasattr(conn, 'test_outbox') |
324 | | True |
325 | | >>> email = EmailMessage('Subject', 'Content', 'bounce@example.com', ['to@example.com'], headers={'From': 'from@example.com'}) |
326 | | >>> conn.send_messages([email]) |
327 | | 1 |
328 | | >>> len(conn.test_outbox) |
329 | | 1 |
330 | | |
331 | | # Test backend argument of mail.get_connection() |
332 | | >>> isinstance(mail.get_connection('django.core.mail.backends.smtp.EmailBackend'), smtp.EmailBackend) |
333 | | True |
334 | | >>> isinstance(mail.get_connection('django.core.mail.backends.locmem.EmailBackend'), locmem.EmailBackend) |
335 | | True |
336 | | >>> isinstance(mail.get_connection('django.core.mail.backends.dummy.EmailBackend'), dummy.EmailBackend) |
337 | | True |
338 | | >>> isinstance(mail.get_connection('django.core.mail.backends.console.EmailBackend'), console.EmailBackend) |
339 | | True |
340 | | >>> tmp_dir = tempfile.mkdtemp() |
341 | | >>> isinstance(mail.get_connection('django.core.mail.backends.filebased.EmailBackend', file_path=tmp_dir), filebased.EmailBackend) |
342 | | True |
343 | | >>> shutil.rmtree(tmp_dir) |
344 | | >>> isinstance(mail.get_connection(), locmem.EmailBackend) |
345 | | True |
346 | | |
347 | | # Test connection argument of send_mail() et al |
348 | | >>> connection = mail.get_connection('django.core.mail.backends.console.EmailBackend') |
349 | | >>> send_mail('Subject', 'Content', 'from@example.com', ['to@example.com'], connection=connection) |
350 | | Content-Type: text/plain; charset="utf-8" |
351 | | MIME-Version: 1.0 |
352 | | Content-Transfer-Encoding: quoted-printable |
353 | | Subject: Subject |
354 | | From: from@example.com |
355 | | To: to@example.com |
356 | | Date: ... |
357 | | Message-ID: ... |
358 | | |
359 | | Content |
360 | | ------------------------------------------------------------------------------- |
361 | | 1 |
362 | | |
363 | | >>> send_mass_mail([ |
364 | | ... ('Subject1', 'Content1', 'from1@example.com', ['to1@example.com']), |
365 | | ... ('Subject2', 'Content2', 'from2@example.com', ['to2@example.com']) |
366 | | ... ], connection=connection) |
367 | | Content-Type: text/plain; charset="utf-8" |
368 | | MIME-Version: 1.0 |
369 | | Content-Transfer-Encoding: quoted-printable |
370 | | Subject: Subject1 |
371 | | From: from1@example.com |
372 | | To: to1@example.com |
373 | | Date: ... |
374 | | Message-ID: ... |
375 | | |
376 | | Content1 |
377 | | ------------------------------------------------------------------------------- |
378 | | Content-Type: text/plain; charset="utf-8" |
379 | | MIME-Version: 1.0 |
380 | | Content-Transfer-Encoding: quoted-printable |
381 | | Subject: Subject2 |
382 | | From: from2@example.com |
383 | | To: to2@example.com |
384 | | Date: ... |
385 | | Message-ID: ... |
386 | | |
387 | | Content2 |
388 | | ------------------------------------------------------------------------------- |
389 | | 2 |
390 | | |
391 | | >>> mail_admins('Subject', 'Content', connection=connection) |
392 | | Content-Type: text/plain; charset="utf-8" |
393 | | MIME-Version: 1.0 |
394 | | Content-Transfer-Encoding: quoted-printable |
395 | | Subject: [Django] Subject |
396 | | From: root@localhost |
397 | | To: nobody@example.com |
398 | | Date: ... |
399 | | Message-ID: ... |
400 | | |
401 | | Content |
402 | | ------------------------------------------------------------------------------- |
403 | | |
404 | | >>> mail_managers('Subject', 'Content', connection=connection) |
405 | | Content-Type: text/plain; charset="utf-8" |
406 | | MIME-Version: 1.0 |
407 | | Content-Transfer-Encoding: quoted-printable |
408 | | Subject: [Django] Subject |
409 | | From: root@localhost |
410 | | To: nobody@example.com |
411 | | Date: ... |
412 | | Message-ID: ... |
413 | | |
414 | | Content |
415 | | ------------------------------------------------------------------------------- |
416 | | |
417 | | >>> settings.ADMINS = old_admins |
418 | | >>> settings.MANAGERS = old_managers |
419 | | |
420 | | # Add Cc to the email argument list (#7722) |
421 | | |
422 | | >>> email = EmailMessage('Subject', 'Content', 'from@example.com', ['to@example.com'], cc=['cc@example.com']) |
423 | | >>> message = email.message() |
424 | | >>> message['Cc'] |
425 | | 'cc@example.com' |
426 | | >>> email.recipients() |
427 | | ['to@example.com', 'cc@example.com'] |
428 | | |
429 | | >>> email = EmailMessage('Subject', 'Content', 'from@example.com', ['to@example.com','other@example.com'], cc=['cc@example.com', 'cc.other@example.com']) |
430 | | >>> message = email.message() |
431 | | >>> message['Cc'] |
432 | | 'cc@example.com, cc.other@example.com' |
433 | | >>> email.recipients() |
434 | | ['to@example.com', 'other@example.com', 'cc@example.com', 'cc.other@example.com'] |
435 | | |
436 | | >>> email = EmailMessage('Subject', 'Content', 'from@example.com', ['to@example.com','other@example.com'], cc=['cc@example.com', 'cc.other@example.com'], bcc=['bcc@example.com']) |
437 | | >>> message = email.message() |
438 | | >>> email.recipients() |
439 | | ['to@example.com', 'other@example.com', 'cc@example.com', 'cc.other@example.com', 'bcc@example.com'] |
440 | | |
441 | | >>> email = EmailMessage('Subject', 'Content', 'from@example.com', ['to@example.com'], cc=['cc@example.com']) |
442 | | >>> message = email.message() |
443 | | >>> message.as_string() |
444 | | 'Content-Type: text/plain; charset="utf-8"\nMIME-Version: 1.0\nContent-Transfer-Encoding: quoted-printable\nSubject: Subject\nFrom: from@example.com\nTo: to@example.com\nCc: cc@example.com\nDate: ...\nMessage-ID: <...>\n\nContent' |
445 | | |
446 | | """ |
| 2 | """Tests for the django.core.mail.""" |
| 3 | |
| 4 | import os |
| 5 | import shutil |
| 6 | import tempfile |
| 7 | import unittest |
| 8 | from email import message_from_file, message_from_string |
| 9 | from StringIO import StringIO |
| 10 | |
| 11 | from django.conf import settings |
| 12 | from django.core import mail |
| 13 | from django.core.mail import BadHeaderError |
| 14 | from django.core.mail import EmailMessage, EmailMultiAlternatives |
| 15 | from django.core.mail import mail_admins, mail_managers |
| 16 | from django.core.mail import send_mail, send_mass_mail |
| 17 | from django.core.mail.backends import console, dummy, filebased, locmem, smtp |
| 18 | from django.utils.translation import ugettext_lazy |
| 19 | |
| 20 | |
| 21 | class MailTests(unittest.TestCase): |
| 22 | |
| 23 | def test_ascii_characters(self): |
| 24 | email = EmailMessage('Subject', 'Content', 'from@example.com', |
| 25 | ['to@example.com']) |
| 26 | message = email.message() |
| 27 | self.assertEqual(message['Subject'].encode(), 'Subject') |
| 28 | self.assertEqual(message.get_payload(), 'Content') |
| 29 | self.assertEqual(message['From'], 'from@example.com') |
| 30 | self.assertEqual(message['To'], 'to@example.com') |
| 31 | |
| 32 | def test_multiple_recipients(self): |
| 33 | email = EmailMessage('Subject', 'Content', 'from@example.com', |
| 34 | ['to@example.com','other@example.com']) |
| 35 | message = email.message() |
| 36 | self.assertEqual(message['Subject'].encode(), 'Subject') |
| 37 | self.assertEqual(message.get_payload(), 'Content') |
| 38 | self.assertEqual(message['From'], 'from@example.com') |
| 39 | self.assertEqual(message['To'], 'to@example.com, other@example.com') |
| 40 | |
| 41 | def test_header_injection(self): |
| 42 | email = EmailMessage('Subject\nInjection Test', 'Content', |
| 43 | 'from@example.com', ['to@example.com']) |
| 44 | self.assertRaises(BadHeaderError, email.message) |
| 45 | # again with lazy translation |
| 46 | email = EmailMessage(ugettext_lazy('Subject\nInjection Test'), |
| 47 | 'Content', 'from@example.com', |
| 48 | ['to@example.com']) |
| 49 | self.assertRaises(BadHeaderError, email.message) |
| 50 | |
| 51 | def test_continuation_char_in_long_header(self): |
| 52 | # Regression test for #7747 |
| 53 | email = EmailMessage( |
| 54 | ('Long subject lines that get wrapped should use a space ' |
| 55 | 'continuation character to get expected behaviour in Outlook ' |
| 56 | 'and Thunderbird'), 'Content', |
| 57 | 'from@example.com', ['to@example.com']) |
| 58 | message = email.message() |
| 59 | self.assertEqual(str(message['Subject']), |
| 60 | ('Long subject lines that get wrapped should use a space continuation\n' |
| 61 | ' character to get expected behaviour in Outlook and Thunderbird')) |
| 62 | |
| 63 | def test_custom_message_id(self): |
| 64 | # Regression test for #9233 |
| 65 | date_str = 'Fri, 09 Nov 2001 01:08:47 -0000' |
| 66 | headers = {'date': date_str, 'Message-ID': 'foo'} |
| 67 | email = EmailMessage('subject', 'content', 'from@example.com', |
| 68 | ['to@example.com'], headers=headers) |
| 69 | message = email.message() |
| 70 | self.assertEqual(str(message['Message-ID']), 'foo') |
| 71 | self.assertEqual(str(message['date']), date_str) |
| 72 | |
| 73 | def test_ignore_empty_managers(self): |
| 74 | # Test that mail_admins/mail_managers doesn't connect to the |
| 75 | # mail server if there are no recipients (#9383). |
| 76 | old_admins = settings.ADMINS |
| 77 | old_managers = settings.MANAGERS |
| 78 | settings.ADMINS = [] |
| 79 | settings.MANAGERS = [] |
| 80 | mail.outbox = [] |
| 81 | try: |
| 82 | mail_admins('hi','there') |
| 83 | self.assertEqual(len(mail.outbox), 0) |
| 84 | mail_managers('hi','there') |
| 85 | self.assertEqual(len(mail.outbox), 0) |
| 86 | settings.ADMINS = settings.MANAGERS = [ |
| 87 | ('nobody','nobody@example.com')] |
| 88 | mail_admins('hi','there') |
| 89 | self.assertEqual(len(mail.outbox), 1) |
| 90 | mail.outbox = [] |
| 91 | mail_managers('hi','there') |
| 92 | self.assertEqual(len(mail.outbox), 1) |
| 93 | finally: |
| 94 | settings.ADMINS = old_admins |
| 95 | settings.MANAGER = old_managers |
| 96 | |
| 97 | def test_set_from_header_manually(self): |
| 98 | # Regression test for #9214 |
| 99 | email = EmailMessage('Subject', 'Content', 'bounce@example.com', |
| 100 | ['to@example.com'], |
| 101 | headers={'From': 'from@example.com'}) |
| 102 | message = email.message() |
| 103 | self.assertEqual(message['From'], 'from@example.com') |
| 104 | |
| 105 | def test_headers_dont_change(self): |
| 106 | # Regression for #13259 - Make sure that headers are not |
| 107 | # changed when calling EmailMessage.message() |
| 108 | email = EmailMessage('Subject', 'Content', 'bounce@example.com', |
| 109 | ['to@example.com'], |
| 110 | headers={'From': 'from@example.com'}) |
| 111 | message = email.message() |
| 112 | self.assertEqual(message['From'], 'from@example.com') |
| 113 | message = email.message() |
| 114 | self.assertEqual(message['From'], 'from@example.com') |
| 115 | |
| 116 | def test_parse_unicode_addresses(self): |
| 117 | # Regression for #11144 - When a to/from/cc header contains |
| 118 | # unicode, make sure the email addresses are parsed correctly |
| 119 | # (especially with regards to commas) |
| 120 | email = EmailMessage('Subject', 'Content', 'from@example.com', |
| 121 | ['"Firstname Sürname" <to@example.com>', |
| 122 | 'other@example.com']) |
| 123 | self.assertEqual(email.message()['To'], |
| 124 | ('=?utf-8?q?Firstname_S=C3=BCrname?= ' |
| 125 | '<to@example.com>, other@example.com')) |
| 126 | |
| 127 | email = EmailMessage('Subject', 'Content', 'from@example.com', |
| 128 | ['"Sürname, Firstname" <to@example.com>', |
| 129 | 'other@example.com']) |
| 130 | self.assertEqual(email.message()['To'], |
| 131 | ('=?utf-8?q?S=C3=BCrname=2C_Firstname?= ' |
| 132 | '<to@example.com>, other@example.com')) |
| 133 | |
| 134 | def test_header_encoding(self): |
| 135 | # Regression for #6918 - When a header contains unicode, |
| 136 | # make sure headers can be set with a different encoding than utf-8 |
| 137 | email = EmailMessage('Message from Firstname Sürname', 'Content', |
| 138 | 'from@example.com', |
| 139 | ['"Sürname, Firstname" <to@example.com>', |
| 140 | 'other@example.com']) |
| 141 | email.encoding = 'iso-8859-1' |
| 142 | self.assertEqual(email.message()['To'], |
| 143 | '=?iso-8859-1?q?S=FCrname=2C_Firstname?= <to@example.com>, other@example.com') |
| 144 | self.assertEqual(email.message()['Subject'].encode(), |
| 145 | u'=?iso-8859-1?q?Message_from_Firstname_S=FCrname?=') |
| 146 | |
| 147 | def test_header_encoding_multipart(self): |
| 148 | # Make sure headers can be set with a different encoding than |
| 149 | # utf-8 in SafeMIMEMultipart as well |
| 150 | headers = {"Date": "Fri, 09 Nov 2001 01:08:47 -0000", |
| 151 | "Message-ID": "foo"} |
| 152 | subject, from_email, to = 'hello', 'from@example.com', '"Sürname, Firstname" <to@example.com>' |
| 153 | text_content = 'This is an important message.' |
| 154 | html_content = '<p>This is an <strong>important</strong> message.</p>' |
| 155 | msg = EmailMultiAlternatives('Message from Firstname Sürname', |
| 156 | text_content, from_email, [to], |
| 157 | headers=headers) |
| 158 | msg.attach_alternative(html_content, "text/html") |
| 159 | msg.encoding = 'iso-8859-1' |
| 160 | self.assertEqual(msg.message()['To'], |
| 161 | '=?iso-8859-1?q?S=FCrname=2C_Firstname?= <to@example.com>') |
| 162 | self.assertEqual(msg.message()['Subject'].encode(), |
| 163 | u'=?iso-8859-1?q?Message_from_Firstname_S=FCrname?=') |
| 164 | |
| 165 | def test_body_encoding(self): |
| 166 | # Regression for #12791 - Encode body correctly with other |
| 167 | # encodings than utf-8 |
| 168 | email = EmailMessage('Subject', 'Firstname Sürname is a great guy.', |
| 169 | 'from@example.com', ['other@example.com']) |
| 170 | email.encoding = 'iso-8859-1' |
| 171 | message = email.message() |
| 172 | self.assertEqual(str(message['Content-Type']), |
| 173 | 'text/plain; charset="iso-8859-1"') |
| 174 | self.assertEqual(message.get_payload(), |
| 175 | 'Firstname S=FCrname is a great guy.') |
| 176 | |
| 177 | def test_attachment_encoding(self): |
| 178 | # Make sure MIME attachments also works correctly with other |
| 179 | # encodings than utf-8 |
| 180 | text_content = 'Firstname Sürname is a great guy.' |
| 181 | html_content = '<p>Firstname Sürname is a <strong>great</strong> guy.</p>' |
| 182 | email = EmailMultiAlternatives('Subject', text_content, |
| 183 | 'from@example.com', ['to@example.com']) |
| 184 | email.encoding = 'iso-8859-1' |
| 185 | email.attach_alternative(html_content, 'text/html') |
| 186 | message = email.message() |
| 187 | payload0 = message.get_payload(0) |
| 188 | payload1 = message.get_payload(1) |
| 189 | self.assertEqual(payload0['Content-Type'], |
| 190 | 'text/plain; charset="iso-8859-1"') |
| 191 | self.assertEqual(payload0.get_payload(), |
| 192 | 'Firstname S=FCrname is a great guy.') |
| 193 | self.assertEqual(payload1['Content-Type'], |
| 194 | 'text/html; charset="iso-8859-1"') |
| 195 | self.assertEqual(payload1.get_payload(), |
| 196 | '<p>Firstname S=FCrname is a <strong>great</strong> guy.</p>') |
| 197 | |
| 198 | def test_handle_attachments(self): |
| 199 | # Handle attachments within an multipart/alternative mail |
| 200 | # correctly (#9367) (test is not as precise/clear as it could |
| 201 | # be w.r.t. email tree structure, but it's good enough.) |
| 202 | headers = {"Date": "Fri, 09 Nov 2001 01:08:47 -0000", |
| 203 | "Message-ID": "foo"} |
| 204 | subject, from_email, to = 'hello', 'from@example.com', 'to@example.com' |
| 205 | text_content = 'This is an important message.' |
| 206 | html_content = '<p>This is an <strong>important</strong> message.</p>' |
| 207 | email = EmailMultiAlternatives(subject, text_content, from_email, |
| 208 | [to], headers=headers) |
| 209 | email.attach_alternative(html_content, "text/html") |
| 210 | email.attach("an attachment.pdf", "%PDF-1.4.%...", |
| 211 | mimetype="application/pdf") |
| 212 | message = email.message() |
| 213 | payload0 = message.get_payload(0) |
| 214 | payload1 = message.get_payload(1) |
| 215 | self.assert_(payload0['Content-Type'].startswith('multipart/alternative')) |
| 216 | self.assertEqual(payload0.get_payload(0)['Content-Type'], |
| 217 | 'text/plain; charset="utf-8"') |
| 218 | self.assertEqual(payload0.get_payload(1)['Content-Type'], |
| 219 | 'text/html; charset="utf-8"') |
| 220 | self.assertEqual(payload0.get_payload(0)['Content-Transfer-Encoding'], |
| 221 | 'quoted-printable') |
| 222 | self.assertEqual(payload0.get_payload(1)['Content-Transfer-Encoding'], |
| 223 | 'quoted-printable') |
| 224 | self.assertEqual(payload0.get_payload(0).get_payload(), |
| 225 | 'This is an important message.') |
| 226 | self.assertEqual(payload0.get_payload(1).get_payload(), |
| 227 | '<p>This is an <strong>important</strong> message.</p>') |
| 228 | self.assertEqual(payload1['Content-Type'], 'application/pdf') |
| 229 | self.assertEqual(payload1['Content-Disposition'], |
| 230 | 'attachment; filename="an attachment.pdf"') |
| 231 | self.assertEqual(payload1['Content-Transfer-Encoding'], 'base64') |
| 232 | |
| 233 | def test_console_backend(self): |
| 234 | # Make sure that the console backend write to arbitrary streams. |
| 235 | stream = StringIO() |
| 236 | connection = console.EmailBackend(stream=stream) |
| 237 | email = EmailMessage('Subject', 'Content', 'bounce@example.com', |
| 238 | ['to@example.com'], |
| 239 | headers={'From': 'from@example.com'}) |
| 240 | self.assertEqual(connection.send_messages([email]), 1) |
| 241 | lines = stream.getvalue().splitlines() |
| 242 | self.assert_('MIME-Version: 1.0' in lines) |
| 243 | self.assert_('Subject: Subject' in lines) |
| 244 | self.assert_('From: from@example.com' in lines) |
| 245 | self.assert_('To: to@example.com' in lines) |
| 246 | |
| 247 | def test_dummy_backend(self): |
| 248 | # Make sure that dummy backends returns correct number of sent |
| 249 | # messages |
| 250 | connection = dummy.EmailBackend() |
| 251 | email = EmailMessage('Subject', 'Content', 'bounce@example.com', |
| 252 | ['to@example.com'], |
| 253 | headers={'From': 'from@example.com'}) |
| 254 | self.assertEqual(connection.send_messages([email, email, email]), 3) |
| 255 | |
| 256 | def test_locmem_backend(self): |
| 257 | # Make sure that locmen backend populates the outbox |
| 258 | mail.outbox = [] |
| 259 | connection = locmem.EmailBackend() |
| 260 | email1 = EmailMessage('Subject', 'Content', 'bounce@example.com', |
| 261 | ['to@example.com'], |
| 262 | headers={'From': 'from@example.com'}) |
| 263 | email2 = EmailMessage('Subject 2', 'Content', 'bounce@example.com', |
| 264 | ['to@example.com'], |
| 265 | headers={'From': 'from@example.com'}) |
| 266 | self.assertEqual(connection.send_messages([email1, email2]), 2) |
| 267 | self.assertEqual(len(mail.outbox), 2) |
| 268 | self.assertEqual(mail.outbox[0].subject, 'Subject') |
| 269 | self.assertEqual(mail.outbox[1].subject, 'Subject 2') |
| 270 | |
| 271 | def test_locmem_backend_multiple(self): |
| 272 | # Make sure that multiple locmem connections share mail.outbox |
| 273 | mail.outbox = [] |
| 274 | connection1 = locmem.EmailBackend() |
| 275 | connection2 = locmem.EmailBackend() |
| 276 | email = EmailMessage('Subject', 'Content', 'bounce@example.com', |
| 277 | ['to@example.com'], |
| 278 | headers={'From': 'from@example.com'}) |
| 279 | self.assertEqual(connection1.send_messages([email]), 1) |
| 280 | self.assertEqual(connection2.send_messages([email]), 1) |
| 281 | self.assertEqual(len(mail.outbox), 2) |
| 282 | |
| 283 | def test_file_backend(self): |
| 284 | # Make sure that the file backend write to the right location |
| 285 | tmp_dir = tempfile.mkdtemp() |
| 286 | connection = filebased.EmailBackend(file_path=tmp_dir) |
| 287 | email = EmailMessage('Subject', 'Content', 'bounce@example.com', |
| 288 | ['to@example.com'], |
| 289 | headers={'From': 'from@example.com'}) |
| 290 | self.assertEqual(connection.send_messages([email]), 1) |
| 291 | self.assertEqual(len(os.listdir(tmp_dir)), 1) |
| 292 | fp = open(os.path.join(tmp_dir, os.listdir(tmp_dir)[0])) |
| 293 | message = message_from_file(fp) |
| 294 | fp.close() |
| 295 | self.assertEqual(message['Content-Type'], |
| 296 | 'text/plain; charset="utf-8"') |
| 297 | self.assertEqual(message['Subject'], 'Subject') |
| 298 | self.assertEqual(message['From'], 'from@example.com') |
| 299 | self.assertEqual(message['To'], 'to@example.com') |
| 300 | |
| 301 | connection2 = filebased.EmailBackend(file_path=tmp_dir) |
| 302 | self.assertEqual(connection2.send_messages([email]), 1) |
| 303 | self.assertEqual(len(os.listdir(tmp_dir)), 2) |
| 304 | self.assertEqual(connection.send_messages([email]), 1) |
| 305 | self.assertEqual(len(os.listdir(tmp_dir)), 2) |
| 306 | |
| 307 | email.connection = filebased.EmailBackend(file_path=tmp_dir) |
| 308 | connection.open() |
| 309 | email.send() |
| 310 | self.assertEqual(len(os.listdir(tmp_dir)), 3) |
| 311 | email.send() |
| 312 | self.assertEqual(len(os.listdir(tmp_dir)), 3) |
| 313 | connection.close() |
| 314 | shutil.rmtree(tmp_dir) |
| 315 | |
| 316 | def test_get_connection(self): |
| 317 | # Make sure that get_connection() accepts arbitrary keyword |
| 318 | # that might be used with custom backends. |
| 319 | c = mail.get_connection(fail_silently=True, foo='bar') |
| 320 | self.assertEqual(c.fail_silently, True) |
| 321 | |
| 322 | def test_custom_backend(self): |
| 323 | # Test custom backend defined in this suite. |
| 324 | conn = mail.get_connection( |
| 325 | 'regressiontests.mail.custombackend.EmailBackend') |
| 326 | self.assert_(hasattr(conn, 'test_outbox')) |
| 327 | email = EmailMessage('Subject', 'Content', 'bounce@example.com', |
| 328 | ['to@example.com'], |
| 329 | headers={'From': 'from@example.com'}) |
| 330 | self.assertEqual(conn.send_messages([email]), 1) |
| 331 | self.assertEqual(len(conn.test_outbox), 1) |
| 332 | |
| 333 | def test_builtin_backends(self): |
| 334 | # Test backend argument of mail.get_connection() |
| 335 | path = 'django.core.mail.backends.%s.EmailBackend' |
| 336 | self.assert_(isinstance(mail.get_connection(path % 'smtp'), |
| 337 | smtp.EmailBackend)) |
| 338 | self.assert_(isinstance(mail.get_connection(path % 'locmem'), |
| 339 | locmem.EmailBackend)) |
| 340 | self.assert_(isinstance(mail.get_connection(path % 'dummy'), |
| 341 | dummy.EmailBackend)) |
| 342 | self.assert_(isinstance(mail.get_connection(path % 'console'), |
| 343 | console.EmailBackend)) |
| 344 | tmp_dir = tempfile.mkdtemp() |
| 345 | self.assert_(isinstance(mail.get_connection(path % 'filebased', |
| 346 | file_path=tmp_dir), |
| 347 | filebased.EmailBackend)) |
| 348 | shutil.rmtree(tmp_dir) |
| 349 | # the default (for tests) |
| 350 | self.assert_(isinstance(mail.get_connection(), locmem.EmailBackend)) |
| 351 | |
| 352 | def test_connection_argument(self): |
| 353 | # Test connection argument of send_mail() et al |
| 354 | stream = StringIO() |
| 355 | connection = mail.get_connection( |
| 356 | 'django.core.mail.backends.console.EmailBackend', stream=stream) |
| 357 | self.assertEqual(send_mail('Subject', 'Content', 'from@example.com', |
| 358 | ['to@example.com'], connection=connection), |
| 359 | 1) |
| 360 | message = message_from_string(stream.getvalue()) |
| 361 | self.assertEqual(message['Content-Type'], |
| 362 | 'text/plain; charset="utf-8"') |
| 363 | self.assertEqual(message['Subject'], 'Subject') |
| 364 | self.assertEqual(message['From'], 'from@example.com') |
| 365 | |
| 366 | stream = StringIO() |
| 367 | connection = mail.get_connection( |
| 368 | 'django.core.mail.backends.console.EmailBackend', stream=stream) |
| 369 | self.assertEqual(send_mass_mail([ |
| 370 | ('Subject1', 'Content1', |
| 371 | 'from1@example.com', ['to1@example.com']), |
| 372 | ('Subject2', 'Content2', |
| 373 | 'from2@example.com', ['to2@example.com']) |
| 374 | ], connection=connection), 2) |
| 375 | # message separator in stream is '-'*79 |
| 376 | raw = [x.strip() for x in stream.getvalue().split('-'*79)] |
| 377 | first, second, _ = map(message_from_string, raw) |
| 378 | self.assertEqual(first['Subject'], 'Subject1') |
| 379 | self.assertEqual(second['Subject'], 'Subject2') |
| 380 | self.assertEqual(first.get_payload(), 'Content1') |
| 381 | self.assertEqual(second.get_payload(), 'Content2') |
| 382 | |
| 383 | stream = StringIO() |
| 384 | connection = mail.get_connection( |
| 385 | 'django.core.mail.backends.console.EmailBackend', stream=stream) |
| 386 | old_admins = settings.ADMINS |
| 387 | settings.ADMINS = [('nobody','nobody@example.com')] |
| 388 | try: |
| 389 | mail_admins('Subject', 'Content', connection=connection) |
| 390 | finally: |
| 391 | settings.ADMINS = old_admins |
| 392 | message = message_from_string(stream.getvalue()) |
| 393 | self.assertEqual(message['From'], 'root@localhost') |
| 394 | self.assertEqual(message['To'], 'nobody@example.com') |
| 395 | |
| 396 | stream = StringIO() |
| 397 | connection = mail.get_connection( |
| 398 | 'django.core.mail.backends.console.EmailBackend', stream=stream) |
| 399 | old_managers = settings.MANAGERS |
| 400 | settings.MANAGERS = [('nobody','nobody@example.com')] |
| 401 | try: |
| 402 | mail_managers('Subject', 'Content', connection=connection) |
| 403 | finally: |
| 404 | settings.MANAGERS = old_managers |
| 405 | message = message_from_string(stream.getvalue()) |
| 406 | self.assertEqual(message['From'], 'root@localhost') |
| 407 | self.assertEqual(message['To'], 'nobody@example.com') |
| 408 | |
| 409 | def test_cc(self): |
| 410 | # Add Cc to the email argument list (#7722) |
| 411 | email = EmailMessage('Subject', 'Content', 'from@example.com', |
| 412 | ['to@example.com'], cc=['cc@example.com']) |
| 413 | message = email.message() |
| 414 | self.assertEqual(message['Cc'], 'cc@example.com') |
| 415 | self.assertEqual(email.recipients(), ['to@example.com', |
| 416 | 'cc@example.com']) |
| 417 | |
| 418 | email = EmailMessage('Subject', 'Content', 'from@example.com', |
| 419 | ['to@example.com','other@example.com'], |
| 420 | cc=['cc@example.com', 'cc.other@example.com']) |
| 421 | message = email.message() |
| 422 | self.assertEqual(message['Cc'], |
| 423 | 'cc@example.com, cc.other@example.com') |
| 424 | self.assertEqual(email.recipients(), |
| 425 | ['to@example.com', 'other@example.com', |
| 426 | 'cc@example.com', 'cc.other@example.com']) |
| 427 | |
| 428 | def test_bcc(self): |
| 429 | email = EmailMessage('Subject', 'Content', 'from@example.com', |
| 430 | ['to@example.com','other@example.com'], |
| 431 | cc=['cc@example.com', 'cc.other@example.com'], |
| 432 | bcc=['bcc@example.com']) |
| 433 | self.assertEqual(email.recipients(), |
| 434 | ['to@example.com', 'other@example.com', |
| 435 | 'cc@example.com', 'cc.other@example.com', |
| 436 | 'bcc@example.com']) |