From 88d63bd931af3c141dcae3a75abd94a854ee931e Mon Sep 17 00:00:00 2001 From: Son NK Date: Sat, 28 Mar 2020 23:15:45 +0100 Subject: [PATCH 1/2] add UNSUBSCRIBER config --- app/config.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/app/config.py b/app/config.py index bb5bef5d..a92fb17c 100644 --- a/app/config.py +++ b/app/config.py @@ -90,6 +90,9 @@ else: # disable the alias suffix, i.e. the ".random_word" part DISABLE_ALIAS_SUFFIX = "DISABLE_ALIAS_SUFFIX" in os.environ +# the email address that receives all unsubscription request +UNSUBSCRIBER = os.environ.get("UNSUBSCRIBER") + DKIM_PRIVATE_KEY_PATH = get_abs_path(os.environ["DKIM_PRIVATE_KEY_PATH"]) DKIM_PUBLIC_KEY_PATH = get_abs_path(os.environ["DKIM_PUBLIC_KEY_PATH"]) DKIM_SELECTOR = b"dkim" From da6441b4b8b645a6eb2e1551a55c9321333ca71e Mon Sep 17 00:00:00 2001 From: Son NK Date: Sat, 28 Mar 2020 23:19:25 +0100 Subject: [PATCH 2/2] Handle on-click unsubcribe --- email_handler.py | 71 +++++++++++++++++-- .../unsubscribe-disable-alias.html | 11 +++ .../unsubscribe-disable-alias.txt | 12 ++++ 3 files changed, 89 insertions(+), 5 deletions(-) create mode 100644 templates/emails/transactional/unsubscribe-disable-alias.html create mode 100644 templates/emails/transactional/unsubscribe-disable-alias.txt diff --git a/email_handler.py b/email_handler.py index aa0b3dea..0e4b11a3 100644 --- a/email_handler.py +++ b/email_handler.py @@ -52,6 +52,7 @@ from app.config import ( URL, ALIAS_DOMAINS, POSTFIX_SUBMISSION_TLS, + UNSUBSCRIBER, ) from app.email_utils import ( send_email, @@ -459,11 +460,15 @@ def handle_forward(envelope, smtp: SMTP, msg: Message, rcpt_to: str) -> (bool, s add_or_replace_header(msg, "To", to_header.strip()) # add List-Unsubscribe header - unsubscribe_link = f"{URL}/dashboard/unsubscribe/{alias.id}" - add_or_replace_header(msg, "List-Unsubscribe", f"<{unsubscribe_link}>") - add_or_replace_header( - msg, "List-Unsubscribe-Post", "List-Unsubscribe=One-Click" - ) + if UNSUBSCRIBER: + unsubscribe_link = f"mailto:{UNSUBSCRIBER}?subject={alias.id}=" + add_or_replace_header(msg, "List-Unsubscribe", f"<{unsubscribe_link}>") + else: + unsubscribe_link = f"{URL}/dashboard/unsubscribe/{alias.id}" + add_or_replace_header(msg, "List-Unsubscribe", f"<{unsubscribe_link}>") + add_or_replace_header( + msg, "List-Unsubscribe-Post", "List-Unsubscribe=One-Click" + ) add_dkim_signature(msg, EMAIL_DOMAIN) @@ -743,6 +748,54 @@ def handle_bounce( ) +def handle_unsubscribe(envelope): + message_data = envelope.content.decode("utf8", errors="replace") + msg = Parser(policy=SMTPUTF8).parsestr(message_data) + + # format: alias_id: + subject = msg["Subject"] + try: + alias_id = int(subject[:-1]) + alias = Alias.get(alias_id) + except Exception: + LOG.warning("Cannot parse alias from subject %s", msg["Subject"]) + return "550 SL ignored" + + if not alias: + LOG.warning("No such alias %s", alias_id) + return "550 SL ignored" + + # This sender cannot unsubscribe + if alias.mailbox_email() != envelope.mail_from: + LOG.d("%s cannot disable alias %s", envelope.mail_from, alias) + return "550 SL ignored" + + # Sender is owner of this alias + alias.enabled = False + db.session.commit() + user = alias.user + + enable_alias_url = URL + f"/dashboard/?highlight_alias_id={alias.id}" + send_email( + envelope.mail_from, + f"Alias {alias.email} has been disabled successfully", + render( + "transactional/unsubscribe-disable-alias.txt", + user=user, + alias=alias.email, + enable_alias_url=enable_alias_url, + ), + render( + "transactional/unsubscribe-disable-alias.html", + user=user, + alias=alias.email, + enable_alias_url=enable_alias_url, + ), + ) + + return "250 Unsubscribe request accepted" + + class MailHandler: async def handle_DATA(self, server, session, envelope): LOG.debug( @@ -757,6 +810,14 @@ class MailHandler: else: smtp = SMTP(POSTFIX_SERVER, 25) + # unsubscribe request + if UNSUBSCRIBER and envelope.rcpt_tos == [UNSUBSCRIBER]: + LOG.d("Handle unsubscribe request from %s", envelope.mail_from) + app = new_app() + + with app.app_context(): + return handle_unsubscribe(envelope) + # result of all deliveries # each element is a couple of whether the delivery is successful and the smtp status res: [(bool, str)] = [] diff --git a/templates/emails/transactional/unsubscribe-disable-alias.html b/templates/emails/transactional/unsubscribe-disable-alias.html new file mode 100644 index 00000000..dc9f6f8a --- /dev/null +++ b/templates/emails/transactional/unsubscribe-disable-alias.html @@ -0,0 +1,11 @@ +{% extends "base.html" %} + +{% block content %} + {{ render_text("Hi " + user.name) }} + {{ render_text("Your alias "+ alias +" has been disabled successfully.") }} + {{ render_text("If this is a mistake, you can re-enable the alias on the dashboard.") }} + {{ render_button("Enable Alias", enable_alias_url) }} + {{ render_text('Thanks,
SimpleLogin Team.') }} + {{ raw_url(enable_alias_url) }} +{% endblock %} + diff --git a/templates/emails/transactional/unsubscribe-disable-alias.txt b/templates/emails/transactional/unsubscribe-disable-alias.txt new file mode 100644 index 00000000..b2a0841e --- /dev/null +++ b/templates/emails/transactional/unsubscribe-disable-alias.txt @@ -0,0 +1,12 @@ +Hi {{user.name}} + +Your alias {{alias}} has been disabled successfully. + +If this is a mistake, you can re-enable the alias on the dashboard via + +{{ enable_alias_url }} + +Please let us know if you have any question. + +Best, +SimpleLogin team.