Ticket #1541: mail.4.diff

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

Slight change to patch, added docs (last patch broken)

Line 
1Index: django/core/mail.py
2===================================================================
3--- django/core/mail.py Tue Jun 26 14:46:08 2007
4+++ django/core/mail.py Tue Jun 26 14:48:15 2007
5@@ -4,6 +4,10 @@
6
7 from django.conf import settings
8 from email.MIMEText import MIMEText
9+from email.MIMEMultipart import MIMEMultipart
10+from email.MIMEBase import MIMEBase
11+from email import Encoders
12+import mimetypes
13 from email.Header import Header
14 from email.Utils import formatdate
15 from email import Charset
16@@ -13,6 +17,9 @@
17 import time
18 import random
19
20+# Default MIME type to use for attachments if it cannot be guessed.
21+DEFAULT_ATTACHMENT_MIME_TYPE = 'application/octet-stream'
22+
23 # Don't BASE64-encode UTF-8 messages so that we avoid unwanted attention from
24 # some spam filters.
25 Charset.add_charset('utf-8', Charset.SHORTEST, Charset.QP, 'utf-8')
26@@ -64,6 +71,15 @@
27 val = Header(val, settings.DEFAULT_CHARSET)
28 MIMEText.__setitem__(self, name, val)
29
30+class SafeMIMEMultipart(MIMEMultipart):
31+ def __setitem__(self, name, val):
32+ "Forbids multi-line headers, to prevent header injection."
33+ if '\n' in val or '\r' in val:
34+ raise BadHeaderError, "Header values can't contain newlines (got %r for header %r)" % (val, name)
35+ if name == 'Subject':
36+ val = Header(val, settings.DEFAULT_CHARSET)
37+ MIMEMultipart.__setitem__(self, name, val)
38+
39 class SMTPConnection(object):
40 """
41 A wrapper that manages the SMTP network connection.
42@@ -161,6 +177,7 @@
43 self.subject = subject
44 self.body = body
45 self.connection = connection
46+ self.attachments = []
47
48 def get_connection(self, fail_silently=False):
49 if not self.connection:
50@@ -169,6 +186,12 @@
51
52 def message(self):
53 msg = SafeMIMEText(self.body, 'plain', settings.DEFAULT_CHARSET)
54+ if self.attachments:
55+ body_msg = msg
56+ msg = SafeMIMEMultipart()
57+ msg.attach(body_msg)
58+ for params in self.attachments:
59+ msg.attach(self._compile_attachment(*params))
60 msg['Subject'] = self.subject
61 msg['From'] = self.from_email
62 msg['To'] = ', '.join(self.to)
63@@ -189,6 +212,39 @@
64 """Send the email message."""
65 return self.get_connection(fail_silently).send_messages([self])
66
67+ def attach(self, filename, content, mimetype=None):
68+ """Attaches a file with the given filename and content."""
69+ self.attachments.append((filename, content, mimetype))
70+
71+ def attach_file(self, path, mimetype=None):
72+ """Attaches a file from the filesystem."""
73+ filename = os.path.basename(path)
74+ f = open(path, 'rb')
75+ content = f.read()
76+ f.close()
77+ self.attach(filename, content, mimetype)
78+
79+ def _compile_attachment(self, filename, content, mimetype):
80+ """
81+ Compiles the given attachment to a MIME object and returns the new
82+ attachment.
83+ """
84+ if mimetype is None:
85+ # Guess the mimetype based on the filename if possible.
86+ mimetype, xx = mimetypes.guess_type(filename)
87+ if mimetype is None:
88+ mimetype = DEFAULT_ATTACHMENT_MIME_TYPE
89+ basetype, subtype = mimetype.split('/', 1)
90+ if basetype == 'text':
91+ attachment = SafeMIMEText(content, subtype, settings.DEFAULT_CHARSET)
92+ else:
93+ # Encode non-text attachments with base64.
94+ attachment = MIMEBase(basetype, subtype)
95+ attachment.set_payload(content)
96+ Encoders.encode_base64(attachment)
97+ attachment.add_header('Content-Disposition', 'attachment', filename=filename)
98+ return attachment
99+
100
101Index: docs/email.txt
102===================================================================
103--- docs/email.txt Mon Jun 25 09:36:30 2007
104+++ docs/email.txt Tue Jun 26 14:33:28 2007
105@@ -198,8 +198,8 @@
106 .. note::
107 Not all features of the ``EmailMessage`` class are available through the
108 ``send_mail()`` and related wrapper functions. If you wish to use advanced
109- features, such as BCC'ed recipients or multi-part e-mail, you'll need to
110- create ``EmailMessage`` instances directly.
111+ features, such as BCC'ed recipients, file attachments, or multi-part
112+ e-mail, you'll need to create ``EmailMessage`` instances directly.
113
114 In general, ``EmailMessage`` is responsible for creating the e-mail message
115 itself. ``SMTPConnection`` is responsible for the network connection side of
116@@ -238,6 +238,20 @@
117 SMTP server needs to be told the full list of recipients when the message
118 is sent. If you add another way to specify recipients in your class, they
119 need to be returned from this method as well.
120+
121+ * ``attach()`` creates a new file attachment and adds it to the message. It
122+ takes three arguments: ``filename``, ``content`` and ``mimetype``.
123+ ``filename`` is the name of the file attachment as it will appear in the
124+ email. ``content`` is the data that will be contained inside the
125+ attachment. ``mimetype`` is optional, and defines the attachment's MIME
126+ content type. If you omit ``mimetype``, the MIME content type will be
127+ guessed from the filename of the attachment.
128+
129+ * ``attach_file()`` creates a new attachment using a file on the
130+ filesystem, and takes two arguments. The first is the path of the file to
131+ attach. The second argument is the MIME content type to use for the
132+ attachment, and is optional. If the MIME type is omitted then it will be
133+ guessed from the filename.
134
135 The ``SMTPConnection`` class is initialized with the host, port, username and
136 password for the SMTP server. If you don't specify one or more of those
Back to Top