From 2ba199349c80ee50ea6094571be96d3134188ae5 Mon Sep 17 00:00:00 2001 From: Corentin Forler Date: Tue, 3 Sep 2024 10:39:06 +0200 Subject: [PATCH] fix(email): Try to parse bad email address headers --- frappe/email/receive.py | 13 +++++++++++-- frappe/email/test_email_body.py | 4 ++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/frappe/email/receive.py b/frappe/email/receive.py index 4d23a4d4b3..0437ba7e28 100644 --- a/frappe/email/receive.py +++ b/frappe/email/receive.py @@ -10,6 +10,7 @@ import poplib import re import ssl from contextlib import suppress +from email.errors import HeaderParseError from email.header import decode_header import _socket @@ -440,11 +441,19 @@ class Email: self.from_real_name = parse_addr(_from_email)[0] if "@" in _from_email else _from_email @staticmethod - def decode_email(email): + def decode_email(email: bytes | str | None) -> str | None: if not email: return + email = frappe.as_unicode(email).replace('"', " ").replace("'", " ") + try: + parts = decode_header(email) + except HeaderParseError: + # Fallback: grab just the email addresses + emails = re.findall(r"(<.*?>)", email) + return ", ".join(emails) + decoded = "" - for part, encoding in decode_header(frappe.as_unicode(email).replace('"', " ").replace("'", " ")): + for part, encoding in parts: if encoding: decoded += part.decode(encoding, "replace") else: diff --git a/frappe/email/test_email_body.py b/frappe/email/test_email_body.py index a95adafac7..3db7cd0175 100644 --- a/frappe/email/test_email_body.py +++ b/frappe/email/test_email_body.py @@ -201,6 +201,10 @@ Reply-To: test2_@erpnext.com ) self.assertIn("user@example.com", mail) + def test_poorly_encoded_messages2(self): + mail = Email.decode_email(" =?UTF-8?B?X\xe0\xe0Y?= ") + self.assertIn("xy@example.com", mail) + def fixed_column_width(string, chunk_size): parts = [string[0 + i : chunk_size + i] for i in range(0, len(string), chunk_size)]