parts/django/tests/regressiontests/mail/tests.py
changeset 307 c6bca38c1cbf
equal deleted inserted replaced
306:5ff1fc726848 307:c6bca38c1cbf
       
     1 # coding: utf-8
       
     2 import email
       
     3 import os
       
     4 import shutil
       
     5 import sys
       
     6 import tempfile
       
     7 from StringIO import StringIO
       
     8 from django.conf import settings
       
     9 from django.core import mail
       
    10 from django.core.mail import EmailMessage, mail_admins, mail_managers, EmailMultiAlternatives
       
    11 from django.core.mail import send_mail, send_mass_mail
       
    12 from django.core.mail.backends.base import BaseEmailBackend
       
    13 from django.core.mail.backends import console, dummy, locmem, filebased, smtp
       
    14 from django.core.mail.message import BadHeaderError
       
    15 from django.test import TestCase
       
    16 from django.utils.translation import ugettext_lazy
       
    17 
       
    18 class MailTests(TestCase):
       
    19 
       
    20     def test_ascii(self):
       
    21         email = EmailMessage('Subject', 'Content', 'from@example.com', ['to@example.com'])
       
    22         message = email.message()
       
    23         self.assertEqual(message['Subject'].encode(), 'Subject')
       
    24         self.assertEqual(message.get_payload(), 'Content')
       
    25         self.assertEqual(message['From'], 'from@example.com')
       
    26         self.assertEqual(message['To'], 'to@example.com')
       
    27 
       
    28     def test_multiple_recipients(self):
       
    29         email = EmailMessage('Subject', 'Content', 'from@example.com', ['to@example.com','other@example.com'])
       
    30         message = email.message()
       
    31         self.assertEqual(message['Subject'].encode(), 'Subject')
       
    32         self.assertEqual(message.get_payload(), 'Content')
       
    33         self.assertEqual(message['From'], 'from@example.com')
       
    34         self.assertEqual(message['To'], 'to@example.com, other@example.com')
       
    35 
       
    36     def test_header_injection(self):
       
    37         email = EmailMessage('Subject\nInjection Test', 'Content', 'from@example.com', ['to@example.com'])
       
    38         self.assertRaises(BadHeaderError, email.message)
       
    39         email = EmailMessage(ugettext_lazy('Subject\nInjection Test'), 'Content', 'from@example.com', ['to@example.com'])
       
    40         self.assertRaises(BadHeaderError, email.message)
       
    41 
       
    42     def test_space_continuation(self):
       
    43         """
       
    44         Test for space continuation character in long (ascii) subject headers (#7747)
       
    45         """
       
    46         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'])
       
    47         message = email.message()
       
    48         self.assertEqual(message['Subject'], 'Long subject lines that get wrapped should use a space continuation\n character to get expected behaviour in Outlook and Thunderbird')
       
    49 
       
    50     def test_message_header_overrides(self):
       
    51         """
       
    52         Specifying dates or message-ids in the extra headers overrides the
       
    53         default values (#9233)
       
    54         """
       
    55         headers = {"date": "Fri, 09 Nov 2001 01:08:47 -0000", "Message-ID": "foo"}
       
    56         email = EmailMessage('subject', 'content', 'from@example.com', ['to@example.com'], headers=headers)
       
    57         self.assertEqual(email.message().as_string(), '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')
       
    58 
       
    59     def test_empty_admins(self):
       
    60         """
       
    61         Test that mail_admins/mail_managers doesn't connect to the mail server
       
    62         if there are no recipients (#9383)
       
    63         """
       
    64         old_admins = settings.ADMINS
       
    65         old_managers = settings.MANAGERS
       
    66 
       
    67         settings.ADMINS = settings.MANAGERS = [('nobody','nobody@example.com')]
       
    68         mail.outbox = []
       
    69         mail_admins('hi', 'there')
       
    70         self.assertEqual(len(mail.outbox), 1)
       
    71         mail.outbox = []
       
    72         mail_managers('hi', 'there')
       
    73         self.assertEqual(len(mail.outbox), 1)
       
    74 
       
    75         settings.ADMINS = settings.MANAGERS = []
       
    76         mail.outbox = []
       
    77         mail_admins('hi', 'there')
       
    78         self.assertEqual(len(mail.outbox), 0)
       
    79         mail.outbox = []
       
    80         mail_managers('hi', 'there')
       
    81         self.assertEqual(len(mail.outbox), 0)
       
    82 
       
    83         settings.ADMINS = old_admins
       
    84         settings.MANAGERS = old_managers
       
    85 
       
    86     def test_from_header(self):
       
    87         """
       
    88         Make sure we can manually set the From header (#9214)
       
    89         """
       
    90         email = EmailMessage('Subject', 'Content', 'bounce@example.com', ['to@example.com'], headers={'From': 'from@example.com'})
       
    91         message = email.message()
       
    92         self.assertEqual(message['From'], 'from@example.com')
       
    93 
       
    94     def test_multiple_message_call(self):
       
    95         """
       
    96         Regression for #13259 - Make sure that headers are not changed when
       
    97         calling EmailMessage.message()
       
    98         """
       
    99         email = EmailMessage('Subject', 'Content', 'bounce@example.com', ['to@example.com'], headers={'From': 'from@example.com'})
       
   100         message = email.message()
       
   101         self.assertEqual(message['From'], 'from@example.com')
       
   102         message = email.message()
       
   103         self.assertEqual(message['From'], 'from@example.com')
       
   104 
       
   105     def test_unicode_header(self):
       
   106         """
       
   107         Regression for #11144 - When a to/from/cc header contains unicode,
       
   108         make sure the email addresses are parsed correctly (especially with
       
   109         regards to commas)
       
   110         """
       
   111         email = EmailMessage('Subject', 'Content', 'from@example.com', ['"Firstname Sürname" <to@example.com>','other@example.com'])
       
   112         self.assertEqual(email.message()['To'], '=?utf-8?q?Firstname_S=C3=BCrname?= <to@example.com>, other@example.com')
       
   113         email = EmailMessage('Subject', 'Content', 'from@example.com', ['"Sürname, Firstname" <to@example.com>','other@example.com'])
       
   114         self.assertEqual(email.message()['To'], '=?utf-8?q?S=C3=BCrname=2C_Firstname?= <to@example.com>, other@example.com')
       
   115 
       
   116     def test_safe_mime_multipart(self):
       
   117         """
       
   118         Make sure headers can be set with a different encoding than utf-8 in
       
   119         SafeMIMEMultipart as well
       
   120         """
       
   121         headers = {"Date": "Fri, 09 Nov 2001 01:08:47 -0000", "Message-ID": "foo"}
       
   122         subject, from_email, to = 'hello', 'from@example.com', '"Sürname, Firstname" <to@example.com>'
       
   123         text_content = 'This is an important message.'
       
   124         html_content = '<p>This is an <strong>important</strong> message.</p>'
       
   125         msg = EmailMultiAlternatives('Message from Firstname Sürname', text_content, from_email, [to], headers=headers)
       
   126         msg.attach_alternative(html_content, "text/html")
       
   127         msg.encoding = 'iso-8859-1'
       
   128         self.assertEqual(msg.message()['To'], '=?iso-8859-1?q?S=FCrname=2C_Firstname?= <to@example.com>')
       
   129         self.assertEqual(msg.message()['Subject'].encode(), u'=?iso-8859-1?q?Message_from_Firstname_S=FCrname?=')
       
   130 
       
   131     def test_encoding(self):
       
   132         """
       
   133         Regression for #12791 - Encode body correctly with other encodings
       
   134         than utf-8
       
   135         """
       
   136         email = EmailMessage('Subject', 'Firstname Sürname is a great guy.', 'from@example.com', ['other@example.com'])
       
   137         email.encoding = 'iso-8859-1'
       
   138         message = email.message()
       
   139         self.assertTrue(message.as_string().startswith('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'))
       
   140         self.assertEqual(message.get_payload(), 'Firstname S=FCrname is a great guy.')
       
   141 
       
   142         # Make sure MIME attachments also works correctly with other encodings than utf-8
       
   143         text_content = 'Firstname Sürname is a great guy.'
       
   144         html_content = '<p>Firstname Sürname is a <strong>great</strong> guy.</p>'
       
   145         msg = EmailMultiAlternatives('Subject', text_content, 'from@example.com', ['to@example.com'])
       
   146         msg.encoding = 'iso-8859-1'
       
   147         msg.attach_alternative(html_content, "text/html")
       
   148         self.assertEqual(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.')
       
   149         self.assertEqual(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>')
       
   150 
       
   151     def test_attachments(self):
       
   152         """Regression test for #9367"""
       
   153         headers = {"Date": "Fri, 09 Nov 2001 01:08:47 -0000", "Message-ID": "foo"}
       
   154         subject, from_email, to = 'hello', 'from@example.com', 'to@example.com'
       
   155         text_content = 'This is an important message.'
       
   156         html_content = '<p>This is an <strong>important</strong> message.</p>'
       
   157         msg = EmailMultiAlternatives(subject, text_content, from_email, [to], headers=headers)
       
   158         msg.attach_alternative(html_content, "text/html")
       
   159         msg.attach("an attachment.pdf", "%PDF-1.4.%...", mimetype="application/pdf")
       
   160         msg_str = msg.message().as_string()
       
   161         message = email.message_from_string(msg_str)
       
   162         self.assertTrue(message.is_multipart())
       
   163         self.assertEqual(message.get_content_type(), 'multipart/mixed')
       
   164         self.assertEqual(message.get_default_type(), 'text/plain')
       
   165         payload = message.get_payload()
       
   166         self.assertEqual(payload[0].get_content_type(), 'multipart/alternative')
       
   167         self.assertEqual(payload[1].get_content_type(), 'application/pdf')
       
   168 
       
   169     def test_arbitrary_stream(self):
       
   170         """
       
   171         Test that the console backend can be pointed at an arbitrary stream.
       
   172         """
       
   173         s = StringIO()
       
   174         connection = mail.get_connection('django.core.mail.backends.console.EmailBackend', stream=s)
       
   175         send_mail('Subject', 'Content', 'from@example.com', ['to@example.com'], connection=connection)
       
   176         self.assertTrue(s.getvalue().startswith('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: '))
       
   177 
       
   178     def test_stdout(self):
       
   179         """Make sure that the console backend writes to stdout by default"""
       
   180         old_stdout = sys.stdout
       
   181         sys.stdout = StringIO()
       
   182         connection = console.EmailBackend()
       
   183         email = EmailMessage('Subject', 'Content', 'bounce@example.com', ['to@example.com'], headers={'From': 'from@example.com'})
       
   184         connection.send_messages([email])
       
   185         self.assertTrue(sys.stdout.getvalue().startswith('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: '))
       
   186         sys.stdout = old_stdout
       
   187 
       
   188     def test_dummy(self):
       
   189         """
       
   190         Make sure that dummy backends returns correct number of sent messages
       
   191         """
       
   192         connection = dummy.EmailBackend()
       
   193         email = EmailMessage('Subject', 'Content', 'bounce@example.com', ['to@example.com'], headers={'From': 'from@example.com'})
       
   194         self.assertEqual(connection.send_messages([email, email, email]), 3)
       
   195 
       
   196     def test_locmem(self):
       
   197         """
       
   198         Make sure that the locmen backend populates the outbox.
       
   199         """
       
   200         mail.outbox = []
       
   201         connection = locmem.EmailBackend()
       
   202         email1 = EmailMessage('Subject', 'Content', 'bounce@example.com', ['to@example.com'], headers={'From': 'from@example.com'})
       
   203         email2 = EmailMessage('Subject 2', 'Content', 'bounce@example.com', ['to@example.com'], headers={'From': 'from@example.com'})
       
   204         connection.send_messages([email1, email2])
       
   205         self.assertEqual(len(mail.outbox), 2)
       
   206         self.assertEqual(mail.outbox[0].subject, 'Subject')
       
   207         self.assertEqual(mail.outbox[1].subject, 'Subject 2')
       
   208         
       
   209         # Make sure that multiple locmem connections share mail.outbox
       
   210         mail.outbox = []
       
   211         connection2 = locmem.EmailBackend()
       
   212         email = EmailMessage('Subject', 'Content', 'bounce@example.com', ['to@example.com'], headers={'From': 'from@example.com'})
       
   213         connection.send_messages([email])
       
   214         connection2.send_messages([email])
       
   215         self.assertEqual(len(mail.outbox), 2)
       
   216 
       
   217     def test_file_backend(self):
       
   218         tmp_dir = tempfile.mkdtemp()
       
   219         connection = filebased.EmailBackend(file_path=tmp_dir)
       
   220         email1 = EmailMessage('Subject', 'Content', 'bounce@example.com', ['to@example.com'], headers={'From': 'from@example.com'})
       
   221         connection.send_messages([email1])
       
   222         self.assertEqual(len(os.listdir(tmp_dir)), 1)
       
   223         message = email.message_from_file(open(os.path.join(tmp_dir, os.listdir(tmp_dir)[0])))
       
   224         self.assertEqual(message.get_content_type(), 'text/plain')
       
   225         self.assertEqual(message.get('subject'), 'Subject')
       
   226         self.assertEqual(message.get('from'), 'from@example.com')
       
   227         self.assertEqual(message.get('to'), 'to@example.com')
       
   228         connection2 = filebased.EmailBackend(file_path=tmp_dir)
       
   229         connection2.send_messages([email1])
       
   230         self.assertEqual(len(os.listdir(tmp_dir)), 2)
       
   231         connection.send_messages([email1])
       
   232         self.assertEqual(len(os.listdir(tmp_dir)), 2)
       
   233         email1.connection = filebased.EmailBackend(file_path=tmp_dir)
       
   234         connection_created = connection.open()
       
   235         email1.send()
       
   236         self.assertEqual(len(os.listdir(tmp_dir)), 3)
       
   237         email1.send()
       
   238         self.assertEqual(len(os.listdir(tmp_dir)), 3)
       
   239         connection.close()
       
   240         shutil.rmtree(tmp_dir)
       
   241 
       
   242     def test_arbitrary_keyword(self):
       
   243         """
       
   244         Make sure that get_connection() accepts arbitrary keyword that might be
       
   245         used with custom backends.
       
   246         """
       
   247         c = mail.get_connection(fail_silently=True, foo='bar')
       
   248         self.assertTrue(c.fail_silently)
       
   249 
       
   250     def test_custom_backend(self):
       
   251         """Test custom backend defined in this suite."""
       
   252         conn = mail.get_connection('regressiontests.mail.custombackend.EmailBackend')
       
   253         self.assertTrue(hasattr(conn, 'test_outbox'))
       
   254         email = EmailMessage('Subject', 'Content', 'bounce@example.com', ['to@example.com'], headers={'From': 'from@example.com'})
       
   255         conn.send_messages([email])
       
   256         self.assertEqual(len(conn.test_outbox), 1)
       
   257 
       
   258     def test_backend_arg(self):
       
   259         """Test backend argument of mail.get_connection()"""
       
   260         self.assertTrue(isinstance(mail.get_connection('django.core.mail.backends.smtp.EmailBackend'), smtp.EmailBackend))
       
   261         self.assertTrue(isinstance(mail.get_connection('django.core.mail.backends.locmem.EmailBackend'), locmem.EmailBackend))
       
   262         self.assertTrue(isinstance(mail.get_connection('django.core.mail.backends.dummy.EmailBackend'), dummy.EmailBackend))
       
   263         self.assertTrue(isinstance(mail.get_connection('django.core.mail.backends.console.EmailBackend'), console.EmailBackend))
       
   264         tmp_dir = tempfile.mkdtemp()
       
   265         self.assertTrue(isinstance(mail.get_connection('django.core.mail.backends.filebased.EmailBackend', file_path=tmp_dir), filebased.EmailBackend))
       
   266         shutil.rmtree(tmp_dir)
       
   267         self.assertTrue(isinstance(mail.get_connection(), locmem.EmailBackend))
       
   268 
       
   269     def test_connection_arg(self):
       
   270         """Test connection argument to send_mail(), et. al."""
       
   271         connection = mail.get_connection('django.core.mail.backends.locmem.EmailBackend')
       
   272 
       
   273         mail.outbox = []
       
   274         send_mail('Subject', 'Content', 'from@example.com', ['to@example.com'], connection=connection)
       
   275         self.assertEqual(len(mail.outbox), 1)
       
   276         message = mail.outbox[0]
       
   277         self.assertEqual(message.subject, 'Subject')
       
   278         self.assertEqual(message.from_email, 'from@example.com')
       
   279         self.assertEqual(message.to, ['to@example.com'])
       
   280 
       
   281         mail.outbox = []
       
   282         send_mass_mail([
       
   283                 ('Subject1', 'Content1', 'from1@example.com', ['to1@example.com']),
       
   284                 ('Subject2', 'Content2', 'from2@example.com', ['to2@example.com'])
       
   285             ], connection=connection)
       
   286         self.assertEqual(len(mail.outbox), 2)
       
   287         message = mail.outbox[0]
       
   288         self.assertEqual(message.subject, 'Subject1')
       
   289         self.assertEqual(message.from_email, 'from1@example.com')
       
   290         self.assertEqual(message.to, ['to1@example.com'])
       
   291         message = mail.outbox[1]
       
   292         self.assertEqual(message.subject, 'Subject2')
       
   293         self.assertEqual(message.from_email, 'from2@example.com')
       
   294         self.assertEqual(message.to, ['to2@example.com'])
       
   295 
       
   296         old_admins = settings.ADMINS
       
   297         old_managers = settings.MANAGERS
       
   298         settings.ADMINS = settings.MANAGERS = [('nobody','nobody@example.com')]
       
   299 
       
   300         mail.outbox = []
       
   301         mail_admins('Subject', 'Content', connection=connection)
       
   302         self.assertEqual(len(mail.outbox), 1)
       
   303         message = mail.outbox[0]
       
   304         self.assertEqual(message.subject, '[Django] Subject')
       
   305         self.assertEqual(message.from_email, 'root@localhost')
       
   306         self.assertEqual(message.to, ['nobody@example.com'])
       
   307 
       
   308         mail.outbox = []
       
   309         mail_managers('Subject', 'Content', connection=connection)
       
   310         self.assertEqual(len(mail.outbox), 1)
       
   311         message = mail.outbox[0]
       
   312         self.assertEqual(message.subject, '[Django] Subject')
       
   313         self.assertEqual(message.from_email, 'root@localhost')
       
   314         self.assertEqual(message.to, ['nobody@example.com'])
       
   315 
       
   316         settings.ADMINS = old_admins
       
   317         settings.MANAGERS = old_managers
       
   318 
       
   319     def test_mail_prefix(self):
       
   320         """Test prefix argument in manager/admin mail."""
       
   321         # Regression for #13494.
       
   322         old_admins = settings.ADMINS
       
   323         old_managers = settings.MANAGERS
       
   324         settings.ADMINS = settings.MANAGERS = [('nobody','nobody@example.com')]
       
   325 
       
   326         mail_managers(ugettext_lazy('Subject'), 'Content')
       
   327         self.assertEqual(len(mail.outbox), 1)
       
   328         message = mail.outbox[0]
       
   329         self.assertEqual(message.subject, '[Django] Subject')
       
   330 
       
   331         mail.outbox = []
       
   332         mail_admins(ugettext_lazy('Subject'), 'Content')
       
   333         self.assertEqual(len(mail.outbox), 1)
       
   334         message = mail.outbox[0]
       
   335         self.assertEqual(message.subject, '[Django] Subject')
       
   336 
       
   337         settings.ADMINS = old_admins
       
   338         settings.MANAGERS = old_managers
       
   339 
       
   340     def test_idn_validation(self):
       
   341         """Test internationalized email adresses"""
       
   342         # Regression for #14301.
       
   343         mail.outbox = []
       
   344         from_email = u'fröm@öäü.com'
       
   345         to_email = u'tö@öäü.com'
       
   346         connection = mail.get_connection('django.core.mail.backends.locmem.EmailBackend')
       
   347         send_mail('Subject', 'Content', from_email, [to_email], connection=connection)
       
   348         self.assertEqual(len(mail.outbox), 1)
       
   349         message = mail.outbox[0]
       
   350         self.assertEqual(message.subject, 'Subject')
       
   351         self.assertEqual(message.from_email, from_email)
       
   352         self.assertEqual(message.to, [to_email])
       
   353         self.assertTrue(message.message().as_string().startswith('Content-Type: text/plain; charset="utf-8"\nMIME-Version: 1.0\nContent-Transfer-Encoding: quoted-printable\nSubject: Subject\nFrom: =?utf-8?b?ZnLDtm1Aw7bDpMO8LmNvbQ==?=\nTo: =?utf-8?b?dMO2QMO2w6TDvC5jb20=?='))
       
   354 
       
   355     def test_idn_smtp_send(self):
       
   356         import smtplib
       
   357         smtplib.SMTP = MockSMTP
       
   358         from_email = u'fröm@öäü.com'
       
   359         to_email = u'tö@öäü.com'
       
   360         connection = mail.get_connection('django.core.mail.backends.smtp.EmailBackend')
       
   361         self.assertTrue(send_mail('Subject', 'Content', from_email, [to_email], connection=connection))
       
   362 
       
   363 class MockSMTP(object):
       
   364     def __init__(self, host='', port=0, local_hostname=None,
       
   365                  timeout=1):
       
   366         pass
       
   367 
       
   368     def sendmail(self, from_addr, to_addrs, msg, mail_options=[],
       
   369                  rcpt_options=[]):
       
   370         for addr in to_addrs:
       
   371             str(addr.split('@', 1)[-1])
       
   372         return {}
       
   373 
       
   374     def quit(self):
       
   375         return 0