diff options
| author | Michael Schwarz <michi@cloudscale.ch> | 2016-08-03 15:53:06 +0200 |
|---|---|---|
| committer | Tim Graham <timograham@gmail.com> | 2016-08-12 16:35:09 -0400 |
| commit | 72d541b61cd7b0a14f70242e2207fdb3f600c4d5 (patch) | |
| tree | aa90cd09c360ed41657ffca31507c8fef579c2ab /django | |
| parent | 311a8e8d505049ff5644a94e16c00246c8a43a18 (diff) | |
Fixed #27007 -- Handled non-UTF-8 bytes objects for text/* attachments.
The fallback logic which allows non-UTF-8 encoded files to be passed to
attach_file() even when a `text/*` mime type has been specified is
moved to attach(). Both functions now fall back to a content type of
`application/octet-stream`.
A side effect is that a file's content is decoded in memory instead of
opening it in text mode and reading it into a string.
Some mimetype-related logic in _create_attachment() has become
obsolete as the code moved from attach_file() to attach() already
handles this.
Diffstat (limited to 'django')
| -rw-r--r-- | django/core/mail/message.py | 57 |
1 files changed, 29 insertions, 28 deletions
diff --git a/django/core/mail/message.py b/django/core/mail/message.py index 29012201ef..44178d2447 100644 --- a/django/core/mail/message.py +++ b/django/core/mail/message.py @@ -356,6 +356,11 @@ class EmailMessage(object): If the first parameter is a MIMEBase subclass it is inserted directly into the resulting message attachments. + + For a text/* mimetype (guessed or specified), when a bytes object is + specified as content, it will be decoded as UTF-8. If that fails, + the mimetype will be set to DEFAULT_ATTACHMENT_MIME_TYPE and the + content is not decoded. """ if isinstance(filename, MIMEBase): assert content is None @@ -363,6 +368,22 @@ class EmailMessage(object): self.attachments.append(filename) else: assert content is not None + + if not mimetype: + mimetype, _ = mimetypes.guess_type(filename) + if not mimetype: + mimetype = DEFAULT_ATTACHMENT_MIME_TYPE + basetype, subtype = mimetype.split('/', 1) + + if basetype == 'text': + if isinstance(content, six.binary_type): + try: + content = content.decode('utf-8') + except UnicodeDecodeError: + # If mimetype suggests the file is text but it's actually + # binary, read() will raise a UnicodeDecodeError on Python 3. + mimetype = DEFAULT_ATTACHMENT_MIME_TYPE + self.attachments.append((filename, content, mimetype)) def attach_file(self, path, mimetype=None): @@ -370,33 +391,17 @@ class EmailMessage(object): Attaches a file from the filesystem. The mimetype will be set to the DEFAULT_ATTACHMENT_MIME_TYPE if it is - not specified and cannot be guessed or (PY3 only) if it suggests - text/* for a binary file. + not specified and cannot be guessed. + + For a text/* mimetype (guessed or specified), the file's content + will be decoded as UTF-8. If that fails, the mimetype will be set to + DEFAULT_ATTACHMENT_MIME_TYPE and the content is not decoded. """ filename = os.path.basename(path) - if not mimetype: - mimetype, _ = mimetypes.guess_type(filename) - if not mimetype: - mimetype = DEFAULT_ATTACHMENT_MIME_TYPE - basetype, subtype = mimetype.split('/', 1) - read_mode = 'r' if basetype == 'text' else 'rb' - content = None - - with open(path, read_mode) as f: - try: - content = f.read() - except UnicodeDecodeError: - # If mimetype suggests the file is text but it's actually - # binary, read() will raise a UnicodeDecodeError on Python 3. - pass - - # If the previous read in text mode failed, try binary mode. - if content is None: - with open(path, 'rb') as f: - content = f.read() - mimetype = DEFAULT_ATTACHMENT_MIME_TYPE - self.attach(filename, content, mimetype) + with open(path, 'rb') as file: + content = file.read() + self.attach(filename, content, mimetype) def _create_message(self, msg): return self._create_attachments(msg) @@ -450,10 +455,6 @@ class EmailMessage(object): Converts 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 attachment = self._create_mime_attachment(content, mimetype) if filename: try: |
