[MERGE] forward port of branch 7.0 up to be7c894
[odoo/odoo.git] / addons / mail / mail_mail.py
index f984f8f..9dcea07 100644 (file)
@@ -22,6 +22,7 @@
 import base64
 import logging
 import re
+from email.utils import formataddr
 from urllib import urlencode
 from urlparse import urljoin
 
@@ -210,10 +211,9 @@ class mail_mail(osv.Model):
         # 2. if 'partner' is specified, but no related document: Partner Name <email>
         # 3; fallback on mail.email_to that we split to have an email addresses list
         if partner and mail.record_name:
-            sanitized_record_name = re.sub(r'[^\w+.]+', '-', mail.record_name)
-            email_to = [_('"Followers of %s" <%s>') % (sanitized_record_name, partner.email)]
+            email_to = [formataddr((_('Followers of %s') % mail.record_name, partner.email))]
         elif partner:
-            email_to = ['%s <%s>' % (partner.name, partner.email)]
+            email_to = [formataddr((partner.name, partner.email))]
         else:
             email_to = tools.email_split(mail.email_to)
 
@@ -240,13 +240,17 @@ class mail_mail(osv.Model):
             :return: True
         """
         ir_mail_server = self.pool.get('ir.mail_server')
-                
+        ir_attachment = self.pool['ir.attachment']
+
         for mail in self.browse(cr, SUPERUSER_ID, ids, context=context):
             try:
-                # handle attachments
-                attachments = []
-                for attach in mail.attachment_ids:
-                    attachments.append((attach.datas_fname, base64.b64decode(attach.datas)))
+                # load attachment binary data with a separate read(), as prefetching all
+                # `datas` (binary field) could bloat the browse cache, triggerring
+                # soft/hard mem limits with temporary data.
+                attachment_ids = [a.id for a in mail.attachment_ids]
+                attachments = [(a['datas_fname'], base64.b64decode(a['datas']))
+                                 for a in ir_attachment.read(cr, SUPERUSER_ID, attachment_ids,
+                                                             ['datas_fname', 'datas'])]
                 # specific behavior to customize the send email for notified partners
                 email_list = []
                 if mail.email_to:
@@ -281,10 +285,20 @@ class mail_mail(osv.Model):
                         subtype='html',
                         subtype_alternative='plain',
                         headers=headers)
-                    res = ir_mail_server.send_email(cr, uid, msg,
+                    try:
+                        res = ir_mail_server.send_email(cr, uid, msg,
                                                     mail_server_id=mail.mail_server_id.id,
                                                     context=context)
-
+                    except AssertionError as error:
+                        if error.message == ir_mail_server.NO_VALID_RECIPIENT:
+                            # No valid recipient found for this particular
+                            # mail item -> ignore error to avoid blocking
+                            # delivery to next recipients, if any. If this is
+                            # the only recipient, the mail will show as failed.
+                            _logger.warning("Ignoring invalid recipients for mail.mail %s: %s",
+                                            mail.message_id, email.get('email_to'))
+                        else:
+                            raise
                 if res:
                     mail.write({'state': 'sent', 'message_id': res})
                     mail_sent = True
@@ -295,10 +309,14 @@ class mail_mail(osv.Model):
                 # /!\ can't use mail.state here, as mail.refresh() will cause an error
                 # see revid:odo@openerp.com-20120622152536-42b2s28lvdv3odyr in 6.1
                 if mail_sent:
+                    _logger.info('Mail with ID %r and Message-Id %r successfully sent', mail.id, mail.message_id)
                     self._postprocess_sent_message(cr, uid, mail, context=context)
             except MemoryError:
                 # prevent catching transient MemoryErrors, bubble up to notify user or abort cron job
                 # instead of marking the mail as failed
+                _logger.exception('MemoryError while processing mail with ID %r and Msg-Id %r. '\
+                                      'Consider raising the --limit-memory-hard startup option',
+                                  mail.id, mail.message_id)
                 raise
             except Exception as e:
                 _logger.exception('failed sending mail.mail %s', mail.id)