Ticket #1541: mail.diff

File mail.diff, 5.2 KB (added by nick.lane.au@…, 8 years ago)

Patch to add attachments to django.core.mail

  • django/core/mail.py

     
    44
    55from django.conf import settings
    66from email.MIMEText import MIMEText
     7from email.mime.multipart import MIMEMultipart
     8from email.mime.base 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.
     
    154170    """
    155171    A container for email information.
    156172    """
    157     def __init__(self, subject='', body='', from_email=None, to=None, bcc=None, connection=None):
     173    def __init__(self, subject='', body='', from_email=None, to=None, bcc=None, attachments=None, connection=None):
    158174        self.to = to or []
    159175        self.bcc = bcc or []
    160176        self.from_email = from_email or settings.DEFAULT_FROM_EMAIL
    161177        self.subject = subject
    162178        self.body = body
    163179        self.connection = connection
     180        self.attachments = attachments or []
    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)
     
    189212        """Send the email message."""
    190213        return self.get_connection(fail_silently).send_messages([self])
    191214
    192 def send_mail(subject, message, from_email, recipient_list, fail_silently=False, auth_user=None, auth_password=None):
     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)
     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
     248def send_mail(subject, message, from_email, recipient_list, attachments=None, fail_silently=False, auth_user=None, auth_password=None):
    193249    """
    194250    Easy wrapper for sending a single message to a recipient list. All members
    195251    of the recipient list will see the other recipients in the 'To' field.
     
    202258    """
    203259    connection = SMTPConnection(username=auth_user, password=auth_password,
    204260                                 fail_silently=fail_silently)
    205     return EmailMessage(subject, message, from_email, recipient_list, connection=connection).send()
     261    return EmailMessage(subject, message, from_email, recipient_list, attachments=attachments, connection=connection).send()
    206262
    207263def send_mass_mail(datatuple, fail_silently=False, auth_user=None, auth_password=None):
    208264    """
     
    233289    EmailMessage(settings.EMAIL_SUBJECT_PREFIX + subject, message,
    234290            settings.SERVER_EMAIL, [a[1] for a in
    235291                settings.MANAGERS]).send(fail_silently=fail_silently)
    236 
Back to Top