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