[FIX] mail: bad CTE/QP decoding of rfc822-headers

Based on RFC3462, a Content-Type text/rfc822-headers
exists and provide a mechanism to label and return
only the RFC 822 headers of a failed message (bounce)

These are only the headers and not the full message.

The Content-Type-Encoding should be either 7-bit(US-ASCII)
or Quoted-Printable (QP) as in the section 2 of the RFC.

Spawn the error:
After getting reported by a customer, I had to reproduce
by spamming wrong outlook addresses and add logging
in a sh database on the message_process of mail_thread.py
and logged the `message` variable.

After few retry, I got one of the part that was defined
as followed:
Content-Description: Undelivered Message Headers
Content-Type: text/rfc822-headers
Content-Transfer-Encoding: quoted-printable

The `get_payload()`function used was only assuming
that there is a full email on that part and that
it could only be encoded as an email, which was not
the case in this situation (quoted-printable:
76 characters per line, character `=` used
as the end of line character).

opw-3064589
task-3131561

closes odoo/odoo#110901

X-original-commit: f86f4b671696178f8fa42b81d8753d2591578431
Signed-off-by: Julien Castiaux <juc@odoo.com>
Co-authored-by: Baptiste Vergote <bve@odoo.com>
This commit is contained in:
Julien Castiaux
2023-01-24 23:21:57 +01:00
committed by bve-odoo
co-authored by Baptiste Vergote
parent 79c6025213
commit 37ca7770a6
3 changed files with 110 additions and 1 deletions
+1 -1
View File
@@ -1474,7 +1474,7 @@ class MailThread(models.AbstractModel):
if email_part:
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=email.policy.SMTP)
email_payload = message_from_string(email_part.get_content(), policy=email.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'))
+84
View File
@@ -958,6 +958,90 @@ OyI+T2RvbzwvYT4uCjwvcD4KPC9kaXY+CiAgICAgICAg
--92726A5F09.1555335666/mail2.test.ironsky--
"""
MAIL_BOUNCE_QP_RFC822_HEADERS = """\
Received: by mailserver.odoo.com (Postfix)
id EA0B917B8E4; Tue, 29 Feb 2023 11:11:11 +0100 (CET)
From: {email_from}
Subject: Undelivered Mail Returned to Sender
To: {email_to}
Auto-Submitted: auto-replied
MIME-Version: 1.0
Content-Type: multipart/report; report-type=delivery-status;
boundary="DFFDC17AA03.1673346179/mailserver.odoo.com"
Message-Id: <40230110102259.EA0B917B8E4@mailserver.odoo.com>
Content-Transfer-Encoding: 7bit
Delivered-To: {delivered_to}
Return-Path: <>
--DFFDC17AA03.1673346179/mailserver.odoo.com
Content-Description: Notification
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable
I'm sorry to have to inform you that your message could not
be delivered to one or more recipients.
<rdesfrdgtfdrfesd@outlook.com>: host
outlook-com.olc.protection.outlook.com[104.47.56.33] said: 550 5.5.0
Requested action not taken: mailbox unavailable (S2017062302). (in re=
ply to
RCPT TO command)
--DFFDC17AA03.1673346179/mailserver.odoo.com
Content-Description: Delivery report
Content-Type: message/delivery-status
Reporting-MTA: dns; mailserver.odoo.com
X-Postfix-Queue-ID: DFFDC17AA03
X-Postfix-Sender: rfc822; bounce@xxx.odoo.com
Arrival-Date: Tue, 29 Feb 2023 10:10:10 +0100 (CET)
Final-Recipient: rfc822; rdesfrdgtfdrfesd@outlook.com
Original-Recipient: rfc822;rdesfrdgtfdrfesd@outlook.com
Action: failed
Status: 5.5.0
Remote-MTA: dns; outlook-com.olc.protection.outlook.com
Diagnostic-Code: smtp; 550 5.5.0 Requested action not taken: mailbox
unavailable (S2017062302).
--DFFDC17AA03.1673346179/mailserver.odoo.com
Content-Description: Undelivered Message Headers
Content-Type: text/rfc822-headers
Content-Transfer-Encoding: quoted-printable
Return-Path: <bounce@xxx.odoo.com>
Received: from eupp00.odoo.com (00.72.79.34.bc.googleusercontent.com [34.=
79.72.00])
by mailserver.odoo.com (Postfix) with ESMTPS id DFFDC17AA03;
Tue, 10 Jan 2023 11:22:57 +0100 (CET)
DKIM-Signature: v=3D1; a=3Drsa-sha256; c=3Dsimple/simple; d=3Dxxx.be;
s=3Dodoo; t=3D1673346178;
bh=3DYPJOqkUi8B28X1MrRUsgmsL8KRz/ZIkpbYyc6wNITXA=3D;
h=3Dreferences:Subject:From:Reply-To:To:Date:From;
b=3DCMqh7mUvpgUw+JpCeGluv1+MZ3y6EsXd0acmsfzpYBjcoy1InvD6FLT1/lQCcgetf
cGyL/8R4vvDKATyE0AtOIpoYDsbpnMoiYWqaSXnDVuLTrEZzyrK/2j10ZTnHZ2uDTC
b7wPjFfQ9pted/t6CAUhVT1XydDNalSwEZovy/QI=3D
Message-Id: <368396033905967.1673346177.695352554321289-openerp-11-sale.o=
rder@eupp00>
references: <792105153140463.1673746527.352018594741821-openerp-11-sale.o=
rder@xxx.odoo.com> <368396033905967.1673346177.695352554321289-openerp-11=
-sale.order@eupp00>
Subject: Thi is a SO (Ref SO/11)
From: info@xxx.odoo.com
Reply-To: "SO/11" <catchall@xxx.odoo.com=
>
To: "rdesfrdgtfdrfesd@outlook.com" <rdesfrdgtfdrfesd@outlook.com>
Date: Tue, 29 Feb 2023 06:09:06 -0000
X-Odoo-Objects: sale.order-11
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary=3D"=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D5706316606908750110=3D=3D"
--DFFDC17AA03.1673346179/mailserver.odoo.com--
"""
MAIL_NO_BODY = '''\
Return-Path: <{email_from}>
Delivered-To: catchall@xxxx.xxxx
@@ -104,6 +104,31 @@ class TestEmailParsing(TestMailCommon):
self.assertEqual(res['bounced_msg_id'], [msg_id], "Message-Id is not extracted from Text/RFC822-Headers attachment")
def test_message_parse_extract_bounce_rfc822_headers_qp(self):
# Incoming bounce for unexisting Outlook address
# bounce back sometimes with a Content-Type `text/rfc822-headers`
# and Content-Type-Encoding `quoted-printable`
partner = self.env['res.partner'].create({
'name':'Mitchelle Admine',
'email':'rdesfrdgtfdrfesd@outlook.com'
})
message = self.env['mail.message'].create({
'message_id' : '<368396033905967.1673346177.695352554321289-openerp-11-sale.order@eupp00>'
})
incoming_bounce = self.format(
test_mail_data.MAIL_BOUNCE_QP_RFC822_HEADERS,
email_from='MAILER-DAEMON@mailserver.odoo.com (Mail Delivery System)',
email_to='bounce@xxx.odoo.com',
delivered_to='bounce@xxx.odoo.com'
)
msg_dict = {}
msg = self.env['mail.thread']._message_parse_extract_bounce(self.from_string(incoming_bounce), msg_dict)
self.assertEqual(msg['bounced_email'], partner.email, "The sender email should be correctly parsed")
self.assertEqual(msg['bounced_partner'], partner, "A partner with this email should exist")
self.assertEqual(msg['bounced_msg_id'][0], message.message_id, "The sender message-id should correctly parsed")
self.assertEqual(msg['bounced_message'], message, "An existing message with this message_id should exist")
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')