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: .+? \r\n' + r'To: .+? \r\n' + r'\r\n' + ) + self.assertRegex(message_str, message_truth) + + msg = email.message.EmailMessage(policy=email.policy.SMTP) + msg['From'] = '"Joé Doe" ' + msg['To'] = '"Joé Doe" ' + + smtp = FakeSMTP() + self.patch(threading.currentThread(), 'testing', False) + self.env['ir.mail_server'].send_email(msg, smtp_session=smtp) + self.assertTrue(smtp.email_sent)