summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJairo Llopis <jairo.llopis@tecnativa.com>2019-12-24 11:56:56 +0000
committerIvan Yelizariev <iel@odoo.com>2020-12-02 09:57:44 +0000
commit375ba5b37053a599922dd61c9e787553d6171291 (patch)
tree177143b33260ddb18612d47142b3c103de36362c
parent82b06dc4c3df502c8e006169f4ee0f6aa33e3ac1 (diff)
[FIX] mail: add support for Text/RFC822-Headers content-type
WHY: Some mail servers may provide `Message-Id` header in a section typed `text/rfc822-headers` instead of a `message/rfc822` According to https://tools.ietf.org/html/rfc6522#section-4 that section's body actually contains the headers from the bounced email. STEPS: a way to reproduce it should be using postfix v2.5+ with setting bounce_size_limit=1. See https://github.com/odoo/odoo/pull/42340#issuecomment-617605521 BEFORE: `bounced_message_id` is empty and thus the little email envelope icon doesn't turn red, nor do the email resend features trigger in the UI. You could be sending an invoice and never knowing it was bounced. AFTER: bounces from mail servers that return the `text/rfc822-headers` part will be handled properly. --- @Tecnativa TT21170 opw-2162067 opw-2344252 closes #42340 closes #62551 closes odoo/odoo#62706 X-original-commit: 3dc72b357c9bfa81867972c5c9b20dc028932d8b Signed-off-by: Ivan Yelizariev // IEL <yelizariev@users.noreply.github.com>
-rw-r--r--addons/mail/models/mail_thread.py9
-rw-r--r--addons/test_mail/data/test_mail_data.py57
-rw-r--r--addons/test_mail/tests/test_mail_gateway.py14
3 files changed, 78 insertions, 2 deletions
diff --git a/addons/mail/models/mail_thread.py b/addons/mail/models/mail_thread.py
index 18962e5b049..c0dd08b9b64 100644
--- a/addons/mail/models/mail_thread.py
+++ b/addons/mail/models/mail_thread.py
@@ -19,6 +19,7 @@ import threading
from collections import namedtuple
from email.message import EmailMessage
+from email import message_from_string, policy
from lxml import etree
from werkzeug import urls
from xmlrpc import client as xmlrpclib
@@ -1327,7 +1328,7 @@ class MailThread(models.AbstractModel):
if not isinstance(email_message, EmailMessage):
raise TypeError('message must be an email.message.EmailMessage at this point')
- email_part = next((part for part in email_message.walk() if part.get_content_type() == 'message/rfc822'), None)
+ email_part = next((part for part in email_message.walk() if part.get_content_type() in {'message/rfc822', 'text/rfc822-headers'}), None)
dsn_part = next((part for part in email_message.walk() if part.get_content_type() == 'message/delivery-status'), None)
bounced_email = False
@@ -1342,7 +1343,11 @@ class MailThread(models.AbstractModel):
bounced_msg_id = False
bounced_message = self.env['mail.message'].sudo()
if email_part:
- email_payload = email_part.get_payload()[0]
+ if email_part.get_content_type() == 'text/rfc822-headers':
+ # Convert the message body into a message itself
+ email_payload = message_from_string(email_part.get_payload(), policy=policy.SMTP)
+ else:
+ email_payload = email_part.get_payload()[0]
bounced_msg_id = tools.mail_header_msgid_re.findall(tools.decode_message_header(email_payload, 'Message-Id'))
if bounced_msg_id:
bounced_message = self.env['mail.message'].sudo().search([('message_id', 'in', bounced_msg_id)])
diff --git a/addons/test_mail/data/test_mail_data.py b/addons/test_mail/data/test_mail_data.py
index fa64fd56e26..d6e38846930 100644
--- a/addons/test_mail/data/test_mail_data.py
+++ b/addons/test_mail/data/test_mail_data.py
@@ -467,6 +467,63 @@ Web: <a class="moz-txt-link-freetext" href="http://www.odoo.com">http://www.odoo
--------------A6B5FD5F68F4D73ECD739009--"""
+MAIL_EML_ATTACHMENT_BOUNCE_HEADERS="""\
+Date: Tue, 24 Dec 2019 11:32:07 +0100 (CET)
+MIME-Version: 1.0
+Content-Type: multipart/mixed; boundary=16063919151.b32bE0eD.7
+Content-Transfer-Encoding: 7bit
+Subject: Undelivered Mail Returned to Sender
+From: {email_from}
+To: {to}
+Message-Id: <20191224103207.415713014C@example.com>
+Return-Path: <MAILER-DAEMON>
+Delivered-To: odoo+82240-account.invoice-19177@mycompany.example.com
+Received: by example.com (Postfix) id 415713014C; Tue, 24 Dec
+ 2019 11:32:07 +0100 (CET)
+Auto-Submitted: auto-replied
+
+
+--16063919151.b32bE0eD.7
+MIME-Version: 1.0
+Content-Type: multipart/alternative; boundary=16063919150.2cD3F37.7
+Content-Transfer-Encoding: 7bit
+Content-ID: <16063919152.fD96.7@8f286b7b7880>
+
+
+--16063919150.2cD3F37.7
+Content-Type: text/plain; charset=US-ASCII
+Content-Disposition: inline
+Content-Transfer-Encoding: 8bit
+
+This is the mail system at host example.com.
+
+I'm sorry to have to inform you that your message could not
+be delivered to one or more recipients. It's attached below.
+
+For further assistance, please send mail to postmaster.
+
+If you do so, please include this problem report. You can
+delete your own text from the attached returned message.
+
+
+--16063919151.b32bE0eD.7
+Content-Type: text/rfc822-headers
+Content-Transfer-Encoding: 7bit
+
+Return-Path: <bounce+82240-account.invoice-19177@mycompany.example.com>
+Received: by example.com (Postfix) id 415713014C; Tue, 24 Dec
+Content-Type: multipart/mixed; boundary="===============3600759226158551994=="
+MIME-Version: 1.0
+Message-Id: {msg_id}
+references: <1571814481.189281940460205.799582441238467-openerp-19177-account.invoice@mycompany.example.com>
+Subject: Test
+From: "Test" <noreply+srglvrz-gmail.com@mycompany.example.com>
+Reply-To: "MY COMPANY" <info@mycompany.example.com>
+To: "Test" <test@anothercompany.example.com>
+Date: Tue, 24 Dec 2019 10:32:05 -0000
+X-Odoo-Objects: account.invoice-19177
+
+--16063919151.b32bE0eD.7--"""
MAIL_XHTML = """Return-Path: <xxxx@xxxx.com>
Received: from xxxx.internal (xxxx.xxxx.internal [1.1.1.1])
diff --git a/addons/test_mail/tests/test_mail_gateway.py b/addons/test_mail/tests/test_mail_gateway.py
index 8c906497176..d3e829248c2 100644
--- a/addons/test_mail/tests/test_mail_gateway.py
+++ b/addons/test_mail/tests/test_mail_gateway.py
@@ -60,6 +60,20 @@ class TestEmailParsing(TestMailCommon):
mail = self.format(test_mail_data.MAIL_EML_ATTACHMENT, email_from='"Sylvie Lelitre" <test.sylvie.lelitre@agrolait.com>', to='generic@test.com')
self.env['mail.thread'].message_parse(self.from_string(mail))
+ def test_message_parse_eml_bounce_headers(self):
+ # Test Text/RFC822-Headers MIME content-type
+ msg_id = '<861878175823148.1577183525.736005783081055-openerp-19177-account.invoice@mycompany.example.com>'
+ mail = self.format(
+ test_mail_data.MAIL_EML_ATTACHMENT_BOUNCE_HEADERS,
+ email_from='MAILER-DAEMON@example.com (Mail Delivery System)',
+ to='test_bounce+82240-account.invoice-19177@mycompany.example.com',
+ # msg_id goes to the attachment's Message-Id header
+ msg_id=msg_id,
+ )
+ res = self.env['mail.thread'].message_parse(self.from_string(mail))
+
+ self.assertEqual(res['bounced_msg_id'], [msg_id], "Message-Id is not extracted from Text/RFC822-Headers attachment")
+
def test_message_parse_plaintext(self):
""" Incoming email in plaintext should be stored as html """
mail = self.format(test_mail_data.MAIL_TEMPLATE_PLAINTEXT, email_from='"Sylvie Lelitre" <test.sylvie.lelitre@agrolait.com>', to='generic@test.com')