diff --git a/cmdeploy/src/cmdeploy/__init__.py b/cmdeploy/src/cmdeploy/__init__.py index f6552ec8..5cbb61c8 100644 --- a/cmdeploy/src/cmdeploy/__init__.py +++ b/cmdeploy/src/cmdeploy/__init__.py @@ -361,6 +361,14 @@ def _configure_dovecot(config: Config, debug: bool = False) -> bool: config=config, ) + files.put( + src=importlib.resources.files(__package__).joinpath("dovecot/remove-seen.py"), + dest="/usr/local/bin/remove-seen.py", + user="root", + group="root", + mode="755" + ) + # as per https://doc.dovecot.org/configuration_manual/os/ # it is recommended to set the following inotify limits for name in ("max_user_instances", "max_user_watches"): diff --git a/cmdeploy/src/cmdeploy/dovecot/expunge.cron.j2 b/cmdeploy/src/cmdeploy/dovecot/expunge.cron.j2 index d4dc81c4..4abb4d99 100644 --- a/cmdeploy/src/cmdeploy/dovecot/expunge.cron.j2 +++ b/cmdeploy/src/cmdeploy/dovecot/expunge.cron.j2 @@ -9,3 +9,4 @@ 2 0 * * * vmail find /home/vmail/mail/{{ config.mail_domain }} -path '*/tmp/*' -mtime +{{ config.delete_mails_after }} -type f -delete 2 0 * * * vmail find /home/vmail/mail/{{ config.mail_domain }} -path '*/.*/tmp/*' -mtime +{{ config.delete_mails_after }} -type f -delete 3 0 * * * vmail find /home/vmail/mail/{{ config.mail_domain }} -name 'maildirsize' -type f -delete +4 0 * * * vmail /usr/local/bin/remove-seen.py /home/vmail/mail/{{ config.mail_domain }} diff --git a/cmdeploy/src/cmdeploy/dovecot/remove-seen.py b/cmdeploy/src/cmdeploy/dovecot/remove-seen.py new file mode 100755 index 00000000..00889b62 --- /dev/null +++ b/cmdeploy/src/cmdeploy/dovecot/remove-seen.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python3 +"""Remove seen messages that are older than two days +if maildir has more than 80 MB of messages.""" +import sys +import time +from pathlib import Path + + +def getdirsize(path): + return sum(f.stat().st_size for f in path.glob("**/*") if f.is_file()) + + +def parse_dovecot_seen(path): + return "S" in path.name.split(":2,")[-1] + + +def main(): + now = time.time() + + mailhome = Path(sys.argv[1]) + + for p in mailhome.iterdir(): + dirsize = getdirsize(p / "cur") + getdirsize(p / "new") + if dirsize < 80000000: + continue + + removed_bytes = 0 + for mailpath in (p / "cur").iterdir(): + seen = parse_dovecot_seen(mailpath) + stat = mailpath.stat() + size = stat.st_size + if seen and now > stat.st_mtime + 2 * 24 * 3600: + removed_bytes += size + mailpath.unlink(missing_ok=True) + + if removed_bytes > 0: + (p / "maildirsize").unlink(missing_ok=True) + + +if __name__ == "__main__": + main()