Commit 84db894f authored by Russell Keith-Magee's avatar Russell Keith-Magee
Browse files

[1.2.X] Fixed #15042 -- Ensured that email addresses without a domain can...

[1.2.X] Fixed #15042 -- Ensured that email addresses without a domain can still be mail recipients. Patch also improves the IDN handling introduced by r15006, and refactors the test suite to ensure even feature coverage. Thanks to net147 for the report, and to Łukasz Rekucki for the awesome patch.

Backport of r15211 from trunk.

git-svn-id: http://code.djangoproject.com/svn/django/branches/releases/1.2.X@15213 bcc190cf-cafb-0310-a4f2-bffc1f526a37
parent f7d04c37
Loading
Loading
Loading
Loading
+5 −8
Original line number Diff line number Diff line
"""SMTP email backend class."""

import smtplib
import socket
import threading
@@ -7,6 +6,8 @@ import threading
from django.conf import settings
from django.core.mail.backends.base import BaseEmailBackend
from django.core.mail.utils import DNS_NAME
from django.core.mail.message import sanitize_address


class EmailBackend(BaseEmailBackend):
    """
@@ -91,17 +92,13 @@ class EmailBackend(BaseEmailBackend):
            self._lock.release()
        return num_sent

    def _sanitize(self, email):
        name, domain = email.split('@', 1)
        email = '@'.join([name, domain.encode('idna')])
        return email

    def _send(self, email_message):
        """A helper method that does the actual sending."""
        if not email_message.recipients():
            return False
        from_email = self._sanitize(email_message.from_email)
        recipients = map(self._sanitize, email_message.recipients())
        from_email = sanitize_address(email_message.from_email, email_message.encoding)
        recipients = [sanitize_address(addr, email_message.encoding)
                      for addr in email_message.recipients()]
        try:
            self.connection.sendmail(from_email, recipients,
                    email_message.message().as_string())
+48 −17
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@ from email.Utils import formatdate, getaddresses, formataddr
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

# Don't BASE64-encode UTF-8 messages so that we avoid unwanted attention from
# some spam filters.
@@ -54,6 +55,22 @@ def make_msgid(idstring=None):
    return msgid


# Header names that contain structured address data (RFC #5322)
ADDRESS_HEADERS = set([
    'from',
    'sender',
    'reply-to',
    'to',
    'cc',
    'bcc',
    'resent-from',
    'resent-sender',
    'resent-to',
    'resent-cc',
    'resent-bcc',
])


def forbid_multi_line_headers(name, val, encoding):
    """Forbids multi-line headers, to prevent header injection."""
    encoding = encoding or settings.DEFAULT_CHARSET
@@ -63,23 +80,35 @@ def forbid_multi_line_headers(name, val, encoding):
    try:
        val = val.encode('ascii')
    except UnicodeEncodeError:
        if name.lower() in ('to', 'from', 'cc'):
            result = []
            for nm, addr in getaddresses((val,)):
                nm = str(Header(nm.encode(encoding), encoding))
                try:
                    addr = addr.encode('ascii')
                except UnicodeEncodeError:  # IDN
                    addr = str(Header(addr.encode(encoding), encoding))
                result.append(formataddr((nm, addr)))
            val = ', '.join(result)
        if name.lower() in ADDRESS_HEADERS:
            val = ', '.join(sanitize_address(addr, encoding)
                for addr in getaddresses((val,)))
        else:
            val = Header(val.encode(encoding), encoding)
            val = str(Header(val, encoding))
    else:
        if name.lower() == 'subject':
            val = Header(val)
    return name, val


def sanitize_address(addr, encoding):
    if isinstance(addr, basestring):
        addr = parseaddr(force_unicode(addr))
    nm, addr = addr
    nm = str(Header(nm, encoding))
    try:
        addr = addr.encode('ascii')
    except UnicodeEncodeError:  # IDN
        if u'@' in addr:
            localpart, domain = addr.split(u'@', 1)
            localpart = str(Header(localpart, encoding))
            domain = domain.encode('idna')
            addr = '@'.join([localpart, domain])
        else:
            addr = str(Header(addr, encoding))
    return formataddr((nm, addr))


class SafeMIMEText(MIMEText):

    def __init__(self, text, subtype, charset):
@@ -90,6 +119,7 @@ class SafeMIMEText(MIMEText):
        name, val = forbid_multi_line_headers(name, val, self.encoding)
        MIMEText.__setitem__(self, name, val)


class SafeMIMEMultipart(MIMEMultipart):

    def __init__(self, _subtype='mixed', boundary=None, _subparts=None, encoding=None, **_params):
@@ -100,6 +130,7 @@ class SafeMIMEMultipart(MIMEMultipart):
        name, val = forbid_multi_line_headers(name, val, self.encoding)
        MIMEMultipart.__setitem__(self, name, val)


class EmailMessage(object):
    """
    A container for email information.
+379 −191

File changed.

Preview size limit exceeded, changes collapsed.