From 36d1626972a185598838cfe858fbfa4e39e4d0e5 Mon Sep 17 00:00:00 2001 From: Son Nguyen Kim Date: Sun, 30 Oct 2022 19:59:42 +0100 Subject: [PATCH] Notify another mailbox about an email sent by a mailbox to a reverse alias (#1381) * Notify another mailbox about an email sent by a mailbox to a reverse alias * keep reverse alias in CC and To header * use alias as From to hint that the email is sent from the alias * keep original subject, improve wording * only add DKIM if custom domain has DKIM enabled --- app/email_utils.py | 5 +++-- email_handler.py | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/app/email_utils.py b/app/email_utils.py index b8be9bb5..91f25399 100644 --- a/app/email_utils.py +++ b/app/email_utils.py @@ -934,7 +934,8 @@ def decode_text(text: str, encoding: EmailEncoding = EmailEncoding.NO) -> str: def add_header(msg: Message, text_header, html_header=None) -> Message: if not html_header: - html_header = text_header + html_header = text_header.replace("\n", "
") + content_type = msg.get_content_type().lower() if content_type == "text/plain": encoding = get_encoding(msg) @@ -942,7 +943,7 @@ def add_header(msg: Message, text_header, html_header=None) -> Message: if type(payload) is str: clone_msg = copy(msg) new_payload = f"""{text_header} ---- +------------------------------ {decode_text(payload, encoding)}""" clone_msg.set_payload(encode_text(new_payload, encoding)) return clone_msg diff --git a/email_handler.py b/email_handler.py index 25f32a40..bd1507a7 100644 --- a/email_handler.py +++ b/email_handler.py @@ -1128,6 +1128,9 @@ def handle_reply(envelope, msg: Message, rcpt_to: str) -> (bool, str): + headers.MIME_HEADERS, ) + orig_to = msg[headers.TO] + orig_cc = msg[headers.CC] + # replace the reverse-alias by the contact email in the email body # as this is usually included when replying if user.replace_reverse_alias: @@ -1254,6 +1257,12 @@ def handle_reply(envelope, msg: Message, rcpt_to: str) -> (bool, str): envelope.rcpt_options, is_forward=False, ) + + # if alias belongs to several mailboxes, notify other mailboxes about this email + other_mailboxes = [mb for mb in alias.mailboxes if mb.email != mailbox.email] + for mb in other_mailboxes: + notify_mailbox(alias, mailbox, mb, msg, orig_to, orig_cc, alias_domain) + except Exception: LOG.w("Cannot send email from %s to %s", alias, contact) EmailLog.delete(email_log.id, commit=True) @@ -1280,6 +1289,38 @@ def handle_reply(envelope, msg: Message, rcpt_to: str) -> (bool, str): return True, status.E200 +def notify_mailbox( + alias, mailbox, other_mb: Mailbox, msg, orig_to, orig_cc, alias_domain +): + """Notify another mailbox about an email sent by a mailbox to a reverse alias""" + LOG.d( + f"notify {other_mb.email} about email sent " + f"from {mailbox.email} on behalf of {alias.email} to {msg[headers.TO]}" + ) + notif = add_header( + msg, + f"""**** Don't forget to remove this section if you reply to this email **** +Email sent on behalf of alias {alias.email} using mailbox {mailbox.email}""", + ) + # use alias as From to hint that the email is sent from the alias + add_or_replace_header(notif, headers.FROM, alias.email) + # keep the reverse alias in CC and To header so user can reply more easily + add_or_replace_header(notif, headers.TO, orig_to) + add_or_replace_header(notif, headers.CC, orig_cc) + + # add DKIM as the email is sent from alias + if should_add_dkim_signature(alias_domain): + add_dkim_signature(msg, alias_domain) + + # this notif is considered transactional email + transaction = TransactionalEmail.create(email=other_mb.email, commit=True) + sl_sendmail( + generate_verp_email(VerpType.transactional, transaction.id), + other_mb.email, + notif, + ) + + def replace_original_message_id(alias: Alias, email_log: EmailLog, msg: Message): """ Replace original Message-ID by SL-Message-ID during the reply phase