mirror of
https://github.com/simple-login/app.git
synced 2024-11-18 01:40:38 +01:00
Store the bounced email in email handling.
This commit is contained in:
parent
18008460e6
commit
3aad3deb81
4 changed files with 64 additions and 11 deletions
|
@ -363,3 +363,18 @@ def mailbox_already_used(email: str, user) -> bool:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
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
|
||||||
|
|
|
@ -37,21 +37,19 @@ from email.mime.application import MIMEApplication
|
||||||
from email.mime.multipart import MIMEMultipart
|
from email.mime.multipart import MIMEMultipart
|
||||||
from email.parser import Parser
|
from email.parser import Parser
|
||||||
from email.policy import SMTPUTF8
|
from email.policy import SMTPUTF8
|
||||||
|
from io import BytesIO
|
||||||
from smtplib import SMTP
|
from smtplib import SMTP
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from aiosmtpd.controller import Controller
|
from aiosmtpd.controller import Controller
|
||||||
import gnupg
|
|
||||||
|
|
||||||
|
from app import pgp_utils, s3
|
||||||
from app.config import (
|
from app.config import (
|
||||||
EMAIL_DOMAIN,
|
EMAIL_DOMAIN,
|
||||||
POSTFIX_SERVER,
|
POSTFIX_SERVER,
|
||||||
URL,
|
URL,
|
||||||
ALIAS_DOMAINS,
|
ALIAS_DOMAINS,
|
||||||
ADMIN_EMAIL,
|
|
||||||
SUPPORT_EMAIL,
|
|
||||||
POSTFIX_SUBMISSION_TLS,
|
POSTFIX_SUBMISSION_TLS,
|
||||||
GNUPGHOME,
|
|
||||||
)
|
)
|
||||||
from app.email_utils import (
|
from app.email_utils import (
|
||||||
get_email_name,
|
get_email_name,
|
||||||
|
@ -65,6 +63,7 @@ from app.email_utils import (
|
||||||
send_cannot_create_domain_alias,
|
send_cannot_create_domain_alias,
|
||||||
email_belongs_to_alias_domains,
|
email_belongs_to_alias_domains,
|
||||||
render,
|
render,
|
||||||
|
get_orig_message_from_bounce,
|
||||||
)
|
)
|
||||||
from app.extensions import db
|
from app.extensions import db
|
||||||
from app.log import LOG
|
from app.log import LOG
|
||||||
|
@ -76,10 +75,10 @@ from app.models import (
|
||||||
Directory,
|
Directory,
|
||||||
User,
|
User,
|
||||||
DeletedAlias,
|
DeletedAlias,
|
||||||
|
RefusedEmail,
|
||||||
)
|
)
|
||||||
from app.utils import random_string
|
from app.utils import random_string
|
||||||
from server import create_app
|
from server import create_app
|
||||||
from app import pgp_utils
|
|
||||||
|
|
||||||
|
|
||||||
# fix the database connection leak issue
|
# 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
|
# in this case Postfix will try to send a bounce report to original sender, which is
|
||||||
# the "reply email"
|
# the "reply email"
|
||||||
if envelope.mail_from == "<>":
|
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(
|
handle_bounce(
|
||||||
alias, envelope, forward_email, gen_email, msg, smtp, user, mailbox_email
|
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(
|
def handle_bounce(
|
||||||
alias, envelope, forward_email, gen_email, msg, smtp, user, mailbox_email
|
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()
|
db.session.commit()
|
||||||
|
|
||||||
nb_bounced = ForwardEmailLog.filter_by(
|
nb_bounced = ForwardEmailLog.filter_by(
|
||||||
|
@ -521,6 +527,30 @@ def handle_bounce(
|
||||||
).count()
|
).count()
|
||||||
disable_alias_link = f"{URL}/dashboard/unsubscribe/{gen_email.id}"
|
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
|
# inform user if this is the first bounced email
|
||||||
if nb_bounced == 1:
|
if nb_bounced == 1:
|
||||||
LOG.d(
|
LOG.d(
|
||||||
|
@ -530,7 +560,9 @@ def handle_bounce(
|
||||||
alias,
|
alias,
|
||||||
)
|
)
|
||||||
send_email(
|
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",
|
f"Email from {forward_email.website_from} to {alias} cannot be delivered to your inbox",
|
||||||
render(
|
render(
|
||||||
"transactional/bounced-email.txt",
|
"transactional/bounced-email.txt",
|
||||||
|
@ -539,6 +571,7 @@ def handle_bounce(
|
||||||
website_from=forward_email.website_from,
|
website_from=forward_email.website_from,
|
||||||
website_email=forward_email.website_email,
|
website_email=forward_email.website_email,
|
||||||
disable_alias_link=disable_alias_link,
|
disable_alias_link=disable_alias_link,
|
||||||
|
refused_email_url=refused_email_url,
|
||||||
),
|
),
|
||||||
render(
|
render(
|
||||||
"transactional/bounced-email.html",
|
"transactional/bounced-email.html",
|
||||||
|
@ -547,8 +580,10 @@ def handle_bounce(
|
||||||
website_from=forward_email.website_from,
|
website_from=forward_email.website_from,
|
||||||
website_email=forward_email.website_email,
|
website_email=forward_email.website_email,
|
||||||
disable_alias_link=disable_alias_link,
|
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
|
# disable the alias the second time email is bounced
|
||||||
elif nb_bounced >= 2:
|
elif nb_bounced >= 2:
|
||||||
|
|
|
@ -4,7 +4,9 @@
|
||||||
{{ render_text("Hi " + name) }}
|
{{ render_text("Hi " + name) }}
|
||||||
{{ render_text("An email sent to your alias <b>" + alias + "</b> from <b>" + website_email + "</b> was <b>refused</b> (or <em>bounced</em>) by your email provider.") }}
|
{{ render_text("An email sent to your alias <b>" + alias + "</b> from <b>" + website_email + "</b> was <b>refused</b> (or <em>bounced</em>) by your email provider.") }}
|
||||||
|
|
||||||
{{ render_text("This is usually due to the email being considered as <b>spam</b> 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 <b>spam</b> 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:') }}
|
{{ render_text('To avoid spams forwarded by SimpleLogin server, please consider the following options:') }}
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,8 @@ Hi {{name}}
|
||||||
An email sent to your alias {{alias}} from {{website_from}} was refused (or bounced) by your email provider.
|
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.
|
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:
|
To avoid spams forwarded by SimpleLogin server, please consider the following options:
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue