mirror of
https://github.com/django/django.git
synced 2025-07-04 17:59:13 +00:00
boulder-oracle-sprint: Merged to [5147]
git-svn-id: http://code.djangoproject.com/svn/django/branches/boulder-oracle-sprint@5148 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
750d2e8c3c
commit
32683de27c
@ -119,6 +119,7 @@ EMAIL_PORT = 25
|
||||
# Optional SMTP authentication information for EMAIL_HOST.
|
||||
EMAIL_HOST_USER = ''
|
||||
EMAIL_HOST_PASSWORD = ''
|
||||
EMAIL_USE_TLS = False
|
||||
|
||||
# List of strings representing installed apps.
|
||||
INSTALLED_APPS = ()
|
||||
|
@ -1,14 +1,22 @@
|
||||
# Use this module for e-mailing.
|
||||
"""
|
||||
Tools for sending email.
|
||||
"""
|
||||
|
||||
from django.conf import settings
|
||||
from email.MIMEText import MIMEText
|
||||
from email.Header import Header
|
||||
from email.Utils import formatdate
|
||||
from email import Charset
|
||||
import os
|
||||
import smtplib
|
||||
import socket
|
||||
import time
|
||||
import random
|
||||
|
||||
# Don't BASE64-encode UTF-8 messages so that we avoid unwanted attention from
|
||||
# some spam filters.
|
||||
Charset.add_charset('utf-8', Charset.SHORTEST, Charset.QP, 'utf-8')
|
||||
|
||||
# Cache the hostname, but do it lazily: socket.getfqdn() can take a couple of
|
||||
# seconds, which slows down the restart of the server.
|
||||
class CachedDnsName(object):
|
||||
@ -22,6 +30,28 @@ class CachedDnsName(object):
|
||||
|
||||
DNS_NAME = CachedDnsName()
|
||||
|
||||
# Copied from Python standard library and modified to used the cached hostname
|
||||
# for performance.
|
||||
def make_msgid(idstring=None):
|
||||
"""Returns a string suitable for RFC 2822 compliant Message-ID, e.g:
|
||||
|
||||
<20020201195627.33539.96671@nightshade.la.mastaler.com>
|
||||
|
||||
Optional idstring if given is a string used to strengthen the
|
||||
uniqueness of the message id.
|
||||
"""
|
||||
timeval = time.time()
|
||||
utcdate = time.strftime('%Y%m%d%H%M%S', time.gmtime(timeval))
|
||||
pid = os.getpid()
|
||||
randint = random.randrange(100000)
|
||||
if idstring is None:
|
||||
idstring = ''
|
||||
else:
|
||||
idstring = '.' + idstring
|
||||
idhost = DNS_NAME
|
||||
msgid = '<%s.%s.%s%s@%s>' % (utcdate, pid, randint, idstring, idhost)
|
||||
return msgid
|
||||
|
||||
class BadHeaderError(ValueError):
|
||||
pass
|
||||
|
||||
@ -34,6 +64,131 @@ class SafeMIMEText(MIMEText):
|
||||
val = Header(val, settings.DEFAULT_CHARSET)
|
||||
MIMEText.__setitem__(self, name, val)
|
||||
|
||||
class SMTPConnection(object):
|
||||
"""
|
||||
A wrapper that manages the SMTP network connection.
|
||||
"""
|
||||
|
||||
def __init__(self, host=None, port=None, username=None, password=None,
|
||||
use_tls=None, fail_silently=False):
|
||||
self.host = host or settings.EMAIL_HOST
|
||||
self.port = port or settings.EMAIL_PORT
|
||||
self.username = username or settings.EMAIL_HOST_USER
|
||||
self.password = password or settings.EMAIL_HOST_PASSWORD
|
||||
self.use_tls = (use_tls is not None) and use_tls or settings.EMAIL_USE_TLS
|
||||
self.fail_silently = fail_silently
|
||||
self.connection = None
|
||||
|
||||
def open(self):
|
||||
"""
|
||||
Ensure we have a connection to the email server. Returns whether or not
|
||||
a new connection was required.
|
||||
"""
|
||||
if self.connection:
|
||||
# Nothing to do if the connection is already open.
|
||||
return False
|
||||
try:
|
||||
self.connection = smtplib.SMTP(self.host, self.port)
|
||||
if self.use_tls:
|
||||
self.connection.ehlo()
|
||||
self.connection.starttls()
|
||||
self.connection.ehlo()
|
||||
if self.username and self.password:
|
||||
self.connection.login(self.username, self.password)
|
||||
return True
|
||||
except:
|
||||
if not self.fail_silently:
|
||||
raise
|
||||
|
||||
def close(self):
|
||||
"""Close the connection to the email server."""
|
||||
try:
|
||||
try:
|
||||
self.connection.quit()
|
||||
except socket.sslerror:
|
||||
# This happens when calling quit() on a TLS connection
|
||||
# sometimes.
|
||||
self.connection.close()
|
||||
except:
|
||||
if self.fail_silently:
|
||||
return
|
||||
raise
|
||||
finally:
|
||||
self.connection = None
|
||||
|
||||
def send_messages(self, email_messages):
|
||||
"""
|
||||
Send one or more EmailMessage objects and return the number of email
|
||||
messages sent.
|
||||
"""
|
||||
if not email_messages:
|
||||
return
|
||||
new_conn_created = self.open()
|
||||
if not self.connection:
|
||||
# We failed silently on open(). Trying to send would be pointless.
|
||||
return
|
||||
num_sent = 0
|
||||
for message in email_messages:
|
||||
sent = self._send(message)
|
||||
if sent:
|
||||
num_sent += 1
|
||||
if new_conn_created:
|
||||
self.close()
|
||||
return num_sent
|
||||
|
||||
def _send(self, email_message):
|
||||
"""A helper method that does the actual sending."""
|
||||
if not email_message.to:
|
||||
return False
|
||||
try:
|
||||
self.connection.sendmail(email_message.from_email,
|
||||
email_message.recipients(),
|
||||
email_message.message().as_string())
|
||||
except:
|
||||
if not self.fail_silently:
|
||||
raise
|
||||
return False
|
||||
return True
|
||||
|
||||
class EmailMessage(object):
|
||||
"""
|
||||
A container for email information.
|
||||
"""
|
||||
def __init__(self, subject='', body='', from_email=None, to=None, bcc=None, connection=None):
|
||||
self.to = to or []
|
||||
self.bcc = bcc or []
|
||||
self.from_email = from_email or settings.DEFAULT_FROM_EMAIL
|
||||
self.subject = subject
|
||||
self.body = body
|
||||
self.connection = connection
|
||||
|
||||
def get_connection(self, fail_silently=False):
|
||||
if not self.connection:
|
||||
self.connection = SMTPConnection(fail_silently=fail_silently)
|
||||
return self.connection
|
||||
|
||||
def message(self):
|
||||
msg = SafeMIMEText(self.body, 'plain', settings.DEFAULT_CHARSET)
|
||||
msg['Subject'] = self.subject
|
||||
msg['From'] = self.from_email
|
||||
msg['To'] = ', '.join(self.to)
|
||||
msg['Date'] = formatdate()
|
||||
msg['Message-ID'] = make_msgid()
|
||||
if self.bcc:
|
||||
msg['Bcc'] = ', '.join(self.bcc)
|
||||
return msg
|
||||
|
||||
def recipients(self):
|
||||
"""
|
||||
Returns a list of all recipients of the email (includes direct
|
||||
addressees as well as Bcc entries).
|
||||
"""
|
||||
return self.to + self.bcc
|
||||
|
||||
def send(self, fail_silently=False):
|
||||
"""Send the email message."""
|
||||
return self.get_connection(fail_silently).send_messages([self])
|
||||
|
||||
def send_mail(subject, message, from_email, recipient_list, fail_silently=False, auth_user=None, auth_password=None):
|
||||
"""
|
||||
Easy wrapper for sending a single message to a recipient list. All members
|
||||
@ -41,8 +196,13 @@ def send_mail(subject, message, from_email, recipient_list, fail_silently=False,
|
||||
|
||||
If auth_user is None, the EMAIL_HOST_USER setting is used.
|
||||
If auth_password is None, the EMAIL_HOST_PASSWORD setting is used.
|
||||
|
||||
NOTE: This method is deprecated. It exists for backwards compatibility.
|
||||
New code should use the EmailMessage class directly.
|
||||
"""
|
||||
return send_mass_mail([[subject, message, from_email, recipient_list]], fail_silently, auth_user, auth_password)
|
||||
connection = SMTPConnection(username=auth_user, password=auth_password,
|
||||
fail_silently=fail_silently)
|
||||
return EmailMessage(subject, message, from_email, recipient_list, connection=connection).send()
|
||||
|
||||
def send_mass_mail(datatuple, fail_silently=False, auth_user=None, auth_password=None):
|
||||
"""
|
||||
@ -53,52 +213,24 @@ def send_mass_mail(datatuple, fail_silently=False, auth_user=None, auth_password
|
||||
If auth_user and auth_password are set, they're used to log in.
|
||||
If auth_user is None, the EMAIL_HOST_USER setting is used.
|
||||
If auth_password is None, the EMAIL_HOST_PASSWORD setting is used.
|
||||
|
||||
NOTE: This method is deprecated. It exists for backwards compatibility.
|
||||
New code should use the EmailMessage class directly.
|
||||
"""
|
||||
if auth_user is None:
|
||||
auth_user = settings.EMAIL_HOST_USER
|
||||
if auth_password is None:
|
||||
auth_password = settings.EMAIL_HOST_PASSWORD
|
||||
try:
|
||||
server = smtplib.SMTP(settings.EMAIL_HOST, settings.EMAIL_PORT)
|
||||
if auth_user and auth_password:
|
||||
server.login(auth_user, auth_password)
|
||||
except:
|
||||
if fail_silently:
|
||||
return
|
||||
raise
|
||||
num_sent = 0
|
||||
for subject, message, from_email, recipient_list in datatuple:
|
||||
if not recipient_list:
|
||||
continue
|
||||
from_email = from_email or settings.DEFAULT_FROM_EMAIL
|
||||
msg = SafeMIMEText(message, 'plain', settings.DEFAULT_CHARSET)
|
||||
msg['Subject'] = subject
|
||||
msg['From'] = from_email
|
||||
msg['To'] = ', '.join(recipient_list)
|
||||
msg['Date'] = formatdate()
|
||||
try:
|
||||
random_bits = str(random.getrandbits(64))
|
||||
except AttributeError: # Python 2.3 doesn't have random.getrandbits().
|
||||
random_bits = ''.join([random.choice('1234567890') for i in range(19)])
|
||||
msg['Message-ID'] = "<%d.%s@%s>" % (time.time(), random_bits, DNS_NAME)
|
||||
try:
|
||||
server.sendmail(from_email, recipient_list, msg.as_string())
|
||||
num_sent += 1
|
||||
except:
|
||||
if not fail_silently:
|
||||
raise
|
||||
try:
|
||||
server.quit()
|
||||
except:
|
||||
if fail_silently:
|
||||
return
|
||||
raise
|
||||
return num_sent
|
||||
connection = SMTPConnection(username=auth_user, password=auth_password,
|
||||
fail_silently=fail_silently)
|
||||
messages = [EmailMessage(subject, message, sender, recipient) for subject, message, sender, recipient in datatuple]
|
||||
return connection.send_messages(messages)
|
||||
|
||||
def mail_admins(subject, message, fail_silently=False):
|
||||
"Sends a message to the admins, as defined by the ADMINS setting."
|
||||
send_mail(settings.EMAIL_SUBJECT_PREFIX + subject, message, settings.SERVER_EMAIL, [a[1] for a in settings.ADMINS], fail_silently)
|
||||
EmailMessage(settings.EMAIL_SUBJECT_PREFIX + subject, message,
|
||||
settings.SERVER_EMAIL, [a[1] for a in
|
||||
settings.ADMINS]).send(fail_silently=fail_silently)
|
||||
|
||||
def mail_managers(subject, message, fail_silently=False):
|
||||
"Sends a message to the managers, as defined by the MANAGERS setting."
|
||||
send_mail(settings.EMAIL_SUBJECT_PREFIX + subject, message, settings.SERVER_EMAIL, [a[1] for a in settings.MANAGERS], fail_silently)
|
||||
EmailMessage(settings.EMAIL_SUBJECT_PREFIX + subject, message,
|
||||
settings.SERVER_EMAIL, [a[1] for a in
|
||||
settings.MANAGERS]).send(fail_silently=fail_silently)
|
||||
|
||||
|
@ -94,12 +94,10 @@ Formatting
|
||||
|
||||
The text documentation is written in ReST (ReStructured Text) format. That
|
||||
means it's easy to read but is also formatted in a way that makes it easy to
|
||||
convert into other formats, such as HTML. If you're interested, the script that
|
||||
converts the ReST text docs into djangoproject.com's HTML lives at
|
||||
`djangoproject.com/django_website/apps/docs/parts/build_documentation.py`_ in
|
||||
the Django Subversion repository.
|
||||
convert into other formats, such as HTML. If you have the `reStructuredText`_
|
||||
library installed, you can use ``rst2html`` to generate your own HTML files.
|
||||
|
||||
.. _djangoproject.com/django_website/apps/docs/parts/build_documentation.py: http://code.djangoproject.com/browser/djangoproject.com/django_website/apps/docs/parts/build_documentation.py
|
||||
.. _reStructuredText: http://docutils.sourceforge.net/rst.html
|
||||
|
||||
Differences between versions
|
||||
============================
|
||||
|
@ -22,7 +22,8 @@ In two lines::
|
||||
|
||||
Mail will be sent using the SMTP host and port specified in the `EMAIL_HOST`_
|
||||
and `EMAIL_PORT`_ settings. The `EMAIL_HOST_USER`_ and `EMAIL_HOST_PASSWORD`_
|
||||
settings, if set, will be used to authenticate to the SMTP server.
|
||||
settings, if set, will be used to authenticate to the SMTP server and the
|
||||
`EMAIL_USE_TLS`_ settings will control whether a secure connection is used.
|
||||
|
||||
.. note::
|
||||
|
||||
@ -34,6 +35,7 @@ settings, if set, will be used to authenticate to the SMTP server.
|
||||
.. _EMAIL_PORT: ../settings/#email-port
|
||||
.. _EMAIL_HOST_USER: ../settings/#email-host-user
|
||||
.. _EMAIL_HOST_PASSWORD: ../settings/#email-host-password
|
||||
.. _EMAIL_USE_TLS: ../settings/#email-use-tls
|
||||
|
||||
|
||||
send_mail()
|
||||
@ -183,3 +185,65 @@ from the request's POST data, sends that to admin@example.com and redirects to
|
||||
return HttpResponse('Make sure all fields are entered and valid.')
|
||||
|
||||
.. _Header injection: http://securephp.damonkohler.com/index.php/Email_Injection
|
||||
|
||||
The EmailMessage and SMTPConnection classes
|
||||
===========================================
|
||||
|
||||
**New in Django development version**
|
||||
|
||||
Django's ``send_mail()`` and ``send_mass_mail()`` functions are actually thin
|
||||
wrappers that make use of the ``EmailMessage`` and ``SMTPConnection`` classes
|
||||
in ``django.mail``. If you ever need to customize the way Django sends email,
|
||||
you can subclass these two classes to suit your needs.
|
||||
|
||||
.. note::
|
||||
Not all features of the ``EmailMessage`` class are available through the
|
||||
``send_mail()`` and related wrapper functions. If you wish to use advanced
|
||||
features such as including BCC recipients or multi-part email, you will
|
||||
need to create ``EmailMessage`` instances directly.
|
||||
|
||||
In general, ``EmailMessage`` is responsible for creating the email message
|
||||
itself. ``SMTPConnection`` is responsible for the network connection side of
|
||||
the operation. This means you can reuse the same connection (an
|
||||
``SMTPConnection`` instance) for multiple messages.
|
||||
|
||||
The ``EmailMessage`` class is initialised as follows::
|
||||
|
||||
email = EmailMessage(subject, body, from_email, to, bcc, connection)
|
||||
|
||||
All of these parameters are optional. If ``from_email`` is omitted, the value
|
||||
from ``settings.DEFAULT_FROM_EMAIL`` is used. Both the ``to`` and ``bcc``
|
||||
parameters are lists of addresses.
|
||||
|
||||
The class has the following methods that you can use:
|
||||
|
||||
* ``send()`` sends the message, using either the connection that is specified
|
||||
in the ``connection`` attribute, or creating a new connection if none already
|
||||
exists.
|
||||
* ``message()`` constructs a ``django.core.mail.SafeMIMEText`` object (a
|
||||
sub-class of Python's ``email.MIMEText.MIMEText`` class) holding the
|
||||
message to be sent. If you ever need to extend the `EmailMessage` class,
|
||||
you will probably want to override this method to put the content you wish
|
||||
into the MIME object.
|
||||
* ``recipients()`` returns a lists of all the recipients of the message,
|
||||
whether they are recorded in the ``to`` or ``bcc`` attributes. This is
|
||||
another method you need to possibly override when sub-classing, since the
|
||||
SMTP server needs to be told the full list of recipients when the message
|
||||
is sent. If you add another way to specify recipients in your class, they
|
||||
need to be returned from this method as well.
|
||||
|
||||
The ``SMTPConnection`` class is initialized with the host, port, username and
|
||||
password for the SMTP server. If you don't specify one or more of those
|
||||
options, they are read from your settings file.
|
||||
|
||||
If you are sending lots of messages at once, the ``send_messages()`` method of
|
||||
the ``SMTPConnection`` class will be useful. It takes a list of ``EmailMessage``
|
||||
instances (or sub-classes) and sends them over a single connection. For
|
||||
example, if you have a function called ``get_notification_email()`` that returns a
|
||||
list of ``EmailMessage`` objects representing some periodic email you wish to
|
||||
send out, you could send this with::
|
||||
|
||||
connection = SMTPConnection() # Use default settings for connection
|
||||
messages = get_notification_email()
|
||||
connection.send_messages(messages)
|
||||
|
||||
|
@ -107,7 +107,7 @@ posts a comment. It doesn't let a user post a comment more than once::
|
||||
This simplistic view logs in a "member" of the site::
|
||||
|
||||
def login(request):
|
||||
m = members.get_object(username__exact=request.POST['username'])
|
||||
m = Member.objects.get(username=request.POST['username'])
|
||||
if m.password == request.POST['password']:
|
||||
request.session['member_id'] = m.id
|
||||
return HttpResponse("You're logged in.")
|
||||
|
@ -428,6 +428,15 @@ Subject-line prefix for e-mail messages sent with ``django.core.mail.mail_admins
|
||||
or ``django.core.mail.mail_managers``. You'll probably want to include the
|
||||
trailing space.
|
||||
|
||||
EMAIL_USE_TLS
|
||||
-------------
|
||||
|
||||
**New in Django development version**
|
||||
|
||||
Default: ``False``
|
||||
|
||||
Whether to use a TLS (secure) connection when talking to the SMTP server.
|
||||
|
||||
FIXTURE_DIRS
|
||||
-------------
|
||||
|
||||
|
@ -646,15 +646,15 @@ This example illustrates all possible attributes and methods for a ``Feed`` clas
|
||||
def item_enclosure_mime_type(self, item):
|
||||
"""
|
||||
Takes an item, as returned by items(), and returns the item's
|
||||
enclosure mime type.
|
||||
enclosure MIME type.
|
||||
"""
|
||||
|
||||
def item_enclosure_mime_type(self):
|
||||
"""
|
||||
Returns the enclosure length, in bytes, for every item in the feed.
|
||||
Returns the enclosure MIME type for every item in the feed.
|
||||
"""
|
||||
|
||||
item_enclosure_mime_type = "audio/mpeg" # Hard-coded enclosure mime-type.
|
||||
item_enclosure_mime_type = "audio/mpeg" # Hard-coded enclosure MIME type.
|
||||
|
||||
# ITEM PUBDATE -- It's optional to use one of these three. This is a
|
||||
# hook that specifies how to get the pubdate for a given item.
|
||||
|
@ -345,7 +345,7 @@ If ``TEMPLATE_CONTEXT_PROCESSORS`` contains this processor, every
|
||||
``request.user.get_and_delete_messages()`` for every request. That method
|
||||
collects the user's messages and deletes them from the database.
|
||||
|
||||
Note that messages are set with ``user.add_message()``. See the
|
||||
Note that messages are set with ``user.message_set.create``. See the
|
||||
`message docs`_ for more.
|
||||
|
||||
* ``perms`` -- An instance of
|
||||
|
Loading…
x
Reference in New Issue
Block a user