diff --git a/app/models.py b/app/models.py index 02dd041c..beefdac0 100644 --- a/app/models.py +++ b/app/models.py @@ -1352,6 +1352,12 @@ class EmailLog(db.Model, ModelMixin): contact = db.relationship(Contact, backref="email_logs") + @property + def mailbox(self) -> Optional["Mailbox"]: + if self.mailbox_id: + return Mailbox.get(self.mailbox_id) + return None + def bounced_mailbox(self) -> str: if self.bounced_mailbox_id: return Mailbox.get(self.bounced_mailbox_id).email diff --git a/email_handler.py b/email_handler.py index 385b6fdc..7c712f91 100644 --- a/email_handler.py +++ b/email_handler.py @@ -126,7 +126,6 @@ from server import create_app, create_light_app _DIRECTION = "X-SimpleLogin-Type" _IP_HEADER = "X-SimpleLogin-Client-IP" -_MAILBOX_ID_HEADER = "X-SimpleLogin-Mailbox-ID" _EMAIL_LOG_ID_HEADER = "X-SimpleLogin-EmailLog-ID" _MESSAGE_ID = "Message-ID" _ENVELOPE_FROM = "X-SimpleLogin-Envelope-From" @@ -694,7 +693,6 @@ def forward_email_to_mailbox( delete_header(msg, "Sender") delete_header(msg, _IP_HEADER) - add_or_replace_header(msg, _MAILBOX_ID_HEADER, str(mailbox.id)) add_or_replace_header(msg, _EMAIL_LOG_ID_HEADER, str(email_log.id)) add_or_replace_header(msg, _MESSAGE_ID, make_msgid(str(email_log.id), EMAIL_DOMAIN)) add_or_replace_header(msg, _ENVELOPE_FROM, envelope.mail_from) @@ -959,7 +957,6 @@ def handle_reply(envelope, msg: Message, rcpt_to: str) -> (bool, str): msg["Date"] = date_header msg[_DIRECTION] = "Reply" - msg[_MAILBOX_ID_HEADER] = str(mailbox.id) msg[_EMAIL_LOG_ID_HEADER] = str(email_log.id) LOG.d( @@ -1167,8 +1164,7 @@ def handle_bounce(contact: Contact, alias: Alias, msg: Message, user: User): s3.upload_email_from_bytesio(full_report_path, BytesIO(to_bytes(msg)), random_name) file_path = None - mailbox = None - email_log: EmailLog = None + orig_msg = get_orig_message_from_bounce(msg) if not orig_msg: # Some MTA does not return the original message in bounce message @@ -1183,37 +1179,6 @@ def handle_bounce(contact: Contact, alias: Alias, msg: Message, user: User): else: file_path = f"refused-emails/{random_name}.eml" s3.upload_email_from_bytesio(file_path, BytesIO(to_bytes(msg)), random_name) - try: - mailbox_id = int(orig_msg[_MAILBOX_ID_HEADER]) - except TypeError: - LOG.warning( - "cannot parse mailbox from original message header %s", - orig_msg[_MAILBOX_ID_HEADER], - ) - else: - mailbox = Mailbox.get(mailbox_id) - if not mailbox or mailbox.user_id != user.id: - LOG.exception( - "Tampered message mailbox_id %s, %s, %s, %s %s", - mailbox_id, - user, - alias, - contact, - full_report_path, - ) - # cannot use this tampered mailbox, reset it - mailbox = None - - # try to get the original email_log - try: - email_log_id = int(orig_msg[_EMAIL_LOG_ID_HEADER]) - except TypeError: - LOG.warning( - "cannot parse email log from original message header %s", - orig_msg[_EMAIL_LOG_ID_HEADER], - ) - else: - email_log = EmailLog.get(email_log_id) refused_email = RefusedEmail.create( path=file_path, full_report_path=full_report_path, user_id=user.id @@ -1221,46 +1186,32 @@ def handle_bounce(contact: Contact, alias: Alias, msg: Message, user: User): db.session.flush() LOG.d("Create refused email %s", refused_email) - if not mailbox: - LOG.debug("Try to get mailbox from bounce report") - try: - mailbox_id = int(get_header_from_bounce(msg, _MAILBOX_ID_HEADER)) - except Exception: - LOG.warning("cannot get mailbox-id from bounce report, %s", refused_email) - else: - mailbox = Mailbox.get(mailbox_id) - if not mailbox or mailbox.user_id != user.id: - LOG.exception( - "Tampered message mailbox_id %s, %s, %s, %s %s", - mailbox_id, - user, - alias, - contact, - full_report_path, - ) - mailbox = None + # try to parse email_log + email_log = None + try: + email_log_id = int(get_header_from_bounce(msg, _EMAIL_LOG_ID_HEADER)) + except Exception: + LOG.warning("cannot get email log id from bounce report, %s", refused_email) + else: + email_log = EmailLog.get(email_log_id) - if not email_log: - LOG.d("Try to get email log from bounce report") - try: - email_log_id = int(get_header_from_bounce(msg, _EMAIL_LOG_ID_HEADER)) - except Exception: - LOG.warning("cannot get email log id from bounce report, %s", refused_email) - else: - email_log = EmailLog.get(email_log_id) - - # use the default mailbox as the last option - if not mailbox: - LOG.warning("Use %s default mailbox %s", alias, refused_email) - mailbox = alias.mailbox - - # create a new email log as the last option + # create new email_log if unable to parse from bounce report if not email_log: LOG.warning("cannot get the original email_log, create a new one") email_log: EmailLog = EmailLog.create( contact_id=contact.id, user_id=contact.user_id ) + # try to get mailbox + mailbox = None + if email_log: + mailbox = email_log.mailbox + + # use the default mailbox if unable to parse from bounce report + if not mailbox: + LOG.warning("Use %s default mailbox %s", alias, refused_email) + mailbox = alias.mailbox + email_log.bounced = True email_log.refused_email_id = refused_email.id email_log.bounced_mailbox_id = mailbox.id @@ -1386,20 +1337,11 @@ def handle_bounce_reply_phase(alias: Alias, msg: Message, user: User): email_log.bounced = True email_log.refused_email_id = refused_email.id - db.session.commit() - try: - mailbox_id = int(get_header_from_bounce(msg, _MAILBOX_ID_HEADER)) - except Exception: - LOG.warning( - "cannot parse mailbox from bounce message report %s %s", alias, user - ) - # fall back to the default mailbox - mailbox = alias.mailbox - else: - mailbox = Mailbox.get(mailbox_id) - email_log.bounced_mailbox_id = mailbox.id - db.session.commit() + mailbox = email_log.mailbox or alias.mailbox + email_log.bounced_mailbox_id = mailbox.id + + db.session.commit() refused_email_url = ( URL + f"/dashboard/refused_email?highlight_id=" + str(email_log.id)