Przeglądaj źródła

Store the bounced email in email handling.

Son NK 5 lat temu
rodzic
commit
0bb9830680

+ 15 - 0
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

+ 44 - 9
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:

+ 3 - 1
templates/emails/transactional/bounced-email.html

@@ -4,7 +4,9 @@
   {{ 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("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:') }}
 

+ 2 - 1
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: