diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..1cb7e68 --- /dev/null +++ b/README.rst @@ -0,0 +1,19 @@ +Fix mail error 553 +================== + +Module updates 'FROM' field to portal@MYDOMAIN.COM value in order to fix 553 error on a mail service that checks FROM field. + +E.g: + +* Customer send email from USER@CUSTOMER.com to info@MYDOMAIN.COM +* odoo accept email and try to send notifcation to related odoo users. E.g to admin@gmail.com. +* By default odoo prepare notification email with parameters as follows: + + * FROM: user@CUSTOMER.com + * TO: admin@gmail.com + +if you mail service provider, e.g. pdd.yandex.ru, doesn't allow emails with a FROM value differ from ...@MYDOMAIN.COM, then you get 553. This is why you need to update FROM value to portal@MYDOMAIN.COM + +You can configure default alias at Settings -> System Parameters -> mail.catchall.alias_from + +Tested on Odoo 8.0 d023c079ed86468436f25da613bf486a4a17d625 diff --git a/__openerp__.py b/__openerp__.py index 1585cc3..7dac0f3 100644 --- a/__openerp__.py +++ b/__openerp__.py @@ -1,44 +1,10 @@ { - "name" : "Fix error 553", + "name" : "Fix mail error 553", "version" : "0.3", "author" : "Ivan Yelizariev", "category" : "Mail", "website" : "https://yelizariev.github.io", - "description": """ -Module uses system parameters: - -* mail.catchall.alias -* mail.catchall.domain - -Module updates reply-to field if it posible to sender alias - -Module updates 'FROM' field to catchall value in order to fix problem like that: - - 2014-01-18 06:25:56,532 6789 INFO trunk openerp.addons.mail.mail_thread: Routing mail from to info@MYDOMAIN.com with Message-Id <49131390026345@web16h.yandex.ru>: direct alias match: (u'res.users', 1, {}, 1, browse_record(mail.alias, 1)) -2014-01-18 06:25:57,212 6789 ERROR trunk openerp.addons.base.ir.ir_mail_server: Mail delivery failed via SMTP server 'smtp.yandex.ru'. -SMTPSenderRefused: 553 -5.7.1 Sender address rejected: not owned by auth user. -user@CUSTOMER.com -Traceback (most recent call last): - File "/mnt/files/src/openerp-server/server/openerp/addons/base/ir/ir_mail_server.py", line 465, in send_email - smtp.sendmail(smtp_from, smtp_to_list, message.as_string()) - File "/usr/lib/python2.7/smtplib.py", line 722, in sendmail - raise SMTPSenderRefused(code, resp, from_addr) -SMTPSenderRefused: (553, '5.7.1 Sender address rejected: not owned by auth user.', 'user@CUSTOMER.com') - -2014-01-18 06:25:57,216 6789 ERROR trunk openerp.addons.mail.mail_mail: failed sending mail.mail 2 -Traceback (most recent call last): - File "/mnt/files/src/openerp-server/addons/mail/mail_mail.py", line 284, in send - context=context) - File "/mnt/files/src/openerp-server/server/openerp/addons/base/ir/ir_mail_server.py", line 478, in send_email - raise MailDeliveryException(_("Mail Delivery Failed"), msg) -MailDeliveryException: (u'Mail Delivery Failed', u"Mail delivery failed via SMTP server 'smtp.yandex.ru'.\nSMTPSenderRefused: 553\n5.7.1 Sender address rejected: not owned by auth user.\nuser@CUSTOMER.com") -2014-01-18 06:25:57,223 6789 INFO trunk openerp.addons.fetchmail.fetchmail: fetched/processed 1 email(s) on imap server yandex - - """, "depends" : ["base", "mail"], - #"init_xml" : [], - #"update_xml" : [], - #"active": True, + "data": ["data.xml"], "installable": True } diff --git a/data.xml b/data.xml new file mode 100644 index 0000000..411d614 --- /dev/null +++ b/data.xml @@ -0,0 +1,10 @@ + + + + + + mail.catchall.alias_from + portal + + + diff --git a/mail_fix_553.py b/mail_fix_553.py index 8eb5ce4..eb50abd 100644 --- a/mail_fix_553.py +++ b/mail_fix_553.py @@ -1,117 +1,33 @@ # -*- coding: utf-8 -*- - -import base64 -import logging import re -from urllib import urlencode -from urlparse import urljoin - -from openerp import tools from openerp import SUPERUSER_ID -from openerp.addons.base.ir.ir_mail_server import MailDeliveryException from openerp.osv import fields, osv -from openerp.tools.translate import _ +import logging _logger = logging.getLogger(__name__) -class mail_mail(osv.Model): +class mail_mail(osv.osv): _inherit = "mail.mail" - def send(self, cr, uid, ids, auto_commit=False, raise_exception=False, context=None): - """ 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 - """ - catchall_alias = self.pool['ir.config_parameter'].get_param(cr, uid, "mail.catchall.alias", context=context) + def _fix_email_from(cr, uid, email_from): + catchall_alias = self.pool['ir.config_parameter'].get_param(cr, uid, "mail.catchall.alias_from", context=context) catchall_domain = self.pool['ir.config_parameter'].get_param(cr, uid, "mail.catchall.domain", context=context) correct_email_from = '@%s>?\s*$'%catchall_domain default_email_from = '%s@%s' % (catchall_alias, catchall_domain) - ir_mail_server = self.pool.get('ir.mail_server') - - 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))) - # 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 not email_from or re.search(correct_email_from, email_from) is None: + email_from = default_email_from + return email_from - # build an RFC2822 email.message.Message object and send it without queuing - res = None - for email in email_list: - email_from = mail.email_from - reply_to = mail.reply_to - if re.search(correct_email_from, email_from) is None: - email_from = default_email_from - else: - reply_to = email_from + def create(self, cr, uid, values, context=None): + if 'email_from' in values: + values['email_from'] = self._fix_email_from(cr, uid, values['email_from']) - msg = ir_mail_server.build_email( - email_from=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=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) - res = ir_mail_server.send_email(cr, uid, msg, - mail_server_id=mail.mail_server_id.id, - context=context) - - if res: - mail.write({'state': 'sent', 'message_id': res}) - mail_sent = True - else: - mail.write({'state': 'exception'}) - mail_sent = False + return super(mail_mail, self).create(cr, uid, values, context=context) - # /!\ 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: - self._postprocess_sent_message(cr, uid, mail, context=context) - except Exception as e: - _logger.exception('failed sending mail.mail %s', mail.id) - mail.write({'state': 'exception'}) - 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 + def write(self, cr, uid, ids, values, context=None): + if 'email_from' in values: + values['email_from'] = self._fix_email_from(cr, uid, values['email_from']) - if auto_commit == True: - cr.commit() - return True + return super(mail_mail, self).write(cr, uid, ids, values, context=context)