From fb29503b81737ef0220216c535b22d12362aba3e Mon Sep 17 00:00:00 2001 From: Son Nguyen Kim Date: Mon, 2 Aug 2021 11:33:58 +0200 Subject: [PATCH] do not send bounce to IgnoreBounceSender --- app/email/status.py | 3 +++ app/email_utils.py | 9 +++++++++ email_handler.py | 33 ++++++++++++++++++++++++++------- tests/test_email_utils.py | 10 +++++++++- 4 files changed, 47 insertions(+), 8 deletions(-) diff --git a/app/email/status.py b/app/email/status.py index 97d4c747..7811c937 100644 --- a/app/email/status.py +++ b/app/email/status.py @@ -8,6 +8,9 @@ E205 = "250 SL E205 bounce handled" # out of office status E206 = "250 SL E206 Out of office" +# if mail_from is a IgnoreBounceSender, no need to send back a bounce report +E207 = "250 SL E207 No bounce report" + # 4** errors # E401 = "421 SL E401 Retry later" E402 = "421 SL E402 Encryption failed - Retry later" diff --git a/app/email_utils.py b/app/email_utils.py index a0999603..f0513ce6 100644 --- a/app/email_utils.py +++ b/app/email_utils.py @@ -58,6 +58,7 @@ from app.models import ( Alias, EmailLog, TransactionalEmail, + IgnoreBounceSender, ) from app.utils import ( random_string, @@ -1213,3 +1214,11 @@ def get_queue_id(msg: Message) -> Optional[str]: with_esmtps = received_header[search_result.start() : search_result.end()] return with_esmtps[len("with ESMTPS id ") :] + + +def should_ignore_bounce(mail_from: str) -> bool: + if IgnoreBounceSender.get_by(mail_from=mail_from): + LOG.w("do not send back bounce report to %s", mail_from) + return True + + return False diff --git a/email_handler.py b/email_handler.py index a242e9a6..fed44266 100644 --- a/email_handler.py +++ b/email_handler.py @@ -111,6 +111,7 @@ from app.email_utils import ( sl_sendmail, sanitize_header, get_queue_id, + should_ignore_bounce, ) from app.extensions import db from app.log import LOG, set_message_id @@ -540,13 +541,19 @@ def handle_forward(envelope, msg: Message, rcpt_to: str) -> List[Tuple[bool, str alias = try_auto_create(address) if not alias: LOG.d("alias %s cannot be created on-the-fly, return 550", address) - return [(False, status.E515)] + if should_ignore_bounce(envelope.mail_from): + return [(True, status.E207)] + else: + return [(False, status.E515)] user = alias.user if user.disabled: LOG.w("User %s disabled, disable forwarding emails for %s", user, alias) - return [(False, status.E504)] + if should_ignore_bounce(envelope.mail_from): + return [(True, status.E207)] + else: + return [(False, status.E504)] # mail_from = envelope.mail_from # for mb in alias.mailboxes: @@ -588,7 +595,10 @@ def handle_forward(envelope, msg: Message, rcpt_to: str) -> List[Tuple[bool, str # no valid mailbox if not mailboxes: - return [(False, status.E516)] + if should_ignore_bounce(envelope.mail_from): + return [(True, status.E207)] + else: + return [(False, status.E516)] # no need to create a copy of message for mailbox in mailboxes: @@ -618,8 +628,11 @@ def forward_email_to_mailbox( LOG.d("Forward %s -> %s -> %s", contact, alias, mailbox) if mailbox.disabled: - LOG.debug("%s disabled, do not forward") - return False, status.E518 + LOG.d("%s disabled, do not forward") + if should_ignore_bounce(envelope.mail_from): + return True, status.E207 + else: + return False, status.E518 # sanity check: make sure mailbox is not actually an alias if get_email_domain_part(alias.email) == get_email_domain_part(mailbox.email): @@ -819,7 +832,10 @@ def forward_email_to_mailbox( alias, mailbox, ) - return False, status.E521 + if should_ignore_bounce(envelope.mail_from): + return True, status.E207 + else: + return False, status.E521 else: db.session.commit() return True, status.E200 @@ -1707,7 +1723,10 @@ def handle(envelope: Envelope) -> str: if rate_limited(mail_from, rcpt_tos): LOG.w("Rate Limiting applied for mail_from:%s rcpt_tos:%s", mail_from, rcpt_tos) - return status.E522 + if should_ignore_bounce(envelope.mail_from): + return status.E207 + else: + return status.E522 # Handle "out of office" auto notice. An automatic response is sent for every forwarded email # todo: remove logging diff --git a/tests/test_email_utils.py b/tests/test_email_utils.py index 036ea7bd..5f432356 100644 --- a/tests/test_email_utils.py +++ b/tests/test_email_utils.py @@ -28,9 +28,10 @@ from app.email_utils import ( decode_text, parse_id_from_bounce, get_queue_id, + should_ignore_bounce, ) from app.extensions import db -from app.models import User, CustomDomain, Alias, Contact, EmailLog +from app.models import User, CustomDomain, Alias, Contact, EmailLog, IgnoreBounceSender # flake8: noqa: E101, W191 from tests.utils import login @@ -740,3 +741,10 @@ def test_get_queue_id(): ) assert get_queue_id(msg) == "4FxQmw1DXdz2vK2" + + +def test_should_ignore_bounce(flask_client): + assert not should_ignore_bounce("not-exist") + + IgnoreBounceSender.create(mail_from="to-ignore@example.com") + assert should_ignore_bounce("to-ignore@example.com")