diff --git a/frappe/email/email_body.py b/frappe/email/email_body.py index 34308f1af8..a1b03a8810 100755 --- a/frappe/email/email_body.py +++ b/frappe/email/email_body.py @@ -127,7 +127,7 @@ class EMail: recipients = split_emails(recipients) # remove null - recipients = filter(None, (strip(r) for r in recipients)) + recipients = list(filter(None, (strip(r) for r in recipients))) self.sender = sender self.reply_to = reply_to or sender diff --git a/frappe/email/receive.py b/frappe/email/receive.py index 514db646f2..f94d7a1c1c 100644 --- a/frappe/email/receive.py +++ b/frappe/email/receive.py @@ -448,7 +448,7 @@ class Email: def decode_email(email: bytes | str | None) -> str | None: if not email: return - email = frappe.as_unicode(email).replace('"', " ").replace("'", " ") + email = frappe.as_unicode(email) try: parts = decode_header(email) except HeaderParseError: @@ -899,8 +899,8 @@ class InboundMail(Email): "sent_or_received": "Received", "sender_full_name": self.from_real_name, "sender": self.from_email, - "recipients": self.mail.get("To"), - "cc": self.mail.get("CC"), + "recipients": self.decode_email(self.mail.get("To") or ""), + "cc": self.decode_email(self.mail.get("CC") or ""), "email_account": self.email_account.name, "communication_medium": "Email", "uid": self.uid, diff --git a/frappe/email/test_email_body.py b/frappe/email/test_email_body.py index 60ff10b84c..901d755942 100644 --- a/frappe/email/test_email_body.py +++ b/frappe/email/test_email_body.py @@ -6,6 +6,7 @@ import os import frappe from frappe import safe_decode +from frappe.core.doctype.communication.communication import Communication from frappe.email.doctype.email_queue.email_queue import QueueBuilder, SendMailContext from frappe.email.email_body import ( get_email, @@ -13,7 +14,7 @@ from frappe.email.email_body import ( inline_style_in_html, replace_filename_with_cid, ) -from frappe.email.receive import Email +from frappe.email.receive import Email, InboundMail from frappe.tests import IntegrationTestCase @@ -205,6 +206,52 @@ Reply-To: test2_@erpnext.com mail = Email.decode_email(" =?UTF-8?B?X\xe0\xe0Y?= ") self.assertIn("xy@example.com", mail) + def test_quotes_in_email_sender(self): + content_bytes = rb"""MIME-Version: 1.0 +Content-Type: text/plain; charset=utf-8 +Content-Disposition: inline +Content-Transfer-Encoding: 8bit +To: "\"fail@example.com\" via ABC" +From: "\"fail@example.com\" via DEF" +Reply-To: "\"fail@example.com\" via GHI" +CC: "\"fail@example.com\" via JKL" +""" + + mail = Email(content_bytes) + self.assertEqual(mail.from_email, "success@example.com") + + self.assertEqual(mail.from_real_name, "failexamplecom via DEF") + # https://github.com/frappe/frappe/pull/3371 + # self.assertEqual(mail.from_real_name, '"fail@example.com" via DEF') + + email_account = frappe._dict({"email_id": "receive@example.com"}) + mail = InboundMail(content_bytes, email_account) + communication: Communication = mail.process() # type: ignore + self.assertEqual(communication.sender_full_name, "failexamplecom via DEF") + + def test_quotes_in_email_recipients(self): + content_bytes = rb"""MIME-Version: 1.0 +Content-Type: text/plain; charset=utf-8 +Content-Disposition: inline +Content-Transfer-Encoding: 8bit +From: "=?utf-8?Q?=F0=9F=98=83?=" + =?utf-8?Q?=3Ctest=40ex?= =?utf-8?Q?ample=2Eco?= =?utf-8?Q?m=3E?= +To: =?iso-8859-1?Q?X=E9Y=40example=2Ecom?= , "fail@example.com" +""" + + # https://ldu2.github.io/rfc2047/ + email_account = frappe._dict({"email_id": "receive@example.com"}) + mail = InboundMail(content_bytes, email_account) + communication: Communication = mail.process() # type: ignore + self.assertEqual(communication.sender_mailid, "test@example.com") + # self.assertEqual(communication.sender_full_name, "😃") + # # TODO: Fix get_name_from_email_string to accept non-ASCII chars + self.assertEqual( + communication.recipients, + 'XéY@example.com , "fail@example.com" ', + ) + frappe.db.rollback() + def fixed_column_width(string, chunk_size): parts = [string[0 + i : chunk_size + i] for i in range(0, len(string), chunk_size)]