diff --git a/app/config.py b/app/config.py
index b3d132f6..ecffc2e3 100644
--- a/app/config.py
+++ b/app/config.py
@@ -333,6 +333,7 @@ AlERT_WRONG_MX_RECORD_CUSTOM_DOMAIN = "custom_domain_mx_record_issue"
ALERT_DIRECTORY_DISABLED_ALIAS_CREATION = "alert_directory_disabled_alias_creation"
ALERT_HOTMAIL_COMPLAINT = "alert_hotmail_complaint"
+ALERT_YAHOO_COMPLAINT = "alert_yahoo_complaint"
# <<<<< END ALERT EMAIL >>>>
diff --git a/app/email/status.py b/app/email/status.py
index 5e2b7b59..b2fbfef9 100644
--- a/app/email/status.py
+++ b/app/email/status.py
@@ -15,6 +15,8 @@ E208 = "250 SL E208 Hotmail complaint handled"
E209 = "250 SL E209 Email Loop"
+E210 = "250 SL E210 Yahoo complaint handled"
+
# 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 c6a5659a..97aa3555 100644
--- a/app/email_utils.py
+++ b/app/email_utils.py
@@ -592,7 +592,6 @@ def get_orig_message_from_bounce(msg: Message) -> Message:
def get_orig_message_from_outlook_complaint(msg: Message) -> Message:
- """parse the original email from Bounce"""
i = 0
for part in msg.walk():
i += 1
@@ -604,6 +603,18 @@ def get_orig_message_from_outlook_complaint(msg: Message) -> Message:
return part
+def get_orig_message_from_yahoo_complaint(msg: Message) -> Message:
+ i = 0
+ for part in msg.walk():
+ i += 1
+
+ # 1st part is the container
+ # 2nd part is the empty body
+ # 6th is original message
+ if i == 6:
+ return part
+
+
def get_header_from_bounce(msg: Message, header: str) -> str:
"""using regex to get header value from bounce message
get_orig_message_from_bounce is better. This should be the last option
diff --git a/email_handler.py b/email_handler.py
index b6aa5dde..d0f6a74c 100644
--- a/email_handler.py
+++ b/email_handler.py
@@ -81,6 +81,7 @@ from app.config import (
NEWRELIC_CONFIG_PATH,
POSTMASTER,
ALERT_HOTMAIL_COMPLAINT,
+ ALERT_YAHOO_COMPLAINT,
)
from app.email import status
from app.email.rate_limit import rate_limited
@@ -118,6 +119,7 @@ from app.email_utils import (
should_ignore_bounce,
get_orig_message_from_outlook_complaint,
parse_full_address,
+ get_orig_message_from_yahoo_complaint,
)
from app.extensions import db
from app.log import LOG, set_message_id
@@ -1314,7 +1316,7 @@ def handle_hotmail_complaint(msg: Message) -> bool:
alias = Alias.get_by(email=alias_address)
if not alias:
- LOG.d("No alias for %s", alias_address)
+ LOG.w("No alias for %s", alias_address)
return False
user = alias.user
@@ -1339,6 +1341,46 @@ def handle_hotmail_complaint(msg: Message) -> bool:
return True
+def handle_yahoo_complaint(msg: Message) -> bool:
+ """
+ Handle yahoo complaint sent to postmaster
+ Return True if the complaint can be handled, False otherwise
+ """
+ orig_msg = get_orig_message_from_yahoo_complaint(msg)
+ to_header = orig_msg["To"]
+ if not to_header:
+ LOG.e("cannot find the alias")
+ return False
+
+ _, alias_address = parse_full_address(get_header_unicode(to_header))
+ alias = Alias.get_by(email=alias_address)
+
+ if not alias:
+ LOG.w("No alias for %s", alias_address)
+ return False
+
+ user = alias.user
+ LOG.w("Handle yahoo complaint for %s %s %s", alias, user, alias.mailboxes)
+
+ send_email_with_rate_control(
+ user,
+ ALERT_YAHOO_COMPLAINT,
+ user.email,
+ f"Yahoo abuse report",
+ render(
+ "transactional/yahoo-complaint.txt.jinja2",
+ alias=alias,
+ ),
+ render(
+ "transactional/yahoo-complaint.html",
+ alias=alias,
+ ),
+ max_nb_alert=2,
+ )
+
+ return True
+
+
def handle_bounce_reply_phase(envelope, msg: Message, email_log: EmailLog):
"""
Handle reply phase bounce
@@ -1758,6 +1800,17 @@ def handle(envelope: Envelope) -> str:
if handle_hotmail_complaint(msg):
return status.E208
+ if (
+ len(rcpt_tos) == 1
+ and mail_from == "feedback@arf.mail.yahoo.com"
+ and rcpt_tos[0] == POSTMASTER
+ ):
+ LOG.w("Handle yahoo complaint")
+
+ # if the complaint cannot be handled, forward it normally
+ if handle_yahoo_complaint(msg):
+ return status.E210
+
# Handle bounce
if (
len(rcpt_tos) == 1
diff --git a/templates/emails/transactional/yahoo-complaint.html b/templates/emails/transactional/yahoo-complaint.html
new file mode 100644
index 00000000..62c69d6e
--- /dev/null
+++ b/templates/emails/transactional/yahoo-complaint.html
@@ -0,0 +1,33 @@
+{% extends "base.html" %}
+
+{% block content %}
+ {% call text() %}
+ This is SimpleLogin team.
+ Yahoo has informed us about an email sent to {{ alias.email }} that might have been marked as spam.
+ {% endcall %}
+
+ {% call text() %}
+ Putting a forwarded email into Spam affects SimpleLogin email delivery, has a negative effect for all users and
+ is a violation of our terms and condition.
+ {% endcall %}
+
+ {% call text() %}
+ If that’s the case, please disable the alias instead if you don't want to receive the emails sent to this alias.
+ {% endcall %}
+
+ {% call text() %}
+ If SimpleLogin isn’t useful for you, please know that you can simply delete your account on the Settings page.
+ {% endcall %}
+
+ {% call text() %}
+ Looking to hear back from you.
+ {% endcall %}
+
+ {% call text() %}
+ Best,
+ SimpleLogin Team.
+ {% endcall %}
+
+{% endblock %}
+
+
diff --git a/templates/emails/transactional/yahoo-complaint.txt.jinja2 b/templates/emails/transactional/yahoo-complaint.txt.jinja2
new file mode 100644
index 00000000..ad1d6c59
--- /dev/null
+++ b/templates/emails/transactional/yahoo-complaint.txt.jinja2
@@ -0,0 +1,17 @@
+Hi,
+
+This is SimpleLogin team.
+
+Yahoo has informed us about an email sent to {{ alias.email }} that might have been marked as spam.
+
+Putting a forwarded email into Spam affects SimpleLogin email delivery, has a negative effect for all users and
+ is a violation of our terms and condition.
+
+If that’s the case, please disable the alias instead if you don't want to receive the emails sent to this alias.
+
+If SimpleLogin isn’t useful for you, please know that you can simply delete your account on the Settings page.
+
+Looking to hear back from you.
+
+Best,
+SimpleLogin Team.
\ No newline at end of file