#!/usr/bin/env python3 """ Upload en activeer de Sieve-mailrule op australius.nl via ManageSieve (RFC 5804). Poort: 4190 met STARTTLS. Gebruik: python3 upload_sieve.py """ import socket import ssl import sys import base64 import config from pathlib import Path SCRIPT_NAME = "mailrules" SCRIPT_FILE = Path(__file__).parent / "mailrules.sieve" # Als fileinto-paden "INBOX." als prefix nodig hebben op deze server: # Zet SIEVE_PREFIX op "INBOX." en het script past de paden automatisch aan. # Zet op "" als de server paden zonder prefix accepteert (meest gangbaar). SIEVE_PREFIX = "" class ManageSieveClient: """Minimale ManageSieve-client (RFC 5804).""" def __init__(self, host: str, port: int): self._sock = None self._host = host self._port = port def connect(self): raw = socket.create_connection((self._host, self._port), timeout=15) self._sock = raw greeting = self._readline() if not greeting.startswith("OK"): raise ConnectionError(f"Onverwachte greeting: {greeting!r}") # Lees eventuele capability-regels na de greeting self._read_capabilities() def starttls(self): self._send("STARTTLS") resp = self._readline() if not resp.startswith("OK"): raise ConnectionError(f"STARTTLS geweigerd: {resp!r}") context = ssl.create_default_context() self._sock = context.wrap_socket(self._sock, server_hostname=self._host) # Nieuwe capabilities na TLS self._read_capabilities() def authenticate(self, username: str, password: str): creds = base64.b64encode(f"\x00{username}\x00{password}".encode()).decode() self._send(f'AUTHENTICATE "PLAIN" "{creds}"') resp = self._readline() if not resp.startswith("OK"): raise PermissionError(f"Authenticatie mislukt: {resp!r}") def put_script(self, name: str, content: str): encoded = content.encode("utf-8") self._send(f'PUTSCRIPT "{name}" {{{len(encoded)}+}}') self._sock.sendall(encoded) resp = self._readline() if not resp.startswith("OK"): raise RuntimeError(f"PUTSCRIPT mislukt: {resp!r}") def set_active(self, name: str): self._send(f'SETACTIVE "{name}"') resp = self._readline() if not resp.startswith("OK"): raise RuntimeError(f"SETACTIVE mislukt: {resp!r}") def list_scripts(self) -> list[str]: self._send("LISTSCRIPTS") scripts = [] while True: line = self._readline() if line.startswith("OK"): break scripts.append(line) return scripts def logout(self): try: self._send("LOGOUT") except Exception: pass try: self._sock.close() except Exception: pass def _send(self, cmd: str): self._sock.sendall((cmd + "\r\n").encode()) def _readline(self) -> str: buf = b"" while not buf.endswith(b"\n"): chunk = self._sock.recv(1) if not chunk: break buf += chunk return buf.decode("utf-8", errors="replace").strip() def _read_capabilities(self): """Lees en negeer capability-regels tot een lege regel of einde.""" # Capabilities eindigen met een lege regel of worden gevolgd door authenticatie # We lezen tot we iets tegenkomen dat niet op een capability lijkt # (dit is een vereenvoudiging; volledige RFC 5804 parsing is complexer) pass def apply_prefix(script: str, prefix: str) -> str: """Voeg SIEVE_PREFIX toe aan alle fileinto-paden als dat nodig is.""" if not prefix: return script import re def add_prefix(m): path = m.group(1) if path.startswith(prefix): return m.group(0) # al aanwezig return f'fileinto "{prefix}{path}"' return re.sub(r'fileinto "([^"]+)"', add_prefix, script) def run(): if not SCRIPT_FILE.exists(): print(f"Script niet gevonden: {SCRIPT_FILE}") sys.exit(1) script_content = SCRIPT_FILE.read_text(encoding="utf-8") script_content = apply_prefix(script_content, SIEVE_PREFIX) cfg = config.load() print(f"Script: {SCRIPT_FILE.name} ({len(script_content)} bytes)") print(f"Server: {cfg.sieve_host}:{cfg.sieve_port}") print(f"Account:{cfg.username}") print(f"Naam: {SCRIPT_NAME}\n") client = ManageSieveClient(cfg.sieve_host, cfg.sieve_port) print("Verbinden ...") try: client.connect() print(" STARTTLS ...") client.starttls() print(" Authenticeren ...") client.authenticate(cfg.username, cfg.password) except Exception as e: print(f"Verbinding mislukt: {e}") sys.exit(1) # Toon bestaande scripts scripts = client.list_scripts() if scripts: print(f"\nBestaande scripts op server:") for s in scripts: print(f" {s}") else: print("\nGeen bestaande scripts.") # Bevestiging vragen print(f"\nScript '{SCRIPT_NAME}' wordt geüpload en geactiveerd.") bevestig = input("Typ JA om door te gaan: ").strip() if bevestig != "JA": print("Afgebroken.") client.logout() sys.exit(0) print("\nUploaden ...") try: client.put_script(SCRIPT_NAME, script_content) print(f" ✓ {SCRIPT_NAME} geüpload") client.set_active(SCRIPT_NAME) print(f" ✓ {SCRIPT_NAME} geactiveerd") except Exception as e: print(f" ✗ Mislukt: {e}") client.logout() sys.exit(1) client.logout() print("\nKlaar. Nieuwe inkomende mail wordt nu automatisch gesorteerd.") print(f"\nAls mail niet aankomt in de verwachte mappen:") print(f" 1. Controleer of alle mappen bestaan (maak_mappen.py)") print(f" 2. Probeer SIEVE_PREFIX = \"INBOX.\" in dit script") print(f" 3. Controleer server-logs via webhosting-paneel") if __name__ == "__main__": run()