From 441746d0119b72125b0338f5cfb97db0b10c399c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= <tde@odoo.com>
Date: Mon, 11 Apr 2022 12:44:54 +0200
Subject: [PATCH] [FIX] mail: avoid multi-emails in outgoing emails 'from'

PURPOSE

Be defensive when dealing with email fields, notably when having multi-emails
or email field containing an already-formatted email.

SPECIFICATIONS

When building the final 'from' of outgoing emails using 'formataddr' we
have issues if email contains multi emails or formatted email. Having a
wrongly formatted email in 'email_from' leads to issues as it is badly
recognized by email providers, could be considered as being phishing and
also breaks reply_to mechanism.

Main fix of this commit is to extract emails and rebuild the 'email_from'
based on found emails. 'from' of sent emails is now the first found email
in 'email_from' field of related <mail.mail> record like

  -> before: email_from: '"Raoul" <raoul@raoul.fr>, raoul2@raoul.fr'
  -> after: email_from: '"Raoul" <raoul@raoul.fr>' and raoul2 is ignored

Task-2612945 (Mail: Defensive email formatting)

Part-of: odoo/odoo#74474
---
 addons/mail/models/mail_mail.py              |  6 +++++-
 addons/test_mail/tests/test_mail_composer.py | 11 ++++++-----
 2 files changed, 11 insertions(+), 6 deletions(-)

diff --git a/addons/mail/models/mail_mail.py b/addons/mail/models/mail_mail.py
index 399f82722beb..b29ccc946c90 100644
--- a/addons/mail/models/mail_mail.py
+++ b/addons/mail/models/mail_mail.py
@@ -361,11 +361,15 @@ class MailMail(models.Model):
                     # see rev. 56596e5240ef920df14d99087451ce6f06ac6d36
                     notifs.flush(fnames=['notification_status', 'failure_type', 'failure_reason'], records=notifs)
 
+                # protect against ill-formatted email_from when formataddr was used on an already formatted email
+                emails_from = tools.email_split_and_format(mail.email_from)
+                email_from = emails_from[0] if emails_from else mail.email_from
+
                 # build an RFC2822 email.message.Message object and send it without queuing
                 res = None
                 for email in email_list:
                     msg = IrMailServer.build_email(
-                        email_from=mail.email_from,
+                        email_from=email_from,
                         email_to=email.get('email_to'),
                         subject=mail.subject,
                         body=email.get('body'),
diff --git a/addons/test_mail/tests/test_mail_composer.py b/addons/test_mail/tests/test_mail_composer.py
index 807b8f1000a7..2a7cf12f3201 100644
--- a/addons/test_mail/tests/test_mail_composer.py
+++ b/addons/test_mail/tests/test_mail_composer.py
@@ -721,8 +721,8 @@ class TestComposerResultsComment(TestMailComposer):
             author=self.partner_employee,
             email_values={
                 'body_content': f'TemplateBody {self.test_record.name}',
-                # currently holding multi-email 'from'
-                'email_from': formataddr((self.user_employee.name, 'email.from.1@test.example.com,email.from.2@test.example.com')),
+                # single email event if email field is multi-email
+                'email_from': formataddr((self.user_employee.name, 'email.from.1@test.example.com')),
                 'subject': f'TemplateSubject {self.test_record.name}',
             },
             fields_values={
@@ -740,7 +740,8 @@ class TestComposerResultsComment(TestMailComposer):
             ] + [[email] for email in mailed_new_partners.mapped('email_formatted')],
             email_values={
                 'body_content': f'TemplateBody {self.test_record.name}',
-                'email_from': formataddr((self.user_employee.name, 'email.from.1@test.example.com,email.from.2@test.example.com')),
+                # single email event if email field is multi-email
+                'email_from': formataddr((self.user_employee.name, 'email.from.1@test.example.com')),
                 'subject': f'TemplateSubject {self.test_record.name}',
             },
             fields_values={
@@ -1175,8 +1176,8 @@ class TestComposerResultsMass(TestMailComposer):
                 ] + [[email] for email in mailed_new_partners.mapped('email_formatted')],
                 email_values={
                     'body_content': f'TemplateBody {record.name}',
-                    # currently holding multi-email 'email_from'
-                    'email_from': self.partner_employee.email_formatted,
+                    # single email event if email field is multi-email
+                    'email_from': formataddr((self.user_employee.name, 'email.from.1@test.example.com')),
                     'reply_to': formataddr((
                         f'{self.env.user.company_id.name} {record.name}',
                         f'{self.alias_catchall}@{self.alias_domain}'
-- 
GitLab