diff --git a/chatmaild/src/chatmaild/config.py b/chatmaild/src/chatmaild/config.py index 815e29f1..854cbd33 100644 --- a/chatmaild/src/chatmaild/config.py +++ b/chatmaild/src/chatmaild/config.py @@ -3,18 +3,22 @@ from pathlib import Path import iniconfig -def read_config(inipath): +def read_config(inipath, mail_basedir=None): cfg = iniconfig.IniConfig(inipath) - return Config(inipath, params=cfg.sections["params"]) + params = cfg.sections["params"] + if mail_basedir is None: + mail_basedir = Path(f"/home/vmail/mail/{params['mail_domain']}") + return Config(inipath, params=params, mail_basedir=mail_basedir) class Config: - def __init__(self, inipath, params): + def __init__(self, inipath, params, mail_basedir): self._inipath = inipath self.mail_domain = params["mail_domain"] self.max_user_send_per_minute = int(params["max_user_send_per_minute"]) self.max_mailbox_size = params["max_mailbox_size"] self.delete_mails_after = params["delete_mails_after"] + self.delete_inactive_users_after = int(params["delete_inactive_users_after"]) self.username_min_length = int(params["username_min_length"]) self.username_max_length = int(params["username_max_length"]) self.password_min_length = int(params["password_min_length"]) @@ -27,7 +31,7 @@ class Config: self.privacy_mail = params.get("privacy_mail") self.privacy_pdo = params.get("privacy_pdo") self.privacy_supervisor = params.get("privacy_supervisor") - self.mail_basedir = Path(f"/home/vmail/mail/{self.mail_domain}") + self.mail_basedir = mail_basedir def _getbytefile(self): return open(self._inipath, "rb") diff --git a/chatmaild/src/chatmaild/remove_stale_users.py b/chatmaild/src/chatmaild/delete_inactive_users.py similarity index 67% rename from chatmaild/src/chatmaild/remove_stale_users.py rename to chatmaild/src/chatmaild/delete_inactive_users.py index e64a94c1..c20c1650 100644 --- a/chatmaild/src/chatmaild/remove_stale_users.py +++ b/chatmaild/src/chatmaild/delete_inactive_users.py @@ -8,7 +8,7 @@ import time from .config import read_config from .database import Database -from .doveauth import get_stale_users +from .doveauth import iter_userdb_lastlogin_before def remove_user(db, config, user): @@ -18,11 +18,14 @@ def remove_user(db, config, user): conn.execute("DELETE FROM users WHERE addr = ?", (user,)) +def delete_inactive_users(db, config): + cutoff_date = time.time() - config.delete_inactive_users_after * 86400 + for user in iter_userdb_lastlogin_before(db, cutoff_date): + remove_user(db, config, user) + print(f"Deleted user: {user}") + + def main(): db = Database(sys.argv[1]) config = read_config(sys.argv[2]) - cutoff_date = time.time() - config.delete_accounts_after * 86400 - - for user in get_stale_users(db, cutoff_date): - remove_user(db, config, user) - print(f"Deleted user: {user}") + delete_inactive_users(db, config) diff --git a/chatmaild/src/chatmaild/ini/chatmail.ini.f b/chatmaild/src/chatmaild/ini/chatmail.ini.f index b419f236..f0e36bbd 100644 --- a/chatmaild/src/chatmaild/ini/chatmail.ini.f +++ b/chatmaild/src/chatmaild/ini/chatmail.ini.f @@ -8,18 +8,21 @@ mail_domain = {mail_domain} # # -# Account Restrictions +# Restrictions on user addresses # # how many mails a user can send out per minute max_user_send_per_minute = 60 -# maximum mailbox size of a chatmail account +# maximum mailbox size of a chatmail address max_mailbox_size = 100M # days after which mails are unconditionally deleted delete_mails_after = 20 +# days after which users without a login are deleted (database and mails) +delete_inactive_users_after = 25 + # minimum length a username must have username_min_length = 9 @@ -29,7 +32,7 @@ username_max_length = 9 # minimum length a password must have password_min_length = 9 -# list of chatmail accounts which can send outbound un-encrypted mail +# list of chatmail addresses which can send outbound un-encrypted mail passthrough_senders = # list of e-mail recipients for which to accept outbound un-encrypted mails diff --git a/chatmaild/src/chatmaild/tests/plugin.py b/chatmaild/src/chatmaild/tests/plugin.py index 061067c6..f83c8f58 100644 --- a/chatmaild/src/chatmaild/tests/plugin.py +++ b/chatmaild/src/chatmaild/tests/plugin.py @@ -17,7 +17,9 @@ def make_config(tmp_path): def make_conf(mail_domain): write_initial_config(inipath, mail_domain=mail_domain) - return read_config(inipath) + basedir = tmp_path.joinpath(f"vmail/{mail_domain}") + basedir.mkdir(parents=True, exist_ok=True) + return read_config(inipath, mail_basedir=basedir) return make_conf diff --git a/chatmaild/src/chatmaild/tests/test_delete_inactive_users.py b/chatmaild/src/chatmaild/tests/test_delete_inactive_users.py new file mode 100644 index 00000000..f2e63f38 --- /dev/null +++ b/chatmaild/src/chatmaild/tests/test_delete_inactive_users.py @@ -0,0 +1,34 @@ +from time import time as now + +from chatmaild.doveauth import lookup_passdb +from chatmaild.delete_inactive_users import delete_inactive_users + + +def test_remove_stale_users(db, example_config): + old = now() - (example_config.delete_inactive_users_after * 86400) - 1000 + new = old + 1001 + + def create_user(addr, last_login): + lookup_passdb(db, example_config, addr, "q9mr3faue", last_login=last_login) + md = example_config.get_user_maildir(addr) + md.mkdir(parents=True) + md.joinpath("cur").mkdir() + md.joinpath("cur", "something").mkdir() + + for i in range(10): + create_user(f"oldold{i:03}@chat.example.org", last_login=old) + + remain = [] + for i in range(5): + create_user(f"newnew{i:03}@chat.example.org", last_login=new) + + udir = example_config.get_user_maildir("oldold001@chat.example.org") + assert udir.exists() + + delete_inactive_users(db, example_config) + + for p in udir.parent.iterdir(): + assert not p.name.startswith("old") + + for addr in remain: + assert example_config.get_user_maildir(addr).exists()