#!/bin/sh
# SecuBox Mailserver Controller
# LXC-based Postfix + Dovecot mail server

VERSION="2.0.0"
CONFIG="mailserver"
CONTAINER="mailserver"
LXC_PATH="/srv/lxc/mailserver"

# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'

log() { echo -e "${GREEN}[MAILSERVER]${NC} $1"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
error() { echo -e "${RED}[ERROR]${NC} $1"; }

# ============================================================================
# Configuration Helpers
# ============================================================================

uci_get() { uci -q get ${CONFIG}.$1; }
uci_set() { uci -q set ${CONFIG}.$1="$2"; }

require_root() {
	[ "$(id -u)" -eq 0 ] || { error "Root required"; exit 1; }
}

defaults() {
	ip_address="$(uci_get main.ip_address)"
	[ -z "$ip_address" ] && ip_address="192.168.255.30"
	domain="$(uci_get main.domain)"
	[ -z "$domain" ] && domain="secubox.in"
	hostname="$(uci_get main.hostname)"
	[ -z "$hostname" ] && hostname="mail.$domain"
}

ensure_dir() { [ -d "$1" ] || mkdir -p "$1"; }

# ============================================================================
# LXC Helpers
# ============================================================================

lxc_running() {
	lxc-info -n "$CONTAINER" 2>/dev/null | grep -q "State:.*RUNNING"
}

lxc_exists() {
	[ -d "$LXC_PATH/rootfs" ]
}

create_lxc_config() {
	defaults
	cat > "$LXC_PATH/config" << EOF
lxc.uts.name = mailserver
lxc.rootfs.path = dir:${LXC_PATH}/rootfs
lxc.net.0.type = veth
lxc.net.0.link = br-lan
lxc.net.0.flags = up
lxc.net.0.ipv4.address = ${ip_address}/24
lxc.net.0.ipv4.gateway = 192.168.255.1
lxc.mount.auto = proc:mixed sys:ro cgroup:mixed
lxc.cap.drop = sys_module mac_admin mac_override sys_time
lxc.seccomp.profile =
lxc.tty.max = 0
lxc.pty.max = 256
lxc.cgroup2.memory.max = 512000000
lxc.init.cmd = /opt/start-mail.sh
EOF
}

create_startup_script() {
	cat > "$LXC_PATH/rootfs/opt/start-mail.sh" << 'EOF'
#!/bin/sh
# Mailserver startup script

# Ensure dovenull is in dovecot group (fixes login directory access)
addgroup dovenull dovecot 2>/dev/null || true

# Ensure dovecot run directory exists with correct permissions
# This fixes anvil-auth-penalty socket permission issues
mkdir -p /run/dovecot /run/dovecot/login /run/dovecot/token-login /run/dovecot/empty
chown -R dovecot:dovecot /run/dovecot
chown root:dovenull /run/dovecot/login /run/dovecot/token-login
chmod 755 /run/dovecot
chmod 750 /run/dovecot/login /run/dovecot/token-login

# Remove stale auth token (prevents "compromised token" errors on restart)
rm -f /run/dovecot/auth-token-secret.dat

# Ensure dovecot users file is readable (fixes LMTP lookup errors)
[ -f /etc/dovecot/users ] && chmod 644 /etc/dovecot/users && chown root:dovecot /etc/dovecot/users

# Start services
/usr/sbin/rsyslogd
sleep 1
/usr/sbin/postfix start
/usr/sbin/dovecot

# Give dovecot a moment to create sockets, then fix permissions
sleep 2
chown -R dovecot:dovecot /run/dovecot
chown root:dovenull /run/dovecot/login /run/dovecot/token-login

echo "Mail services started"

# Keep container running
exec tail -f /var/log/dovecot.log /var/log/messages 2>/dev/null || exec sleep infinity
EOF
	chmod +x "$LXC_PATH/rootfs/opt/start-mail.sh"
}

# ============================================================================
# Alpine Bootstrap
# ============================================================================

bootstrap_alpine() {
	require_root
	defaults

	log "Bootstrapping Alpine Linux rootfs..."

	ensure_dir "$LXC_PATH"

	# Download apk-tools-static
	cd "$LXC_PATH"
	if [ ! -f sbin/apk.static ]; then
		log "Downloading apk-tools-static..."
		curl -L -o apk-tools-static.apk \
			"https://dl-cdn.alpinelinux.org/alpine/v3.21/main/aarch64/apk-tools-static-2.14.6-r3.apk"
		tar -xzf apk-tools-static.apk sbin/apk.static
		rm -f apk-tools-static.apk
	fi

	# Bootstrap rootfs
	log "Installing base system..."
	./sbin/apk.static -X https://dl-cdn.alpinelinux.org/alpine/v3.21/main \
		-U --allow-untrusted --root rootfs --initdb add \
		alpine-base alpine-baselayout busybox musl openrc

	# Set up repositories
	mkdir -p rootfs/etc/apk
	cat > rootfs/etc/apk/repositories << 'REPOEOF'
https://dl-cdn.alpinelinux.org/alpine/v3.21/main
https://dl-cdn.alpinelinux.org/alpine/v3.21/community
REPOEOF

	# Set up DNS
	cat > rootfs/etc/resolv.conf << 'DNSEOF'
nameserver 8.8.8.8
nameserver 1.1.1.1
DNSEOF

	# Set hostname
	echo "mailserver" > rootfs/etc/hostname

	log "Base system installed"
}

install_mail_packages() {
	require_root

	if ! lxc_running; then
		log "Starting container for package installation..."
		lxc-start -n "$CONTAINER" -d
		sleep 3
	fi

	log "Installing mail packages..."
	lxc-attach -n "$CONTAINER" -- apk update
	lxc-attach -n "$CONTAINER" -- apk add --no-cache \
		postfix \
		dovecot \
		dovecot-lmtpd \
		ca-certificates \
		openssl \
		rsyslog

	log "Packages installed"
}

# ============================================================================
# Mail Configuration
# ============================================================================

configure_postfix() {
	defaults
	local rootfs="$LXC_PATH/rootfs"

	log "Configuring Postfix..."

	cat > "$rootfs/etc/postfix/main.cf" << EOF
# Basic config
myhostname = $hostname
mydomain = $domain
myorigin = \$mydomain
mydestination = \$myhostname, localhost.\$mydomain, localhost
mynetworks = 127.0.0.0/8 [::1]/128 192.168.255.0/24

# Virtual mailbox
virtual_mailbox_domains = $domain
virtual_mailbox_base = /var/mail
virtual_mailbox_maps = lmdb:/etc/postfix/vmailbox
virtual_uid_maps = static:102
virtual_gid_maps = static:105
virtual_transport = lmtp:unix:private/dovecot-lmtp

# SASL auth via Dovecot
smtpd_sasl_auth_enable = yes
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_sasl_security_options = noanonymous
smtpd_sasl_local_domain = \$mydomain
broken_sasl_auth_clients = yes

# TLS
smtpd_tls_cert_file = /etc/ssl/certs/mail.crt
smtpd_tls_key_file = /etc/ssl/private/mail.key
smtpd_tls_security_level = may
smtp_tls_security_level = may

# Restrictions
smtpd_recipient_restrictions = permit_sasl_authenticated, permit_mynetworks, reject_unauth_destination
smtpd_sender_restrictions = permit_sasl_authenticated, permit_mynetworks

# Limits
mailbox_size_limit = 0
message_size_limit = 52428800
inet_interfaces = all
inet_protocols = ipv4
EOF

	cat > "$rootfs/etc/postfix/master.cf" << 'EOF'
smtp      inet  n       -       n       -       -       smtpd
submission inet n       -       n       -       -       smtpd
  -o syslog_name=postfix/submission
  -o smtpd_tls_security_level=encrypt
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_recipient_restrictions=permit_sasl_authenticated,reject
smtps     inet  n       -       n       -       -       smtpd
  -o syslog_name=postfix/smtps
  -o smtpd_tls_wrappermode=yes
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_recipient_restrictions=permit_sasl_authenticated,reject
pickup    unix  n       -       n       60      1       pickup
cleanup   unix  n       -       n       -       0       cleanup
qmgr      unix  n       -       n       300     1       qmgr
tlsmgr    unix  -       -       n       1000?   1       tlsmgr
rewrite   unix  -       -       n       -       -       trivial-rewrite
bounce    unix  -       -       n       -       0       bounce
defer     unix  -       -       n       -       0       bounce
trace     unix  -       -       n       -       0       bounce
verify    unix  -       -       n       -       1       verify
flush     unix  n       -       n       1000?   0       flush
proxymap  unix  -       -       n       -       -       proxymap
proxywrite unix -       -       n       -       1       proxymap
smtp      unix  -       -       n       -       -       smtp
relay     unix  -       -       n       -       -       smtp
showq     unix  n       -       n       -       -       showq
error     unix  -       -       n       -       -       error
retry     unix  -       -       n       -       -       error
discard   unix  -       -       n       -       -       discard
local     unix  -       n       n       -       -       local
virtual   unix  -       n       n       -       -       virtual
lmtp      unix  -       -       n       -       -       lmtp
anvil     unix  -       -       n       -       1       anvil
scache    unix  -       -       n       -       1       scache
EOF

	log "Postfix configured"
}

configure_dovecot() {
	defaults
	local rootfs="$LXC_PATH/rootfs"

	log "Configuring Dovecot..."

	cat > "$rootfs/etc/dovecot/dovecot.conf" << 'EOF'
protocols = imap lmtp
listen = *
mail_location = maildir:/var/mail/%d/%n
mail_uid = 102
mail_gid = 105
first_valid_uid = 102
last_valid_uid = 102

# Auth
auth_mechanisms = plain login
passdb {
  driver = passwd-file
  args = /etc/dovecot/users
}
userdb {
  driver = static
  args = uid=102 gid=105 home=/var/mail/%d/%n
}

# SSL
ssl = yes
ssl_cert = </etc/ssl/certs/mail.crt
ssl_key = </etc/ssl/private/mail.key

# Services
service imap-login {
  inet_listener imap {
    port = 143
  }
  inet_listener imaps {
    port = 993
    ssl = yes
  }
}

service lmtp {
  unix_listener /var/spool/postfix/private/dovecot-lmtp {
    mode = 0600
    user = postfix
    group = postfix
  }
}

service auth {
  unix_listener /var/spool/postfix/private/auth {
    mode = 0660
    user = postfix
    group = postfix
  }
}

namespace inbox {
  inbox = yes
  separator = /
}

log_path = /var/log/dovecot.log
info_log_path = /var/log/dovecot-info.log
EOF

	log "Dovecot configured"
}

generate_ssl_cert() {
	local rootfs="$LXC_PATH/rootfs"
	defaults

	log "Generating SSL certificate..."

	mkdir -p "$rootfs/etc/ssl/private" "$rootfs/etc/ssl/certs"

	openssl req -x509 -nodes -days 3650 \
		-newkey rsa:2048 \
		-keyout "$rootfs/etc/ssl/private/mail.key" \
		-out "$rootfs/etc/ssl/certs/mail.crt" \
		-subj "/CN=$hostname/O=SecuBox/C=FR" 2>/dev/null

	chmod 600 "$rootfs/etc/ssl/private/mail.key"
	log "SSL certificate generated"
}

# ============================================================================
# User Management
# ============================================================================

cmd_add_user() {
	local email="$1"
	local password="$2"

	if [ -z "$email" ] || [ -z "$password" ]; then
		echo "Usage: mailserverctl add-user <email> <password>"
		return 1
	fi

	local user=$(echo "$email" | cut -d@ -f1)
	local domain=$(echo "$email" | cut -d@ -f2)
	local rootfs="$LXC_PATH/rootfs"

	# Create mailbox entry
	echo "$email ${domain}/${user}/" >> "$rootfs/etc/postfix/vmailbox"

	# Generate password hash and add to users file
	if lxc_running; then
		local pass_hash=$(lxc-attach -n "$CONTAINER" -- doveadm pw -s SHA512-CRYPT -p "$password")
		echo "${email}:${pass_hash}:102:105::/var/mail/${domain}/${user}::" >> "$rootfs/etc/dovecot/users"
		# Fix permissions (dovecot needs read access)
		chmod 644 "$rootfs/etc/dovecot/users"
		chown root:102 "$rootfs/etc/dovecot/users"
	else
		error "Container not running. Start it first."
		return 1
	fi

	# Create mail directory
	mkdir -p "$rootfs/var/mail/${domain}/${user}"
	lxc-attach -n "$CONTAINER" -- chown -R vmail:vmail "/var/mail/${domain}"

	# Rebuild postfix maps
	lxc-attach -n "$CONTAINER" -- postmap /etc/postfix/vmailbox

	log "User $email added"
}

# ============================================================================
# Service Control
# ============================================================================

cmd_install() {
	require_root
	log "Installing Mailserver LXC..."

	defaults

	if lxc_exists; then
		log "Container already exists"
	else
		bootstrap_alpine
	fi

	create_lxc_config
	create_startup_script

	# Start for package installation
	lxc-start -n "$CONTAINER" -d
	sleep 3

	if lxc_running; then
		install_mail_packages
		lxc-stop -n "$CONTAINER"
	fi

	configure_postfix
	configure_dovecot
	generate_ssl_cert

	# Create vmail user directories
	local rootfs="$LXC_PATH/rootfs"
	mkdir -p "$rootfs/var/mail"
	mkdir -p "$rootfs/var/spool/postfix/private"

	# Create minimal interfaces file
	cat > "$rootfs/etc/network/interfaces" << 'EOF'
auto lo
iface lo inet loopback
EOF

	uci_set main.enabled '1'
	uci commit ${CONFIG}

	log "Mailserver installed!"
	log "Add users with: mailserverctl add-user user@domain.com password"
	log "Start with: mailserverctl start"
}

cmd_start() {
	require_root

	if lxc_running; then
		log "Mailserver already running"
		return 0
	fi

	if ! lxc_exists; then
		error "Mailserver not installed. Run 'mailserverctl install' first"
		return 1
	fi

	defaults
	create_lxc_config

	log "Starting Mailserver LXC..."
	lxc-start -n "$CONTAINER" -d
	sleep 5

	if lxc_running; then
		log "Mailserver started at $ip_address"
		log "IMAP: ${ip_address}:993 (SSL)"
		log "SMTP: ${ip_address}:465 (SSL)"
	else
		error "Failed to start Mailserver"
		return 1
	fi
}

cmd_stop() {
	require_root

	if ! lxc_running; then
		log "Mailserver is not running"
		return 0
	fi

	log "Stopping Mailserver..."
	lxc-stop -n "$CONTAINER"
	log "Mailserver stopped"
}

cmd_restart() {
	cmd_stop
	sleep 2
	cmd_start
}

cmd_status() {
	defaults

	echo ""
	echo "========================================"
	echo "  SecuBox Mailserver v$VERSION (LXC)"
	echo "========================================"
	echo ""

	echo "Configuration:"
	echo "  Domain:     $domain"
	echo "  Hostname:   $hostname"
	echo "  IP Address: $ip_address"
	echo ""

	echo "Container:"
	if lxc_running; then
		echo -e "  Status:     ${GREEN}Running${NC}"
		local pid=$(lxc-info -n "$CONTAINER" 2>/dev/null | grep PID | awk '{print $2}')
		echo "  PID:        $pid"

		# Test IMAP
		local imap_test=$(echo "a LOGOUT" | openssl s_client -connect ${ip_address}:993 -quiet 2>/dev/null | head -1)
		if echo "$imap_test" | grep -q "OK"; then
			echo -e "  IMAP:       ${GREEN}OK${NC}"
		else
			echo -e "  IMAP:       ${YELLOW}Not responding${NC}"
		fi
	elif lxc_exists; then
		echo -e "  Status:     ${YELLOW}Stopped${NC}"
	else
		echo -e "  Status:     ${RED}Not installed${NC}"
	fi

	echo ""
	echo "Ports:"
	echo "  SMTP:       25, 465 (SSL), 587 (submission)"
	echo "  IMAP:       143, 993 (SSL)"
	echo ""
}

cmd_shell() {
	if ! lxc_running; then
		error "Container not running"
		return 1
	fi
	lxc-attach -n "$CONTAINER" -- /bin/sh
}

# ============================================================================
# Main
# ============================================================================

show_help() {
	cat << EOF
SecuBox Mailserver Control v$VERSION (LXC)

Usage: mailserverctl <command> [options]

Commands:
  install              Install LXC mail server
  start                Start mail server
  stop                 Stop mail server
  restart              Restart mail server
  status               Show status

  add-user <email> <pass>  Add mail user
  shell                    Open shell in container

Examples:
  mailserverctl install
  mailserverctl add-user admin@example.com MyPassword123
  mailserverctl start

EOF
}

case "${1:-}" in
	install)     shift; cmd_install "$@" ;;
	start)       shift; cmd_start "$@" ;;
	stop)        shift; cmd_stop "$@" ;;
	restart)     shift; cmd_restart "$@" ;;
	status)      shift; cmd_status "$@" ;;
	add-user)    shift; cmd_add_user "$@" ;;
	shell)       shift; cmd_shell "$@" ;;
	service-run) cmd_start ;;
	service-stop) cmd_stop ;;
	help|--help|-h|'') show_help ;;
	*)           error "Unknown command: $1"; show_help >&2; exit 1 ;;
esac

exit 0
