mirror of
https://github.com/chatmail/relay.git
synced 2026-05-18 10:08:58 +00:00
Compare commits
4 Commits
hpk/fixup
...
link2xt/rs
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
32fa1d6cb8 | ||
|
|
fa9aa5b015 | ||
|
|
0155f32df6 | ||
|
|
9ddd5d8b2b |
@@ -2,6 +2,9 @@
|
|||||||
|
|
||||||
## untagged
|
## untagged
|
||||||
|
|
||||||
|
- Require TLS 1.2 for outgoing SMTP connections
|
||||||
|
([#685](https://github.com/chatmail/relay/pull/685))
|
||||||
|
|
||||||
- filtermail: run CPU-intensive handle_DATA in a thread pool executor
|
- filtermail: run CPU-intensive handle_DATA in a thread pool executor
|
||||||
([#676](https://github.com/chatmail/relay/pull/676))
|
([#676](https://github.com/chatmail/relay/pull/676))
|
||||||
|
|
||||||
|
|||||||
@@ -22,11 +22,30 @@ def iter_mailboxes(basedir, maxnum):
|
|||||||
print_info(f"no mailboxes found at: {basedir}")
|
print_info(f"no mailboxes found at: {basedir}")
|
||||||
return
|
return
|
||||||
|
|
||||||
for name in os.listdir(basedir)[:maxnum]:
|
for name in os_listdir_if_exists(basedir)[:maxnum]:
|
||||||
if "@" in name:
|
if "@" in name:
|
||||||
yield MailboxStat(basedir + "/" + name)
|
yield MailboxStat(basedir + "/" + name)
|
||||||
|
|
||||||
|
|
||||||
|
def get_file_entry(path):
|
||||||
|
"""return a FileEntry or None if the path does not exist or is not a regular file."""
|
||||||
|
try:
|
||||||
|
st = os.stat(path)
|
||||||
|
except FileNotFoundError:
|
||||||
|
return None
|
||||||
|
if not S_ISREG(st.st_mode):
|
||||||
|
return None
|
||||||
|
return FileEntry(path, st.st_mtime, st.st_size)
|
||||||
|
|
||||||
|
|
||||||
|
def os_listdir_if_exists(path):
|
||||||
|
"""return a list of names obtained from os.listdir or an empty list if the path does not exist."""
|
||||||
|
try:
|
||||||
|
return os.listdir(path)
|
||||||
|
except FileNotFoundError:
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
class MailboxStat:
|
class MailboxStat:
|
||||||
last_login = None
|
last_login = None
|
||||||
|
|
||||||
@@ -40,19 +59,23 @@ class MailboxStat:
|
|||||||
|
|
||||||
# scan all relevant files (without recursion)
|
# scan all relevant files (without recursion)
|
||||||
old_cwd = os.getcwd()
|
old_cwd = os.getcwd()
|
||||||
os.chdir(self.basedir)
|
try:
|
||||||
for name in os.listdir("."):
|
os.chdir(self.basedir)
|
||||||
|
except FileNotFoundError:
|
||||||
|
return
|
||||||
|
for name in os_listdir_if_exists("."):
|
||||||
if name in ("cur", "new", "tmp"):
|
if name in ("cur", "new", "tmp"):
|
||||||
for msg_name in os.listdir(name):
|
for msg_name in os_listdir_if_exists(name):
|
||||||
relpath = name + "/" + msg_name
|
entry = get_file_entry(f"{name}/{msg_name}")
|
||||||
st = os.stat(relpath)
|
if entry is not None:
|
||||||
self.messages.append(FileEntry(relpath, st.st_mtime, st.st_size))
|
self.messages.append(entry)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
st = os.stat(name)
|
entry = get_file_entry(name)
|
||||||
if S_ISREG(st.st_mode):
|
if entry is not None:
|
||||||
self.extrafiles.append(FileEntry(name, st.st_mtime, st.st_size))
|
self.extrafiles.append(entry)
|
||||||
if name == "password":
|
if name == "password":
|
||||||
self.last_login = st.st_mtime
|
self.last_login = entry.mtime
|
||||||
self.extrafiles.sort(key=lambda x: -x.size)
|
self.extrafiles.sort(key=lambda x: -x.size)
|
||||||
os.chdir(old_cwd)
|
os.chdir(old_cwd)
|
||||||
|
|
||||||
@@ -80,9 +103,13 @@ class Expiry:
|
|||||||
shutil.rmtree(mboxdir)
|
shutil.rmtree(mboxdir)
|
||||||
self.del_mboxes += 1
|
self.del_mboxes += 1
|
||||||
|
|
||||||
def remove_file(self, path):
|
def remove_file(self, path, mtime=None):
|
||||||
if self.verbose:
|
if self.verbose:
|
||||||
print_info(f"removing {path}")
|
if mtime is not None:
|
||||||
|
date = datetime.fromtimestamp(mtime).strftime("%b %d")
|
||||||
|
print_info(f"removing {date} {path}")
|
||||||
|
else:
|
||||||
|
print_info(f"removing {path}")
|
||||||
if not self.dry:
|
if not self.dry:
|
||||||
try:
|
try:
|
||||||
os.unlink(path)
|
os.unlink(path)
|
||||||
@@ -104,18 +131,27 @@ class Expiry:
|
|||||||
return
|
return
|
||||||
|
|
||||||
# all to-be-removed files are relative to the mailbox basedir
|
# all to-be-removed files are relative to the mailbox basedir
|
||||||
os.chdir(mbox.basedir)
|
try:
|
||||||
|
os.chdir(mbox.basedir)
|
||||||
|
except FileNotFoundError:
|
||||||
|
print_info(f"mailbox not found/vanished {mbox.basedir}")
|
||||||
|
return
|
||||||
|
|
||||||
mboxname = os.path.basename(mbox.basedir)
|
mboxname = os.path.basename(mbox.basedir)
|
||||||
if self.verbose:
|
if self.verbose:
|
||||||
print_info(f"checking for mailbox messages in: {mboxname}")
|
date = datetime.fromtimestamp(mbox.last_login) if mbox.last_login else None
|
||||||
|
if date:
|
||||||
|
print_info(f"checking mailbox {date.strftime('%b %d')} {mboxname}")
|
||||||
|
else:
|
||||||
|
print_info(f"checking mailbox (no last_login) {mboxname}")
|
||||||
self.all_files += len(mbox.messages)
|
self.all_files += len(mbox.messages)
|
||||||
for message in mbox.messages:
|
for message in mbox.messages:
|
||||||
if message.mtime < cutoff_mails:
|
if message.mtime < cutoff_mails:
|
||||||
self.remove_file(message.relpath)
|
self.remove_file(message.relpath, mtime=message.mtime)
|
||||||
elif message.size > 200000 and message.mtime < cutoff_large_mails:
|
elif message.size > 200000 and message.mtime < cutoff_large_mails:
|
||||||
# we only remove noticed large files (not unnoticed ones in new/)
|
# we only remove noticed large files (not unnoticed ones in new/)
|
||||||
if message.relpath.startswith("cur/"):
|
if message.relpath.startswith("cur/"):
|
||||||
self.remove_file(message.relpath)
|
self.remove_file(message.relpath, mtime=message.mtime)
|
||||||
else:
|
else:
|
||||||
continue
|
continue
|
||||||
changed = True
|
changed = True
|
||||||
|
|||||||
@@ -6,7 +6,13 @@ from pathlib import Path
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from chatmaild.expire import FileEntry, MailboxStat, iter_mailboxes
|
from chatmaild.expire import (
|
||||||
|
FileEntry,
|
||||||
|
MailboxStat,
|
||||||
|
get_file_entry,
|
||||||
|
iter_mailboxes,
|
||||||
|
os_listdir_if_exists,
|
||||||
|
)
|
||||||
from chatmaild.expire import main as expiry_main
|
from chatmaild.expire import main as expiry_main
|
||||||
from chatmaild.fsreport import main as report_main
|
from chatmaild.fsreport import main as report_main
|
||||||
|
|
||||||
@@ -127,3 +133,18 @@ def test_expiry_cli_old_files(capsys, example_config, mbox1):
|
|||||||
pytest.fail(f"failed to remove {path}\n{err}")
|
pytest.fail(f"failed to remove {path}\n{err}")
|
||||||
|
|
||||||
assert "shouldstay" not in err
|
assert "shouldstay" not in err
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_file_entry(tmp_path):
|
||||||
|
assert get_file_entry(str(tmp_path.joinpath("123123"))) is None
|
||||||
|
p = tmp_path.joinpath("x")
|
||||||
|
p.write_text("hello")
|
||||||
|
entry = get_file_entry(str(p))
|
||||||
|
assert entry.size == 5
|
||||||
|
assert entry.mtime
|
||||||
|
|
||||||
|
|
||||||
|
def test_os_listdir_if_exists(tmp_path):
|
||||||
|
tmp_path.joinpath("x").write_text("hello")
|
||||||
|
assert len(os_listdir_if_exists(str(tmp_path))) == 1
|
||||||
|
assert len(os_listdir_if_exists(str(tmp_path.joinpath("123123")))) == 0
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ request:
|
|||||||
provider: https://acme-v02.api.letsencrypt.org/directory
|
provider: https://acme-v02.api.letsencrypt.org/directory
|
||||||
key:
|
key:
|
||||||
type: rsa
|
type: rsa
|
||||||
|
rsa-size: 4096
|
||||||
challenge:
|
challenge:
|
||||||
webroot-paths:
|
webroot-paths:
|
||||||
- /var/www/html/.well-known/acme-challenge
|
- /var/www/html/.well-known/acme-challenge
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ smtp_tls_security_level=verify
|
|||||||
smtp_tls_servername = hostname
|
smtp_tls_servername = hostname
|
||||||
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
|
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
|
||||||
smtp_tls_policy_maps = inline:{nauta.cu=may}
|
smtp_tls_policy_maps = inline:{nauta.cu=may}
|
||||||
|
smtp_tls_protocols = >=TLSv1.2
|
||||||
smtpd_tls_protocols = >=TLSv1.2
|
smtpd_tls_protocols = >=TLSv1.2
|
||||||
|
|
||||||
# Disable anonymous cipher suites
|
# Disable anonymous cipher suites
|
||||||
|
|||||||
Reference in New Issue
Block a user