mirror of
https://github.com/chatmail/relay.git
synced 2026-05-18 20:08:21 +00:00
various fixes
This commit is contained in:
@@ -1,21 +1,19 @@
|
||||
|
||||
|
||||
from pathlib import Path
|
||||
from fnmatch import fnmatch
|
||||
import iniconfig
|
||||
|
||||
|
||||
class Config:
|
||||
def __init__(self, mailname, section):
|
||||
self.mailname = mailname
|
||||
self.privacy_postal = section.get("privacy_postal")
|
||||
self.privacy_mail = section.get("privacy_mail")
|
||||
self.privacy_pdo = section.get("privacy_pdo")
|
||||
self.privacy_supervisor = section.get("privacy_supervisor")
|
||||
self.has_privacy_policy = self.privacy_mail != None
|
||||
system_mailname_path = Path("/etc/mailname")
|
||||
|
||||
|
||||
def read_config(inipath, mailname):
|
||||
def read_config(inipath, mailname=None):
|
||||
if mailname is None:
|
||||
with open(system_mailname_path) as f:
|
||||
mailname = f.read().strip()
|
||||
|
||||
ini = iniconfig.IniConfig(inipath)
|
||||
privacy = None
|
||||
privacy = {}
|
||||
for section in ini:
|
||||
if section.name.startswith("privacy:"):
|
||||
domain = section["domain"]
|
||||
@@ -23,4 +21,20 @@ def read_config(inipath, mailname):
|
||||
privacy = section
|
||||
break
|
||||
|
||||
return Config(mailname, privacy or {})
|
||||
return Config(inipath, mailname, privacy, params=ini.sections["params"])
|
||||
|
||||
|
||||
class Config:
|
||||
def __init__(self, inipath, mailname, privacy, params):
|
||||
self._inipath = inipath
|
||||
self.mailname = mailname
|
||||
self.privacy_postal = privacy.get("privacy_postal")
|
||||
self.privacy_mail = privacy.get("privacy_mail")
|
||||
self.privacy_pdo = privacy.get("privacy_pdo")
|
||||
self.privacy_supervisor = privacy.get("privacy_supervisor")
|
||||
self.max_user_send_per_minute = int(params["max_user_send_per_minute"])
|
||||
self.filtermail_smtp_port = int(params["filtermail_smtp_port"])
|
||||
self.postfix_reinject_port = int(params["postfix_reinject_port"])
|
||||
|
||||
def _getbytefile(self):
|
||||
return open(self._inipath, "rb")
|
||||
|
||||
@@ -11,6 +11,8 @@ from aiosmtpd.smtp import SMTP
|
||||
from aiosmtpd.controller import Controller
|
||||
from smtplib import SMTP as SMTPClient
|
||||
|
||||
from .config import read_config
|
||||
|
||||
|
||||
def check_encrypted(message):
|
||||
"""Check that the message is an OpenPGP-encrypted message."""
|
||||
@@ -76,13 +78,15 @@ class SMTPController(Controller):
|
||||
|
||||
|
||||
class BeforeQueueHandler:
|
||||
def __init__(self):
|
||||
def __init__(self, config):
|
||||
self.config = config
|
||||
self.send_rate_limiter = SendRateLimiter()
|
||||
|
||||
async def handle_MAIL(self, server, session, envelope, address, mail_options):
|
||||
logging.info(f"handle_MAIL from {address}")
|
||||
envelope.mail_from = address
|
||||
if not self.send_rate_limiter.is_sending_allowed(address):
|
||||
max_sent = self.config.max_user_send_per_minute
|
||||
if not self.send_rate_limiter.is_sending_allowed(address, max_sent):
|
||||
return f"450 4.7.1: Too much mail from {address}"
|
||||
|
||||
parts = envelope.mail_from.split("@")
|
||||
@@ -97,13 +101,14 @@ class BeforeQueueHandler:
|
||||
if error:
|
||||
return error
|
||||
logging.info("re-injecting the mail that passed checks")
|
||||
client = SMTPClient("localhost", "10025")
|
||||
client = SMTPClient("localhost", self.config.postfix_reinject_port)
|
||||
client.sendmail(envelope.mail_from, envelope.rcpt_tos, envelope.content)
|
||||
return "250 OK"
|
||||
|
||||
|
||||
async def asyncmain_beforequeue(port):
|
||||
Controller(BeforeQueueHandler(), hostname="127.0.0.1", port=port).start()
|
||||
async def asyncmain_beforequeue(config):
|
||||
port = config.filtermail_smtp_port
|
||||
Controller(BeforeQueueHandler(config), hostname="127.0.0.1", port=port).start()
|
||||
|
||||
|
||||
def check_DATA(envelope):
|
||||
@@ -142,16 +147,14 @@ def check_DATA(envelope):
|
||||
|
||||
|
||||
class SendRateLimiter:
|
||||
MAX_USER_SEND_PER_MINUTE = 80
|
||||
|
||||
def __init__(self):
|
||||
self.addr2timestamps = {}
|
||||
|
||||
def is_sending_allowed(self, mail_from):
|
||||
def is_sending_allowed(self, mail_from, max_send_per_minute):
|
||||
last = self.addr2timestamps.setdefault(mail_from, [])
|
||||
now = time.time()
|
||||
last[:] = [ts for ts in last if ts >= (now - 60)]
|
||||
if len(last) <= self.MAX_USER_SEND_PER_MINUTE:
|
||||
if len(last) <= max_send_per_minute:
|
||||
last.append(now)
|
||||
return True
|
||||
return False
|
||||
@@ -160,9 +163,10 @@ class SendRateLimiter:
|
||||
def main():
|
||||
args = sys.argv[1:]
|
||||
assert len(args) == 1
|
||||
config = read_config(args[0])
|
||||
logging.basicConfig(level=logging.WARN)
|
||||
loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(loop)
|
||||
task = asyncmain_beforequeue(port=int(args[0]))
|
||||
task = asyncmain_beforequeue(config)
|
||||
loop.create_task(task)
|
||||
loop.run_forever()
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Description=Chatmail Postfix BeforeQeue filter
|
||||
|
||||
[Service]
|
||||
ExecStart={execpath} 10080
|
||||
ExecStart={execpath} {config_path}
|
||||
Restart=always
|
||||
RestartSec=30
|
||||
|
||||
|
||||
Reference in New Issue
Block a user