def forward_email_to_mailbox( alias, msg: Message, email_log: EmailLog, contact: Contact, envelope, smtp: SMTP, mailbox, user, ) -> (bool, str): LOG.d("Forward %s -> %s -> %s", contact, alias, mailbox) spam_check = True is_spam, spam_status = get_spam_info(msg) if is_spam: LOG.warning("Email detected as spam. Alias: %s, from: %s", alias, contact) email_log.is_spam = True email_log.spam_status = spam_status handle_spam(contact, alias, msg, user, mailbox.email, email_log) return False, "550 SL E1" # create PGP email if needed if mailbox.pgp_finger_print and user.is_premium(): LOG.d("Encrypt message using mailbox %s", mailbox) msg = prepare_pgp_message(msg, mailbox.pgp_finger_print) # add custom header add_or_replace_header(msg, "X-SimpleLogin-Type", "Forward") # remove reply-to & sender header if present delete_header(msg, "Reply-To") delete_header(msg, "Sender") delete_header(msg, _IP_HEADER) add_or_replace_header(msg, _MAILBOX_ID_HEADER, str(mailbox.id)) # change the from header so the sender comes from @SL # so it can pass DMARC check # replace the email part in from: header contact_from_header = msg["From"] new_from_header = contact.new_addr() add_or_replace_header(msg, "From", new_from_header) LOG.d("new_from_header:%s, old header %s", new_from_header, contact_from_header) # replace CC & To emails by reply-email for all emails that are not alias replace_header_when_forward(msg, alias, "Cc") replace_header_when_forward(msg, alias, "To") # append alias into the TO header if it's not present in To or CC if should_append_alias(msg, alias.email): LOG.d("append alias %s to TO header %s", alias, msg["To"]) if msg["To"]: to_header = msg["To"] + "," + alias.email else: to_header = alias.email add_or_replace_header(msg, "To", to_header.strip()) # add List-Unsubscribe header if UNSUBSCRIBER: unsubscribe_link = f"mailto:{UNSUBSCRIBER}?subject={alias.id}=" add_or_replace_header(msg, "List-Unsubscribe", f"<{unsubscribe_link}>") else: unsubscribe_link = f"{URL}/dashboard/unsubscribe/{alias.id}" add_or_replace_header(msg, "List-Unsubscribe", f"<{unsubscribe_link}>") add_or_replace_header(msg, "List-Unsubscribe-Post", "List-Unsubscribe=One-Click") add_dkim_signature(msg, EMAIL_DOMAIN) LOG.d( "Forward mail from %s to %s, mail_options %s, rcpt_options %s ", contact.website_email, mailbox.email, envelope.mail_options, envelope.rcpt_options, ) # smtp.send_message has UnicodeEncodeErroremail issue # encode message raw directly instead smtp.sendmail( contact.reply_email, mailbox.email, msg.as_bytes(), envelope.mail_options, envelope.rcpt_options, ) db.session.commit() return True, "250 Message accepted for delivery"
async def forward_email_to_mailbox( alias, msg: Message, email_log: EmailLog, contact: Contact, envelope, smtp: SMTP, mailbox, user, ) -> (bool, str): LOG.d("Forward %s -> %s -> %s", contact, alias, mailbox) # sanity check: make sure mailbox is not actually an alias if get_email_domain_part(alias.email) == get_email_domain_part( mailbox.email): LOG.exception( "Mailbox has the same domain as alias. %s -> %s -> %s", contact, alias, mailbox, ) return False, "550 SL E14" # Spam check spam_status = "" is_spam = False if SPAMASSASSIN_HOST: start = time.time() spam_score = await get_spam_score(msg) LOG.d( "%s -> %s - spam score %s in %s seconds", contact, alias, spam_score, time.time() - start, ) email_log.spam_score = spam_score db.session.commit() if (user.max_spam_score and spam_score > user.max_spam_score) or ( not user.max_spam_score and spam_score > MAX_SPAM_SCORE): is_spam = True spam_status = "Spam detected by SpamAssassin server" else: is_spam, spam_status = get_spam_info(msg, max_score=user.max_spam_score) if is_spam: LOG.warning("Email detected as spam. Alias: %s, from: %s", alias, contact) email_log.is_spam = True email_log.spam_status = spam_status db.session.commit() handle_spam(contact, alias, msg, user, mailbox, email_log) return False, "550 SL E1 Email detected as spam" # create PGP email if needed if mailbox.pgp_finger_print and user.is_premium( ) and not alias.disable_pgp: LOG.d("Encrypt message using mailbox %s", mailbox) try: msg = prepare_pgp_message(msg, mailbox.pgp_finger_print) except PGPException: LOG.exception("Cannot encrypt message %s -> %s. %s %s", contact, alias, mailbox, user) # so the client can retry later return False, "421 SL E12 Retry later" # add custom header add_or_replace_header(msg, _DIRECTION, "Forward") # remove reply-to & sender header if present delete_header(msg, "Reply-To") 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)) # change the from header so the sender comes from @SL # so it can pass DMARC check # replace the email part in from: header contact_from_header = msg["From"] new_from_header = contact.new_addr() add_or_replace_header(msg, "From", new_from_header) LOG.d("new_from_header:%s, old header %s", new_from_header, contact_from_header) # replace CC & To emails by reply-email for all emails that are not alias replace_header_when_forward(msg, alias, "Cc") replace_header_when_forward(msg, alias, "To") # append alias into the TO header if it's not present in To or CC if should_append_alias(msg, alias.email): LOG.d("append alias %s to TO header %s", alias, msg["To"]) if msg["To"]: to_header = msg["To"] + "," + alias.email else: to_header = alias.email add_or_replace_header(msg, "To", to_header.strip()) # add List-Unsubscribe header if UNSUBSCRIBER: unsubscribe_link = f"mailto:{UNSUBSCRIBER}?subject={alias.id}=" add_or_replace_header(msg, "List-Unsubscribe", f"<{unsubscribe_link}>") else: unsubscribe_link = f"{URL}/dashboard/unsubscribe/{alias.id}" add_or_replace_header(msg, "List-Unsubscribe", f"<{unsubscribe_link}>") add_or_replace_header(msg, "List-Unsubscribe-Post", "List-Unsubscribe=One-Click") add_dkim_signature(msg, EMAIL_DOMAIN) LOG.d( "Forward mail from %s to %s, mail_options %s, rcpt_options %s ", contact.website_email, mailbox.email, envelope.mail_options, envelope.rcpt_options, ) # smtp.send_message has UnicodeEncodeErroremail issue # encode message raw directly instead smtp.sendmail( contact.reply_email, mailbox.email, msg.as_bytes(), envelope.mail_options, envelope.rcpt_options, ) db.session.commit() return True, "250 Message accepted for delivery"