mirror of
https://github.com/simple-login/app.git
synced 2024-09-30 05:31:30 +02:00
refactor email-handler: extract try_auto_create()
This commit is contained in:
parent
78ed6eab5a
commit
fc09f911a4
@ -280,7 +280,7 @@ def delete_header(msg: Message, header: str):
|
|||||||
|
|
||||||
|
|
||||||
def email_belongs_to_alias_domains(email: str) -> bool:
|
def email_belongs_to_alias_domains(email: str) -> bool:
|
||||||
"""return True if an emails ends with one of the alias domains provided by SimpleLogin"""
|
"""return True if an email ends with one of the alias domains provided by SimpleLogin"""
|
||||||
for domain in ALIAS_DOMAINS:
|
for domain in ALIAS_DOMAINS:
|
||||||
if email.endswith("@" + domain):
|
if email.endswith("@" + domain):
|
||||||
return True
|
return True
|
||||||
|
192
email_handler.py
192
email_handler.py
@ -35,6 +35,7 @@ from email.message import Message
|
|||||||
from email.parser import Parser
|
from email.parser import Parser
|
||||||
from email.policy import SMTPUTF8
|
from email.policy import SMTPUTF8
|
||||||
from smtplib import SMTP
|
from smtplib import SMTP
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
from aiosmtpd.controller import Controller
|
from aiosmtpd.controller import Controller
|
||||||
|
|
||||||
@ -90,6 +91,91 @@ def new_app():
|
|||||||
return app
|
return app
|
||||||
|
|
||||||
|
|
||||||
|
def try_auto_create(alias: str) -> Optional[GenEmail]:
|
||||||
|
"""Try to auto-create the alias using directory or catch-all domain
|
||||||
|
"""
|
||||||
|
# check if alias belongs to a directory, ie having directory/anything@EMAIL_DOMAIN format
|
||||||
|
if email_belongs_to_alias_domains(alias):
|
||||||
|
# if there's no directory separator in the alias, no way to auto-create it
|
||||||
|
if "/" not in alias and "+" not in alias and "#" not in alias:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# alias contains one of the 3 special directory separator: "/", "+" or "#"
|
||||||
|
if "/" in alias:
|
||||||
|
sep = "/"
|
||||||
|
elif "+" in alias:
|
||||||
|
sep = "+"
|
||||||
|
else:
|
||||||
|
sep = "#"
|
||||||
|
|
||||||
|
directory_name = alias[: alias.find(sep)]
|
||||||
|
LOG.d("directory_name %s", directory_name)
|
||||||
|
|
||||||
|
directory = Directory.get_by(name=directory_name)
|
||||||
|
if not directory:
|
||||||
|
return None
|
||||||
|
|
||||||
|
dir_user: User = directory.user
|
||||||
|
|
||||||
|
if not dir_user.can_create_new_alias():
|
||||||
|
send_cannot_create_directory_alias(dir_user, alias, directory_name)
|
||||||
|
return None
|
||||||
|
|
||||||
|
# if alias has been deleted before, do not auto-create it
|
||||||
|
if DeletedAlias.get_by(email=alias, user_id=directory.user_id):
|
||||||
|
LOG.error(
|
||||||
|
"Alias %s was deleted before, cannot auto-create using directory %s, user %s",
|
||||||
|
alias,
|
||||||
|
directory_name,
|
||||||
|
dir_user,
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
|
||||||
|
LOG.d("create alias %s for directory %s", alias, directory)
|
||||||
|
|
||||||
|
gen_email = GenEmail.create(
|
||||||
|
email=alias, user_id=directory.user_id, directory_id=directory.id,
|
||||||
|
)
|
||||||
|
db.session.commit()
|
||||||
|
return gen_email
|
||||||
|
|
||||||
|
# try to create alias on-the-fly with custom-domain catch-all feature
|
||||||
|
# check if alias is custom-domain alias and if the custom-domain has catch-all enabled
|
||||||
|
alias_domain = get_email_domain_part(alias)
|
||||||
|
custom_domain = CustomDomain.get_by(domain=alias_domain)
|
||||||
|
|
||||||
|
if not custom_domain or custom_domain.catch_all:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# custom_domain has catch-all enabled
|
||||||
|
domain_user: User = custom_domain.user
|
||||||
|
|
||||||
|
if not domain_user.can_create_new_alias():
|
||||||
|
send_cannot_create_domain_alias(domain_user, alias, alias_domain)
|
||||||
|
return None
|
||||||
|
|
||||||
|
# if alias has been deleted before, do not auto-create it
|
||||||
|
if DeletedAlias.get_by(email=alias, user_id=custom_domain.user_id):
|
||||||
|
LOG.error(
|
||||||
|
"Alias %s was deleted before, cannot auto-create using domain catch-all %s, user %s",
|
||||||
|
alias,
|
||||||
|
custom_domain,
|
||||||
|
domain_user,
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
|
||||||
|
LOG.d("create alias %s for domain %s", alias, custom_domain)
|
||||||
|
|
||||||
|
gen_email = GenEmail.create(
|
||||||
|
email=alias,
|
||||||
|
user_id=custom_domain.user_id,
|
||||||
|
custom_domain_id=custom_domain.id,
|
||||||
|
automatic_creation=True,
|
||||||
|
)
|
||||||
|
db.session.commit()
|
||||||
|
return gen_email
|
||||||
|
|
||||||
|
|
||||||
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 <<<")
|
||||||
@ -98,17 +184,12 @@ class MailHandler:
|
|||||||
LOG.debug("Rcpt to %s", envelope.rcpt_tos)
|
LOG.debug("Rcpt to %s", envelope.rcpt_tos)
|
||||||
message_data = envelope.content.decode("utf8", errors="replace")
|
message_data = envelope.content.decode("utf8", errors="replace")
|
||||||
|
|
||||||
# Only when debug
|
|
||||||
# LOG.debug("Message data:\n")
|
|
||||||
# LOG.debug(message_data)
|
|
||||||
|
|
||||||
# host IP, setup via Docker network
|
|
||||||
smtp = SMTP(POSTFIX_SERVER, 25)
|
smtp = SMTP(POSTFIX_SERVER, 25)
|
||||||
msg = Parser(policy=SMTPUTF8).parsestr(message_data)
|
msg = Parser(policy=SMTPUTF8).parsestr(message_data)
|
||||||
|
|
||||||
for rcpt_to in envelope.rcpt_tos:
|
for rcpt_to in envelope.rcpt_tos:
|
||||||
# Reply case
|
# Reply case
|
||||||
# reply+ or 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")
|
||||||
app = new_app()
|
app = new_app()
|
||||||
@ -131,103 +212,8 @@ class MailHandler:
|
|||||||
LOG.d(
|
LOG.d(
|
||||||
"alias %s not exist. Try to see if it can be created on the fly", alias
|
"alias %s not exist. Try to see if it can be created on the fly", alias
|
||||||
)
|
)
|
||||||
|
gen_email = try_auto_create(alias)
|
||||||
# try to see if alias could be created on-the-fly
|
if not gen_email:
|
||||||
on_the_fly = False
|
|
||||||
|
|
||||||
# check if alias belongs to a directory, ie having directory/anything@EMAIL_DOMAIN format
|
|
||||||
if email_belongs_to_alias_domains(alias):
|
|
||||||
if "/" in alias or "+" in alias or "#" in alias:
|
|
||||||
if "/" in alias:
|
|
||||||
sep = "/"
|
|
||||||
elif "+" in alias:
|
|
||||||
sep = "+"
|
|
||||||
else:
|
|
||||||
sep = "#"
|
|
||||||
|
|
||||||
directory_name = alias[: alias.find(sep)]
|
|
||||||
LOG.d("directory_name %s", directory_name)
|
|
||||||
|
|
||||||
directory = Directory.get_by(name=directory_name)
|
|
||||||
|
|
||||||
# Only premium user can use the directory feature
|
|
||||||
if directory:
|
|
||||||
dir_user: User = directory.user
|
|
||||||
if dir_user.can_create_new_alias():
|
|
||||||
# if alias has been deleted before, do not auto-create it
|
|
||||||
if DeletedAlias.get_by(
|
|
||||||
email=alias, user_id=directory.user_id
|
|
||||||
):
|
|
||||||
LOG.error(
|
|
||||||
"Alias %s has been deleted before, cannot auto-create using directory %s, user %s",
|
|
||||||
alias,
|
|
||||||
directory_name,
|
|
||||||
dir_user,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
|
|
||||||
LOG.d(
|
|
||||||
"create alias %s for directory %s", alias, directory
|
|
||||||
)
|
|
||||||
on_the_fly = True
|
|
||||||
|
|
||||||
gen_email = GenEmail.create(
|
|
||||||
email=alias,
|
|
||||||
user_id=directory.user_id,
|
|
||||||
directory_id=directory.id,
|
|
||||||
)
|
|
||||||
db.session.commit()
|
|
||||||
else:
|
|
||||||
LOG.error(
|
|
||||||
"User %s is not premium anymore and cannot create alias with directory",
|
|
||||||
dir_user,
|
|
||||||
)
|
|
||||||
send_cannot_create_directory_alias(
|
|
||||||
dir_user, alias, directory_name
|
|
||||||
)
|
|
||||||
|
|
||||||
# try to create alias on-the-fly with custom-domain catch-all feature
|
|
||||||
# check if alias is custom-domain alias and if the custom-domain has catch-all enabled
|
|
||||||
if not on_the_fly:
|
|
||||||
alias_domain = get_email_domain_part(alias)
|
|
||||||
custom_domain = CustomDomain.get_by(domain=alias_domain)
|
|
||||||
|
|
||||||
# Only premium user can continue using the catch-all feature
|
|
||||||
if custom_domain and custom_domain.catch_all:
|
|
||||||
domain_user: User = custom_domain.user
|
|
||||||
if domain_user.can_create_new_alias():
|
|
||||||
# if alias has been deleted before, do not auto-create it
|
|
||||||
if DeletedAlias.get_by(
|
|
||||||
email=alias, user_id=custom_domain.user_id
|
|
||||||
):
|
|
||||||
LOG.error(
|
|
||||||
"Alias %s has been deleted before, cannot auto-create using domain catch-all %s, user %s",
|
|
||||||
alias,
|
|
||||||
custom_domain,
|
|
||||||
domain_user,
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
LOG.d("create alias %s for domain %s", alias, custom_domain)
|
|
||||||
on_the_fly = True
|
|
||||||
|
|
||||||
gen_email = GenEmail.create(
|
|
||||||
email=alias,
|
|
||||||
user_id=custom_domain.user_id,
|
|
||||||
custom_domain_id=custom_domain.id,
|
|
||||||
automatic_creation=True,
|
|
||||||
)
|
|
||||||
db.session.commit()
|
|
||||||
else:
|
|
||||||
LOG.error(
|
|
||||||
"User %s is not premium anymore and cannot create alias with domain %s",
|
|
||||||
domain_user,
|
|
||||||
alias_domain,
|
|
||||||
)
|
|
||||||
send_cannot_create_domain_alias(
|
|
||||||
domain_user, alias, alias_domain
|
|
||||||
)
|
|
||||||
|
|
||||||
if not on_the_fly:
|
|
||||||
LOG.d("alias %s cannot be created on-the-fly, return 510", alias)
|
LOG.d("alias %s cannot be created on-the-fly, return 510", alias)
|
||||||
return "510 Email not exist"
|
return "510 Email not exist"
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user