Avoid double-encode address headers. Fixes #40
This commit is contained in:
parent
20249b6e76
commit
5583523d98
|
@ -113,6 +113,8 @@ if is_py2:
|
|||
return x.encode(charset, errors)
|
||||
raise TypeError('Expected bytes')
|
||||
|
||||
from email.utils import formataddr
|
||||
|
||||
|
||||
elif is_py3:
|
||||
import urllib.parse as urlparse
|
||||
|
@ -142,6 +144,25 @@ elif is_py3:
|
|||
return x.encode(charset, errors)
|
||||
raise TypeError('Expected bytes')
|
||||
|
||||
from email.utils import escapesre, specialsre
|
||||
|
||||
def formataddr(pair):
|
||||
"""
|
||||
This code is copy of python2 email.utils.formataddr.
|
||||
Takes a 2-tuple of the form (realname, email_address) and returns RFC2822-like string.
|
||||
Does not encode non-ascii realname.
|
||||
|
||||
Python3 email.utils.formataddr do encode realname.
|
||||
"""
|
||||
name, address = pair
|
||||
if name:
|
||||
quotes = ''
|
||||
if specialsre.search(name):
|
||||
quotes = '"'
|
||||
name = escapesre.sub(r'\\\g<0>', name)
|
||||
return '%s%s%s <%s>' % (quotes, name, quotes, address)
|
||||
return address
|
||||
|
||||
|
||||
def to_unicode(x, charset=sys.getdefaultencoding(), errors='strict',
|
||||
allow_none_charset=False):
|
||||
|
|
|
@ -10,7 +10,8 @@ import errno
|
|||
from zipfile import ZipFile
|
||||
import email
|
||||
|
||||
from ..compat import to_unicode, string_types, to_native
|
||||
from ..compat import to_unicode, string_types, to_native, formataddr as compat_formataddr
|
||||
|
||||
from ..loader.helpers import decode_text
|
||||
from ..message import Message
|
||||
from ..utils import decode_header
|
||||
|
@ -207,22 +208,6 @@ class ZipLoader(BaseLoader):
|
|||
return sorted(self._filenames)
|
||||
|
||||
|
||||
def _human_formataddr(pair):
|
||||
"""Takes a 2-tuple of the form (realname, email_address) and returns RFC2822-like string.
|
||||
Does not encode non-ascii realname.
|
||||
The same code in python2 email.utils.formataddr
|
||||
"""
|
||||
from email.utils import escapesre, specialsre
|
||||
name, address = pair
|
||||
if name:
|
||||
quotes = ''
|
||||
if specialsre.search(name):
|
||||
quotes = '"'
|
||||
name = escapesre.sub(r'\\\g<0>', name)
|
||||
return '%s%s%s <%s>' % (quotes, name, quotes, address)
|
||||
return address
|
||||
|
||||
|
||||
class MsgLoader(BaseLoader):
|
||||
"""
|
||||
Load files from email.Message
|
||||
|
@ -368,7 +353,7 @@ class MsgLoader(BaseLoader):
|
|||
if not skip_invalid:
|
||||
r.append(decode_header(email))
|
||||
else:
|
||||
r.append(_human_formataddr([decode_header(name), email]))
|
||||
r.append(compat_formataddr([decode_header(name), email]))
|
||||
|
||||
return r
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ from __future__ import unicode_literals
|
|||
|
||||
from email.utils import getaddresses, formataddr
|
||||
|
||||
from .compat import (string_types, is_callable, to_bytes)
|
||||
from .compat import (string_types, is_callable, to_bytes, formataddr as compat_formataddr)
|
||||
from .utils import (SafeMIMEText, SafeMIMEMultipart, sanitize_address,
|
||||
parse_name_and_email, load_email_charsets,
|
||||
encode_header as encode_header_,
|
||||
|
@ -175,10 +175,13 @@ class MessageBuildMixin(object):
|
|||
else:
|
||||
return value
|
||||
|
||||
def encode_address_header(self, name, email):
|
||||
return formataddr((self.encode_header(name or ''), email))
|
||||
def encode_address_header(self, pair):
|
||||
if not pair:
|
||||
return None
|
||||
name, email = pair
|
||||
return compat_formataddr((name or '', email))
|
||||
|
||||
encode_name_header = encode_address_header
|
||||
encode_name_header = encode_address_header # legacy name
|
||||
|
||||
def set_header(self, msg, key, value, encode=True):
|
||||
|
||||
|
@ -215,11 +218,8 @@ class MessageBuildMixin(object):
|
|||
if subject is not None:
|
||||
self.set_header(msg, 'Subject', subject)
|
||||
|
||||
mail_from = self._mail_from and self.encode_address_header(*self._mail_from) or None
|
||||
self.set_header(msg, 'From', mail_from, encode=False)
|
||||
|
||||
mail_to = self._mail_to and self.encode_address_header(*self._mail_to[0]) or None
|
||||
self.set_header(msg, 'To', mail_to, encode=False)
|
||||
self.set_header(msg, 'From', self.encode_address_header(self._mail_from), encode=False)
|
||||
self.set_header(msg, 'To', self._mail_to and self.encode_address_header(self._mail_to[0]) or None, encode=False)
|
||||
|
||||
return msg
|
||||
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
# coding: utf-8
|
||||
from __future__ import unicode_literals, print_function
|
||||
import datetime
|
||||
from email.utils import parseaddr, formataddr
|
||||
from dateutil.parser import parse as dateutil_parse
|
||||
import pytest
|
||||
|
||||
import emails
|
||||
from emails import Message
|
||||
import emails.exc
|
||||
from emails.compat import to_unicode, StringIO, is_py2, is_py34_plus
|
||||
from emails.utils import decode_header, sanitize_address
|
||||
|
||||
from .helpers import common_email_data
|
||||
|
||||
|
||||
|
@ -103,6 +107,17 @@ def test_sanitize_header():
|
|||
emails.Message(html='...', **{header: value}).as_message()
|
||||
|
||||
|
||||
def test_address_header_not_double_encoded():
|
||||
msg = {}
|
||||
m = Message()
|
||||
TEXT = 'Пушкин А.С.'
|
||||
|
||||
m.mail_from = (TEXT, 'a@b.c')
|
||||
m.html = '...'
|
||||
msg = m.as_message()
|
||||
assert decode_header(parseaddr(msg['From'])[0]) == TEXT
|
||||
|
||||
|
||||
def test_message_policy():
|
||||
|
||||
if is_py34_plus:
|
||||
|
|
Reference in New Issue