Commits

Andriy Kornatskyy committed 0f5e0a2

Added mail_address helper to properly handle unicode names and IDNA.

Comments (0)

Files changed (2)

src/wheezy/core/mail.py

 try:
     from email.charset import CHARSETS
     from email.encoders import encode_base64
+    from email.header import Header
     from email.message import Message
+    from email.utils import formataddr
     from email.utils import formatdate
     from email.utils import make_msgid
 except ImportError:  # pragma: nocover, python2.4
     from email.Charset import CHARSETS  # noqa
     from email.Encoders import encode_base64  # noqa
+    from email.Header import Header
     from email.Message import Message  # noqa
+    from email.Utils import formataddr  # noqa
     from email.Utils import formatdate  # noqa
     from email.Utils import make_msgid  # noqa
 
 CHARSETS['utf-8'] = (3, None, 'utf-8')
 
 
+def mail_address(addr, name=None, charset='utf8'):
+    """ Returns mail address formatted string.
+    """
+    try:
+        addr.encode('us-ascii')
+    except UnicodeEncodeError:
+        addr = '@'.join([p.encode('idna').decode('us-ascii')
+                         for p in addr.split('@', 1)])
+    return name and formataddr((mime_header(name, charset), addr)) or addr
+
+
 class MailMessage(object):
     """ Mail message.
     """
         """ Creates an attachment from file.
         """
         ignore, name = path_split(path)
-        print(open.__module__)
         f = open(path, 'rb')
         try:
             return cls(name, f.read())
 # region: internal details
 
 def mime(message):
-    m = mime_part(message.content, message.content_type, message.charset)
+    charset = message.charset
+    m = mime_part(message.content, message.content_type, charset)
     subparts = message.content and [m] or []
     if message.alternatives:
         subparts += [mime_alternative(a) for a in message.alternatives]
     return m
 
 
+def mime_header(value, charset):
+    try:
+        value.encode('us-ascii')
+    except UnicodeEncodeError:
+        return Header(value, charset).encode()
+    else:
+        return value
+
+
 def mime_part(content, content_type, content_charset=None):
     m = Message()
     m.add_header('Content-Type', content_type)

src/wheezy/core/tests/test_mail.py

         assert 'application/octet-stream' == r.content_type
 
 
+class MailAddressTestCase(unittest.TestCase):
+
+    def test_mail_and_name(self):
+        """ Name or mail are ascii valid.
+        """
+        from wheezy.core.mail import mail_address
+        assert 'someone@dev.local' == mail_address('someone@dev.local')
+        assert 'Someone <someone@dev.local>' == mail_address(
+            'someone@dev.local', 'Someone')
+        assert '=?utf-8?b?0L/RgNC40LLQtdGC?= <x@dev.local>' == mail_address(
+            'x@dev.local',
+            '\\u043f\\u0440\\u0438\\u0432\\u0435\\u0442'.decode(
+                'unicode_escape'))
+
+    def test_idna(self):
+        """ IDNA mail
+        """
+        from wheezy.core.mail import mail_address
+        mail = '\\u043f\\u0440\\u0438\\u0432\\u0435\\u0442@dev.local'.decode(
+            'unicode_escape')
+        assert 'xn--b1agh1afp@dev.local' == mail_address(mail)
+        assert '=?utf-8?b?0L/RgNC40LLQtdGC?= <xn--b1agh1afp@dev.local>' == \
+            mail_address(
+                mail,
+                '\\u043f\\u0440\\u0438\\u0432\\u0435\\u0442'.decode(
+                    'unicode_escape'))
+        mail = 'x@\\u043f\\u043e\\u0447\\u0442\\u0430.\\u0440\\u0443'.decode(
+            'unicode_escape')
+        assert 'x@xn--80a1acny.xn--p1ag' == mail_address(mail)
+        mail = '\\u043f\\u0440\\u0438\\u0432\\u0435\\u0442@' \
+               '\\u043f\\u043e\\u0447\\u0442\\u0430.\\u0440\\u0443'.decode(
+                   'unicode_escape')
+        assert 'xn--b1agh1afp@xn--80a1acny.xn--p1ag' == mail_address(mail)
+
+
 class SMTPClientTestCase(unittest.TestCase):
 
     def setUp(self):
         assert 'al' == subparts[1].get_payload()
 
 
+class MimeHeaderTestCase(unittest.TestCase):
+
+    def test_ascii(self):
+        """ Value is ascii valid.
+        """
+        from wheezy.core.mail import mime_header
+        assert 'x' == mime_header('x', 'ascii')
+
+    def test_encode(self):
+        """ Value is not ascii valid.
+        """
+        from wheezy.core.mail import mime_header
+        value = '\\u043f\\u0440\\u0438\\u0432\\u0435\\u0442'.decode(
+            'unicode_escape')
+        assert '=?utf-8?b?0L/RgNC40LLQtdGC?=' == mime_header(value, 'utf-8')
+
+
 class MIMEPartsTestCase(unittest.TestCase):
 
     def test_part(self):