mirror of
https://github.com/chatmail/relay.git
synced 2026-05-10 16:04:37 +00:00
Compare commits
4 Commits
ssh-host-r
...
tmpfs-inde
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
151d6ef445 | ||
|
|
27443ca044 | ||
|
|
be35244371 | ||
|
|
f7f2c9600d |
@@ -56,6 +56,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.tmpfs_index = params.get("tmpfs_index", "false").lower() == "true"
|
||||
|
||||
# deprecated option
|
||||
mbdir = params.get("mailboxes_dir", f"/home/vmail/mail/{self.mail_domain}")
|
||||
@@ -111,10 +112,10 @@ def get_default_config_content(mail_domain, **overrides):
|
||||
|
||||
if mail_domain.endswith(".testrun.org"):
|
||||
override_inipath = inidir.joinpath("override-testrun.ini")
|
||||
privacy = iniconfig.IniConfig(override_inipath)["privacy"]
|
||||
params = iniconfig.IniConfig(override_inipath)["params"]
|
||||
lines = []
|
||||
for line in content.split("\n"):
|
||||
for key, value in privacy.items():
|
||||
for key, value in params.items():
|
||||
value_lines = value.format(mail_domain=mail_domain).strip().split("\n")
|
||||
if not line.startswith(f"{key} =") or not value_lines:
|
||||
continue
|
||||
|
||||
@@ -17,14 +17,14 @@ from chatmaild.config import read_config
|
||||
FileEntry = namedtuple("FileEntry", ("path", "mtime", "size"))
|
||||
|
||||
|
||||
def iter_mailboxes(basedir, maxnum):
|
||||
def iter_mailboxes(basedir, maxnum, tmpfs_index):
|
||||
if not os.path.exists(basedir):
|
||||
print_info(f"no mailboxes found at: {basedir}")
|
||||
return
|
||||
|
||||
for name in os_listdir_if_exists(basedir)[:maxnum]:
|
||||
if "@" in name:
|
||||
yield MailboxStat(basedir + "/" + name)
|
||||
yield MailboxStat(basedir + "/" + name, name, tmpfs_index)
|
||||
|
||||
|
||||
def get_file_entry(path):
|
||||
@@ -49,11 +49,14 @@ def os_listdir_if_exists(path):
|
||||
class MailboxStat:
|
||||
last_login = None
|
||||
|
||||
def __init__(self, basedir):
|
||||
def __init__(self, basedir, name, tmpfs_index):
|
||||
self.basedir = str(basedir)
|
||||
self.name = name
|
||||
self.messages = []
|
||||
self.extrafiles = []
|
||||
self.scandir(self.basedir)
|
||||
if tmpfs_index:
|
||||
self.scandir("/dev/shm/" + name)
|
||||
|
||||
def scandir(self, folderdir):
|
||||
for name in os_listdir_if_exists(folderdir):
|
||||
@@ -90,11 +93,13 @@ class Expiry:
|
||||
self.all_files = 0
|
||||
self.start = time.time()
|
||||
|
||||
def remove_mailbox(self, mboxdir):
|
||||
def remove_mailbox(self, mboxdir, name):
|
||||
if self.verbose:
|
||||
print_info(f"removing {mboxdir}")
|
||||
if not self.dry:
|
||||
shutil.rmtree(mboxdir)
|
||||
if self.config.tmpfs_index:
|
||||
shutil.rmtree("/dev/shm/" + name)
|
||||
self.del_mboxes += 1
|
||||
|
||||
def remove_file(self, path, mtime=None):
|
||||
@@ -121,7 +126,7 @@ class Expiry:
|
||||
self.all_mboxes += 1
|
||||
changed = False
|
||||
if mbox.last_login and mbox.last_login < cutoff_without_login:
|
||||
self.remove_mailbox(mbox.basedir)
|
||||
self.remove_mailbox(mbox.basedir, mbox.name)
|
||||
return
|
||||
|
||||
mboxname = os.path.basename(mbox.basedir)
|
||||
@@ -145,6 +150,9 @@ class Expiry:
|
||||
changed = True
|
||||
if changed:
|
||||
self.remove_file(f"{mbox.basedir}/maildirsize")
|
||||
for file in mbox.extrafiles:
|
||||
if "dovecot.index" in file.path.split("/")[-1] and file.size > 500 * 1024:
|
||||
self.remove_file(file.path)
|
||||
|
||||
def get_summary(self):
|
||||
return (
|
||||
@@ -197,7 +205,9 @@ def main(args=None):
|
||||
|
||||
maxnum = int(args.maxnum) if args.maxnum else None
|
||||
exp = Expiry(config, dry=not args.remove, now=now, verbose=args.verbose)
|
||||
for mailbox in iter_mailboxes(str(config.mailboxes_dir), maxnum=maxnum):
|
||||
for mailbox in iter_mailboxes(
|
||||
str(config.mailboxes_dir), maxnum, config.tmpfs_index
|
||||
):
|
||||
exp.process_mailbox_stat(mailbox)
|
||||
print(exp.get_summary())
|
||||
|
||||
|
||||
@@ -159,7 +159,7 @@ def main(args=None):
|
||||
|
||||
maxnum = int(args.maxnum) if args.maxnum else None
|
||||
rep = Report(now=now, min_login_age=int(args.min_login_age), mdir=args.mdir)
|
||||
for mbox in iter_mailboxes(str(config.mailboxes_dir), maxnum=maxnum):
|
||||
for mbox in iter_mailboxes(str(config.mailboxes_dir), maxnum, config.tmpfs_index):
|
||||
rep.process_mailbox_stat(mbox)
|
||||
rep.dump_summary()
|
||||
|
||||
|
||||
@@ -48,6 +48,9 @@ passthrough_senders =
|
||||
# (space-separated, item may start with "@" to whitelist whole recipient domains)
|
||||
passthrough_recipients =
|
||||
|
||||
# store index files in tmpfs (good for disk size and I/O, bad for ram)
|
||||
tmpfs_index = false
|
||||
|
||||
# path to www directory - documented here: https://chatmail.at/doc/relay/getting_started.html#custom-web-pages
|
||||
#www_folder = www
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
[params]
|
||||
|
||||
[privacy]
|
||||
tmpfs_index = true
|
||||
|
||||
passthrough_recipients = privacy@testrun.org echo@{mail_domain}
|
||||
|
||||
|
||||
@@ -43,20 +43,22 @@ def create_new_messages(basedir, relpaths, size=1000, days=0):
|
||||
|
||||
@pytest.fixture
|
||||
def mbox1(example_config):
|
||||
mboxdir = example_config.mailboxes_dir.joinpath("mailbox1@example.org")
|
||||
addr = "mailbox1@example.org"
|
||||
mboxdir = example_config.mailboxes_dir.joinpath(addr)
|
||||
mboxdir.mkdir()
|
||||
fill_mbox(mboxdir)
|
||||
return MailboxStat(mboxdir)
|
||||
return MailboxStat(mboxdir, addr, False)
|
||||
|
||||
|
||||
def test_deltachat_folder(example_config):
|
||||
"""Test old setups that might have a .DeltaChat folder where messages also need to get removed."""
|
||||
mboxdir = example_config.mailboxes_dir.joinpath("mailbox1@example.org")
|
||||
addr = "mailbox1@example.org"
|
||||
mboxdir = example_config.mailboxes_dir.joinpath(addr)
|
||||
mboxdir.mkdir()
|
||||
mbox2dir = mboxdir.joinpath(".DeltaChat")
|
||||
mbox2dir.mkdir()
|
||||
fill_mbox(mbox2dir)
|
||||
mb = MailboxStat(mboxdir)
|
||||
mb = MailboxStat(mboxdir, addr, False)
|
||||
assert len(mb.messages) == 2
|
||||
|
||||
|
||||
@@ -69,7 +71,11 @@ def test_filentry_ordering(tmp_path):
|
||||
|
||||
|
||||
def test_no_mailbxoes(tmp_path, capsys):
|
||||
assert [] == list(iter_mailboxes(str(tmp_path.joinpath("notexists")), maxnum=10))
|
||||
assert [] == list(
|
||||
iter_mailboxes(
|
||||
str(tmp_path.joinpath("notexists")), maxnum=10, tmpfs_index=False
|
||||
)
|
||||
)
|
||||
out, err = capsys.readouterr()
|
||||
assert "no mailboxes" in err
|
||||
|
||||
@@ -86,13 +92,13 @@ def test_stats_mailbox(mbox1):
|
||||
|
||||
create_new_messages(mbox1.basedir, ["large-extra"], size=1000)
|
||||
create_new_messages(mbox1.basedir, ["index-something"], size=3)
|
||||
mbox2 = MailboxStat(mbox1.basedir)
|
||||
mbox2 = MailboxStat(mbox1.basedir, mbox1.name, False)
|
||||
assert len(mbox2.extrafiles) == 5
|
||||
assert mbox2.extrafiles[0].size == 1000
|
||||
|
||||
# cope well with mailbox dirs that have no password (for whatever reason)
|
||||
Path(mbox1.basedir).joinpath("password").unlink()
|
||||
mbox3 = MailboxStat(mbox1.basedir)
|
||||
mbox3 = MailboxStat(mbox1.basedir, mbox1.name, False)
|
||||
assert mbox3.last_login is None
|
||||
|
||||
|
||||
|
||||
@@ -68,13 +68,11 @@ userdb {
|
||||
##
|
||||
|
||||
# Mailboxes are stored in the "mail" directory of the vmail user home.
|
||||
{% if config.tmpfs_index %}
|
||||
mail_location = maildir:{{ config.mailboxes_dir }}/%u:INDEX=/dev/shm/%u
|
||||
{% else %}
|
||||
mail_location = maildir:{{ config.mailboxes_dir }}/%u
|
||||
|
||||
# index/cache files are not very useful for chatmail relay operations
|
||||
# but it's not clear how to disable them completely.
|
||||
# According to https://doc.dovecot.org/2.3/settings/advanced/#core_setting-mail_cache_max_size
|
||||
# if the cache file becomes larger than the specified size, it is truncated by dovecot
|
||||
mail_cache_max_size = 500K
|
||||
{% endif %}
|
||||
|
||||
namespace inbox {
|
||||
inbox = yes
|
||||
|
||||
Reference in New Issue
Block a user