diff --git a/django/core/mail/message.py b/django/core/mail/message.py index 96ff689fd4..bed4966ef9 100644 --- a/django/core/mail/message.py +++ b/django/core/mail/message.py @@ -3,16 +3,21 @@ import os import random import time from email import Charset, Encoders +from email.generator import Generator from email.MIMEText import MIMEText from email.MIMEMultipart import MIMEMultipart from email.MIMEBase import MIMEBase from email.Header import Header -from email.Utils import formatdate, getaddresses, formataddr +from email.Utils import formatdate, getaddresses, formataddr, parseaddr from django.conf import settings from django.core.mail.utils import DNS_NAME from django.utils.encoding import smart_str, force_unicode -from email.Utils import parseaddr + +try: + from cStringIO import StringIO +except ImportError: + from StringIO import StringIO # Don't BASE64-encode UTF-8 messages so that we avoid unwanted attention from # some spam filters. @@ -119,6 +124,19 @@ class SafeMIMEText(MIMEText): name, val = forbid_multi_line_headers(name, val, self.encoding) MIMEText.__setitem__(self, name, val) + def as_string(self, unixfrom=False): + """Return the entire formatted message as a string. + Optional `unixfrom' when True, means include the Unix From_ envelope + header. + + This overrides the default as_string() implementation to not mangle + lines that begin with 'From '. See bug #13433 for details. + """ + fp = StringIO() + g = Generator(fp, mangle_from_ = False) + g.flatten(self, unixfrom=unixfrom) + return fp.getvalue() + class SafeMIMEMultipart(MIMEMultipart): @@ -130,6 +148,19 @@ class SafeMIMEMultipart(MIMEMultipart): name, val = forbid_multi_line_headers(name, val, self.encoding) MIMEMultipart.__setitem__(self, name, val) + def as_string(self, unixfrom=False): + """Return the entire formatted message as a string. + Optional `unixfrom' when True, means include the Unix From_ envelope + header. + + This overrides the default as_string() implementation to not mangle + lines that begin with 'From '. See bug #13433 for details. + """ + fp = StringIO() + g = Generator(fp, mangle_from_ = False) + g.flatten(self, unixfrom=unixfrom) + return fp.getvalue() + class EmailMessage(object): """ diff --git a/tests/regressiontests/mail/tests.py b/tests/regressiontests/mail/tests.py index 8bdc562c81..224254c044 100644 --- a/tests/regressiontests/mail/tests.py +++ b/tests/regressiontests/mail/tests.py @@ -11,8 +11,8 @@ import threading from django.conf import settings from django.core import mail -from django.core.mail import EmailMessage, mail_admins, mail_managers, EmailMultiAlternatives -from django.core.mail import send_mail, send_mass_mail +from django.core.mail import (EmailMessage, mail_admins, mail_managers, + EmailMultiAlternatives, send_mail, send_mass_mail) from django.core.mail.backends import console, dummy, locmem, filebased, smtp from django.core.mail.message import BadHeaderError from django.test import TestCase @@ -282,6 +282,12 @@ class MailTests(TestCase): self.assertEqual(len(connection.test_outbox), 1) self.assertEqual(connection.test_outbox[0].subject, '[Django] Manager message') + def test_dont_mangle_from_in_body(self): + # Regression for #13433 - Make sure that EmailMessage doesn't mangle + # 'From ' in message body. + email = EmailMessage('Subject', 'From the future', 'bounce@example.com', ['to@example.com'], headers={'From': 'from@example.com'}) + self.assertFalse('>From the future' in email.message().as_string()) + class BaseEmailBackendTests(object): email_backend = None