Permalink
Browse files

Use email.header.Header to properly handle encoding/decoding headers

with non-ascii characters.

Removes the need for the properly_encode_header function.  We also add
special handling of `ascii` or `latin_1` so that headers that can be
encoded without the ugly RFC 2047 encoding if possible.
  • Loading branch information...
1 parent 2c984c4 commit bc7ae3ae532e52c5a78ce9c2f1517a89d235576c @rpatterson rpatterson committed Mar 14, 2012
Showing with 20 additions and 67 deletions.
  1. +14 −35 pyramid_mailer/response.py
  2. +6 −32 pyramid_mailer/tests.py
View
@@ -42,6 +42,7 @@
import string
from email import encoders
from email.charset import Charset
+from email import header
from email.utils import parseaddr
from email.mime.base import MIMEBase
@@ -344,10 +345,9 @@ def to_message(mail):
for k in mail.keys():
if k in ADDRESS_HEADERS_WHITELIST:
- out[k.encode('ascii')] = header_to_mime_encoding(mail[k])
+ out[k] = header_to_mime_encoding(mail[k])
else:
- out[k.encode('ascii')] = header_to_mime_encoding(mail[k],
- not_email=True)
+ out[k] = header_to_mime_encoding(mail[k])
out.extract_payload(mail)
@@ -419,36 +419,15 @@ def is_nonstr_iter(v):
return hasattr(v, '__iter__')
-def header_to_mime_encoding(value, not_email=False):
- if not value: return ""
-
- encoder = Charset(DEFAULT_ENCODING)
+def header_to_mime_encoding(value, charset=DEFAULT_ENCODING):
if is_nonstr_iter(value): # not a string
- return b", ".join(properly_encode_header(
- v, encoder, not_email) for v in value)
- else:
- return properly_encode_header(value, encoder, not_email)
-
-def properly_encode_header(value, encoder, not_email):
- """
- The only thing special (weird) about this function is that it tries
- to do a fast check to see if the header value has an email address in
- it. Since random headers could have an email address, and email addresses
- have weird special formatting rules, we have to check for it.
-
- Normally this works fine, but in Librelist, we need to "obfuscate" email
- addresses by changing the '@' to '-AT-'. This is where
- VALUE_IS_EMAIL_ADDRESS exists. It's a simple lambda returning True/False
- to check if a header value has an email address. If you need to make this
- check different, then change this.
- """
- try:
- return value.encode("ascii")
- except UnicodeEncodeError:
- if not_email is False and VALUE_IS_EMAIL_ADDRESS(value):
- # this could have an email address, make sure we don't screw it up
- name, address = parseaddr(value)
- return b'"%s" <%s>' % (
- encoder.header_encode(name.encode("utf-8")), address)
-
- return encoder.header_encode(value.encode("utf-8"))
+ value = ", ".join(value)
+ if value:
+ for charset in 'ascii', 'latin_1', 'utf_8':
+ try:
+ value.encode(charset)
+ except UnicodeError:
+ pass
+ else:
+ break
+ return header.Header(value, charset=charset)
View
@@ -826,8 +826,8 @@ def test_to_message_multiple_to_recipients(self):
From='From', Subject='Subject',
Body='Body', Html='Html')
message = response.to_message()
- self.assertEqual(message[b'To'],
- b'chrism@plope.com, billg@microsoft.com')
+ self.assertEqual(str(message['To']),
+ 'chrism@plope.com, billg@microsoft.com')
def test_to_message_multipart(self):
from pyramid_mailer.response import MIMEPart
@@ -949,7 +949,7 @@ def test___repr__(self):
class Test_header_to_mime_encoding(unittest.TestCase):
def _callFUT(self, value, not_email=False):
from pyramid_mailer.response import header_to_mime_encoding
- return header_to_mime_encoding(value, not_email=not_email)
+ return header_to_mime_encoding(value)
def test_empty_value(self):
result = self._callFUT('')
@@ -958,39 +958,13 @@ def test_empty_value(self):
def test_list_value(self):
L = ['chrism@plope.com', 'billg@microsoft.com']
result = self._callFUT(L)
- self.assertEqual(result, b'chrism@plope.com, billg@microsoft.com')
+ self.assertEqual(str(result), 'chrism@plope.com, billg@microsoft.com')
def test_nonempty_nonlist_value(self):
val = 'chrism@plope.com'
result = self._callFUT(val)
- self.assertEqual(result, b'chrism@plope.com')
-
-class Test_properly_encode_header(unittest.TestCase):
- def _callFUT(self, value, encoder, not_email):
- from pyramid_mailer.response import properly_encode_header
- return properly_encode_header(value, encoder, not_email)
-
- def test_ascii_encodable(self):
- result = self._callFUT('a', None, None)
- self.assertEqual(result, b'a')
-
- def test_not_ascii_encodable_email(self):
- la = b'LaPe\xc3\xb1a@plope.com'.decode('utf-8')
- class Encoder(object):
- def header_encode(self, val):
- return b'encoded'
- encoder = Encoder()
- result = self._callFUT(la, encoder, False)
- self.assertEqual(result, '"encoded" <LaPe\xf1a@plope.com>')
-
- def test_not_ascii_encodable(self):
- la = b'LaPe\xc3\xb1a'.decode('utf-8')
- class Encoder(object):
- def header_encode(self, val):
- return b'encoded'
- encoder = Encoder()
- result = self._callFUT(la, encoder, False)
- self.assertEqual(result, b'encoded')
+ self.assertEqual(str(result), 'chrism@plope.com')
+
class Dummy(object):
pass

0 comments on commit bc7ae3a

Please sign in to comment.