From 88fce13a901da5b802e56e827b85e8a640054c72 Mon Sep 17 00:00:00 2001
From: "Nasreddin Boulif (bon)" <bon@odoo.com>
Date: Tue, 22 Aug 2023 15:13:59 +0200
Subject: [PATCH] [FIX] mail: return str instead of bytes when post processing
 attachment

Steps to reproduce:

  On Odoo:
  - Install `Documents` module
  - Go to `Settings` and set a Custom Email Server(ex. `mydomain.com`)
  - Ensure an alias exist for the model `document.document` with
    `inbox-financial` as alias name

  In mail client:
  - Send a mail with an image in the body to the following email:
    inbox-financial@mydomain.com

Issue:

  Mail not received (traceback in logs)

Cause:

  Since we use the email alias `inbox-financial`, we process the mail
  through the `document.document` model where we have an override of the
  `_message_post_after_hook` method that is called after that the
  `msg_values` values are post-processed and where we do another
  message_post() for the new document create for the attachment (in
  this case, an image).

  https://github.com/odoo/enterprise/blob/2c3596e4e18c201809558d3ea878b141e366a027/documents/models/document.py#L305

  During the parsing, the mail values are updated through the
  `_process_attachments_for_post` method:

  https://github.com/odoo/odoo/blob/6c0d2d7a9d44459f3e09a38bd80ef9b018e8c946/addons/mail/models/mail_thread.py#L1881-L1904

  On posting the first time, the original type of the `body` value is a
  `str`, but the post-processed value (because there is some CIDS in the
  body) is of type `bytes` (because using `encoding='UTF-8'` with
  `lxml.html.tostring`).

  https://github.com/odoo/odoo/blob/510a997017a9cbe14522a0013a578f6d1d9b257a/addons/mail/models/mail_thread.py#L2209

  Then in the `_message_post_after_hook` we call post again on the
  newly created document record by using post-processed value of body
  who is of type `bytes`.

  On posting the second time (for the document record), the `body`
  value is of type `bytes` and when checking if the body is empty with
  `is_html_empty` that received a string as param, an error is
  raised.

Solution:

  Use `encoding='unicode'` to return a string instead of bytes.

  https://lxml.de/api/lxml.etree-module.html#tostring

ENT PR : https://github.com/odoo/enterprise/pull/42653

opw-3273583

closes odoo/odoo#135021

X-original-commit: f910987cb4af84c1e7afabf67f05d4eebc31e765
Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
---
 addons/mail/models/mail_thread.py           |  2 +-
 addons/test_mail/tests/test_mail_gateway.py | 12 +++++++++++-
 2 files changed, 12 insertions(+), 2 deletions(-)

diff --git a/addons/mail/models/mail_thread.py b/addons/mail/models/mail_thread.py
index c2cebb1b1f4a..48c5a55ef41d 100644
--- a/addons/mail/models/mail_thread.py
+++ b/addons/mail/models/mail_thread.py
@@ -1788,7 +1788,7 @@ class MailThread(models.AbstractModel):
                         node.set('src', '/web/image/%s?access_token=%s' % attachment_data)
                         postprocessed = True
                 if postprocessed:
-                    return_values['body'] = lxml.html.tostring(root, pretty_print=False, encoding='UTF-8')
+                    return_values['body'] = lxml.html.tostring(root, pretty_print=False, encoding='unicode')
         return_values['attachment_ids'] = m2m_attachment_ids
         return return_values
 
diff --git a/addons/test_mail/tests/test_mail_gateway.py b/addons/test_mail/tests/test_mail_gateway.py
index 041c211fb9a0..84c990a8c446 100644
--- a/addons/test_mail/tests/test_mail_gateway.py
+++ b/addons/test_mail/tests/test_mail_gateway.py
@@ -8,6 +8,7 @@ from unittest.mock import DEFAULT
 from unittest.mock import patch
 
 from odoo import exceptions
+from odoo.addons.mail.models.mail_thread import MailThread
 from odoo.addons.mail.tests.common import mail_new_test_user
 from odoo.addons.test_mail.data import test_mail_data
 from odoo.addons.test_mail.data.test_mail_data import MAIL_TEMPLATE
@@ -348,7 +349,16 @@ class TestMailgateway(TestMailCommon):
 
     @mute_logger('odoo.addons.mail.models.mail_thread')
     def test_message_process_cid(self):
-        record = self.format_and_process(test_mail_data.MAIL_MULTIPART_IMAGE, self.email_from, 'groups@test.com')
+        origin_message_parse_extract_payload = MailThread._message_parse_extract_payload
+
+        def _message_parse_extract_payload(this, *args, **kwargs):
+            res = origin_message_parse_extract_payload(this, *args, **kwargs)
+            self.assertTrue(isinstance(res['body'], str), 'Body from extracted payload should still be a string.')
+            return res
+
+        with patch.object(MailThread, '_message_parse_extract_payload', _message_parse_extract_payload):
+            record = self.format_and_process(test_mail_data.MAIL_MULTIPART_IMAGE, self.email_from, 'groups@test.com')
+
         message = record.message_ids[0]
         for attachment in message.attachment_ids:
             self.assertIn('/web/image/%s' % attachment.id, message.body)
-- 
GitLab