2008-07-19 10:38:41 +08:00
# coding: utf-8
2012-06-08 00:08:47 +08:00
from __future__ import unicode_literals
2011-01-15 13:55:24 +08:00
import asyncore
2010-10-11 23:11:55 +08:00
import email
import os
import shutil
2011-01-15 13:55:24 +08:00
import smtpd
2010-10-11 23:11:55 +08:00
import sys
2011-01-15 13:55:24 +08:00
import tempfile
import threading
2010-10-11 23:11:55 +08:00
from django . core import mail
2011-02-28 10:42:28 +08:00
from django . core . mail import ( EmailMessage , mail_admins , mail_managers ,
EmailMultiAlternatives , send_mail , send_mass_mail )
2010-10-11 23:11:55 +08:00
from django . core . mail . backends import console , dummy , locmem , filebased , smtp
from django . core . mail . message import BadHeaderError
from django . test import TestCase
2011-05-18 20:08:53 +08:00
from django . test . utils import override_settings
2012-08-09 18:12:22 +08:00
from django . utils . six import PY3 , StringIO
2010-10-11 23:11:55 +08:00
from django . utils . translation import ugettext_lazy
2011-01-15 13:55:24 +08:00
2010-10-11 23:11:55 +08:00
class MailTests ( TestCase ) :
2011-01-15 13:55:24 +08:00
"""
Non - backend specific tests .
"""
2010-10-11 23:11:55 +08:00
def test_ascii ( self ) :
email = EmailMessage ( ' Subject ' , ' Content ' , ' from@example.com ' , [ ' to@example.com ' ] )
message = email . message ( )
2012-08-09 18:12:22 +08:00
self . assertEqual ( message [ ' Subject ' ] , ' Subject ' )
2010-10-11 23:11:55 +08:00
self . assertEqual ( message . get_payload ( ) , ' Content ' )
self . assertEqual ( message [ ' From ' ] , ' from@example.com ' )
self . assertEqual ( message [ ' To ' ] , ' to@example.com ' )
def test_multiple_recipients ( self ) :
2011-01-15 13:55:24 +08:00
email = EmailMessage ( ' Subject ' , ' Content ' , ' from@example.com ' , [ ' to@example.com ' , ' other@example.com ' ] )
2010-10-11 23:11:55 +08:00
message = email . message ( )
2012-08-09 18:12:22 +08:00
self . assertEqual ( message [ ' Subject ' ] , ' Subject ' )
2010-10-11 23:11:55 +08:00
self . assertEqual ( message . get_payload ( ) , ' Content ' )
self . assertEqual ( message [ ' From ' ] , ' from@example.com ' )
self . assertEqual ( message [ ' To ' ] , ' to@example.com, other@example.com ' )
def test_cc ( self ) :
""" Regression test for #7722 """
email = EmailMessage ( ' Subject ' , ' Content ' , ' from@example.com ' , [ ' to@example.com ' ] , cc = [ ' cc@example.com ' ] )
message = email . message ( )
self . assertEqual ( message [ ' Cc ' ] , ' cc@example.com ' )
self . assertEqual ( email . recipients ( ) , [ ' to@example.com ' , ' cc@example.com ' ] )
# Test multiple CC with multiple To
email = EmailMessage ( ' Subject ' , ' Content ' , ' from@example.com ' , [ ' to@example.com ' , ' other@example.com ' ] , cc = [ ' cc@example.com ' , ' cc.other@example.com ' ] )
message = email . message ( )
self . assertEqual ( message [ ' Cc ' ] , ' cc@example.com, cc.other@example.com ' )
self . assertEqual ( email . recipients ( ) , [ ' to@example.com ' , ' other@example.com ' , ' cc@example.com ' , ' cc.other@example.com ' ] )
# Testing with Bcc
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 ' ] )
message = email . message ( )
self . assertEqual ( message [ ' Cc ' ] , ' cc@example.com, cc.other@example.com ' )
self . assertEqual ( email . recipients ( ) , [ ' to@example.com ' , ' other@example.com ' , ' cc@example.com ' , ' cc.other@example.com ' , ' bcc@example.com ' ] )
2011-04-30 22:00:15 +08:00
def test_recipients_as_tuple ( self ) :
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 ' , ) )
message = email . message ( )
self . assertEqual ( message [ ' Cc ' ] , ' cc@example.com, cc.other@example.com ' )
self . assertEqual ( email . recipients ( ) , [ ' to@example.com ' , ' other@example.com ' , ' cc@example.com ' , ' cc.other@example.com ' , ' bcc@example.com ' ] )
2010-10-11 23:11:55 +08:00
def test_header_injection ( self ) :
email = EmailMessage ( ' Subject \n Injection Test ' , ' Content ' , ' from@example.com ' , [ ' to@example.com ' ] )
self . assertRaises ( BadHeaderError , email . message )
email = EmailMessage ( ugettext_lazy ( ' Subject \n Injection Test ' ) , ' Content ' , ' from@example.com ' , [ ' to@example.com ' ] )
self . assertRaises ( BadHeaderError , email . message )
def test_space_continuation ( self ) :
"""
Test for space continuation character in long ( ascii ) subject headers ( #7747)
"""
2012-08-09 18:12:22 +08:00
email = EmailMessage ( ' Long subject lines that get wrapped should contain a space continuation character to get expected behavior in Outlook and Thunderbird ' , ' Content ' , ' from@example.com ' , [ ' to@example.com ' ] )
2010-10-11 23:11:55 +08:00
message = email . message ( )
2012-08-09 18:12:22 +08:00
# Note that in Python 3, maximum line length has increased from 76 to 78
self . assertEqual ( message [ ' Subject ' ] . encode ( ) , b ' Long subject lines that get wrapped should contain a space continuation \n character to get expected behavior in Outlook and Thunderbird ' )
2010-10-11 23:11:55 +08:00
def test_message_header_overrides ( self ) :
"""
Specifying dates or message - ids in the extra headers overrides the
default values ( #9233)
"""
headers = { " date " : " Fri, 09 Nov 2001 01:08:47 -0000 " , " Message-ID " : " foo " }
email = EmailMessage ( ' subject ' , ' content ' , ' from@example.com ' , [ ' to@example.com ' ] , headers = headers )
2012-08-09 18:12:22 +08:00
self . assertEqual ( email . message ( ) . as_string ( ) , ' Content-Type: text/plain; charset= " utf-8 " \n MIME-Version: 1.0 \n Content-Transfer-Encoding: 7bit \n Subject: subject \n From: from@example.com \n To: to@example.com \n date: Fri, 09 Nov 2001 01:08:47 -0000 \n Message-ID: foo \n \n content ' )
2010-10-11 23:11:55 +08:00
def test_from_header ( self ) :
"""
Make sure we can manually set the From header ( #9214)
"""
email = EmailMessage ( ' Subject ' , ' Content ' , ' bounce@example.com ' , [ ' to@example.com ' ] , headers = { ' From ' : ' from@example.com ' } )
message = email . message ( )
self . assertEqual ( message [ ' From ' ] , ' from@example.com ' )
2011-12-30 20:44:35 +08:00
def test_to_header ( self ) :
"""
Make sure we can manually set the To header ( #17444)
"""
email = EmailMessage ( ' Subject ' , ' Content ' , ' bounce@example.com ' ,
[ ' list-subscriber@example.com ' , ' list-subscriber2@example.com ' ] ,
headers = { ' To ' : ' mailing-list@example.com ' } )
message = email . message ( )
self . assertEqual ( message [ ' To ' ] , ' mailing-list@example.com ' )
self . assertEqual ( email . to , [ ' list-subscriber@example.com ' , ' list-subscriber2@example.com ' ] )
# If we don't set the To header manually, it should default to the `to` argument to the constructor
email = EmailMessage ( ' Subject ' , ' Content ' , ' bounce@example.com ' ,
[ ' list-subscriber@example.com ' , ' list-subscriber2@example.com ' ] )
message = email . message ( )
self . assertEqual ( message [ ' To ' ] , ' list-subscriber@example.com, list-subscriber2@example.com ' )
self . assertEqual ( email . to , [ ' list-subscriber@example.com ' , ' list-subscriber2@example.com ' ] )
2010-10-11 23:11:55 +08:00
def test_multiple_message_call ( self ) :
"""
Regression for #13259 - Make sure that headers are not changed when
calling EmailMessage . message ( )
"""
email = EmailMessage ( ' Subject ' , ' Content ' , ' bounce@example.com ' , [ ' to@example.com ' ] , headers = { ' From ' : ' from@example.com ' } )
message = email . message ( )
self . assertEqual ( message [ ' From ' ] , ' from@example.com ' )
message = email . message ( )
self . assertEqual ( message [ ' From ' ] , ' from@example.com ' )
2011-01-15 13:55:24 +08:00
def test_unicode_address_header ( self ) :
2010-10-11 23:11:55 +08:00
"""
Regression for #11144 - When a to/from/cc header contains unicode,
make sure the email addresses are parsed correctly ( especially with
regards to commas )
"""
2011-01-15 13:55:24 +08:00
email = EmailMessage ( ' Subject ' , ' Content ' , ' from@example.com ' , [ ' " Firstname Sürname " <to@example.com> ' , ' other@example.com ' ] )
2010-10-11 23:11:55 +08:00
self . assertEqual ( email . message ( ) [ ' To ' ] , ' =?utf-8?q?Firstname_S=C3=BCrname?= <to@example.com>, other@example.com ' )
2011-01-15 13:55:24 +08:00
email = EmailMessage ( ' Subject ' , ' Content ' , ' from@example.com ' , [ ' " Sürname, Firstname " <to@example.com> ' , ' other@example.com ' ] )
2010-10-11 23:11:55 +08:00
self . assertEqual ( email . message ( ) [ ' To ' ] , ' =?utf-8?q?S=C3=BCrname=2C_Firstname?= <to@example.com>, other@example.com ' )
2011-01-15 13:55:24 +08:00
def test_unicode_headers ( self ) :
2012-06-08 00:08:47 +08:00
email = EmailMessage ( " Gżegżółka " , " Content " , " from@example.com " , [ " to@example.com " ] ,
2011-01-15 13:55:24 +08:00
headers = { " Sender " : ' " Firstname Sürname " <sender@example.com> ' ,
" Comments " : ' My Sürname is non-ASCII ' } )
message = email . message ( )
self . assertEqual ( message [ ' Subject ' ] , ' =?utf-8?b?R8W8ZWfFvMOzxYJrYQ==?= ' )
self . assertEqual ( message [ ' Sender ' ] , ' =?utf-8?q?Firstname_S=C3=BCrname?= <sender@example.com> ' )
self . assertEqual ( message [ ' Comments ' ] , ' =?utf-8?q?My_S=C3=BCrname_is_non-ASCII?= ' )
2010-10-11 23:11:55 +08:00
def test_safe_mime_multipart ( self ) :
"""
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 '
self . assertEqual ( msg . message ( ) [ ' To ' ] , ' =?iso-8859-1?q?S=FCrname=2C_Firstname?= <to@example.com> ' )
2012-08-09 18:12:22 +08:00
self . assertEqual ( msg . message ( ) [ ' Subject ' ] , ' =?iso-8859-1?q?Message_from_Firstname_S=FCrname?= ' )
2010-10-11 23:11:55 +08:00
def test_encoding ( self ) :
"""
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 ( )
2012-08-09 18:12:22 +08:00
self . assertTrue ( message . as_string ( ) . startswith ( ' Content-Type: text/plain; charset= " iso-8859-1 " \n MIME-Version: 1.0 \n Content-Transfer-Encoding: quoted-printable \n Subject: Subject \n From: from@example.com \n To: other@example.com ' ) )
2010-10-11 23:11:55 +08:00
self . assertEqual ( message . get_payload ( ) , ' Firstname 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 " )
2012-08-09 18:12:22 +08:00
self . assertEqual ( msg . message ( ) . get_payload ( 0 ) . as_string ( ) , ' Content-Type: text/plain; charset= " iso-8859-1 " \n MIME-Version: 1.0 \n Content-Transfer-Encoding: quoted-printable \n \n Firstname S=FCrname is a great guy. ' )
self . assertEqual ( msg . message ( ) . get_payload ( 1 ) . as_string ( ) , ' Content-Type: text/html; charset= " iso-8859-1 " \n MIME-Version: 1.0 \n Content-Transfer-Encoding: quoted-printable \n \n <p>Firstname S=FCrname is a <strong>great</strong> guy.</p> ' )
2010-10-11 23:11:55 +08:00
def test_attachments ( self ) :
""" Regression test for #9367 """
headers = { " Date " : " Fri, 09 Nov 2001 01:08:47 -0000 " , " Message-ID " : " foo " }
subject , from_email , to = ' hello ' , ' from@example.com ' , ' to@example.com '
text_content = ' This is an important message. '
html_content = ' <p>This is an <strong>important</strong> message.</p> '
msg = EmailMultiAlternatives ( subject , text_content , from_email , [ to ] , headers = headers )
msg . attach_alternative ( html_content , " text/html " )
2012-05-19 23:43:34 +08:00
msg . attach ( " an attachment.pdf " , b " % PDF-1.4. % ... " , mimetype = " application/pdf " )
2010-10-11 23:11:55 +08:00
msg_str = msg . message ( ) . as_string ( )
message = email . message_from_string ( msg_str )
self . assertTrue ( message . is_multipart ( ) )
self . assertEqual ( message . get_content_type ( ) , ' multipart/mixed ' )
self . assertEqual ( message . get_default_type ( ) , ' text/plain ' )
payload = message . get_payload ( )
self . assertEqual ( payload [ 0 ] . get_content_type ( ) , ' multipart/alternative ' )
self . assertEqual ( payload [ 1 ] . get_content_type ( ) , ' application/pdf ' )
2012-01-15 10:33:31 +08:00
def test_non_ascii_attachment_filename ( self ) :
""" Regression test for #14964 """
headers = { " Date " : " Fri, 09 Nov 2001 01:08:47 -0000 " , " Message-ID " : " foo " }
subject , from_email , to = ' hello ' , ' from@example.com ' , ' to@example.com '
content = ' This is the message. '
msg = EmailMessage ( subject , content , from_email , [ to ] , headers = headers )
# Unicode in file name
2012-06-08 00:08:47 +08:00
msg . attach ( " une pièce jointe.pdf " , b " % PDF-1.4. % ... " , mimetype = " application/pdf " )
2012-01-15 10:33:31 +08:00
msg_str = msg . message ( ) . as_string ( )
message = email . message_from_string ( msg_str )
payload = message . get_payload ( )
2012-06-08 00:08:47 +08:00
self . assertEqual ( payload [ 1 ] . get_filename ( ) , ' une pièce jointe.pdf ' )
2012-01-15 10:33:31 +08:00
2011-01-15 13:55:24 +08:00
def test_dummy_backend ( self ) :
2010-10-11 23:11:55 +08:00
"""
Make sure that dummy backends returns correct number of sent messages
"""
connection = dummy . EmailBackend ( )
email = EmailMessage ( ' Subject ' , ' Content ' , ' bounce@example.com ' , [ ' to@example.com ' ] , headers = { ' From ' : ' from@example.com ' } )
self . assertEqual ( connection . send_messages ( [ email , email , email ] ) , 3 )
def test_arbitrary_keyword ( self ) :
"""
Make sure that get_connection ( ) accepts arbitrary keyword that might be
used with custom backends .
"""
c = mail . get_connection ( fail_silently = True , foo = ' bar ' )
self . assertTrue ( c . fail_silently )
def test_custom_backend ( self ) :
""" Test custom backend defined in this suite. """
conn = mail . get_connection ( ' regressiontests.mail.custombackend.EmailBackend ' )
self . assertTrue ( hasattr ( conn , ' test_outbox ' ) )
email = EmailMessage ( ' Subject ' , ' Content ' , ' bounce@example.com ' , [ ' to@example.com ' ] , headers = { ' From ' : ' from@example.com ' } )
conn . send_messages ( [ email ] )
self . assertEqual ( len ( conn . test_outbox ) , 1 )
def test_backend_arg ( self ) :
""" Test backend argument of mail.get_connection() """
self . assertTrue ( isinstance ( mail . get_connection ( ' django.core.mail.backends.smtp.EmailBackend ' ) , smtp . EmailBackend ) )
self . assertTrue ( isinstance ( mail . get_connection ( ' django.core.mail.backends.locmem.EmailBackend ' ) , locmem . EmailBackend ) )
self . assertTrue ( isinstance ( mail . get_connection ( ' django.core.mail.backends.dummy.EmailBackend ' ) , dummy . EmailBackend ) )
self . assertTrue ( isinstance ( mail . get_connection ( ' django.core.mail.backends.console.EmailBackend ' ) , console . EmailBackend ) )
tmp_dir = tempfile . mkdtemp ( )
2011-01-15 13:55:24 +08:00
try :
self . assertTrue ( isinstance ( mail . get_connection ( ' django.core.mail.backends.filebased.EmailBackend ' , file_path = tmp_dir ) , filebased . EmailBackend ) )
finally :
shutil . rmtree ( tmp_dir )
2010-10-11 23:11:55 +08:00
self . assertTrue ( isinstance ( mail . get_connection ( ) , locmem . EmailBackend ) )
2011-05-18 20:08:53 +08:00
@override_settings (
2011-01-15 13:55:24 +08:00
EMAIL_BACKEND = ' django.core.mail.backends.locmem.EmailBackend ' ,
ADMINS = [ ( ' nobody ' , ' nobody@example.com ' ) ] ,
MANAGERS = [ ( ' nobody ' , ' nobody@example.com ' ) ] )
2010-10-11 23:11:55 +08:00
def test_connection_arg ( self ) :
""" Test connection argument to send_mail(), et. al. """
mail . outbox = [ ]
2011-01-15 13:55:24 +08:00
# Send using non-default connection
connection = mail . get_connection ( ' regressiontests.mail.custombackend.EmailBackend ' )
2010-10-11 23:11:55 +08:00
send_mail ( ' Subject ' , ' Content ' , ' from@example.com ' , [ ' to@example.com ' ] , connection = connection )
2011-01-15 13:55:24 +08:00
self . assertEqual ( mail . outbox , [ ] )
self . assertEqual ( len ( connection . test_outbox ) , 1 )
self . assertEqual ( connection . test_outbox [ 0 ] . subject , ' Subject ' )
2010-10-11 23:11:55 +08:00
2011-01-15 13:55:24 +08:00
connection = mail . get_connection ( ' regressiontests.mail.custombackend.EmailBackend ' )
2010-10-11 23:11:55 +08:00
send_mass_mail ( [
( ' Subject1 ' , ' Content1 ' , ' from1@example.com ' , [ ' to1@example.com ' ] ) ,
2011-01-15 13:55:24 +08:00
( ' Subject2 ' , ' Content2 ' , ' from2@example.com ' , [ ' to2@example.com ' ] ) ,
2010-10-11 23:11:55 +08:00
] , connection = connection )
2011-01-15 13:55:24 +08:00
self . assertEqual ( mail . outbox , [ ] )
self . assertEqual ( len ( connection . test_outbox ) , 2 )
self . assertEqual ( connection . test_outbox [ 0 ] . subject , ' Subject1 ' )
self . assertEqual ( connection . test_outbox [ 1 ] . subject , ' Subject2 ' )
2010-10-11 23:11:55 +08:00
2011-01-15 13:55:24 +08:00
connection = mail . get_connection ( ' regressiontests.mail.custombackend.EmailBackend ' )
mail_admins ( ' Admin message ' , ' Content ' , connection = connection )
self . assertEqual ( mail . outbox , [ ] )
self . assertEqual ( len ( connection . test_outbox ) , 1 )
self . assertEqual ( connection . test_outbox [ 0 ] . subject , ' [Django] Admin message ' )
2010-10-11 23:11:55 +08:00
2011-01-15 13:55:24 +08:00
connection = mail . get_connection ( ' regressiontests.mail.custombackend.EmailBackend ' )
mail_managers ( ' Manager message ' , ' Content ' , connection = connection )
self . assertEqual ( mail . outbox , [ ] )
self . assertEqual ( len ( connection . test_outbox ) , 1 )
self . assertEqual ( connection . test_outbox [ 0 ] . subject , ' [Django] Manager message ' )
2011-02-28 10:42:28 +08:00
def test_dont_mangle_from_in_body ( self ) :
# Regression for #13433 - Make sure that EmailMessage doesn't mangle
# 'From ' in message body.
email = EmailMessage ( ' Subject ' , ' From the future ' , ' bounce@example.com ' , [ ' to@example.com ' ] , headers = { ' From ' : ' from@example.com ' } )
2012-08-09 18:12:22 +08:00
self . assertFalse ( ' >From the future ' in email . message ( ) . as_string ( ) )
2011-02-28 10:42:28 +08:00
2011-05-08 00:59:33 +08:00
def test_dont_base64_encode ( self ) :
# Ticket #3472
# Shouldn't use Base64 encoding at all
msg = EmailMessage ( ' Subject ' , ' UTF-8 encoded body ' , ' bounce@example.com ' , [ ' to@example.com ' ] , headers = { ' From ' : ' from@example.com ' } )
2012-08-09 18:12:22 +08:00
self . assertFalse ( ' Content-Transfer-Encoding: base64 ' in msg . message ( ) . as_string ( ) )
2011-05-08 00:59:33 +08:00
# Ticket #11212
# Shouldn't use quoted printable, should detect it can represent content with 7 bit data
msg = EmailMessage ( ' Subject ' , ' Body with only ASCII characters. ' , ' bounce@example.com ' , [ ' to@example.com ' ] , headers = { ' From ' : ' from@example.com ' } )
s = msg . message ( ) . as_string ( )
2012-08-09 18:12:22 +08:00
self . assertFalse ( ' Content-Transfer-Encoding: quoted-printable ' in s )
self . assertTrue ( ' Content-Transfer-Encoding: 7bit ' in s )
2011-05-08 00:59:33 +08:00
# Shouldn't use quoted printable, should detect it can represent content with 8 bit data
msg = EmailMessage ( ' Subject ' , ' Body with latin characters: àáä. ' , ' bounce@example.com ' , [ ' to@example.com ' ] , headers = { ' From ' : ' from@example.com ' } )
s = msg . message ( ) . as_string ( )
2012-08-09 18:12:22 +08:00
self . assertFalse ( str ( ' Content-Transfer-Encoding: quoted-printable ' ) in s )
self . assertTrue ( str ( ' Content-Transfer-Encoding: 8bit ' ) in s )
2011-05-08 00:59:33 +08:00
2012-06-08 00:08:47 +08:00
msg = EmailMessage ( ' Subject ' , ' Body with non latin characters: А Б В Г Д Е Ж Ѕ З И І К Л М Н О П. ' , ' bounce@example.com ' , [ ' to@example.com ' ] , headers = { ' From ' : ' from@example.com ' } )
2011-05-08 00:59:33 +08:00
s = msg . message ( ) . as_string ( )
2012-08-09 18:12:22 +08:00
self . assertFalse ( str ( ' Content-Transfer-Encoding: quoted-printable ' ) in s )
self . assertTrue ( str ( ' Content-Transfer-Encoding: 8bit ' ) in s )
2011-05-08 00:59:33 +08:00
2011-01-15 13:55:24 +08:00
class BaseEmailBackendTests ( object ) :
email_backend = None
def setUp ( self ) :
2011-05-18 20:08:53 +08:00
self . settings_override = override_settings ( EMAIL_BACKEND = self . email_backend )
self . settings_override . enable ( )
2011-01-15 13:55:24 +08:00
def tearDown ( self ) :
2011-05-18 20:08:53 +08:00
self . settings_override . disable ( )
2010-10-12 06:27:45 +08:00
2011-01-15 13:55:24 +08:00
def assertStartsWith ( self , first , second ) :
if not first . startswith ( second ) :
self . longMessage = True
self . assertEqual ( first [ : len ( second ) ] , second , " First string doesn ' t start with the second. " )
def get_mailbox_content ( self ) :
raise NotImplementedError
def flush_mailbox ( self ) :
raise NotImplementedError
def get_the_message ( self ) :
mailbox = self . get_mailbox_content ( )
self . assertEqual ( len ( mailbox ) , 1 ,
" Expected exactly one message, got %d . \n %r " % ( len ( mailbox ) , [
m . as_string ( ) for m in mailbox ] ) )
return mailbox [ 0 ]
def test_send ( self ) :
email = EmailMessage ( ' Subject ' , ' Content ' , ' from@example.com ' , [ ' to@example.com ' ] )
num_sent = mail . get_connection ( ) . send_messages ( [ email ] )
self . assertEqual ( num_sent , 1 )
message = self . get_the_message ( )
self . assertEqual ( message [ " subject " ] , " Subject " )
self . assertEqual ( message . get_payload ( ) , " Content " )
self . assertEqual ( message [ " from " ] , " from@example.com " )
self . assertEqual ( message . get_all ( " to " ) , [ " to@example.com " ] )
def test_send_many ( self ) :
email1 = EmailMessage ( ' Subject ' , ' Content1 ' , ' from@example.com ' , [ ' to@example.com ' ] )
email2 = EmailMessage ( ' Subject ' , ' Content2 ' , ' from@example.com ' , [ ' to@example.com ' ] )
num_sent = mail . get_connection ( ) . send_messages ( [ email1 , email2 ] )
self . assertEqual ( num_sent , 2 )
messages = self . get_mailbox_content ( )
2011-03-03 23:04:39 +08:00
self . assertEqual ( len ( messages ) , 2 )
2011-01-15 13:55:24 +08:00
self . assertEqual ( messages [ 0 ] . get_payload ( ) , " Content1 " )
self . assertEqual ( messages [ 1 ] . get_payload ( ) , " Content2 " )
def test_send_verbose_name ( self ) :
email = EmailMessage ( " Subject " , " Content " , ' " Firstname Sürname " <from@example.com> ' ,
[ " to@example.com " ] )
email . send ( )
message = self . get_the_message ( )
self . assertEqual ( message [ " subject " ] , " Subject " )
self . assertEqual ( message . get_payload ( ) , " Content " )
self . assertEqual ( message [ " from " ] , " =?utf-8?q?Firstname_S=C3=BCrname?= <from@example.com> " )
2011-05-18 20:08:53 +08:00
@override_settings ( MANAGERS = [ ( ' nobody ' , ' nobody@example.com ' ) ] )
2011-01-15 13:55:24 +08:00
def test_html_mail_managers ( self ) :
""" Test html_message argument to mail_managers """
mail_managers ( ' Subject ' , ' Content ' , html_message = ' HTML Content ' )
message = self . get_the_message ( )
self . assertEqual ( message . get ( ' subject ' ) , ' [Django] Subject ' )
self . assertEqual ( message . get_all ( ' to ' ) , [ ' nobody@example.com ' ] )
self . assertTrue ( message . is_multipart ( ) )
self . assertEqual ( len ( message . get_payload ( ) ) , 2 )
self . assertEqual ( message . get_payload ( 0 ) . get_payload ( ) , ' Content ' )
self . assertEqual ( message . get_payload ( 0 ) . get_content_type ( ) , ' text/plain ' )
self . assertEqual ( message . get_payload ( 1 ) . get_payload ( ) , ' HTML Content ' )
self . assertEqual ( message . get_payload ( 1 ) . get_content_type ( ) , ' text/html ' )
2011-05-18 20:08:53 +08:00
@override_settings ( ADMINS = [ ( ' nobody ' , ' nobody@example.com ' ) ] )
2011-01-15 13:55:24 +08:00
def test_html_mail_admins ( self ) :
""" Test html_message argument to mail_admins """
mail_admins ( ' Subject ' , ' Content ' , html_message = ' HTML Content ' )
message = self . get_the_message ( )
self . assertEqual ( message . get ( ' subject ' ) , ' [Django] Subject ' )
self . assertEqual ( message . get_all ( ' to ' ) , [ ' nobody@example.com ' ] )
self . assertTrue ( message . is_multipart ( ) )
self . assertEqual ( len ( message . get_payload ( ) ) , 2 )
self . assertEqual ( message . get_payload ( 0 ) . get_payload ( ) , ' Content ' )
self . assertEqual ( message . get_payload ( 0 ) . get_content_type ( ) , ' text/plain ' )
self . assertEqual ( message . get_payload ( 1 ) . get_payload ( ) , ' HTML Content ' )
self . assertEqual ( message . get_payload ( 1 ) . get_content_type ( ) , ' text/html ' )
2011-05-18 20:08:53 +08:00
@override_settings (
ADMINS = [ ( ' nobody ' , ' nobody+admin@example.com ' ) ] ,
MANAGERS = [ ( ' nobody ' , ' nobody+manager@example.com ' ) ] )
2011-01-15 13:55:24 +08:00
def test_manager_and_admin_mail_prefix ( self ) :
"""
String prefix + lazy translated subject = bad output
Regression for #13494
"""
2010-10-12 06:27:45 +08:00
mail_managers ( ugettext_lazy ( ' Subject ' ) , ' Content ' )
2011-01-15 13:55:24 +08:00
message = self . get_the_message ( )
self . assertEqual ( message . get ( ' subject ' ) , ' [Django] Subject ' )
2010-10-12 06:27:45 +08:00
2011-01-15 13:55:24 +08:00
self . flush_mailbox ( )
2010-10-12 06:27:45 +08:00
mail_admins ( ugettext_lazy ( ' Subject ' ) , ' Content ' )
2011-01-15 13:55:24 +08:00
message = self . get_the_message ( )
self . assertEqual ( message . get ( ' subject ' ) , ' [Django] Subject ' )
2010-10-12 06:27:45 +08:00
2011-05-18 20:08:53 +08:00
@override_settings ( ADMINS = ( ) , MANAGERS = ( ) )
2011-01-15 13:55:24 +08:00
def test_empty_admins ( self ) :
"""
Test that mail_admins / mail_managers doesn ' t connect to the mail server
if there are no recipients ( #9383)
"""
mail_admins ( ' hi ' , ' there ' )
self . assertEqual ( self . get_mailbox_content ( ) , [ ] )
mail_managers ( ' hi ' , ' there ' )
self . assertEqual ( self . get_mailbox_content ( ) , [ ] )
2010-10-15 02:37:05 +08:00
2011-01-15 13:55:24 +08:00
def test_message_cc_header ( self ) :
"""
Regression test for #7722
"""
email = EmailMessage ( ' Subject ' , ' Content ' , ' from@example.com ' , [ ' to@example.com ' ] , cc = [ ' cc@example.com ' ] )
mail . get_connection ( ) . send_messages ( [ email ] )
message = self . get_the_message ( )
2012-08-09 18:12:22 +08:00
self . assertStartsWith ( message . as_string ( ) , ' Content-Type: text/plain; charset= " utf-8 " \n MIME-Version: 1.0 \n Content-Transfer-Encoding: 7bit \n Subject: Subject \n From: from@example.com \n To: to@example.com \n Cc: cc@example.com \n Date: ' )
2010-12-06 22:21:51 +08:00
2011-01-15 13:55:24 +08:00
def test_idn_send ( self ) :
"""
Regression test for #14301
"""
2012-06-08 00:08:47 +08:00
self . assertTrue ( send_mail ( ' Subject ' , ' Content ' , ' from@öäü.com ' , [ ' to@öäü.com ' ] ) )
2011-01-15 13:55:24 +08:00
message = self . get_the_message ( )
self . assertEqual ( message . get ( ' subject ' ) , ' Subject ' )
self . assertEqual ( message . get ( ' from ' ) , ' from@xn--4ca9at.com ' )
self . assertEqual ( message . get ( ' to ' ) , ' to@xn--4ca9at.com ' )
self . flush_mailbox ( )
m = EmailMessage ( ' Subject ' , ' Content ' , ' from@öäü.com ' ,
2012-06-08 00:08:47 +08:00
[ ' to@öäü.com ' ] , cc = [ ' cc@öäü.com ' ] )
2011-01-15 13:55:24 +08:00
m . send ( )
message = self . get_the_message ( )
self . assertEqual ( message . get ( ' subject ' ) , ' Subject ' )
self . assertEqual ( message . get ( ' from ' ) , ' from@xn--4ca9at.com ' )
self . assertEqual ( message . get ( ' to ' ) , ' to@xn--4ca9at.com ' )
self . assertEqual ( message . get ( ' cc ' ) , ' cc@xn--4ca9at.com ' )
def test_recipient_without_domain ( self ) :
"""
Regression test for #15042
"""
self . assertTrue ( send_mail ( " Subject " , " Content " , " tester " , [ " django " ] ) )
message = self . get_the_message ( )
self . assertEqual ( message . get ( ' subject ' ) , ' Subject ' )
self . assertEqual ( message . get ( ' from ' ) , " tester " )
self . assertEqual ( message . get ( ' to ' ) , " django " )
2010-12-06 22:21:51 +08:00
2011-01-15 13:55:24 +08:00
class LocmemBackendTests ( BaseEmailBackendTests , TestCase ) :
email_backend = ' django.core.mail.backends.locmem.EmailBackend '
2010-12-06 22:21:51 +08:00
2011-01-15 13:55:24 +08:00
def get_mailbox_content ( self ) :
return [ m . message ( ) for m in mail . outbox ]
2010-12-06 22:21:51 +08:00
2011-01-15 13:55:24 +08:00
def flush_mailbox ( self ) :
mail . outbox = [ ]
2010-12-06 22:21:51 +08:00
2011-01-15 13:55:24 +08:00
def tearDown ( self ) :
super ( LocmemBackendTests , self ) . tearDown ( )
2010-10-15 02:37:05 +08:00
mail . outbox = [ ]
2011-01-15 13:55:24 +08:00
def test_locmem_shared_messages ( self ) :
"""
Make sure that the locmen backend populates the outbox .
"""
connection = locmem . EmailBackend ( )
connection2 = locmem . EmailBackend ( )
email = EmailMessage ( ' Subject ' , ' Content ' , ' bounce@example.com ' , [ ' to@example.com ' ] , headers = { ' From ' : ' from@example.com ' } )
connection . send_messages ( [ email ] )
connection2 . send_messages ( [ email ] )
self . assertEqual ( len ( mail . outbox ) , 2 )
2012-09-22 21:17:13 +08:00
def test_validate_multiline_headers ( self ) :
# Ticket #18861 - Validate emails when using the locmem backend
with self . assertRaises ( BadHeaderError ) :
send_mail ( ' Subject \n Multiline ' , ' Content ' , ' from@example.com ' , [ ' to@example.com ' ] )
2011-01-15 13:55:24 +08:00
class FileBackendTests ( BaseEmailBackendTests , TestCase ) :
email_backend = ' django.core.mail.backends.filebased.EmailBackend '
def setUp ( self ) :
2012-06-03 01:44:06 +08:00
super ( FileBackendTests , self ) . setUp ( )
2011-01-15 13:55:24 +08:00
self . tmp_dir = tempfile . mkdtemp ( )
2011-05-18 20:08:53 +08:00
self . addCleanup ( shutil . rmtree , self . tmp_dir )
2012-06-03 01:44:06 +08:00
self . _settings_override = override_settings ( EMAIL_FILE_PATH = self . tmp_dir )
self . _settings_override . enable ( )
2011-01-15 13:55:24 +08:00
def tearDown ( self ) :
2012-06-03 01:44:06 +08:00
self . _settings_override . disable ( )
2011-01-15 13:55:24 +08:00
super ( FileBackendTests , self ) . tearDown ( )
def flush_mailbox ( self ) :
for filename in os . listdir ( self . tmp_dir ) :
os . unlink ( os . path . join ( self . tmp_dir , filename ) )
def get_mailbox_content ( self ) :
messages = [ ]
for filename in os . listdir ( self . tmp_dir ) :
2012-08-09 18:12:22 +08:00
with open ( os . path . join ( self . tmp_dir , filename ) , ' r ' ) as fp :
session = fp . read ( ) . split ( ' \n ' + ( ' - ' * 79 ) + ' \n ' )
messages . extend ( email . message_from_string ( str ( m ) ) for m in session if m )
2011-01-15 13:55:24 +08:00
return messages
def test_file_sessions ( self ) :
""" Make sure opening a connection creates a new file """
msg = EmailMessage ( ' Subject ' , ' Content ' , ' bounce@example.com ' , [ ' to@example.com ' ] , headers = { ' From ' : ' from@example.com ' } )
connection = mail . get_connection ( )
connection . send_messages ( [ msg ] )
self . assertEqual ( len ( os . listdir ( self . tmp_dir ) ) , 1 )
2012-05-05 20:01:38 +08:00
with open ( os . path . join ( self . tmp_dir , os . listdir ( self . tmp_dir ) [ 0 ] ) ) as fp :
message = email . message_from_file ( fp )
2011-01-15 13:55:24 +08:00
self . assertEqual ( message . get_content_type ( ) , ' text/plain ' )
self . assertEqual ( message . get ( ' subject ' ) , ' Subject ' )
self . assertEqual ( message . get ( ' from ' ) , ' from@example.com ' )
self . assertEqual ( message . get ( ' to ' ) , ' to@example.com ' )
connection2 = mail . get_connection ( )
connection2 . send_messages ( [ msg ] )
self . assertEqual ( len ( os . listdir ( self . tmp_dir ) ) , 2 )
connection . send_messages ( [ msg ] )
self . assertEqual ( len ( os . listdir ( self . tmp_dir ) ) , 2 )
msg . connection = mail . get_connection ( )
self . assertTrue ( connection . open ( ) )
msg . send ( )
self . assertEqual ( len ( os . listdir ( self . tmp_dir ) ) , 3 )
msg . send ( )
self . assertEqual ( len ( os . listdir ( self . tmp_dir ) ) , 3 )
2012-08-15 17:16:28 +08:00
connection . close ( )
2011-01-15 13:55:24 +08:00
class ConsoleBackendTests ( BaseEmailBackendTests , TestCase ) :
email_backend = ' django.core.mail.backends.console.EmailBackend '
def setUp ( self ) :
super ( ConsoleBackendTests , self ) . setUp ( )
self . __stdout = sys . stdout
self . stream = sys . stdout = StringIO ( )
def tearDown ( self ) :
del self . stream
sys . stdout = self . __stdout
del self . __stdout
super ( ConsoleBackendTests , self ) . tearDown ( )
def flush_mailbox ( self ) :
self . stream = sys . stdout = StringIO ( )
def get_mailbox_content ( self ) :
2012-08-09 18:12:22 +08:00
messages = self . stream . getvalue ( ) . split ( ' \n ' + ( ' - ' * 79 ) + ' \n ' )
return [ email . message_from_string ( str ( m ) ) for m in messages if m ]
2011-01-15 13:55:24 +08:00
def test_console_stream_kwarg ( self ) :
"""
Test that the console backend can be pointed at an arbitrary stream .
"""
s = StringIO ( )
connection = mail . get_connection ( ' django.core.mail.backends.console.EmailBackend ' , stream = s )
send_mail ( ' Subject ' , ' Content ' , ' from@example.com ' , [ ' to@example.com ' ] , connection = connection )
2011-05-08 00:59:33 +08:00
self . assertTrue ( s . getvalue ( ) . startswith ( ' Content-Type: text/plain; charset= " utf-8 " \n MIME-Version: 1.0 \n Content-Transfer-Encoding: 7bit \n Subject: Subject \n From: from@example.com \n To: to@example.com \n Date: ' ) )
2011-01-15 13:55:24 +08:00
class FakeSMTPServer ( smtpd . SMTPServer , threading . Thread ) :
"""
Asyncore SMTP server wrapped into a thread . Based on DummyFTPServer from :
http : / / svn . python . org / view / python / branches / py3k / Lib / test / test_ftplib . py ? revision = 86061 & view = markup
"""
def __init__ ( self , * args , * * kwargs ) :
threading . Thread . __init__ ( self )
smtpd . SMTPServer . __init__ ( self , * args , * * kwargs )
self . _sink = [ ]
self . active = False
self . active_lock = threading . Lock ( )
self . sink_lock = threading . Lock ( )
def process_message ( self , peer , mailfrom , rcpttos , data ) :
m = email . message_from_string ( data )
2012-08-09 18:12:22 +08:00
if PY3 :
maddr = email . utils . parseaddr ( m . get ( ' from ' ) ) [ 1 ]
else :
maddr = email . Utils . parseaddr ( m . get ( ' from ' ) ) [ 1 ]
2011-01-15 13:55:24 +08:00
if mailfrom != maddr :
return " 553 ' %s ' != ' %s ' " % ( mailfrom , maddr )
2012-06-23 23:11:15 +08:00
with self . sink_lock :
self . _sink . append ( m )
2011-01-15 13:55:24 +08:00
def get_sink ( self ) :
2012-06-23 23:11:15 +08:00
with self . sink_lock :
2011-01-15 13:55:24 +08:00
return self . _sink [ : ]
def flush_sink ( self ) :
2012-06-23 23:11:15 +08:00
with self . sink_lock :
self . _sink [ : ] = [ ]
2011-01-15 13:55:24 +08:00
def start ( self ) :
assert not self . active
self . __flag = threading . Event ( )
threading . Thread . start ( self )
self . __flag . wait ( )
def run ( self ) :
self . active = True
self . __flag . set ( )
while self . active and asyncore . socket_map :
2012-06-23 23:11:15 +08:00
with self . active_lock :
asyncore . loop ( timeout = 0.1 , count = 1 )
2011-01-15 13:55:24 +08:00
asyncore . close_all ( )
def stop ( self ) :
assert self . active
self . active = False
self . join ( )
class SMTPBackendTests ( BaseEmailBackendTests , TestCase ) :
email_backend = ' django.core.mail.backends.smtp.EmailBackend '
@classmethod
def setUpClass ( cls ) :
cls . server = FakeSMTPServer ( ( ' 127.0.0.1 ' , 0 ) , None )
2012-06-03 01:44:06 +08:00
cls . _settings_override = override_settings (
2011-01-15 13:55:24 +08:00
EMAIL_HOST = " 127.0.0.1 " ,
EMAIL_PORT = cls . server . socket . getsockname ( ) [ 1 ] )
2012-06-03 01:44:06 +08:00
cls . _settings_override . enable ( )
2011-01-15 13:55:24 +08:00
cls . server . start ( )
@classmethod
def tearDownClass ( cls ) :
2012-06-03 01:44:06 +08:00
cls . _settings_override . disable ( )
2011-01-15 13:55:24 +08:00
cls . server . stop ( )
def setUp ( self ) :
super ( SMTPBackendTests , self ) . setUp ( )
self . server . flush_sink ( )
def tearDown ( self ) :
self . server . flush_sink ( )
super ( SMTPBackendTests , self ) . tearDown ( )
def flush_mailbox ( self ) :
self . server . flush_sink ( )
def get_mailbox_content ( self ) :
return self . server . get_sink ( )
2011-07-04 01:56:05 +08:00
@override_settings ( EMAIL_HOST_USER = " not empty username " ,
EMAIL_HOST_PASSWORD = " not empty password " )
def test_email_authentication_use_settings ( self ) :
backend = smtp . EmailBackend ( )
self . assertEqual ( backend . username , ' not empty username ' )
self . assertEqual ( backend . password , ' not empty password ' )
@override_settings ( EMAIL_HOST_USER = " not empty username " ,
EMAIL_HOST_PASSWORD = " not empty password " )
def test_email_authentication_override_settings ( self ) :
backend = smtp . EmailBackend ( username = ' username ' , password = ' password ' )
self . assertEqual ( backend . username , ' username ' )
self . assertEqual ( backend . password , ' password ' )
@override_settings ( EMAIL_HOST_USER = " not empty username " ,
EMAIL_HOST_PASSWORD = " not empty password " )
def test_email_disabled_authentication ( self ) :
backend = smtp . EmailBackend ( username = ' ' , password = ' ' )
self . assertEqual ( backend . username , ' ' )
self . assertEqual ( backend . password , ' ' )