1
0
mirror of https://github.com/django/django.git synced 2025-10-24 14:16:09 +00:00

Fixed #1541 -- Added ability to create multipart email messages. Thanks, Nick

Lane.


git-svn-id: http://code.djangoproject.com/svn/django/trunk@5547 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Malcolm Tredinnick
2007-06-27 09:44:55 +00:00
parent 551a36131e
commit 2d082a34dc
2 changed files with 122 additions and 11 deletions

View File

@@ -3,10 +3,13 @@ Tools for sending email.
"""
from django.conf import settings
from email import Charset, Encoders
from email.MIMEText import MIMEText
from email.MIMEMultipart import MIMEMultipart
from email.MIMEBase import MIMEBase
from email.Header import Header
from email.Utils import formatdate
from email import Charset
import mimetypes
import os
import smtplib
import socket
@@ -17,6 +20,10 @@ import random
# some spam filters.
Charset.add_charset('utf-8', Charset.SHORTEST, Charset.QP, 'utf-8')
# Default MIME type to use on attachments (if it is not explicitly given
# and cannot be guessed).
DEFAULT_ATTACHMENT_MIME_TYPE = 'application/octet-stream'
# 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):
@@ -55,14 +62,22 @@ def make_msgid(idstring=None):
class BadHeaderError(ValueError):
pass
class SafeMIMEText(MIMEText):
class SafeHeaderMixin(object):
def __setitem__(self, name, val):
"Forbids multi-line headers, to prevent header injection."
if '\n' in val or '\r' in val:
raise BadHeaderError, "Header values can't contain newlines (got %r for header %r)" % (val, name)
if name == "Subject":
val = Header(val, settings.DEFAULT_CHARSET)
MIMEText.__setitem__(self, name, val)
# Note: using super() here is safe; any __setitem__ overrides must use
# the same argument signature.
super(SafeHeaderMixin, self).__setitem__(name, val)
class SafeMIMEText(MIMEText, SafeHeaderMixin):
pass
class SafeMIMEMultipart(MIMEMultipart, SafeHeaderMixin):
pass
class SMTPConnection(object):
"""
@@ -154,12 +169,14 @@ class EmailMessage(object):
"""
A container for email information.
"""
def __init__(self, subject='', body='', from_email=None, to=None, bcc=None, connection=None):
def __init__(self, subject='', body='', from_email=None, to=None, bcc=None,
connection=None, attachments=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.attachments = attachments or []
self.connection = connection
def get_connection(self, fail_silently=False):
@@ -169,6 +186,16 @@ class EmailMessage(object):
def message(self):
msg = SafeMIMEText(self.body, 'plain', settings.DEFAULT_CHARSET)
if self.attachments:
body_msg = msg
msg = SafeMIMEMultipart()
if self.body:
msg.attach(body_msg)
for attachment in self.attachments:
if isinstance(attachment, MIMEBase):
msg.attach(attachment)
else:
msg.attach(self._create_attachment(*attachment))
msg['Subject'] = self.subject
msg['From'] = self.from_email
msg['To'] = ', '.join(self.to)
@@ -189,6 +216,45 @@ class EmailMessage(object):
"""Send the email message."""
return self.get_connection(fail_silently).send_messages([self])
def attach(self, filename, content=None, mimetype=None):
"""
Attaches a file with the given filename and content.
Alternatively, the first parameter can be a MIMEBase subclass, which
is inserted directly into the resulting message attachments.
"""
if isinstance(filename, MIMEBase):
self.attachements.append(filename)
else:
assert content is not None
self.attachments.append((filename, content, mimetype))
def attach_file(self, path, mimetype=None):
"""Attaches a file from the filesystem."""
filename = os.path.basename(path)
content = open(path, 'rb').read()
self.attach(filename, content, mimetype)
def _create_attachment(self, filename, content, mimetype=None):
"""
Convert the filename, content, mimetype triple into a MIME attachment
object.
"""
if mimetype is None:
mimetype, _ = mimetypes.guess_type(filename)
if mimetype is None:
mimetype = DEFAULT_ATTACHMENT_MIME_TYPE
basetype, subtype = mimetype.split('/', 1)
if basetype == 'text':
attachment = SafeMIMEText(content, subtype, settings.DEFAULT_CHARSET)
else:
# Encode non-text attachments with base64.
attachment = MIMEBase(basetype, subtype)
attachment.set_payload(content)
Encoders.encode_base64(attachment)
attachment.add_header('Content-Disposition', 'attachment', filename=filename)
return attachment
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