From a03ae3765ed2d9b9b3c039a143d08a4dfd14d1d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 26 Oct 2023 14:10:12 +0200 Subject: [PATCH] [IMP] base: fallback on 'email_re' when getadresses fails When 'getadresses' fails at parsing some input and give us a result like 'gmail.com' (see previous commit adding test cases) we fallback on using 'email_re' which is better at finding email addresses in a global string. We use it only in this specific case as fallback mechanism to rely on 'getadresses' when possible. Task-3572208 X-original-commit: odoo/odoo@8e61a3b690230d3bc1f67e345df647f707d74437 Part-of: odoo/odoo#141856 --- odoo/addons/base/tests/test_mail.py | 64 +++++++++++++++++++++-------- odoo/tools/mail.py | 16 +++++++- 2 files changed, 60 insertions(+), 20 deletions(-) diff --git a/odoo/addons/base/tests/test_mail.py b/odoo/addons/base/tests/test_mail.py index 56beaf365c5..9c855caf198 100644 --- a/odoo/addons/base/tests/test_mail.py +++ b/odoo/addons/base/tests/test_mail.py @@ -520,7 +520,7 @@ class TestEmailTools(BaseCase): 'deboulonneur@example.com', 'deboulonneur@example.comdéboulonneur', False, - '@example.com', # funny + False, # need fix over 'getadresses' 'deboulonneur.😊@example.com', 'déboulonneur@examplé.com', 'DéBoulonneur@examplé.com', @@ -530,7 +530,7 @@ class TestEmailTools(BaseCase): f'"{format_name}" ', f'"{format_name}" ', f'"{format_name}" <@>', - f'"{format_name}" <@example.com>', + f'"{format_name}" <@>', f'"{format_name}" ', f'"{format_name}" ', f'"{format_name}" ', @@ -540,7 +540,7 @@ class TestEmailTools(BaseCase): f'{format_name_ascii} ', f'{format_name_ascii} ', f'{format_name_ascii} <@>', - f'{format_name_ascii} <@example.com>', + f'{format_name_ascii} <@>', f'{format_name_ascii} ', f'{format_name_ascii} ', f'{format_name_ascii} ', @@ -594,24 +594,52 @@ class TestEmailTools(BaseCase): ('"john@gmail.com"', ['johnny@gmail.com']), # double-quoting ('"" ', ['johnny@gmail.com']), # double-quoting with brackets ('@gmail.com', ['@gmail.com']), # no left-part - # TFR cases + # '@domain' corner cases -- all those return a '@gmail.com' (or equivalent) + # email address when going through 'getaddresses' # - multi @ - ('fr@ncois.th@notgmail.com', ['@notgmail.com']), - ('f@r@nc.gz,ois@notgmail.com', ['@nc.gz', 'ois@notgmail.com']), - ('@notgmail.com esteban_gnole@coldmail.com@notgmail.com', ['@notgmail.com']), + ('fr@ncois.th@notgmail.com', ['fr@ncois.th']), + ('f@r@nc.gz,ois@notgmail.com', ['r@nc.gz', 'ois@notgmail.com']), # still failing, but differently from 'getaddresses' alone + ('@notgmail.com esteban_gnole@coldmail.com@notgmail.com', ['esteban_gnole@coldmail.com']), # - multi emails (with invalid) - ('Ivan@dezotos.com Cc iv.an@notgmail.com', ['@notgmail.com']), - ('ivan-dredi@coldmail.com ivan.dredi@notgmail.com', ['@notgmail.com']), - ('@notgmail.com ivan@coincoin.com.ar jeanine@coincoin.com.ar', ['@coincoin.com.ar']), - ('@notgmail.com whoareyou@youhou.com. ivan.dezotos@notgmail.com', ['@notgmail.com']), - ('francois@nc.gz CC: ois@notgmail.com ivan@dezotos.com', ['francois@nc.gzCC', '@dezotos.com']), - ('francois@nc.gz CC: ois@notgmail.com,ivan@dezotos.com', ['francois@nc.gzCC', 'ois@notgmail.com', 'ivan@dezotos.com']), + ( + 'Ivan@dezotos.com Cc iv.an@notgmail.com', + ['Ivan@dezotos.com', 'iv.an@notgmail.com'] + ), + ( + 'ivan-dredi@coldmail.com ivan.dredi@notgmail.com', + ['ivan-dredi@coldmail.com', 'ivan.dredi@notgmail.com'] + ), + ( + '@notgmail.com ivan@coincoin.com.ar jeanine@coincoin.com.ar', + ['ivan@coincoin.com.ar', 'jeanine@coincoin.com.ar'] + ), + ( + '@notgmail.com whoareyou@youhou.com. ivan.dezotos@notgmail.com', + ['whoareyou@youhou.com', 'ivan.dezotos@notgmail.com'] + ), + ( + 'francois@nc.gz CC: ois@notgmail.com ivan@dezotos.com', + ['francois@nc.gz', 'ois@notgmail.com', 'ivan@dezotos.com'] + ), + ( + 'francois@nc.gz CC: ois@notgmail.com,ivan@dezotos.com', + ['francois@nc.gzCC', 'ois@notgmail.com', 'ivan@dezotos.com'] + ), # - separated with '/'' - ('ivan.plein@dezotos.com / ivan.plu@notgmail.com', ['@notgmail.com']), - ('@notgmail.com ivan.parfois@notgmail.com/ ivan.souvent@notgmail.com', ['@notgmail.com']), + ( + 'ivan.plein@dezotos.com / ivan.plu@notgmail.com', + ['ivan.plein@dezotos.com', 'ivan.plu@notgmail.com'] + ), + ( + '@notgmail.com ivan.parfois@notgmail.com/ ivan.souvent@notgmail.com', + ['ivan.parfois@notgmail.com', 'ivan.souvent@notgmail.com'] + ), # - separated with '-'' - ('ivan@dezotos.com - ivan.dezotos@notgmail.com', ['@notgmail.com']), - ('car.pool@notgmail.com - co (TAMBO) Registration car.warsh@notgmail.com', ['@notgmail.com']), + ('ivan@dezotos.com - ivan.dezotos@notgmail.com', ['ivan@dezotos.com', 'ivan.dezotos@notgmail.com']), + ( + 'car.pool@notgmail.com - co (TAMBO) Registration car.warsh@notgmail.com', + ['car.pool@notgmail.com', 'car.warsh@notgmail.com'] + ), ] for source, expected in cases: with self.subTest(source=source): @@ -649,7 +677,7 @@ class TestEmailTools(BaseCase): # multi ['deboulonneur@example.com'], ['deboulonneur@example.com', 'deboulonneur2@example.com'], - ['@example.com'], # funny one + ['deboulonneur@example.com', 'deboulonneur2@example.com'], # need fix over 'getadresses' # format / misc ['deboulonneur@example.com'], ['"Super Déboulonneur" ', '"Super Déboulonneur 2" '], diff --git a/odoo/tools/mail.py b/odoo/tools/mail.py index b9a9e6f48ae..8be77b37af8 100644 --- a/odoo/tools/mail.py +++ b/odoo/tools/mail.py @@ -549,13 +549,25 @@ def email_split_tuples(text): if not text: return [] - return list(map(_parse_based_on_spaces, [ + + # found valid pairs, filtering out failed parsing + valid_pairs = [ (addr[0], addr[1]) for addr in getaddresses([text]) # getaddresses() returns '' when email parsing fails, and # sometimes returns emails without at least '@'. The '@' # is strictly required in RFC2822's `addr-spec`. if addr[1] and '@' in addr[1] - ])) + ] + # corner case: returning '@gmail.com'-like email (see test_email_split) + if any(pair[1].startswith('@') for pair in valid_pairs): + filtered = [ + found_email for found_email in email_re.findall(text) + if found_email and not found_email.startswith('@') + ] + if filtered: + valid_pairs = [('', found_email) for found_email in filtered] + + return list(map(_parse_based_on_spaces, valid_pairs)) def email_split(text): """ Return a list of the email addresses found in ``text`` """