150 lines
4.6 KiB
Bash
Executable File
150 lines
4.6 KiB
Bash
Executable File
#!/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=<unprivileged 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" <<EOF
|
|
jan@test.local:{PLAIN}Test-Password-123:$UIDN:$GIDN::$DIR/mail/jan
|
|
anna@test.local:{PLAIN}Test-Password-123:$UIDN:$GIDN::$DIR/mail/anna
|
|
biuro@test.local:{PLAIN}Test-Password-123:$UIDN:$GIDN::$DIR/mail/biuro
|
|
kasia@test.local:{PLAIN}Test-Password-123:$UIDN:$GIDN::$DIR/mail/kasia
|
|
EOF
|
|
cat > "$DIR/dovecot.conf" <<EOF
|
|
protocols = imap
|
|
listen = 127.0.0.1
|
|
base_dir = $DIR/run
|
|
state_dir = $DIR/run
|
|
default_internal_user = $RUN_USER
|
|
default_login_user = $RUN_USER
|
|
default_internal_group = $(id -gn "$RUN_USER")
|
|
first_valid_uid = 1
|
|
ssl = no
|
|
disable_plaintext_auth = no
|
|
auth_mechanisms = plain login
|
|
log_path = $DIR/dovecot.log
|
|
mail_location = maildir:~/Maildir
|
|
passdb {
|
|
driver = passwd-file
|
|
args = $DIR/users
|
|
}
|
|
userdb {
|
|
driver = passwd-file
|
|
args = $DIR/users
|
|
}
|
|
service imap-login {
|
|
inet_listener imap {
|
|
address = 127.0.0.1
|
|
port = 11143
|
|
}
|
|
inet_listener imaps {
|
|
port = 0
|
|
}
|
|
}
|
|
service anvil {
|
|
chroot =
|
|
}
|
|
service imap-login {
|
|
chroot =
|
|
}
|
|
namespace inbox {
|
|
inbox = yes
|
|
separator = /
|
|
mailbox Drafts {
|
|
special_use = \Drafts
|
|
auto = subscribe
|
|
}
|
|
mailbox Sent {
|
|
special_use = \Sent
|
|
auto = subscribe
|
|
}
|
|
mailbox Trash {
|
|
special_use = \Trash
|
|
auto = subscribe
|
|
}
|
|
mailbox Junk {
|
|
special_use = \Junk
|
|
auto = subscribe
|
|
}
|
|
}
|
|
EOF
|
|
|
|
# Radicale: htpasswd plain
|
|
for u in jan anna biuro kasia; do printf '%s@test.local:Test-Password-123\n' "$u"; done > "$DIR/radicale/users"
|
|
cat > "$DIR/radicale/config" <<EOF
|
|
[server]
|
|
hosts = 127.0.0.1:15232
|
|
[auth]
|
|
type = htpasswd
|
|
htpasswd_filename = $DIR/radicale/users
|
|
htpasswd_encryption = plain
|
|
[storage]
|
|
filesystem_folder = $DIR/radicale/collections
|
|
[rights]
|
|
type = owner_only
|
|
EOF
|
|
|
|
cat > "$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 <<EOF
|
|
Test backends running (data in $DIR):
|
|
IMAP 127.0.0.1:11143 (security: none)
|
|
CalDAV http://127.0.0.1:15232 path /%u/
|
|
CardDAV http://127.0.0.1:15232 path /%u/
|
|
SMTP 127.0.0.1:12525 (auth: none, security: none) -> $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
|