← Back to team overview

openerp-dev-web team mailing list archive

[Merge] lp:~openerp-dev/openobject-server/6.0-bug-732512-xrg into lp:openobject-server/6.0

 

xrg has proposed merging lp:~openerp-dev/openobject-server/6.0-bug-732512-xrg into lp:openobject-server/6.0.

Requested reviews:
  OpenERP Core Team (openerp)
Related bugs:
  Bug #732512 in OpenERP Server: "tools/misc: try harder to encode smtp address headers"
  https://bugs.launchpad.net/openobject-server/+bug/732512

For more details, see:
https://code.launchpad.net/~openerp-dev/openobject-server/6.0-bug-732512-xrg/+merge/52827
-- 
https://code.launchpad.net/~openerp-dev/openobject-server/6.0-bug-732512-xrg/+merge/52827
Your team OpenERP R&D Team is subscribed to branch lp:~openerp-dev/openobject-server/6.0-bug-732512-xrg.
=== modified file 'bin/tools/misc.py'
--- bin/tools/misc.py	2011-01-20 12:40:20 +0000
+++ bin/tools/misc.py	2011-03-10 11:29:28 +0000
@@ -43,6 +43,7 @@
 from email.Header import Header
 from email.Utils import formatdate, COMMASPACE
 from email import Encoders
+from email import Charset
 from itertools import islice, izip
 from lxml import etree
 from which import which
@@ -432,12 +433,14 @@
         :return: True if the mail was delivered successfully to the smtp,
                  else False (+ exception logged)
     """
+    logger = logging.getLogger('email_send')
+    
     class WriteToLogger(object):
-        def __init__(self):
-            self.logger = netsvc.Logger()
+        def __init__(self, logger):
+            self.logger = logger
 
         def write(self, s):
-            self.logger.notifyChannel('email_send', netsvc.LOG_DEBUG, s)
+            self.logger.debug(s)
 
     if openobject_id:
         message['Message-Id'] = generate_tracking_message_id(openobject_id)
@@ -448,19 +451,22 @@
         if smtp_server.startswith('maildir:/'):
             from mailbox import Maildir
             maildir_path = smtp_server[8:]
-            mdir = Maildir(maildir_path,factory=None, create = True)
+            mdir = Maildir(maildir_path, factory=None, create = True)
             mdir.add(message.as_string(True))
+            logger.info("1 message to %d recepients saved at %s. Dry run.", len(smtp_to_list), maildir_path)
             return True
 
         oldstderr = smtplib.stderr
-        if not ssl: ssl = config.get('smtp_ssl', False)
+        ssl = ssl or config.get('smtp_ssl', False)
         s = smtplib.SMTP()
         try:
             # in case of debug, the messages are printed to stderr.
             if debug:
-                smtplib.stderr = WriteToLogger()
+                smtplib.stderr = WriteToLogger(logger)
 
+            logger.debug("Sending Message to %s through %s", ','.join(smtp_to_list), smtp_server)
             s.set_debuglevel(int(bool(debug)))  # 0 or 1
+            
             s.connect(smtp_server, config['smtp_port'])
             if ssl:
                 s.ehlo()
@@ -471,6 +477,7 @@
                 s.login(config['smtp_user'], config['smtp_password'])
 
             s.sendmail(smtp_from, smtp_to_list, message.as_string())
+            logger.info("1 message sent to %d recepients through %s", len(smtp_to_list), smtp_server)
         finally:
             try:
                 s.quit()
@@ -481,7 +488,7 @@
                 pass
 
     except Exception:
-        _logger.error('could not deliver email', exc_info=True)
+        logger.error('could not deliver email', exc_info=True)
         return False
 
     return True
@@ -503,50 +510,109 @@
     if x_headers is None:
         x_headers = {}
 
+    def Header_Encoded(hstr):
+        """Format hstr for an email header, possibly through utf8 encoding
+        """
+
+        if not isinstance(hstr, unicode):
+            hstr = ustr(hstr)
+
+        try:
+            return hstr.encode('us-ascii')
+        except UnicodeError:
+            return Header(hstr, 'utf-8')
+
+    email_adre = re.compile(r'^((?:"[^"]+?")|(?:[^,<]+?)|\A)\s*<([\w\.-~]+?(?:@[\w\.-~]+)?)>$')
+    email_charset = config.get_misc('smtp', 'charset', None)
+
+    def Address_Encoded(inp, header_name=None):
+        """ Encode the inp address into a valid RFC 2047 header
+        The problem is, that some mail transports (like Postfix <= 2.5) don't
+        like all the header encoded into one string (that spans lines) and the
+        protocol doesn't provide a line-continuation mark around that case.
+        So, we have to split tokens and encode them separately.
+        """
+        if isinstance(inp, basestring):
+            inp = [inp,]
+
+        tokens = []
+        for itok in inp:
+            if not isinstance(itok, unicode):
+                itok = ustr(itok)
+            itok = itok.strip()
+            if not itok: # skip empty ones
+                continue
+            if tokens:
+                tokens.append(',')
+            m = email_adre.match(itok)
+            if m:
+                if m.group(1):
+                    tokens.append(m.group(1))
+                tokens.append('<' + m.group(2) + '>')
+            else:
+                tokens.append(itok)
+        
+        # Now, encode them one by one
+        ret = Header(header_name=header_name)
+        for t in tokens:
+            # this method will try 'us-ascii', email_charset and 'utf-8'
+            ret.append(t, charset=email_charset)
+        
+        return ret
 
     if not (email_from or config['email_from']):
         raise ValueError("Sending an email requires either providing a sender "
                          "address or having configured one")
 
     if not email_from: email_from = config.get('email_from', False)
-    email_from = ustr(email_from).encode('utf-8')
 
     if not email_cc: email_cc = []
     if not email_bcc: email_bcc = []
-    if not body: body = u''
-
-    email_body = ustr(body).encode('utf-8')
-    email_text = MIMEText(email_body or '',_subtype=subtype,_charset='utf-8')
-
-    msg = MIMEMultipart()
-
-    msg['Subject'] = Header(ustr(subject), 'utf-8')
-    msg['From'] = email_from
+    body_charset = 'us-ascii'
+    if not body:
+        body = u''
+    else:
+        body = ustr(body)
+        try:
+            txt_body = body.encode('us-ascii')
+        except UnicodeError:
+            txt_body = body.encode('utf-8')
+            body_charset = 'utf-8'
+
+    email_text = MIMEText(txt_body,_subtype=subtype,_charset=body_charset)
+
+    if attach or (html2text and subtype == 'html'):
+        msg = MIMEMultipart()
+    else:
+        msg = email_text
+
+    msg['Subject'] = Header_Encoded(subject)
+    msg['From'] = Address_Encoded(email_from, 'From')
     del msg['Reply-To']
     if reply_to:
-        msg['Reply-To'] = reply_to
+        msg['Reply-To'] = Address_Encoded(reply_to, 'Reply-To')
     else:
         msg['Reply-To'] = msg['From']
-    msg['To'] = COMMASPACE.join(email_to)
+    msg['To'] = Address_Encoded(email_to, 'To')
     if email_cc:
-        msg['Cc'] = COMMASPACE.join(email_cc)
+        msg['Cc'] = Address_Encoded(email_cc, 'Cc')
     if email_bcc:
-        msg['Bcc'] = COMMASPACE.join(email_bcc)
+        msg['Bcc'] = Address_Encoded(email_bcc, 'Bcc')
     msg['Date'] = formatdate(localtime=True)
 
     msg['X-Priority'] = priorities.get(priority, '3 (Normal)')
 
     # Add dynamic X Header
     for key, value in x_headers.iteritems():
-        msg['%s' % key] = str(value)
+        msg['%s' % key] =  Header_Encoded(value)
 
     if html2text and subtype == 'html':
-        text = html2text(email_body.decode('utf-8')).encode('utf-8')
+        text = html2text(body).encode(body_charset)
         alternative_part = MIMEMultipart(_subtype="alternative")
-        alternative_part.attach(MIMEText(text, _charset='utf-8', _subtype='plain'))
+        alternative_part.attach(MIMEText(text, _charset=body_charset, _subtype='plain'))
         alternative_part.attach(email_text)
         msg.attach(alternative_part)
-    else:
+    elif msg is not email_text:
         msg.attach(email_text)
 
     if attach:


Follow ups