handle out-of-office email in addition to bounce

This commit is contained in:
Son 2022-01-05 15:30:44 +01:00
parent 6c42872440
commit 385dd1e755
2 changed files with 95 additions and 33 deletions

View File

@ -1,4 +1,4 @@
# 2** status
# region 2** status
E200 = "250 Message accepted for delivery"
E201 = "250 SL E201"
E202 = "250 Unsubscribe request accepted"
@ -19,7 +19,9 @@ E210 = "250 SL E210 Yahoo complaint handled"
E211 = "250 SL E211 Bounce Forward phase handled"
E212 = "250 SL E212 Bounce Reply phase handled"
# 4** errors
# endregion
# region 4** errors
# E401 = "421 SL E401 Retry later"
E402 = "421 SL E402 Encryption failed - Retry later"
# E403 = "421 SL E403 Retry later"
@ -27,8 +29,12 @@ E404 = "421 SL E404 Unexpected error - Retry later"
E405 = "421 SL E405 Mailbox domain problem - Retry later"
E406 = "421 SL E406 Retry later"
E407 = "421 SL E407 Retry later"
E408 = "421 SL E408 Retry later"
E409 = "421 SL E409 Retry later"
E410 = "421 SL E410 Retry later"
# endregion
# 5** errors
# region 5** errors
E501 = "550 SL E501"
E502 = "550 SL E502 Email not exist"
E503 = "550 SL E503"
@ -53,3 +59,4 @@ E522 = (
"at a rate that prevents additional messages from being delivered."
)
E523 = "550 SL E523 Unknown error"
# endregion

View File

@ -2051,15 +2051,26 @@ def handle(envelope: Envelope) -> str:
LOG.d("Handle unsubscribe request from %s", mail_from)
return handle_unsubscribe(envelope, msg)
# emails sent to sender. Probably bounce emails
# emails sent to transactional VERP. Either bounce emails or out-of-office
if (
len(rcpt_tos) == 1
and rcpt_tos[0].startswith(TRANSACTIONAL_BOUNCE_PREFIX)
and rcpt_tos[0].endswith(TRANSACTIONAL_BOUNCE_SUFFIX)
):
LOG.d("Handle email sent to sender from %s", mail_from)
handle_transactional_bounce(envelope, msg, rcpt_tos[0])
return status.E205
if is_bounce(envelope, msg):
handle_transactional_bounce(envelope, msg, rcpt_tos[0])
return status.E205
elif is_automatic_out_of_office(msg):
LOG.d(
"Ignore out-of-office for transactional emails. Headers: %s", msg.items
)
return status.E206
else:
LOG.e(
"cannot handle email sent to transactional VERP, saved at %s",
save_email_for_debugging(msg), # todo: remove
)
return status.E408
if (
len(rcpt_tos) == 1
@ -2083,7 +2094,7 @@ def handle(envelope: Envelope) -> str:
if handle_yahoo_complaint(msg):
return status.E210
# Handle bounce
# Mails sent to forward VERP, can be either bounce or out-of-office
if (
len(rcpt_tos) == 1
and rcpt_tos[0].startswith(BOUNCE_PREFIX)
@ -2092,20 +2103,41 @@ def handle(envelope: Envelope) -> str:
email_log_id = parse_id_from_bounce(rcpt_tos[0])
email_log = EmailLog.get(email_log_id)
# out of office is sent to the mail_from and not the From: header
# more info on https://support.google.com/mail/thread/21246740/my-auto-reply-filter-isn-t-replying-to-original-sender-address?hl=en&msgid=21261237
# convert the email into a normal email sent to the reverse alias
if (
is_automatic_out_of_office(msg)
and msg[headers.TO] == rcpt_tos[0]
and email_log
):
LOG.d("send the out-of-office email to the contact")
rcpt_tos[0] = email_log.contact.reply_email
msg[headers.TO] = email_log.contact.reply_email
envelope.rcpt_tos = [email_log.contact.reply_email]
else:
if not email_log:
LOG.w("No such email log")
return status.E512
if is_bounce(envelope, msg):
return handle_bounce(envelope, email_log, msg)
elif is_automatic_out_of_office(msg):
# convert the email into a normal email sent to the reverse alias, so it can be forwarded to contact
LOG.d(
"send the out-of-office email to the contact %s %s %s",
email_log.contact,
msg[headers.TO],
rcpt_tos,
)
reverse_alias = email_log.contact.reply_email
rcpt_tos[0] = reverse_alias
envelope.rcpt_tos = [reverse_alias]
add_or_replace_header(msg, headers.TO, reverse_alias)
# delete reply-to header that can affect email delivery
delete_header(msg, headers.REPLY_TO)
LOG.d(
"after out-of-office transformation %s %s %s",
msg.get_all(headers.TO),
msg.get_all(headers.REPLY_TO),
rcpt_tos,
)
else:
LOG.e(
"cannot handle email sent to forward VERP, saved at %s",
save_email_for_debugging(msg),
)
return status.E409
if len(rcpt_tos) == 1 and rcpt_tos[0].startswith(
f"{BOUNCE_PREFIX_FOR_REPLY_PHASE}+"
@ -2113,19 +2145,42 @@ def handle(envelope: Envelope) -> str:
email_log_id = parse_id_from_bounce(rcpt_tos[0])
email_log = EmailLog.get(email_log_id)
# out-of-office email sent by the contact
# convert the email into a normal email sent to the alias
if (
is_automatic_out_of_office(msg)
and msg[headers.TO] == rcpt_tos[0]
and email_log
):
LOG.d("send the out-of-office email to the contact")
rcpt_tos[0] = email_log.contact.alias.email
msg[headers.TO] = email_log.contact.alias.email
envelope.rcpt_tos = [email_log.contact.alias.email]
if not email_log:
LOG.w("No such email log")
return status.E512
return handle_bounce(envelope, email_log, msg)
if is_bounce(envelope, msg):
return handle_bounce(envelope, email_log, msg)
elif is_automatic_out_of_office(msg):
# convert the email into a normal email sent to the alias, so it can be forwarded to mailbox
LOG.d(
"send the out-of-office email to the alias %s %s %s",
email_log.alias,
msg[headers.TO],
rcpt_tos,
)
alias_address = email_log.alias.email
rcpt_tos[0] = alias_address
envelope.rcpt_tos = [alias_address]
replace(msg, headers.TO, alias_address)
# delete reply-to header that can affect email delivery
delete_header(msg, headers.REPLY_TO)
LOG.d(
"after out-of-office transformation %s %s %s",
msg.get_all(headers.TO),
msg.get_all(headers.REPLY_TO),
rcpt_tos,
)
else:
LOG.e(
"cannot handle email sent to reply VERP, saved at %s",
save_email_for_debugging(msg),
)
return status.E410
# iCloud returns the bounce with mail_from=bounce+{email_log_id}+@simplelogin.co, rcpt_to=alias
if (