Handle multiple rcpt_to
This commit is contained in:
parent
e9cd043760
commit
5771eaeb63
|
@ -399,8 +399,10 @@ def prepare_pgp_message(orig_msg: Message, pgp_fingerprint: str):
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
|
|
||||||
def handle_forward(envelope, smtp: SMTP, msg: Message, rcpt_to: str) -> str:
|
def handle_forward(envelope, smtp: SMTP, msg: Message, rcpt_to: str) -> (bool, str):
|
||||||
"""return *status_code message*"""
|
"""return whether an email has been delivered and
|
||||||
|
the smtp status ("250 Message accepted", "550 Non-existent email address", etc)
|
||||||
|
"""
|
||||||
address = rcpt_to.lower() # alias@SL
|
address = rcpt_to.lower() # alias@SL
|
||||||
|
|
||||||
alias = Alias.get_by(email=address)
|
alias = Alias.get_by(email=address)
|
||||||
|
@ -409,7 +411,7 @@ def handle_forward(envelope, smtp: SMTP, msg: Message, rcpt_to: str) -> str:
|
||||||
alias = try_auto_create(address)
|
alias = try_auto_create(address)
|
||||||
if not alias:
|
if not alias:
|
||||||
LOG.d("alias %s cannot be created on-the-fly, return 550", address)
|
LOG.d("alias %s cannot be created on-the-fly, return 550", address)
|
||||||
return "550 SL Email not exist"
|
return False, "550 SL Email not exist"
|
||||||
|
|
||||||
mailbox = alias.mailbox
|
mailbox = alias.mailbox
|
||||||
mailbox_email = mailbox.email
|
mailbox_email = mailbox.email
|
||||||
|
@ -486,21 +488,25 @@ def handle_forward(envelope, smtp: SMTP, msg: Message, rcpt_to: str) -> str:
|
||||||
forward_log.blocked = True
|
forward_log.blocked = True
|
||||||
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
return "250 Message accepted for delivery"
|
return True, "250 Message accepted for delivery"
|
||||||
|
|
||||||
|
|
||||||
def handle_reply(envelope, smtp: SMTP, msg: Message, rcpt_to: str) -> str:
|
def handle_reply(envelope, smtp: SMTP, msg: Message, rcpt_to: str) -> (bool, str):
|
||||||
|
"""
|
||||||
|
return whether an email has been delivered and
|
||||||
|
the smtp status ("250 Message accepted", "550 Non-existent email address", etc)
|
||||||
|
"""
|
||||||
reply_email = rcpt_to.lower()
|
reply_email = rcpt_to.lower()
|
||||||
|
|
||||||
# reply_email must end with EMAIL_DOMAIN
|
# reply_email must end with EMAIL_DOMAIN
|
||||||
if not reply_email.endswith(EMAIL_DOMAIN):
|
if not reply_email.endswith(EMAIL_DOMAIN):
|
||||||
LOG.warning(f"Reply email {reply_email} has wrong domain")
|
LOG.warning(f"Reply email {reply_email} has wrong domain")
|
||||||
return "550 SL wrong reply email"
|
return False, "550 SL wrong reply email"
|
||||||
|
|
||||||
contact = Contact.get_by(reply_email=reply_email)
|
contact = Contact.get_by(reply_email=reply_email)
|
||||||
if not contact:
|
if not contact:
|
||||||
LOG.warning(f"No such forward-email with {reply_email} as reply-email")
|
LOG.warning(f"No such forward-email with {reply_email} as reply-email")
|
||||||
return "550 SL wrong reply email"
|
return False, "550 SL wrong reply email"
|
||||||
|
|
||||||
alias = contact.alias
|
alias = contact.alias
|
||||||
address: str = contact.alias.email
|
address: str = contact.alias.email
|
||||||
|
@ -509,7 +515,7 @@ def handle_reply(envelope, smtp: SMTP, msg: Message, rcpt_to: str) -> str:
|
||||||
# alias must end with one of the ALIAS_DOMAINS or custom-domain
|
# alias must end with one of the ALIAS_DOMAINS or custom-domain
|
||||||
if not email_belongs_to_alias_domains(alias.email):
|
if not email_belongs_to_alias_domains(alias.email):
|
||||||
if not CustomDomain.get_by(domain=alias_domain):
|
if not CustomDomain.get_by(domain=alias_domain):
|
||||||
return "550 SL alias unknown by SimpleLogin"
|
return False, "550 SL alias unknown by SimpleLogin"
|
||||||
|
|
||||||
user = alias.user
|
user = alias.user
|
||||||
mailbox_email = alias.mailbox_email()
|
mailbox_email = alias.mailbox_email()
|
||||||
|
@ -527,7 +533,7 @@ def handle_reply(envelope, smtp: SMTP, msg: Message, rcpt_to: str) -> str:
|
||||||
)
|
)
|
||||||
|
|
||||||
handle_bounce(contact, alias, msg, user, mailbox_email)
|
handle_bounce(contact, alias, msg, user, mailbox_email)
|
||||||
return "550 SL ignored"
|
return False, "550 SL ignored"
|
||||||
|
|
||||||
# only mailbox can send email to the reply-email
|
# only mailbox can send email to the reply-email
|
||||||
if envelope.mail_from.lower() != mailbox_email.lower():
|
if envelope.mail_from.lower() != mailbox_email.lower():
|
||||||
|
@ -571,7 +577,7 @@ def handle_reply(envelope, smtp: SMTP, msg: Message, rcpt_to: str) -> str:
|
||||||
"",
|
"",
|
||||||
)
|
)
|
||||||
|
|
||||||
return "550 SL ignored"
|
return False, "550 SL ignored"
|
||||||
|
|
||||||
delete_header(msg, "DKIM-Signature")
|
delete_header(msg, "DKIM-Signature")
|
||||||
|
|
||||||
|
@ -619,7 +625,7 @@ def handle_reply(envelope, smtp: SMTP, msg: Message, rcpt_to: str) -> str:
|
||||||
EmailLog.create(contact_id=contact.id, is_reply=True, user_id=contact.user_id)
|
EmailLog.create(contact_id=contact.id, is_reply=True, user_id=contact.user_id)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
return "250 Message accepted for delivery"
|
return True, "250 Message accepted for delivery"
|
||||||
|
|
||||||
|
|
||||||
def handle_bounce(
|
def handle_bounce(
|
||||||
|
@ -737,11 +743,11 @@ def handle_bounce(
|
||||||
|
|
||||||
class MailHandler:
|
class MailHandler:
|
||||||
async def handle_DATA(self, server, session, envelope):
|
async def handle_DATA(self, server, session, envelope):
|
||||||
LOG.debug(">>> New message <<<")
|
LOG.debug(
|
||||||
|
"===>> New message, mail from %s, rctp tos %s ",
|
||||||
LOG.debug("Mail from %s", envelope.mail_from)
|
envelope.mail_from,
|
||||||
LOG.debug("Rcpt to %s", envelope.rcpt_tos)
|
envelope.rcpt_tos,
|
||||||
message_data = envelope.content.decode("utf8", errors="replace")
|
)
|
||||||
|
|
||||||
if POSTFIX_SUBMISSION_TLS:
|
if POSTFIX_SUBMISSION_TLS:
|
||||||
smtp = SMTP(POSTFIX_SERVER, 587)
|
smtp = SMTP(POSTFIX_SERVER, 587)
|
||||||
|
@ -749,23 +755,42 @@ class MailHandler:
|
||||||
else:
|
else:
|
||||||
smtp = SMTP(POSTFIX_SERVER, 25)
|
smtp = SMTP(POSTFIX_SERVER, 25)
|
||||||
|
|
||||||
msg = Parser(policy=SMTPUTF8).parsestr(message_data)
|
# result of all deliveries
|
||||||
|
# each element is a couple of whether the delivery is successful and the smtp status
|
||||||
|
res: [(bool, str)] = []
|
||||||
|
|
||||||
for rcpt_to in envelope.rcpt_tos:
|
for rcpt_to in envelope.rcpt_tos:
|
||||||
|
message_data = envelope.content.decode("utf8", errors="replace")
|
||||||
|
msg = Parser(policy=SMTPUTF8).parsestr(message_data)
|
||||||
|
|
||||||
# Reply case
|
# Reply case
|
||||||
# recipient starts with "reply+" or "ra+" (ra=reverse-alias) prefix
|
# recipient starts with "reply+" or "ra+" (ra=reverse-alias) prefix
|
||||||
if rcpt_to.startswith("reply+") or rcpt_to.startswith("ra+"):
|
if rcpt_to.startswith("reply+") or rcpt_to.startswith("ra+"):
|
||||||
LOG.debug("Reply phase")
|
LOG.debug(">>> Reply phase %s -> %s", envelope.mail_from, rcpt_to)
|
||||||
app = new_app()
|
app = new_app()
|
||||||
|
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
return handle_reply(envelope, smtp, msg, rcpt_to)
|
is_delivered, smtp_status = handle_reply(
|
||||||
|
envelope, smtp, msg, rcpt_to
|
||||||
|
)
|
||||||
|
res.append((is_delivered, smtp_status))
|
||||||
else: # Forward case
|
else: # Forward case
|
||||||
LOG.debug("Forward phase")
|
LOG.debug(">>> Forward phase %s -> %s", envelope.mail_from, rcpt_to)
|
||||||
app = new_app()
|
app = new_app()
|
||||||
|
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
return handle_forward(envelope, smtp, msg, rcpt_to)
|
is_delivered, smtp_status = handle_forward(
|
||||||
|
envelope, smtp, msg, rcpt_to
|
||||||
|
)
|
||||||
|
res.append((is_delivered, smtp_status))
|
||||||
|
|
||||||
|
for (is_success, smtp_status) in res:
|
||||||
|
# Consider all deliveries successful if 1 delivery is successful
|
||||||
|
if is_success:
|
||||||
|
return smtp_status
|
||||||
|
|
||||||
|
# Failed delivery for all, return the first failure
|
||||||
|
return res[0][1]
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
Loading…
Reference in New Issue