#!/usr/bin/env bash # Local throw-away backends for development and end-to-end tests: # Dovecot IMAP 127.0.0.1:11143 (plain, test only) # Radicale 127.0.0.1:15232 (CalDAV + CardDAV) # SMTP sink 127.0.0.1:12525 (prints/stores every message, no auth) # Test account: jan@test.local / Test-Password-123 # # NEVER use this configuration in production. Ctrl+C stops everything. set -euo pipefail ROOT="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd)" DIR="${AP_TEST_DIR:-$ROOT/data/test-backends}" RUN_USER="${AP_TEST_USER:-$(id -un)}" if [[ "$RUN_USER" == "root" ]]; then printf 'Run as a normal user or set AP_TEST_USER=.\n' >&2 exit 2 fi for bin in dovecot radicale python3; do command -v "$bin" >/dev/null 2>&1 || { printf 'Missing %s (apt install dovecot-imapd; pip install radicale aiosmtpd)\n' "$bin" >&2; exit 127; } done python3 -c 'import aiosmtpd' 2>/dev/null || { printf 'Missing python aiosmtpd (pip install aiosmtpd)\n' >&2; exit 127; } UIDN="$(id -u "$RUN_USER")"; GIDN="$(id -g "$RUN_USER")" mkdir -p "$DIR"/{mail,run,radicale,sink} chown -R "$RUN_USER" "$DIR" 2>/dev/null || true cat > "$DIR/users" < "$DIR/dovecot.conf" < "$DIR/radicale/users" cat > "$DIR/radicale/config" < "$DIR/sink.py" <<'EOF' import asyncio, sys, time, os from aiosmtpd.controller import Controller out = sys.argv[1] class H: async def handle_DATA(self, server, session, envelope): name = os.path.join(out, f"{time.time():.6f}.eml") with open(name, "wb") as f: f.write(f"X-Envelope-From: {envelope.mail_from}\r\nX-Envelope-To: {', '.join(envelope.rcpt_tos)}\r\n".encode()) f.write(envelope.content) print(f"[smtp-sink] {envelope.mail_from} -> {envelope.rcpt_tos} ({len(envelope.content)} bytes) saved {name}", flush=True) return "250 OK" c = Controller(H(), hostname="127.0.0.1", port=12525) c.start() print("[smtp-sink] listening on 127.0.0.1:12525", flush=True) try: asyncio.new_event_loop().run_forever() except KeyboardInterrupt: pass EOF pids=() cleanup() { for p in "${pids[@]}"; do kill "$p" 2>/dev/null || true; done; dovecot -c "$DIR/dovecot.conf" stop 2>/dev/null || true; } trap cleanup EXIT INT TERM dovecot -c "$DIR/dovecot.conf" if [[ "$(id -un)" == "$RUN_USER" ]]; then radicale -C "$DIR/radicale/config" >"$DIR/radicale.log" 2>&1 & pids+=($!) python3 "$DIR/sink.py" "$DIR/sink" & pids+=($!) else su -s /bin/bash "$RUN_USER" -c "radicale -C '$DIR/radicale/config' >'$DIR/radicale.log' 2>&1" & pids+=($!) su -s /bin/bash "$RUN_USER" -c "python3 '$DIR/sink.py' '$DIR/sink'" & pids+=($!) fi sleep 1 cat < $DIR/sink accounts jan, anna, biuro, kasia @test.local, password Test-Password-123 Apply matching settings with: ./dev.sh config apply tools/dev/test-settings.json Press Ctrl+C to stop. EOF wait