summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJulien Castiaux <juc@odoo.com>2019-10-29 13:41:04 +0000
committerOlivier Dony <odo@odoo.com>2019-11-14 12:29:36 +0000
commit7bf2dd43393be3758b3d8d6eeb94c877517c1bcf (patch)
treed2d755d514aa93acf06e515fe5e648605d1dda93
parent7b65a63c97d185d9b6e6f1b0bfbc7968d1e4de49 (diff)
[FIX] ir.mail.server: squash redundant CRs (bpo-34424)
Problem description: Using the chatter, send an email to someone having non-ascii characters in their name: the body of the received email looks like it contains a mix of the original body and headers. Python encodes the name into either base64 or quoted-printable but leaves some redundant carriage returns at the end of the header. Those redundant carriage returns are not RFC conformant but are interpreted as newlines by some email clients, thus are read like a headers/body separator and the next headers are considered part of the body, corrupting the email structure. This problem is internal to Python and has been fixed in 3.8, cfr bpo-34424 [1], and later backported to 3.7.4. As we also support 3.6 and earlier 3.7 and it is not possible to easily monkey-patch the function, we fixed it by squashing duplicate carriage returns. (There is no case where a series of bare CRs can occur in a normal RFC5322 email message) Task: 2003936 [1]: https://bugs.python.org/issue34424
-rw-r--r--odoo/addons/base/models/ir_mail_server.py10
-rw-r--r--odoo/addons/base/tests/test_mail.py34
2 files changed, 41 insertions, 3 deletions
diff --git a/odoo/addons/base/models/ir_mail_server.py b/odoo/addons/base/models/ir_mail_server.py
index 65b6ee47c1e..8c5399236c7 100644
--- a/odoo/addons/base/models/ir_mail_server.py
+++ b/odoo/addons/base/models/ir_mail_server.py
@@ -9,6 +9,7 @@ import email.policy
import logging
import re
import smtplib
+import sys
import threading
import html2text
@@ -414,7 +415,14 @@ class IrMailServer(models.Model):
smtp = smtp or self.connect(
smtp_server, smtp_port, smtp_user, smtp_password,
smtp_encryption, smtp_debug, mail_server_id=mail_server_id)
- smtp.sendmail(smtp_from, smtp_to_list, message.as_string())
+
+ message_str = message.as_string()
+ # header folding code is buggy and adds redundant carriage
+ # returns, it got fixed in 3.7.4 thanks to bpo-34424
+ if sys.version_info < (3, 7, 4):
+ message_str = re.sub('\r+', '\r', message_str)
+
+ smtp.sendmail(smtp_from, smtp_to_list, message_str)
# do not quit() a pre-established smtp_session
if not smtp_session:
smtp.quit()
diff --git a/odoo/addons/base/tests/test_mail.py b/odoo/addons/base/tests/test_mail.py
index 8dbb2cddb65..e1b80c52c28 100644
--- a/odoo/addons/base/tests/test_mail.py
+++ b/odoo/addons/base/tests/test_mail.py
@@ -3,9 +3,11 @@
from unittest.mock import patch
+import email.policy
+import email.message
+import threading
-from odoo.tests.common import BaseCase
-from odoo.tests.common import SavepointCase
+from odoo.tests.common import BaseCase, SavepointCase, TransactionCase
from odoo.tools import (
html_sanitize, append_content_to_html, plaintext2html, email_split,
misc, formataddr,
@@ -374,3 +376,31 @@ class EmailConfigCase(SavepointCase):
"The body of an email",
)
self.assertEqual(message["From"], "settings@example.com")
+
+
+class TestEmailMessage(TransactionCase):
+ def test_as_string(self):
+ """Ensure all email sent are bpo-34424 free"""
+
+ class FakeSMTP:
+ """SMTP stub"""
+ def __init__(this):
+ this.email_sent = False
+
+ def sendmail(this, smtp_from, smtp_to_list, message_str):
+ this.email_sent = True
+ message_truth = (
+ r'From: .+? <joe@example\.com>\r\n'
+ r'To: .+? <joe@example\.com>\r\n'
+ r'\r\n'
+ )
+ self.assertRegex(message_str, message_truth)
+
+ msg = email.message.EmailMessage(policy=email.policy.SMTP)
+ msg['From'] = '"Joé Doe" <joe@example.com>'
+ msg['To'] = '"Joé Doe" <joe@example.com>'
+
+ smtp = FakeSMTP()
+ self.patch(threading.currentThread(), 'testing', False)
+ self.env['ir.mail_server'].send_email(msg, smtp_session=smtp)
+ self.assertTrue(smtp.email_sent)