diff --git a/app/email_utils.py b/app/email_utils.py
index ca9a8e44..2db45f8a 100644
--- a/app/email_utils.py
+++ b/app/email_utils.py
@@ -363,3 +363,18 @@ def mailbox_already_used(email: str, user) -> bool:
return True
return False
+
+
+def get_orig_message_from_bounce(msg: Message) -> Message:
+ """parse the original email from Bounce"""
+ i = 0
+ for part in msg.walk():
+ i += 1
+
+ # the original message is the 4th part
+ # 1st part is the root part, multipart/report
+ # 2nd is text/plain, Postfix log
+ # ...
+ # 7th is original message
+ if i == 7:
+ return part
diff --git a/email_handler.py b/email_handler.py
index e1f05306..238510bf 100644
--- a/email_handler.py
+++ b/email_handler.py
@@ -37,21 +37,19 @@ from email.mime.application import MIMEApplication
from email.mime.multipart import MIMEMultipart
from email.parser import Parser
from email.policy import SMTPUTF8
+from io import BytesIO
from smtplib import SMTP
from typing import Optional
from aiosmtpd.controller import Controller
-import gnupg
+from app import pgp_utils, s3
from app.config import (
EMAIL_DOMAIN,
POSTFIX_SERVER,
URL,
ALIAS_DOMAINS,
- ADMIN_EMAIL,
- SUPPORT_EMAIL,
POSTFIX_SUBMISSION_TLS,
- GNUPGHOME,
)
from app.email_utils import (
get_email_name,
@@ -65,6 +63,7 @@ from app.email_utils import (
send_cannot_create_domain_alias,
email_belongs_to_alias_domains,
render,
+ get_orig_message_from_bounce,
)
from app.extensions import db
from app.log import LOG
@@ -76,10 +75,10 @@ from app.models import (
Directory,
User,
DeletedAlias,
+ RefusedEmail,
)
from app.utils import random_string
from server import create_app
-from app import pgp_utils
# fix the database connection leak issue
@@ -406,7 +405,12 @@ def handle_reply(envelope, smtp: SMTP, msg: Message, rcpt_to: str) -> str:
# in this case Postfix will try to send a bounce report to original sender, which is
# the "reply email"
if envelope.mail_from == "<>":
- LOG.error("Bounce when sending to alias %s, user %s", alias, gen_email.user)
+ LOG.error(
+ "Bounce when sending to alias %s from %s, user %s",
+ alias,
+ forward_email.website_from,
+ gen_email.user,
+ )
handle_bounce(
alias, envelope, forward_email, gen_email, msg, smtp, user, mailbox_email
@@ -513,7 +517,9 @@ def handle_reply(envelope, smtp: SMTP, msg: Message, rcpt_to: str) -> str:
def handle_bounce(
alias, envelope, forward_email, gen_email, msg, smtp, user, mailbox_email
):
- ForwardEmailLog.create(forward_id=forward_email.id, bounced=True)
+ fel: ForwardEmailLog = ForwardEmailLog.create(
+ forward_id=forward_email.id, bounced=True
+ )
db.session.commit()
nb_bounced = ForwardEmailLog.filter_by(
@@ -521,6 +527,30 @@ def handle_bounce(
).count()
disable_alias_link = f"{URL}/dashboard/unsubscribe/{gen_email.id}"
+ # Store the bounced email
+ random_name = random_string(50)
+
+ full_report_path = f"refused-emails/full-{random_name}.eml"
+ s3.upload_from_bytesio(full_report_path, BytesIO(msg.as_bytes()))
+
+ file_path = f"refused-emails/{random_name}.eml"
+ orig_msg = get_orig_message_from_bounce(msg)
+ s3.upload_from_bytesio(file_path, BytesIO(orig_msg.as_bytes()))
+
+ refused_email = RefusedEmail.create(
+ path=file_path, full_report_path=full_report_path, user_id=user.id
+ )
+ db.session.flush()
+
+ fel.refused_email_id = refused_email.id
+ db.session.commit()
+
+ LOG.d("Create refused email %s", refused_email)
+
+ refused_email_url = (
+ URL + f"/dashboard/refused_email?highlight_fel_id=" + str(fel.id)
+ )
+
# inform user if this is the first bounced email
if nb_bounced == 1:
LOG.d(
@@ -530,7 +560,9 @@ def handle_bounce(
alias,
)
send_email(
- mailbox_email,
+ # TOOD: use mailbox_email instead
+ user.email,
+ # mailbox_email,
f"Email from {forward_email.website_from} to {alias} cannot be delivered to your inbox",
render(
"transactional/bounced-email.txt",
@@ -539,6 +571,7 @@ def handle_bounce(
website_from=forward_email.website_from,
website_email=forward_email.website_email,
disable_alias_link=disable_alias_link,
+ refused_email_url=refused_email_url,
),
render(
"transactional/bounced-email.html",
@@ -547,8 +580,10 @@ def handle_bounce(
website_from=forward_email.website_from,
website_email=forward_email.website_email,
disable_alias_link=disable_alias_link,
+ refused_email_url=refused_email_url,
),
- bounced_email=msg,
+ # cannot include bounce email as it can contain spammy text
+ # bounced_email=msg,
)
# disable the alias the second time email is bounced
elif nb_bounced >= 2:
diff --git a/templates/emails/transactional/bounced-email.html b/templates/emails/transactional/bounced-email.html
index e1e3361e..2358289d 100644
--- a/templates/emails/transactional/bounced-email.html
+++ b/templates/emails/transactional/bounced-email.html
@@ -4,7 +4,9 @@
{{ render_text("Hi " + name) }}
{{ render_text("An email sent to your alias " + alias + " from " + website_email + " was refused (or bounced) by your email provider.") }}
- {{ render_text("This is usually due to the email being considered as spam by your email provider. The email is included at the end of this message so you can take a look at its content.") }}
+ {{ render_text('This is usually due to the email being considered as spam by your email provider.') }}
+
+ {{ render_button("View the refused email", refused_email_url) }}
{{ render_text('To avoid spams forwarded by SimpleLogin server, please consider the following options:') }}
diff --git a/templates/emails/transactional/bounced-email.txt b/templates/emails/transactional/bounced-email.txt
index 0fd0c688..8b8bcb8f 100644
--- a/templates/emails/transactional/bounced-email.txt
+++ b/templates/emails/transactional/bounced-email.txt
@@ -3,7 +3,8 @@ Hi {{name}}
An email sent to your alias {{alias}} from {{website_from}} was refused (or bounced) by your email provider.
This is usually due to the email being considered as spam by your email provider.
-The email is included at the end of this message so you can take a look at its content.
+You can view this email here:
+{{ refused_email_url }}
To avoid spams forwarded by SimpleLogin server, please consider the following options: