From f5c0328fd3f5a965bace09f7aef0ada67751fad5 Mon Sep 17 00:00:00 2001 From: Ramiro Morales Date: Mon, 28 Feb 2011 02:56:02 +0000 Subject: [PATCH] [1.2.X] Fixed #13433 -- Changed default behavior of Django email message wrappers to not mangle lines starting with 'From '. Thanks Leo for the report and patch. Backport of [15669] from trunk. git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.2.X@15670 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- django/core/mail/message.py | 35 +++++++++++++++++++++++++++-- tests/regressiontests/mail/tests.py | 10 +++++++-- 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/django/core/mail/message.py b/django/core/mail/message.py index 90df647724..f63157e75f 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 d77d0baeab..062eb3220f 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 @@ -263,6 +263,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