Ticket #1541: mail.2.diff

File mail.2.diff, 5.6 KB (added by nick.lane.au@…, 17 years ago)

Slight change to patch, added docs

  • django/common/core/mail.py

     
    44
    55from django.conf import settings
    66from email.MIMEText import MIMEText
     7from email.MIMEMultipart import MIMEMultipart
     8from email.MIMEBase import MIMEBase
     9from email import Encoders
     10import mimetypes
    711from email.Header import Header
    812from email.Utils import formatdate
    913from email import Charset
     
    1317import time
    1418import random
    1519
     20# Default MIME type to use for attachments if it cannot be guessed.
     21DEFAULT_ATTACHMENT_MIME_TYPE = 'application/octet-stream'
     22
    1623# Don't BASE64-encode UTF-8 messages so that we avoid unwanted attention from
    1724# some spam filters.
    1825Charset.add_charset('utf-8', Charset.SHORTEST, Charset.QP, 'utf-8')
     
    6471            val = Header(val, settings.DEFAULT_CHARSET)
    6572        MIMEText.__setitem__(self, name, val)
    6673
     74class SafeMIMEMultipart(MIMEMultipart):
     75    def __setitem__(self, name, val):
     76        "Forbids multi-line headers, to prevent header injection."
     77        if '\n' in val or '\r' in val:
     78            raise BadHeaderError, "Header values can't contain newlines (got %r for header %r)" % (val, name)
     79        if name == 'Subject':
     80            val = Header(val, settings.DEFAULT_CHARSET)
     81        MIMEMultipart.__setitem__(self, name, val)
     82
    6783class SMTPConnection(object):
    6884    """
    6985    A wrapper that manages the SMTP network connection.
     
    161177        self.subject = subject
    162178        self.body = body
    163179        self.connection = connection
     180        self.attachments = []
    164181
    165182    def get_connection(self, fail_silently=False):
    166183        if not self.connection:
     
    169186
    170187    def message(self):
    171188        msg = SafeMIMEText(self.body, 'plain', settings.DEFAULT_CHARSET)
     189        if self.attachments:
     190            body_msg = msg
     191            msg = SafeMIMEMultipart()
     192            msg.attach(body_msg)
     193            for params in self.attachments:
     194                msg.attach(self._compile_attachment(*params))
    172195        msg['Subject'] = self.subject
    173196        msg['From'] = self.from_email
    174197        msg['To'] = ', '.join(self.to)
    (this hunk was shorter than expected)  
    189212        """Send the email message."""
    190213        return self.get_connection(fail_silently).send_messages([self])
    191214
     215    def attach(self, filename, content, mimetype=None):
     216        """Attaches a file with the given filename and content."""
     217        self.attachments.append((filename, content, mimetype))
     218
     219    def attach_file(self, path, mimetype=None):
     220        """Attaches a file from the filesystem."""
     221        filename = os.path.basename(path)
     222        f = open(path, 'rb')
     223        content = f.read()
     224        f.close()
     225        self.attach(filename, content, mimetype)
     226
     227    def _compile_attachment(self, filename, content, mimetype):
     228        """
     229        Compiles the given attachment to a MIME object and returns the new
     230        attachment.
     231        """
     232        if mimetype is None:
     233            # Guess the mimetype based on the filename if possible.
     234            mimetype, xx = mimetypes.guess_type(filename)
     235            if mimetype is None:
     236                mimetype = DEFAULT_ATTACHMENT_MIME_TYPE
     237        basetype, subtype = mimetype.split('/', 1)
     238        if basetype == 'text':
     239            attachment = SafeMIMEText(content, subtype, settings.DEFAULT_CHARSET)
     240        else:
     241            # Encode non-text attachments with base64.
     242            attachment = MIMEBase(basetype, subtype)
     243            attachment.set_payload(content)
     244            Encoders.encode_base64(attachment)
     245        attachment.add_header('Content-Disposition', 'attachment', filename=filename)
     246        return attachment
     247
    192248
    193 -- docs/email.txt       Mon Jun 25 09:36:30 2007
     249++ docs/email.txt       Tue Jun 26 14:33:28 2007
     
    198198.. note::
    199199    Not all features of the ``EmailMessage`` class are available through the
    200200    ``send_mail()`` and related wrapper functions. If you wish to use advanced
    201     features, such as BCC'ed recipients or multi-part e-mail, you'll need to
    202     create ``EmailMessage`` instances directly.
     201    features, such as BCC'ed recipients, file attachments, or multi-part
     202    e-mail, you'll need to create ``EmailMessage`` instances directly.
    203203
    204204In general, ``EmailMessage`` is responsible for creating the e-mail message
    205205itself. ``SMTPConnection`` is responsible for the network connection side of
     
    238238      SMTP server needs to be told the full list of recipients when the message
    239239      is sent. If you add another way to specify recipients in your class, they
    240240      need to be returned from this method as well.
     241
     242    * ``attach()`` creates a new file attachment and adds it to the message. It
     243      takes three arguments: ``filename``, ``content`` and ``mimetype``.
     244      ``filename`` is the name of the file attachment as it will appear in the
     245      email. ``content`` is the data that will be contained inside the
     246      attachment. ``mimetype`` is optional, and defines the attachment's MIME
     247      content type. If you omit ``mimetype``, the MIME content type will be
     248      guessed from the filename of the attachment.
     249
     250    * ``attach_file()`` creates a new attachment using a file on the
     251      filesystem, and takes two arguments. The first is the path of the file to
     252      attach. The second argument is the MIME content type to use for the
     253      attachment, and is optional. If the MIME type is omitted then it will be
     254      guessed from the filename.
    241255
    242256The ``SMTPConnection`` class is initialized with the host, port, username and
    243257password for the SMTP server. If you don't specify one or more of those
Back to Top