Switch to side-by-side view

--- a/Allura/allura/lib/mail_util.py
+++ b/Allura/allura/lib/mail_util.py
@@ -23,16 +23,39 @@
     return_path='forgemail.return_path')
 EMAIL_VALIDATOR=fev.Email(not_empty=True)
 
-def Header(text, charset):
-    '''Helper to make sure we don't over-encode headers
-
-    (gmail barfs with encoded email addresses.)'''
+def Header(text, *more_text):
+    '''Helper to make sure we encode headers properly'''
     if isinstance(text, header.Header):
         return text
-    hdr = header.Header('', charset)
-    for word in text.split(' '):
-        hdr.append(word)
-    return hdr
+    # email.header.Header handles str vs unicode differently
+    # see http://docs.python.org/library/email.header.html#email.header.Header.append
+    if type(text) != unicode:
+        raise TypeError('This must be unicode: %r' % text)
+    head = header.Header(text)
+    for m in more_text:
+        if type(m) != unicode:
+            raise TypeError('This must be unicode: %r' % text)
+        head.append(m)
+    return head
+
+def AddrHeader(fromaddr):
+    '''Accepts any of:
+        Header() instance
+        foo@bar.com
+        "Foo Bar" <foo@bar.com>
+    '''
+    if isinstance(fromaddr, basestring) and ' <' in fromaddr:
+        name, addr = fromaddr.rsplit(' <', 1)
+        addr = '<' + addr # restore the char we just split off
+        addrheader = Header(name, addr)
+        if str(addrheader).startswith('=?'): # encoding escape chars
+            # then quoting the name is no longer necessary
+            name = name.strip('"')
+            addrheader = Header(name, addr)
+    else:
+        addrheader = Header(fromaddr)
+    return addrheader
+
 
 def parse_address(addr):
     userpart, domain = addr.split('@')
@@ -103,7 +126,7 @@
 
 def encode_email_part(content, content_type):
     try:
-        return MIMEText(content.encode('iso-8859-1'), content_type, 'iso-8859-1')
+        return MIMEText(content.encode('ascii'), content_type, 'ascii')
     except:
         return MIMEText(content.encode('utf-8'), content_type, 'utf-8')
 
@@ -142,21 +165,19 @@
     def __init__(self):
         self._client = None
 
-    def sendmail(self, addrs, addrfrom, reply_to, subject, message_id, in_reply_to, message):
+    def sendmail(self, addrs, fromaddr, reply_to, subject, message_id, in_reply_to, message):
         if not addrs: return
-        charset = message.get_charset()
-        if charset is None:
-            charset = 'iso-8859-1'
-        message['To'] = Header(reply_to, charset)
-        message['From'] = Header(addrfrom, charset)
-        message['Reply-To'] = Header(reply_to, charset)
-        message['Subject'] = Header(subject, charset)
-        message['Message-ID'] = Header('<' + message_id + '>', charset)
+        # We send one message with multiple envelope recipients, so use a generic To: addr
+        # It might be nice to refactor to send one message per recipient, and use the actual To: addr
+        message['To'] = Header(reply_to)
+        message['From'] = AddrHeader(fromaddr)
+        message['Reply-To'] = Header(reply_to)
+        message['Subject'] = Header(subject)
+        message['Message-ID'] = Header('<' + message_id + u'>')
         if in_reply_to:
-            if isinstance(in_reply_to, basestring):
-                in_reply_to = [ in_reply_to ]
-            in_reply_to = ','.join(('<' + irt + '>') for irt in in_reply_to)
-            message['In-Reply-To'] = Header(in_reply_to, charset)
+            if not isinstance(in_reply_to, basestring):
+                raise TypeError('Only strings are supported now, not lists')
+            message['In-Reply-To'] = Header(u'<%s>' % in_reply_to)
         content = message.as_string()
         smtp_addrs = map(_parse_smtp_addr, addrs)
         smtp_addrs = [ a for a in smtp_addrs if isvalid(a) ]