summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorgreg <marianigregory@pm.me>2025-01-30 15:04:29 +0100
committerSarah Boyce <42296566+sarahboyce@users.noreply.github.com>2025-01-31 11:54:12 +0000
commit89e28e13ecbf9fbcf235e16d453c08bbf2271244 (patch)
treef9d29fe43534deb8db4249fc75e5da852b4b9f5e
parentfd3cfd80bebad292d639a03e58632e494369eb92 (diff)
Fixed #36119 -- Fixed UnicodeEncodeError when attaching a file with 8bit Content-Transfer-Encoding.
-rw-r--r--django/core/mail/message.py8
-rw-r--r--tests/mail/tests.py24
2 files changed, 28 insertions, 4 deletions
diff --git a/django/core/mail/message.py b/django/core/mail/message.py
index e6d0ec2dc8..adcd9fc504 100644
--- a/django/core/mail/message.py
+++ b/django/core/mail/message.py
@@ -2,7 +2,7 @@ import mimetypes
from collections import namedtuple
from email import charset as Charset
from email import encoders as Encoders
-from email import generator, message_from_string
+from email import generator, message_from_bytes
from email.errors import HeaderParseError
from email.header import Header
from email.headerregistry import Address, parser
@@ -17,7 +17,7 @@ from pathlib import Path
from django.conf import settings
from django.core.mail.utils import DNS_NAME
-from django.utils.encoding import force_str, punycode
+from django.utils.encoding import force_bytes, force_str, punycode
# Don't BASE64-encode UTF-8 messages so that we avoid unwanted attention from
# some spam filters.
@@ -152,7 +152,7 @@ class MIMEMixin:
class SafeMIMEMessage(MIMEMixin, MIMEMessage):
def __setitem__(self, name, val):
- # message/rfc822 attachments must be ASCII
+ # Per RFC 2046 Section 5.2.1, message/rfc822 attachment headers must be ASCII.
name, val = forbid_multi_line_headers(name, val, "ascii")
MIMEMessage.__setitem__(self, name, val)
@@ -399,7 +399,7 @@ class EmailMessage:
elif not isinstance(content, Message):
# For compatibility with existing code, parse the message
# into an email.Message object if it is not one already.
- content = message_from_string(force_str(content))
+ content = message_from_bytes(force_bytes(content))
attachment = SafeMIMEMessage(content, subtype)
else:
diff --git a/tests/mail/tests.py b/tests/mail/tests.py
index 2bc1364bed..993cdc5f42 100644
--- a/tests/mail/tests.py
+++ b/tests/mail/tests.py
@@ -913,6 +913,30 @@ class MailTests(MailTestsMixin, SimpleTestCase):
self.assertEqual(content, b"\xff")
self.assertEqual(mimetype, "application/octet-stream")
+ def test_attach_8bit_rfc822_message_non_ascii(self):
+ """
+ Attaching a message that uses 8bit content transfer encoding for
+ non-ASCII characters should not raise a UnicodeEncodeError (#36119).
+ """
+ attachment = dedent(
+ """\
+ Subject: A message using 8bit CTE
+ Content-Type: text/plain; charset=utf-8
+ Content-Transfer-Encoding: 8bit
+
+ ¡8-bit content!
+ """
+ ).encode()
+ email = EmailMessage()
+ email.attach("attachment.eml", attachment, "message/rfc822")
+ attachments = self.get_raw_attachments(email)
+ self.assertEqual(len(attachments), 1)
+ self.assertEqual(attachments[0].get_content_type(), "message/rfc822")
+ attached_message = attachments[0].get_content()
+ self.assertEqual(attached_message.get_content().rstrip(), "¡8-bit content!")
+ self.assertEqual(attached_message["Content-Transfer-Encoding"], "8bit")
+ self.assertEqual(attached_message.get_content_type(), "text/plain")
+
def test_attach_mime_image(self):
"""
EmailMessage.attach() docs: "You can pass it