Ejemplo n.º 1
0
    def test_update_email_smtp_fail(self, mocked_add_status_msg):
        data = {
            'email': '*****@*****.**',
        }
        request = fake_request(form_data=data, method='POST')
        # get the form to edit partner1
        form = self.get_form('cms.form.my.account',
                             sudo_uid=self.user1.id,
                             req=request,
                             main_object=self.partner1)
        form.o_request.website = self.env['website'].browse(1)

        with mock.patch.object(type(form), '_logout_and_notify') as mocked:
            with mock.patch.object(
                    type(form), '_handle_login_update',
                    **{'side_effect': MailDeliveryException('err', 'val')}):
                form.form_process()
                mocked.assert_not_called()
Ejemplo n.º 2
0
    def send(self, auto_commit=False, raise_exception=False):
        """ Sends the selected emails immediately, ignoring their current
            state (mails that have already been sent should not be passed
            unless they should actually be re-sent).
            Emails successfully delivered are marked as 'sent', and those
            that fail to be deliver are marked as 'exception', and the
            corresponding error mail is output in the server logs.

            :param bool auto_commit: whether to force a commit of the mail status
                after sending each mail (meant only for scheduler processing);
                should never be True during normal transactions (default: False)
            :param bool raise_exception: whether to raise an exception if the
                email sending process has failed
            :return: True
        """
        for server_id, batch_ids in self._split_by_server():
            smtp_session = None
            try:
                smtp_session = self.env['ir.mail_server'].connect(
                    mail_server_id=server_id)
            except Exception as exc:
                if raise_exception:
                    # To be consistent and backward compatible with mail_mail.send() raised
                    # exceptions, it is encapsulated into an Odoo MailDeliveryException
                    raise MailDeliveryException(
                        _('Unable to connect to SMTP Server'), exc)
                else:
                    self.browse(batch_ids).write({
                        'state': 'exception',
                        'failure_reason': exc
                    })
            else:
                self.browse(batch_ids)._send(auto_commit=auto_commit,
                                             raise_exception=raise_exception,
                                             smtp_session=smtp_session)
                _logger.info('Sent batch %s emails via mail server ID #%s',
                             len(batch_ids), server_id)
            finally:
                if smtp_session:
                    smtp_session.quit()
Ejemplo n.º 3
0
    def send(self, auto_commit=False, raise_exception=False):
        """ Sends the selected emails immediately, ignoring their current
            state (mails that have already been sent should not be passed
            unless they should actually be re-sent).
            Emails successfully delivered are marked as 'sent', and those
            that fail to be deliver are marked as 'exception', and the
            corresponding error mail is output in the server logs.

            :param bool auto_commit: whether to force a commit of the mail status
                after sending each mail (meant only for scheduler processing);
                should never be True during normal transactions (default: False)
            :param bool raise_exception: whether to raise an exception if the
                email sending process has failed
            :return: True
        """
        IrMailServer = self.env['ir.mail_server']
        domain_link = self.env['ir.config_parameter'].get_param('web.base.url')
        for mail_id in self.ids:
            try:
                mail = self.browse(mail_id)
                # TDE note: remove me when model_id field is present on mail.message - done here to avoid doing it multiple times in the sub method
                if mail.model:
                    model = self.env['ir.model'].sudo().search([
                        ('model', '=', mail.model)
                    ])[0]
                else:
                    model = None
                if model:
                    mail = mail.with_context(model_name=model.name)

                # 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.
                attachments = [(a['datas_fname'], base64.b64decode(a['datas']))
                               for a in mail.attachment_ids.sudo().read(
                                   ['datas_fname', 'datas'])]

                # specific behavior to customize the send email for notified partners
                email_list = []
                if mail.email_to:
                    email_list.append(mail.send_get_email_dict())
                for partner in mail.recipient_ids:
                    email_data = mail.send_get_email_dict(partner=partner)
                    email_data.update({'partner_id': partner.id})
                    email_list.append(email_data)

                # headers
                headers = {}
                bounce_alias = self.env['ir.config_parameter'].get_param(
                    "mail.bounce.alias")
                catchall_domain = self.env['ir.config_parameter'].get_param(
                    "mail.catchall.domain")
                if bounce_alias and catchall_domain:
                    if mail.model and mail.res_id:
                        headers['Return-Path'] = '%s+%d-%s-%d@%s' % (
                            bounce_alias, mail.id, mail.model, mail.res_id,
                            catchall_domain)
                    else:
                        headers['Return-Path'] = '%s+%d@%s' % (
                            bounce_alias, mail.id, catchall_domain)
                if mail.headers:
                    try:
                        headers.update(safe_eval(mail.headers))
                    except Exception:
                        pass

                # Writing on the mail object may fail (e.g. lock on user) which
                # would trigger a rollback *after* actually sending the email.
                # To avoid sending twice the same email, provoke the failure earlier
                mail.write({
                    'state':
                    'exception',
                    'failure_reason':
                    _('Error without exception. Probably due do sending an email without computed recipients.'
                      ),
                })
                mail_sent = False

                # build an RFC2822 email.message.Message object and send it without queuing
                res = None
                for email in email_list:
                    body = email.get('body')
                    for attachment in mail.attachment_ids:
                        data = {
                            'mail_id':
                            mail.id,
                            'crete_uid':
                            1,
                            'attachment_id':
                            str(attachment.id),
                            'partner_id':
                            email.get('partner_id'),
                            'email_to':
                            email.get('email_to') and email.get('email_to')[0]
                            or False,
                        }
                        queue = self.env['otp.link.queue'].create(data)
                        link_download = domain_link + '''/otp/download/%s''' % str(
                            queue.id)
                        fname = attachment.datas_fname or ''
                        body += "\n<a href='" + link_download + """' style='display: inline-block;
                                margin-top: 8px;
                                white-space: nowrap;
                                padding: 6px 15px;
                                font-size: 14px;
                                line-height: 1.42857143;
                                border-radius: 2px;
                                text-decoration: none;
                                color: #fff;
                                border-color: #21b799;
                                background-color: #21b799;
                                font-weight:bold'>Click to download """ + fname + "</a><br/>"

                    msg = IrMailServer.build_email(
                        email_from=mail.email_from,
                        email_to=email.get('email_to'),
                        subject=mail.subject,
                        body=body,
                        body_alternative=email.get('body_alternative'),
                        email_cc=tools.email_split(mail.email_cc),
                        reply_to=mail.reply_to,
                        # attachments=attachments,
                        message_id=mail.message_id,
                        references=mail.references,
                        object_id=mail.res_id
                        and ('%s-%s' % (mail.res_id, mail.model)),
                        subtype='html',
                        subtype_alternative='plain',
                        headers=headers)
                    try:
                        res = IrMailServer.send_email(
                            msg, mail_server_id=mail.mail_server_id.id)
                    except AssertionError as error:
                        if error.message == IrMailServer.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.info(
                                "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,
                        'failure_reason': False
                    })
                    mail_sent = True
                # /!\ can't use mail.state here, as mail.refresh() will cause an error
                # see revid:[email protected] in 6.1
                if mail_sent:
                    _logger.info(
                        'Mail with ID %r and Message-Id %r successfully sent',
                        mail.id, mail.message_id)
                mail._postprocess_sent_message(mail_sent=mail_sent)
            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 psycopg2.Error:
                # If an error with the database occurs, chances are that the cursor is unusable.
                # This will lead to an `psycopg2.InternalError` being raised when trying to write
                # `state`, shadowing the original exception and forbid a retry on concurrent
                # update. Let's bubble it.
                raise
            except Exception as e:
                failure_reason = tools.ustr(e)
                _logger.exception('failed sending mail (id: %s) due to %s',
                                  mail.id, failure_reason)
                mail.write({
                    'state': 'exception',
                    'failure_reason': failure_reason
                })
                mail._postprocess_sent_message(mail_sent=False)
                if raise_exception:
                    if isinstance(e, AssertionError):
                        # get the args of the original error, wrap into a value and throw a MailDeliveryException
                        # that is an except_orm, with name and value as arguments
                        value = '. '.join(e.args)
                        raise MailDeliveryException(_("Mail Delivery Failed"),
                                                    value)
                    raise

            if auto_commit is True:
                self._cr.commit()
        return True
Ejemplo n.º 4
0
    def send(self, auto_commit=False, raise_exception=False):
        """ Sends the selected emails immediately, ignoring their current
            state (mails that have already been sent should not be passed
            unless they should actually be re-sent).
            Emails successfully delivered are marked as 'sent', and those
            that fail to be deliver are marked as 'exception', and the
            corresponding error mail is output in the server logs.

            :param bool auto_commit: whether to force a commit of the mail status
                after sending each mail (meant only for scheduler processing);
                should never be True during normal transactions (default: False)
            :param bool raise_exception: whether to raise an exception if the
                email sending process has failed
            :return: True
        """
        server_email_line = self.env['ir.mail_server.email.line']
        default_outgoing = self.env['ir.mail_server']

        IrMailServer = self.env['ir.mail_server.outgoing']
        mail_server_id = False

        for mail_id in self.ids:
            try:
                mail = self.browse(mail_id)
                email_from = parseaddr(mail.email_from)[1]

                # Check Email From Helpdesk and check case for AWS ESE email

                if mail.model and mail.model == 'helpdesk.ticket' and mail.res_id:
                    try:
                        mail_server_ids = server_email_line.sudo().search([
                            ('name', '=', email_from)
                        ]).mapped('server_id')
                        mail_server_id = mail_server_ids.sorted(
                            key=lambda r: r.sequence)[0].id
                        IrMailServer = self.env['ir.mail_server']
                    except:
                        raise Warning(
                            'Please config Outgoing Mail for account %s' %
                            (email_from))
                else:
                    if self._uid:
                        user_id = self.env['res.users'].browse(self._uid)
                        if user_id.outgoing_mail_server and user_id.outgoing_mail_server.id:
                            mail_server_id = user_id.outgoing_mail_server.id
                        mail_server_ids = server_email_line.sudo().search([
                            ('name', '=', email_from)
                        ]).mapped('server_id')
                        server_ids = mail_server_ids.sorted(
                            key=lambda r: r.sequence)
                        if server_ids and len(server_ids) > 0:
                            mail_server_id = server_ids[0].id
                            IrMailServer = self.env['ir.mail_server']

                # TDE note: remove me when model_id field is present on mail.message - done here to avoid doing it multiple times in the sub method
                if mail.model:
                    model = self.env['ir.model'].sudo().search([
                        ('model', '=', mail.model)
                    ])[0]
                else:
                    model = None
                if model:
                    mail = mail.with_context(model_name=model.name)

                # 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.
                attachments = [(a['datas_fname'], base64.b64decode(a['datas']))
                               for a in mail.attachment_ids.sudo().read(
                                   ['datas_fname', 'datas'])]

                # specific behavior to customize the send email for notified partners
                email_list = []
                if mail.email_to:
                    email_list.append(mail.send_get_email_dict())
                for partner in mail.recipient_ids:
                    email_list.append(
                        mail.send_get_email_dict(partner=partner))

                # headers
                headers = {}
                bounce_alias = self.env['ir.config_parameter'].get_param(
                    "mail.bounce.alias")
                catchall_domain = self.env['ir.config_parameter'].get_param(
                    "mail.catchall.domain")
                if bounce_alias and catchall_domain:
                    if mail.model and mail.res_id:
                        headers['Return-Path'] = '%s+%d-%s-%d@%s' % (
                            bounce_alias, mail.id, mail.model, mail.res_id,
                            catchall_domain)
                    else:
                        headers['Return-Path'] = '%s+%d@%s' % (
                            bounce_alias, mail.id, catchall_domain)
                headers['Return-Path'] = mail.email_from
                if mail.headers:
                    try:
                        headers.update(safe_eval(mail.headers))
                    except Exception:
                        pass

                # Writing on the mail object may fail (e.g. lock on user) which
                # would trigger a rollback *after* actually sending the email.
                # To avoid sending twice the same email, provoke the failure earlier
                mail.write({
                    'state':
                    'exception',
                    'failure_reason':
                    _('Error without exception. Probably due do sending an email without computed recipients.'
                      ),
                })
                mail_sent = False
                email_cc = ''
                if mail.email_cc:
                    email_cc = mail.email_cc
                if mail.email_cc_partner and len(mail.email_cc_partner) > 0:
                    for partner_id in mail.email_cc_partner:
                        if email_cc == '':
                            email_cc += partner_id.email or ''
                        else:
                            email_cc += ',' + partner_id.email or ''
                # build an RFC2822 email.message.Message object and send it without queuing
                res = None
                for email in email_list:
                    msg = IrMailServer.build_email(
                        email_from=mail.email_from,
                        email_to=email.get('email_to'),
                        subject=mail.subject,
                        body=email.get('body'),
                        body_alternative=email.get('body_alternative'),
                        email_cc=tools.email_split(email_cc),
                        reply_to=mail.reply_to,
                        attachments=attachments,
                        message_id=mail.message_id,
                        references=mail.references,
                        object_id=mail.res_id
                        and ('%s-%s' % (mail.res_id, mail.model)),
                        subtype='html',
                        subtype_alternative='plain',
                        headers=headers)
                    try:
                        res = IrMailServer.send_email(
                            msg, mail_server_id=mail_server_id)
                    except AssertionError as error:
                        if error.message == IrMailServer.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.info(
                                "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,
                        'failure_reason': False
                    })
                    mail_sent = True

                # /!\ can't use mail.state here, as mail.refresh() will cause an error
                # see revid:[email protected] in 6.1
                if mail_sent:
                    _logger.info(
                        'Mail with ID %r and Message-Id %r successfully sent',
                        mail.id, mail.message_id)
                mail._postprocess_sent_message(mail_sent=mail_sent)
            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 psycopg2.Error:
                # If an error with the database occurs, chances are that the cursor is unusable.
                # This will lead to an `psycopg2.InternalError` being raised when trying to write
                # `state`, shadowing the original exception and forbid a retry on concurrent
                # update. Let's bubble it.
                raise
            except Exception as e:
                failure_reason = tools.ustr(e)
                _logger.exception('failed sending mail (id: %s) due to %s',
                                  mail.id, failure_reason)
                mail.write({
                    'state': 'exception',
                    'failure_reason': failure_reason
                })
                mail._postprocess_sent_message(mail_sent=False)
                if raise_exception:
                    if isinstance(e, AssertionError):
                        # get the args of the original error, wrap into a value and throw a MailDeliveryException
                        # that is an except_orm, with name and value as arguments
                        value = '. '.join(e.args)
                        raise MailDeliveryException(_("Mail Delivery Failed"),
                                                    value)
                    raise

            if auto_commit is True:
                self._cr.commit()
        return True
Ejemplo n.º 5
0
    def send(self, auto_commit=False, raise_exception=False):
        IrMailServer = self.env['ir.mail_server']

        for mail in self:
            try:
                # TDE note: remove me when model_id field is present on mail.message - done here to avoid doing it multiple times in the sub method
                if mail.model:
                    model = self.env['ir.model'].sudo().search([
                        ('model', '=', mail.model)
                    ])[0]
                else:
                    model = None
                if model:
                    mail = mail.with_context(model_name=model.name)

                # 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.
                attachments = [(a['datas_fname'], base64.b64decode(a['datas']))
                               for a in mail.attachment_ids.sudo().read(
                                   ['datas_fname', 'datas'])]

                # specific behavior to customize the send email for notified partners
                email_list = []
                if mail.email_to:
                    email_list.append(mail.send_get_email_dict())
                for partner in mail.recipient_ids:
                    email_list.append(
                        mail.send_get_email_dict(partner=partner))
                # email cc
                email_cc = ''
                for partner in mail.email_partner_cc:
                    email_cc += partner.email + ';'
                #email bcc
                email_bcc = ''
                for partner in mail.email_partner_bcc:
                    email_bcc += partner.email + ';'
#                 headers
                headers = {}
                bounce_alias = self.env['ir.config_parameter'].get_param(
                    "mail.bounce.alias")
                catchall_domain = self.env['ir.config_parameter'].get_param(
                    "mail.catchall.domain")
                if bounce_alias and catchall_domain:
                    if mail.model and mail.res_id:
                        headers['Return-Path'] = '%s-%d-%s-%d@%s' % (
                            bounce_alias, mail.id, mail.model, mail.res_id,
                            catchall_domain)
                    else:
                        headers['Return-Path'] = '%s-%d@%s' % (
                            bounce_alias, mail.id, catchall_domain)
                if mail.headers:
                    try:
                        headers.update(eval(mail.headers))
                    except Exception:
                        pass

                # Writing on the mail object may fail (e.g. lock on user) which
                # would trigger a rollback *after* actually sending the email.
                # To avoid sending twice the same email, provoke the failure earlier
                mail.write({
                    'state':
                    'exception',
                    'failure_reason':
                    _('Error without exception. Probably due do sending an email without computed recipients.'
                      ),
                })
                mail_sent = False

                # build an RFC2822 email.message.Message object and send it without queuing
                res = None
                for email in email_list:
                    msg = IrMailServer.build_email(
                        email_from=mail.email_from,
                        email_to=email.get('email_to'),
                        subject=mail.subject,
                        body=email.get('body'),
                        body_alternative=email.get('body_alternative'),
                        email_cc=tools.email_split(email_cc),
                        email_bcc=tools.email_split(email_bcc),
                        reply_to=mail.reply_to,
                        attachments=attachments,
                        message_id=mail.message_id,
                        references=mail.references,
                        object_id=mail.res_id
                        and ('%s-%s' % (mail.res_id, mail.model)),
                        subtype='html',
                        subtype_alternative='plain',
                        headers=headers)
                    try:
                        res = IrMailServer.send_email(
                            msg, mail_server_id=mail.mail_server_id.id)
                    except AssertionError as error:
                        if error.message == IrMailServer.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.info(
                                "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,
                        'failure_reason': False
                    })
                    mail_sent = True

                # /!\ can't use mail.state here, as mail.refresh() will cause an error
                # see revid:[email protected] in 6.1
                if mail_sent:
                    _logger.info(
                        'Mail with ID %r and Message-Id %r successfully sent',
                        mail.id, mail.message_id)
                mail._postprocess_sent_message(mail_sent=mail_sent)
            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 psycopg2.Error:
                # If an error with the database occurs, chances are that the cursor is unusable.
                # This will lead to an `psycopg2.InternalError` being raised when trying to write
                # `state`, shadowing the original exception and forbid a retry on concurrent
                # update. Let's bubble it.
                raise
            except Exception as e:
                failure_reason = tools.ustr(e)
                _logger.exception('failed sending mail (id: %s) due to %s',
                                  mail.id, failure_reason)
                mail.write({
                    'state': 'exception',
                    'failure_reason': failure_reason
                })
                mail._postprocess_sent_message(mail_sent=False)
                if raise_exception:
                    if isinstance(e, AssertionError):
                        # get the args of the original error, wrap into a value and throw a MailDeliveryException
                        # that is an except_orm, with name and value as arguments
                        value = '. '.join(e.args)
                        raise MailDeliveryException(_("Mail Delivery Failed"),
                                                    value)
                    raise

            if auto_commit is True:
                self._cr.commit()
        return True
Ejemplo n.º 6
0
    def send(self, auto_commit=False, raise_exception=False):
        """ Sends the selected emails immediately, ignoring their current
            state (mails that have already been sent should not be passed
            unless they should actually be re-sent).
            Emails successfully delivered are marked as 'sent', and those
            that fail to be deliver are marked as 'exception', and the
            corresponding error mail is output in the server logs.

            :param bool auto_commit: whether to force a commit of the mail status
                after sending each mail (meant only for scheduler processing);
                should never be True during normal transactions (default: False)
            :param bool raise_exception: whether to raise an exception if the
                email sending process has failed
            :return: True
        """

        ir_mail_server = self.env['ir.mail_server']
        ir_values = self.env['ir.values']
        default_mail_server = ir_values.get_default('mail.mail',
                                                    'mail_server_id')
        existing_ir_mail_server = self.env['ir.mail_server'].sudo().search([
            ('id', '=', default_mail_server)
        ])
        if len(existing_ir_mail_server) > 0:
            default_mail_address = existing_ir_mail_server.smtp_user
        else:
            default_mail_address = False

        for mail in self.sudo().browse(self.ids):
            subject = mail.subject  # hier müssen wir Betreff extra holen
            try:
                # TDE note: remove me when model_id field is present on mail.message - done here to avoid doing it multiple times in the sub method
                if mail.model:
                    # beide self.pool
                    model_id = self.env['ir.model'].sudo().search([
                        ('model', '=', mail.model)
                    ])[0]
                    model = model_id
                else:
                    model = None
                if model:
                    mail = mail.with_context(model_name=model.name)

                # 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(self.sudo(), attachment_ids,
                #                                             ['datas_fname', 'datas'])]

                attachments = [(a['datas_fname'], base64.b64decode(a['datas']))
                               for a in mail.attachment_ids.sudo().read(
                                   ['datas_fname', 'datas'])]

                # specific behavior to customize the send email for notified partners
                email_list = []
                if mail.email_to:
                    email_list.append(mail.send_get_email_dict())

                for partner in mail.recipient_ids:
                    email_list.append(
                        mail.send_get_email_dict(partner=partner))

                # headers
                headers = {}
                bounce_alias = self.env['ir.config_parameter'].get_param(
                    "mail.bounce.alias")
                catchall_domain = self.env['ir.config_parameter'].get_param(
                    "mail.catchall.domain")

                ### Übernahme der Anpassung vom Equitania Modul (siehe ReleaseNotes des Equitania Moduls vom 15.01.2016)
                if bounce_alias and catchall_domain:
                    headers['Return-Path'] = '%s@%s' % (bounce_alias,
                                                        catchall_domain)
                else:
                    headers['Return-Path'] = mail.email_from


#### Kern-Version
#                 if bounce_alias and catchall_domain:
#                     if mail.model and mail.res_id:
#                         headers['Return-Path'] = '%s-%d-%s-%d@%s' % (bounce_alias, mail.id, mail.model, mail.res_id, catchall_domain)
#                     else:
#                         headers['Return-Path'] = '%s-%d@%s' % (bounce_alias, mail.id, catchall_domain)

                if mail.headers:
                    try:
                        headers.update(eval(mail.headers))
                    except Exception:
                        pass

                # Writing on the mail object may fail (e.g. lock on user) which
                # would trigger a rollback *after* actually sending the email.
                # To avoid sending twice the same email, provoke the failure earlier
                mail.write({'state': 'exception'})
                mail_sent = False

                # build an RFC2822 email.message.Message object and send it without queuing
                res = None
                context = {}

                user = context.get(self._uid, self.sudo())

                if user != self.sudo():
                    mail_server = ir_mail_server.search([('user_id', '=', user)
                                                         ])
                else:
                    partner_id = mail.author_id.id
                    res_users_pool = self.env['res.users']
                    res_users_id = res_users_pool.search([('partner_id', '=',
                                                           partner_id)])
                    mail_server = ir_mail_server.search([('user_id', '=',
                                                          res_users_id.id)])

                for email in email_list:
                    if len(mail_server) == 0 and default_mail_address:
                        msg = ir_mail_server.build_email(
                            email_from=default_mail_address,
                            email_to=email.get('email_to'),
                            #subject=email.get('subject'),                  # alte Version
                            subject=
                            subject,  # neue Version - nur so können wir Betreff korrekt übergeben
                            body=email.get('body'),
                            body_alternative=email.get('body_alternative'),
                            email_cc=tools.email_split(mail.email_cc),
                            reply_to=mail.email_from,
                            attachments=attachments,
                            message_id=mail.message_id,
                            references=mail.references,
                            object_id=mail.res_id
                            and ('%s-%s' % (mail.res_id, mail.model)),
                            subtype='html',
                            subtype_alternative='plain',
                            headers=headers)
                        msg['Return-Path'] = default_mail_address
                        res = ir_mail_server.send_email(
                            msg,
                            mail_server_id=default_mail_server,
                        )
                    else:
                        msg = ir_mail_server.build_email(
                            email_from=mail.email_from,
                            email_to=email.get('email_to'),
                            subject=email.get('subject'),
                            body=email.get('body'),
                            body_alternative=email.get('body_alternative'),
                            email_cc=tools.email_split(mail.email_cc),
                            reply_to=mail.email_from,
                            attachments=attachments,
                            message_id=mail.message_id,
                            references=mail.references,
                            object_id=mail.res_id
                            and ('%s-%s' % (mail.res_id, mail.model)),
                            subtype='html',
                            subtype_alternative='plain',
                            headers=headers)
                        msg['Return-Path'] = mail.email_from

                        if len(mail_server) == 0:
                            pass
                        else:
                            res = ir_mail_server.send_email(
                                msg,
                                mail_server_id=mail_server[0].id,
                            )

                if res:
                    mail.write({'state': 'sent', 'message_id': res})
                    mail_sent = True

                # /!\ can't use mail.state here, as mail.refresh() will cause an error
                # see revid:[email protected] 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(mail_sent=mail_sent)
            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)
                mail.write({'state': 'exception'})
                self._postprocess_sent_message(mail_sent=False)
                if raise_exception:
                    if isinstance(e, AssertionError):
                        # get the args of the original error, wrap into a value and throw a MailDeliveryException
                        # that is an except_orm, with name and value as arguments

                        value = '. '.join(e.args)
                        raise MailDeliveryException(_("Mail Delivery Failed"),
                                                    value)
                    raise
            if auto_commit is True:
                self._cr.commit()
        return True
Ejemplo n.º 7
0
    def _send(
        self,
        auto_commit=False,
        raise_exception=False,
        smtp_session=None,
    ):
        IrMailServer = self.env["ir.mail_server"]
        for mail_id in self.ids:
            try:
                mail = self.browse(mail_id)
                if mail.state != "outgoing":
                    if mail.state != "exception" and mail.auto_delete:
                        mail.sudo().unlink()
                    continue
                # TDE note: remove me when model_id field is present
                #  on mail.message - done here to avoid doing it multiple times in the sub method
                if mail.model:
                    model = self.env["ir.model"]._get(mail.model)[0]
                else:
                    model = None
                if model:
                    mail = mail.with_context(model_name=model.name)

                # 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.
                attachments = [(
                    a["datas_fname"],
                    base64.b64decode(a["datas"]),
                    a["mimetype"],
                ) for a in mail.attachment_ids.sudo().read(
                    ["datas_fname", "datas", "mimetype"])]

                # specific behavior to customize the send
                #  email for notified partners
                email_list = []
                if mail.email_to:
                    email_list.append(mail.send_get_email_dict())
                for partner in mail.recipient_ids:
                    email_list.append(
                        mail.send_get_email_dict(partner=partner))
                _logger.info("email_list? %r ", email_list)
                # headers
                headers = {}
                ICP = self.env["ir.config_parameter"].sudo()
                bounce_alias = ICP.get_param("mail.bounce.alias")
                catchall_domain = ICP.get_param("mail.catchall.domain")
                if bounce_alias and catchall_domain:
                    if mail.model and mail.res_id:
                        headers["Return-Path"] = "%s+%d-%s-%d@%s" % (
                            bounce_alias,
                            mail.id,
                            mail.model,
                            mail.res_id,
                            catchall_domain,
                        )
                    else:
                        headers["Return-Path"] = "%s+%d@%s" % (
                            bounce_alias,
                            mail.id,
                            catchall_domain,
                        )
                if mail.headers:
                    try:
                        headers.update(safe_eval(mail.headers))
                    except Exception:
                        pass

                # Writing on the mail object may fail (e.g. lock on user) which
                # would trigger a rollback *after* actually sending the email.
                # To avoid sending twice the same email,
                #  provoke the failure earlier
                mail.write({
                    "state":
                    "exception",
                    "failure_reason":
                    _("Error without exception. Probably due do sending an email without computed recipients."
                      ),
                })
                mail_sent = False

                # build an RFC2822 email.message.Message object
                #  and send it without queuing

                res = None
                for email in email_list[:1]:
                    if mail.email_from not in email.get("email_cc"):
                        _logger.info(
                            "must assign to cc list %r ",
                            mail.email_from,
                        )
                    else:
                        _logger.info(
                            "from is assigned to cc list %r ",
                            mail.email_from,
                        )
                    _logger.info("email_from %r ", mail.email_from)
                    _logger.info("email_to %r ", email.get("email_to"))
                    _logger.info("email_cc %r ", email.get("email_cc"))
                    _logger.info("email_replay_to %r ", mail.reply_to)
                    _logger.info("email_subhject %r ", mail.subject)
                    msg = IrMailServer.build_email(
                        email_from=mail.email_from,
                        email_to=email.get("email_to"),
                        subject=mail.subject,
                        body=email.get("body"),
                        body_alternative=email.get("body_alternative"),
                        email_cc=email.get("email_cc"),
                        reply_to=mail.reply_to,
                        attachments=attachments,
                        message_id=mail.message_id,
                        references=mail.references,
                        object_id=mail.res_id
                        and ("%s-%s" % (mail.res_id, mail.model)),
                        subtype="html",
                        subtype_alternative="plain",
                        headers=headers,
                    )
                    try:
                        res = IrMailServer.send_email(
                            msg,
                            mail_server_id=mail.mail_server_id.id,
                            smtp_session=smtp_session,
                        )
                    except AssertionError as error:
                        if (str(error) == IrMailServer.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.info(
                                "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,
                            "failure_reason": False,
                        })
                        mail_sent = True

                # /!\ can't use mail.state here, as mail.refresh()
                # will cause an error
                # see revid:[email protected]
                # in 6.1
                if mail_sent:
                    _logger.info(
                        "Mail with ID %r and Message-Id %r successfully sent",
                        mail.id,
                        mail.message_id,
                    )
                mail._postprocess_sent_message(mail_sent=mail_sent)
            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 psycopg2.Error:
                # If an error with the database occurs, chances are that
                #  the cursor is unusable.
                # This will lead to an `psycopg2.InternalError` being
                # raised when trying to write
                # `state`, shadowing the original exception and forbid
                #  a retry on concurrent
                # update. Let's bubble it.
                raise
            except Exception as e:
                failure_reason = tools.ustr(e)
                _logger.exception(
                    "failed sending mail (id: %s) due to %s",
                    mail.id,
                    failure_reason,
                )
                mail.write({
                    "state": "exception",
                    "failure_reason": failure_reason,
                })
                mail._postprocess_sent_message(mail_sent=False)
                if raise_exception:
                    if isinstance(e, AssertionError):
                        # get the args of the original error, wrap
                        #  into a value and throw a MailDeliveryException
                        # that is an except_orm, with name and value
                        #  as arguments
                        value = ". ".join(e.args)
                        raise MailDeliveryException(_("Mail Delivery Failed"),
                                                    value)
                    raise

            if auto_commit is True:
                self._cr.commit()
        return True
Ejemplo n.º 8
0
    def send(self):

        # Get GCM Server Details
        gcm_server = None
        if self.gcm_server_id:
            gcm_server = self.gcm_server_id
        elif not gcm_server:
            gcm_server = self.env['google_gcm.server'].sudo().search(
                [('active', '=', True), ('state', '=', 'done')],
                limit=1,
                order='sequence')

        if not gcm_server:
            raise UserError(
                _("Missing GCM Server") + "\n" +
                _("Please re-compute server, or provide the GCM parameters explicitly."
                  ))

        gcm_server.ensure_one()

        for gc_message in self:
            try:

                # specific behavior to customize the send gcm for notified partners
                gcm_device_list = []
                for partner in gc_message.recipient_ids:
                    for device in partner.gcm_device_ids:
                        gcm_device_list.append(device.gcm_reg_id)

                if len(gcm_device_list) == 0:
                    raise UserError(
                        _("Missing Recipients") + "\n" +
                        _("Please define recipients that own at least one registered device."
                          ))

                # Writing on the gcm object may fail (e.g. lock on user) which
                # would trigger a rollback *after* actually sending the email.
                # To avoid sending twice the same email, provoke the failure earlier
                gc_message.write({
                    'state':
                    'exception',
                    'state_reason':
                    _('Error without exception. Probably due do sending an google notification without computed recipients.'
                      ),
                })

                gcm = GCM(gcm_server.api_key)

                data = {
                    'Title': gc_message.send_get_gcm_body(gc_message.name),
                    'Content': gc_message.send_get_gcm_body(gc_message.content)
                }
                """response = gcm.json_request(
                    registration_id = gcm_device_list,
                    data = data,
                    collapse_key = gcm_server.collapse_key,
                    delay_while_idle = gcm_server.delay_while_idle,
                    time_to_live = gcm_server.time_to_live
                )
                """

                message = JSONMessage(
                    gcm_device_list,
                    data,
                    collapse_key=gcm_server.collapse_key,
                    delay_while_idle=gcm_server.delay_while_idle,
                    time_to_live=gcm_server.time_to_live,
                    dry_run=True)
                response = gcm.send(message)

                state_reason = ''

                for reg_id, msg_id in response.success.items():
                    state_reason += '-Success for reg_id %s' % reg_id
                    _logger.info(
                        'GCM Notification with Title %r successfully sent to device reg_id %r',
                        gc_message.name, reg_id)
                    if gc_message.auto_delete:
                        _logger.info(
                            "Deleting google_gcm.message %s :%s after success sending ('auto delete' option checked)",
                            gc_message.id, gc_message.name)

                for reg_id in response.not_registered:
                    device = self.env['google_gcm.device'].search([
                        ('gcm_reg_id', '=', reg_id)
                    ])
                    _logger.info(
                        "Deleting invalid recipients for google_gcm.message %s: %s",
                        device.partner_id.name, device.gcm_reg_id)
                    state_reason += "Deleting invalid recipients for google_gcm.message " + device.partner_id.name + ":" + device.gcm_reg_id
                    device.unlink()

                for reg_id, err_code in response.failed.items():
                    device = self.env['google_gcm.device'].search([
                        ('gcm_reg_id', '=', reg_id)
                    ])
                    _logger.info(
                        "Deleting  google_gcm.device %s: %s because %s",
                        device.partner_id.name, device.gcm_reg_id, err_code)
                    state_reason += "Deleting  google_gcm.device " + device.partner_id.name + ":" + device.gcm_reg_id + " because " + err_code
                    device.unlink()

                for reg_id, new_reg_id in response.canonical.items():
                    # Repace reg_id with canonical_id in your database
                    device = self.env['google_gcm.device'].search([
                        ('gcm_reg_id', '=', reg_id)
                    ])
                    _logger.info(
                        "Updating recipients registration_id %s: %s to %s during google_gcm.message sent",
                        device.partner_id.name, device.gcm_reg_id, new_reg_id)
                    state_reason += "Updating recipients registration_id of " + device.partner_id.name + ":" + device.gcm_reg_id + " to " + new_reg_id + " during google_gcm.message sent"
                    device.gcm_reg_id = new_reg_id

                if response.needs_retry():
                    # construct new message with only failed regids
                    retry_msg = response.retry()
                    # you have to wait before attemting again. delay()
                    # will tell you how long to wait depending on your
                    # current retry counter, starting from 0.
                    _logger.info("Wait or schedule task after %s seconds",
                                 response.delay(retry))
                    state_reason += "Wait or schedule task after " + response.delay(
                        retry) + " seconds"
                    # retry += 1 and send retry_msg again

                gc_message.write({
                    'state': 'sent',
                    'state_reason': state_reason
                })
            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 GCM Notification with ID %r and Title %r. Consider raising the --limit-memory-hard startup option',
                    gc_message.id, gc_message.name)
                raise
            except psycopg2.Error:
                # If an error with the database occurs, chances are that the cursor is unusable.
                # This will lead to an `psycopg2.InternalError` being raised when trying to write
                # `state`, shadowing the original exception and forbid a retry on concurrent
                # update. Let's bubble it.
                raise
            except Exception as e:
                failure_reason = tools.ustr(e)
                _logger.exception(
                    'failed sending google notification (id: %s) due to %s',
                    gc_message.id, failure_reason)
                gc_message.write({
                    'state': 'exception',
                    'state_reason': failure_reason
                })
                if isinstance(e, AssertionError):
                    # get the args of the original error, wrap into a value and throw a MailDeliveryException
                    # that is an except_orm, with name and value as arguments
                    value = '. '.join(e.args)
                    raise MailDeliveryException(
                        _("Google Cloud Message Delivery Failed"), value)
                raise

        return True
Ejemplo n.º 9
0
    def _send(self,
              auto_commit=False,
              raise_exception=False,
              smtp_session=None):
        IrMailServer = self.env['ir.mail_server']
        for mail_id in self.ids:
            try:
                mail = self.browse(mail_id)
                if mail.state != 'outgoing':
                    if mail.state != 'exception' and mail.auto_delete:
                        mail.sudo().unlink()
                    continue
                # TDE note: remove me when model_id field is present on mail.message - done here to avoid doing it multiple times in the sub method
                if mail.model:
                    model = self.env['ir.model']._get(mail.model)[0]
                else:
                    model = None
                if model:
                    mail = mail.with_context(model_name=model.name)

                # 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.
                attachments = [(a['datas_fname'], base64.b64decode(a['datas']),
                                a['mimetype'])
                               for a in mail.attachment_ids.sudo().read(
                                   ['datas_fname', 'datas', 'mimetype'])]

                # specific behavior to customize the send email for notified partners
                email_list = []
                if mail.email_to:
                    email_list.append(mail.send_get_email_dict())
                for partner in mail.recipient_ids:
                    email_list.append(
                        mail.send_get_email_dict(partner=partner))

                if mail.cc_visible:
                    email_cc_list = []
                    for partner_cc in mail.recipient_cc_ids:
                        email_to = formataddr(
                            (partner_cc.name or 'False', partner_cc.email
                             or 'False'))
                        email_cc_list.append(email_to)
                    # Convert Email List To String For BCC & CC
                    email_cc_string = ','.join(email_cc_list)
                else:
                    email_cc_string = ''

                if mail.bcc_visible:
                    email_bcc_list = []
                    for partner_bcc in mail.recipient_bcc_ids:
                        email_to = formataddr(
                            (partner_bcc.name or 'False', partner_bcc.email
                             or 'False'))
                        email_bcc_list.append(email_to)
                    # Convert Email List To String For BCC & CC
                    email_bcc_string = ','.join(email_bcc_list)
                else:
                    email_bcc_string = ''

                # headers
                headers = {}
                ICP = self.env['ir.config_parameter'].sudo()
                bounce_alias = ICP.get_param("mail.bounce.alias")
                catchall_domain = ICP.get_param("mail.catchall.domain")
                if bounce_alias and catchall_domain:
                    if mail.model and mail.res_id:
                        headers['Return-Path'] = '%s+%d-%s-%d@%s' % (
                            bounce_alias, mail.id, mail.model, mail.res_id,
                            catchall_domain)
                    else:
                        headers['Return-Path'] = '%s+%d@%s' % (
                            bounce_alias, mail.id, catchall_domain)
                if mail.headers:
                    try:
                        headers.update(safe_eval(mail.headers))
                    except Exception:
                        pass

                # Writing on the mail object may fail (e.g. lock on user) which
                # would trigger a rollback *after* actually sending the email.
                # To avoid sending twice the same email, provoke the failure earlier
                mail.write({
                    'state':
                    'exception',
                    'failure_reason':
                    _('Error without exception. Probably due do sending an email without computed recipients.'
                      ),
                })
                mail_sent = False

                # Update notification in a transient exception state to avoid concurrent
                # update in case an email bounces while sending all emails related to current
                # mail record.
                notifs = self.env['mail.notification'].search([
                    ('is_email', '=', True),
                    ('mail_message_id', 'in',
                     mail.mapped('mail_message_id').ids),
                    ('res_partner_id', 'in', mail.mapped('recipient_ids').ids),
                    ('email_status', 'not in', ('sent', 'canceled'))
                ])
                if notifs:
                    notifs.sudo().write({
                        'email_status': 'exception',
                    })

                # build an RFC2822 email.message.Message object and send it without queuing
                res = None
                for email in email_list:
                    msg = IrMailServer.build_email(
                        email_from=mail.email_from,
                        email_to=email.get('email_to'),
                        subject=mail.subject,
                        body=email.get('body'),
                        body_alternative=email.get('body_alternative'),
                        email_cc=tools.email_split_and_format(email_cc_string),
                        email_bcc=tools.email_split_and_format(
                            email_bcc_string),
                        reply_to=mail.reply_to,
                        attachments=attachments,
                        message_id=mail.message_id,
                        references=mail.references,
                        object_id=mail.res_id
                        and ('%s-%s' % (mail.res_id, mail.model)),
                        subtype='html',
                        subtype_alternative='plain',
                        headers=headers)
                    try:
                        res = IrMailServer.send_email(
                            msg,
                            mail_server_id=mail.mail_server_id.id,
                            smtp_session=smtp_session)
                    except AssertionError as error:
                        if str(error) == IrMailServer.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.info(
                                "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,
                        'failure_reason': False
                    })
                    mail_sent = True

                # /!\ can't use mail.state here, as mail.refresh() will cause an error
                # see revid:[email protected] in 6.1
                if mail_sent:
                    _logger.info(
                        'Mail with ID %r and Message-Id %r successfully sent',
                        mail.id, mail.message_id)
                mail._postprocess_sent_message(mail_sent=mail_sent)
            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 (psycopg2.Error, smtplib.SMTPServerDisconnected):
                # If an error with the database or SMTP session occurs, chances are that the cursor
                # or SMTP session are unusable, causing further errors when trying to save the state.
                _logger.exception(
                    'Exception while processing mail with ID %r and Msg-Id %r.',
                    mail.id, mail.message_id)
                raise
            except Exception as e:
                failure_reason = tools.ustr(e)
                _logger.exception('failed sending mail (id: %s) due to %s',
                                  mail.id, failure_reason)
                mail.write({
                    'state': 'exception',
                    'failure_reason': failure_reason
                })
                mail._postprocess_sent_message(mail_sent=False)
                if raise_exception:
                    if isinstance(e, AssertionError):
                        # get the args of the original error, wrap into a value and throw a MailDeliveryException
                        # that is an except_orm, with name and value as arguments
                        value = '. '.join(e.args)
                        raise MailDeliveryException(_("Mail Delivery Failed"),
                                                    value)
                    raise

            if auto_commit is True:
                self._cr.commit()
        return True
Ejemplo n.º 10
0
    def send(self,
             cr,
             uid,
             ids,
             auto_commit=False,
             raise_exception=False,
             context=None):
        # copy-paste from addons/mail/mail_mail.py
        """ Sends the selected emails immediately, ignoring their current
            state (mails that have already been sent should not be passed
            unless they should actually be re-sent).
            Emails successfully delivered are marked as 'sent', and those
            that fail to be deliver are marked as 'exception', and the
            corresponding error mail is output in the server logs.

            :param bool auto_commit: whether to force a commit of the mail status
                after sending each mail (meant only for scheduler processing);
                should never be True during normal transactions (default: False)
            :param bool raise_exception: whether to raise an exception if the
                email sending process has failed
            :return: True
        """

        # NEW STUFF
        catchall_alias = self.pool["ir.config_parameter"].get_param(
            cr, uid, "mail.catchall.alias_from", context=context)
        catchall_alias_name = self.pool["ir.config_parameter"].get_param(
            cr, uid, "mail.catchall.name_alias_from", context=context)
        catchall_domain = self.pool["ir.config_parameter"].get_param(
            cr, uid, "mail.catchall.domain", context=context)

        correct_email_from = r"@%s>?\s*$" % catchall_domain
        default_email_from = "{}@{}".format(catchall_alias, catchall_domain)

        context = dict(context or {})
        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:
                # TDE note: remove me when model_id field is present on mail.message - done here to avoid doing it multiple times in the sub method
                if mail.model:
                    model_id = self.pool["ir.model"].search(
                        cr,
                        SUPERUSER_ID, [("model", "=", mail.model)],
                        context=context)[0]
                    model = self.pool["ir.model"].browse(cr,
                                                         SUPERUSER_ID,
                                                         model_id,
                                                         context=context)
                else:
                    model = None
                if model:
                    context["model_name"] = model.name

                # 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:
                    email_list.append(
                        self.send_get_email_dict(cr,
                                                 uid,
                                                 mail,
                                                 context=context))
                for partner in mail.recipient_ids:
                    email_list.append(
                        self.send_get_email_dict(cr,
                                                 uid,
                                                 mail,
                                                 partner=partner,
                                                 context=context))
                # headers
                headers = {}
                bounce_alias = self.pool["ir.config_parameter"].get_param(
                    cr, uid, "mail.bounce.alias", context=context)
                catchall_domain = self.pool["ir.config_parameter"].get_param(
                    cr, uid, "mail.catchall.domain", context=context)
                if bounce_alias and catchall_domain:
                    if mail.model and mail.res_id:
                        headers["Return-Path"] = "%s-%d-%s-%d@%s" % (
                            bounce_alias,
                            mail.id,
                            mail.model,
                            mail.res_id,
                            catchall_domain,
                        )
                    else:
                        headers["Return-Path"] = "%s-%d@%s" % (
                            bounce_alias,
                            mail.id,
                            catchall_domain,
                        )
                if mail.headers:
                    try:
                        headers.update(safe_eval(mail.headers))
                    except Exception:
                        pass

                # Writing on the mail object may fail (e.g. lock on user) which
                # would trigger a rollback *after* actually sending the email.
                # To avoid sending twice the same email, provoke the failure earlier
                mail.write({"state": "exception"})
                mail_sent = False

                # build an RFC2822 email.message.Message object and send it without queuing
                res = None
                for email in email_list:

                    # NEW STUFF
                    email_from = mail.email_from
                    if re.search(correct_email_from, email_from) is None:
                        email_from = default_email_from
                    if catchall_alias_name:
                        email_from = formataddr(
                            (catchall_alias_name, email_from))

                    msg = ir_mail_server.build_email(
                        email_from=email_from,  # NEW STUFF
                        email_to=email.get("email_to"),
                        subject=email.get("subject"),
                        body=email.get("body"),
                        body_alternative=email.get("body_alternative"),
                        email_cc=tools.email_split(mail.email_cc),
                        reply_to=mail.reply_to,
                        attachments=attachments,
                        message_id=mail.message_id,
                        references=mail.references,
                        object_id=mail.res_id
                        and ("{}-{}".format(mail.res_id, mail.model)),
                        subtype="html",
                        subtype_alternative="plain",
                        headers=headers,
                    )
                    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 str(error) == 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

                # /!\ can't use mail.state here, as mail.refresh() will cause an error
                # see revid:[email protected] 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,
                                               mail_sent=mail_sent)
            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)
                mail.write({"state": "exception"})
                self._postprocess_sent_message(cr,
                                               uid,
                                               mail,
                                               context=context,
                                               mail_sent=False)
                if raise_exception:
                    if isinstance(e, AssertionError):
                        # get the args of the original error, wrap into a value and throw a MailDeliveryException
                        # that is an except_orm, with name and value as arguments
                        value = ". ".join(e.args)
                        raise MailDeliveryException(_("Mail Delivery Failed"),
                                                    value)
                    raise

            if auto_commit is True:
                cr.commit()
        return True