From 677f150fefe1bfe888eb7ce122dfdccac31640d4 Mon Sep 17 00:00:00 2001 From: Son NK <> Date: Thu, 22 Oct 2020 10:44:05 +0200 Subject: [PATCH] add unsubscribe header to com emails --- app/email_utils.py | 20 ++++++++++++++++++-- app/models.py | 25 +++++++++++++++++-------- job_runner.py | 16 ++++++++++++---- 3 files changed, 47 insertions(+), 14 deletions(-) diff --git a/app/email_utils.py b/app/email_utils.py index fa9646e1..82cbd275 100644 --- a/app/email_utils.py +++ b/app/email_utils.py @@ -52,7 +52,7 @@ def render(template_name, **kwargs) -> str: def send_welcome_email(user): - to_email = user.get_communication_email() + to_email, unsubscribe_link, via_email = user.get_communication_email() if not to_email: return @@ -64,6 +64,8 @@ def send_welcome_email(user): f"Welcome to SimpleLogin {user.name}", render("com/welcome.txt", name=user.name, user=user, alias=alias), render("com/welcome.html", name=user.name, user=user, alias=alias), + unsubscribe_link, + via_email, ) @@ -186,7 +188,14 @@ def send_cannot_create_domain_alias(user, alias, domain): ) -def send_email(to_email, subject, plaintext, html=None): +def send_email( + to_email, + subject, + plaintext, + html=None, + unsubscribe_link=None, + unsubscribe_via_email=False, +): if NOT_SEND_EMAIL: LOG.d( "send email with subject '%s' to '%s', plaintext: %s", @@ -221,6 +230,13 @@ def send_email(to_email, subject, plaintext, html=None): date_header = formatdate() msg["Date"] = date_header + if unsubscribe_link: + add_or_replace_header(msg, "List-Unsubscribe", f"<{unsubscribe_link}>") + if not unsubscribe_via_email: + add_or_replace_header( + msg, "List-Unsubscribe-Post", "List-Unsubscribe=One-Click" + ) + # add DKIM email_domain = SUPPORT_EMAIL[SUPPORT_EMAIL.find("@") + 1 :] add_dkim_signature(msg, email_domain) diff --git a/app/models.py b/app/models.py index 17bd6dbd..5372c2ea 100644 --- a/app/models.py +++ b/app/models.py @@ -24,6 +24,7 @@ from app.config import ( LANDING_PAGE_URL, FIRST_ALIAS_DOMAIN, DISABLE_ONBOARDING, + UNSUBSCRIBER, ) from app.errors import AliasInTrashError from app.extensions import db @@ -538,21 +539,29 @@ class User(db.Model, ModelMixin, UserMixin): def two_factor_authentication_enabled(self) -> bool: return self.enable_otp or self.fido_enabled() - def get_communication_email(self) -> Optional[str]: - """return the email that user uses to receive email communication""" + def get_communication_email(self) -> (Optional[str], str, bool): + """ + Return + - the email that user uses to receive email communication. None if user unsubscribes from newsletter + - the unsubscribe URL + - whether the unsubscribe method is via sending email (mailto:) or Http POST + """ if self.notification and self.activated: if self.newsletter_alias_id: alias = Alias.get(self.newsletter_alias_id) if alias.enabled: - return alias.email - - # user doesn't want to receive newsletter + unsubscribe_link, via_email = alias.unsubscribe_link() + return alias.email, unsubscribe_link, via_email + # alias disabled -> user doesn't want to receive newsletter else: - return None + return None, None, False else: - return self.email + # do not handle http POST unsubscribe + if UNSUBSCRIBER: + # use * as suffix instead of = as for alias unsubscribe + return self.email, f"mailto:{UNSUBSCRIBER}?subject={self.id}*", True - return None + return None, None, False def available_sl_domains(self) -> [str]: """ diff --git a/job_runner.py b/job_runner.py index 048c727f..b7fa0d4e 100644 --- a/job_runner.py +++ b/job_runner.py @@ -52,7 +52,7 @@ def new_app(): def onboarding_send_from_alias(user): - to_email = user.get_communication_email() + to_email, unsubscribe_link, via_email = user.get_communication_email() if not to_email: return @@ -61,11 +61,13 @@ def onboarding_send_from_alias(user): f"SimpleLogin Tip: Send emails from your alias", render("com/onboarding/send-from-alias.txt", user=user, to_email=to_email), render("com/onboarding/send-from-alias.html", user=user, to_email=to_email), + unsubscribe_link, + via_email, ) def onboarding_pgp(user): - to_email = user.get_communication_email() + to_email, unsubscribe_link, via_email = user.get_communication_email() if not to_email: return @@ -74,11 +76,13 @@ def onboarding_pgp(user): f"SimpleLogin Tip: Secure your emails with PGP", render("com/onboarding/pgp.txt", user=user, to_email=to_email), render("com/onboarding/pgp.html", user=user, to_email=to_email), + unsubscribe_link, + via_email, ) def onboarding_browser_extension(user): - to_email = user.get_communication_email() + to_email, unsubscribe_link, via_email = user.get_communication_email() if not to_email: return @@ -87,11 +91,13 @@ def onboarding_browser_extension(user): f"SimpleLogin Tip: Chrome/Firefox/Safari extensions and Android/iOS apps", render("com/onboarding/browser-extension.txt", user=user, to_email=to_email), render("com/onboarding/browser-extension.html", user=user, to_email=to_email), + unsubscribe_link, + via_email, ) def onboarding_mailbox(user): - to_email = user.get_communication_email() + to_email, unsubscribe_link, via_email = user.get_communication_email() if not to_email: return @@ -100,6 +106,8 @@ def onboarding_mailbox(user): f"SimpleLogin Tip: Multiple mailboxes", render("com/onboarding/mailbox.txt", user=user, to_email=to_email), render("com/onboarding/mailbox.html", user=user, to_email=to_email), + unsubscribe_link, + via_email, )