1
0
mirror of https://github.com/django/django.git synced 2025-03-12 18:30:48 +00:00

[5.2.x] Fixed #36119 -- Fixed UnicodeEncodeError when attaching a file with 8bit Content-Transfer-Encoding.

Backport of 89e28e13ecbf9fbcf235e16d453c08bbf2271244 from main.
This commit is contained in:
greg 2025-01-30 15:04:29 +01:00 committed by Sarah Boyce
parent b406907af5
commit 2146bd1261
2 changed files with 28 additions and 4 deletions

View File

@ -2,7 +2,7 @@ import mimetypes
from collections import namedtuple from collections import namedtuple
from email import charset as Charset from email import charset as Charset
from email import encoders as Encoders 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.errors import HeaderParseError
from email.header import Header from email.header import Header
from email.headerregistry import Address, parser from email.headerregistry import Address, parser
@ -17,7 +17,7 @@ from pathlib import Path
from django.conf import settings from django.conf import settings
from django.core.mail.utils import DNS_NAME 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 # Don't BASE64-encode UTF-8 messages so that we avoid unwanted attention from
# some spam filters. # some spam filters.
@ -152,7 +152,7 @@ class MIMEMixin:
class SafeMIMEMessage(MIMEMixin, MIMEMessage): class SafeMIMEMessage(MIMEMixin, MIMEMessage):
def __setitem__(self, name, val): 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") name, val = forbid_multi_line_headers(name, val, "ascii")
MIMEMessage.__setitem__(self, name, val) MIMEMessage.__setitem__(self, name, val)
@ -399,7 +399,7 @@ class EmailMessage:
elif not isinstance(content, Message): elif not isinstance(content, Message):
# For compatibility with existing code, parse the message # For compatibility with existing code, parse the message
# into an email.Message object if it is not one already. # 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) attachment = SafeMIMEMessage(content, subtype)
else: else:

View File

@ -913,6 +913,30 @@ class MailTests(MailTestsMixin, SimpleTestCase):
self.assertEqual(content, b"\xff") self.assertEqual(content, b"\xff")
self.assertEqual(mimetype, "application/octet-stream") 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): def test_attach_mime_image(self):
""" """
EmailMessage.attach() docs: "You can pass it EmailMessage.attach() docs: "You can pass it