#!/bin/bash
# ============================================================
# alpha-mailrelay — configure Postfix to send via an authenticated
# smarthost on port 587.
#
# This box is a Namecheap KVM and Namecheap intercepts outbound port 25:
# every :25 connection, to any host (even 1.1.1.1, which runs no mail server),
# is answered with a spoofed "550 5.7.1 ... (JFE050005)". Receiving MX servers
# only listen on 25, so direct delivery cannot work here — a relay is mandatory.
#
# The panel (php-fpm, www-data) calls this via sudo. The credential is handed
# over in a 0600 drop file rather than argv, so it never appears in `ps`.
#
#   set <[host]:port>   read /var/www/alpha-private/.relay-cred, install, reload
#   host <[host]:port>  point at a different smarthost, keep the stored credential
#   from <address>      rewrite the envelope+header sender to this address
#   off                 back to direct delivery
#   test                TCP + SMTP AUTH check against the configured relay
#   sendtest <addr>     queue a real message and report what the log says
# ============================================================
set -u

CRED_DROP=/var/www/alpha-private/.relay-cred
SASL_MAP=/etc/postfix/sasl_passwd
# The owner's domain and this box's mail hostname, never literals: on a customer's
# server the test mail, the EHLO and the sender rewrite must all be THEIRS.
[ -r /etc/alpha-panel.conf ] && . /etc/alpha-panel.conf
DOMAIN="${PRIMARY_DOMAIN:-$(hostname -d 2>/dev/null || true)}"; DOMAIN="${DOMAIN:-localhost}"
MYHOST="$(postconf -h myhostname 2>/dev/null || hostname -f)"

die() { echo "ERROR: $*"; exit 1; }

apply_common() {
  postconf -e "smtp_sasl_auth_enable = yes"
  postconf -e "smtp_sasl_password_maps = hash:${SASL_MAP}"
  postconf -e "smtp_sasl_security_options = noanonymous"
  postconf -e "smtp_tls_security_level = encrypt"
  postconf -e "smtp_use_tls = yes"
  # Gmail and several others reject the bare 8-bit our templates can emit
  postconf -e "smtp_generic_maps = hash:/etc/postfix/generic"
}

case "${1:-}" in

  set)
    TARGET="${2:-}"; [ -n "$TARGET" ] || die "usage: set [host]:port"
    [ -s "$CRED_DROP" ] || die "no credential drop file"
    install -m 600 -o root -g root "$CRED_DROP" "$SASL_MAP" || die "could not install credential map"
    shred -u "$CRED_DROP" 2>/dev/null || rm -f "$CRED_DROP"
    postmap "$SASL_MAP" || die "postmap failed"
    chmod 600 "${SASL_MAP}.db"
    postconf -e "relayhost = ${TARGET}"
    apply_common
    postfix check 2>/dev/null
    systemctl reload postfix || systemctl restart postfix
    echo "OK relay=${TARGET}"
    ;;

  host)
    TARGET="${2:-}"; [ -n "$TARGET" ] || die "usage: host [host]:port"
    [ -s "$SASL_MAP" ] || die "no stored credential — save the password once first"
    # the map is keyed by the relay target: re-key the stored credential, or Postfix finds no password for the new
    # host and connects without authenticating
    awk -v t="$TARGET" 'NR==1 { $1 = t } { print }' "$SASL_MAP" > "${SASL_MAP}.tmp" && install -m 600 -o root -g root "${SASL_MAP}.tmp" "$SASL_MAP" && rm -f "${SASL_MAP}.tmp"
    postmap "$SASL_MAP" || die "postmap failed"
    chmod 600 "${SASL_MAP}.db"
    postconf -e "relayhost = ${TARGET}"
    apply_common
    systemctl reload postfix || systemctl restart postfix
    echo "OK relay=${TARGET}"
    ;;

  from)
    ADDR="${2:-}"; [ -n "$ADDR" ] || die "usage: from <address>"
    # many relays only allow a verified sender; rewrite everything to it
    grep -qF "@${MYHOST} " /etc/postfix/generic 2>/dev/null || \
      echo "@${MYHOST} ${ADDR}" >> /etc/postfix/generic
    postmap /etc/postfix/generic
    systemctl reload postfix
    echo "OK from=${ADDR}"
    ;;

  off)
    postconf -e "relayhost ="
    postconf -e "smtp_sasl_auth_enable = no"
    # `on` demands TLS (right for a relay on 587). Left in place, direct MX delivery defers
    # forever with "TLS is required, but was not offered" wherever STARTTLS is missing on 25.
    postconf -e "smtp_tls_security_level = may"
    systemctl reload postfix || systemctl restart postfix
    echo "OK direct"
    ;;

  test)
    RELAY=$(postconf -h relayhost | tr -d '[]')
    [ -n "$RELAY" ] || die "no relayhost configured"
    HOST="${RELAY%%:*}"; PORT="${RELAY##*:}"; [ "$PORT" = "$HOST" ] && PORT=587
    CRED=$(head -1 "$SASL_MAP" 2>/dev/null | awk '{print $2}')
    [ -n "$CRED" ] || die "no stored credential"
    USER="${CRED%%:*}"; PASS="${CRED#*:}"
    AUTH=$(printf '\0%s\0%s' "$USER" "$PASS" | base64 -w0)
    OUT=$(timeout 25 openssl s_client -quiet -starttls smtp -crlf \
            -connect "${HOST}:${PORT}" 2>/dev/null <<EOF
EHLO ${MYHOST}
AUTH PLAIN ${AUTH}
QUIT
EOF
)
    if echo "$OUT" | grep -q "^235"; then
      echo "AUTH-OK $(echo "$OUT" | grep -m1 '^235' | cut -c1-60)"
    else
      echo "$OUT" | grep -E "^(4|5)[0-9][0-9]" | head -2 || echo "no SMTP response from ${HOST}:${PORT}"
    fi
    ;;

  sendtest)
    ADDR="${2:-}"; [ -n "$ADDR" ] || die "usage: sendtest <address>"
    TAG="alpharelay-$(date +%s)"
    printf 'Subject: Relay test\nFrom: %s <no-reply@%s>\nTo: %s\nX-Alpha-Tag: %s\n\nIf you are reading this, outbound mail works.\n' \
      "$DOMAIN" "$DOMAIN" "$ADDR" "$TAG" | /usr/sbin/sendmail -f "no-reply@${DOMAIN}" -t
    # give the queue a moment, then report what actually happened
    sleep 6
    # only the queue folders, regular files only: /var/spool/postfix/*/* also matched the chroot's dev/urandom (an
    # endless stream) and the private/ + public/ FIFOs — grep never returned, and every panel "Send test" left a stuck
    # php-fpm worker behind it
    QID=$(find /var/spool/postfix/{incoming,active,deferred,hold,maildrop} -type f 2>/dev/null | xargs -r timeout 10 grep -l "$TAG" 2>/dev/null | head -1 | xargs -r basename)
    if [ -z "$QID" ]; then
      LINE=$(grep "to=<${ADDR}>" /var/log/mail.log | tail -1)
    else
      LINE=$(grep "$QID" /var/log/mail.log | tail -1)
    fi
    echo "${LINE:-queued; no log line yet}" | sed 's/^[^ ]* server1 //' | cut -c1-300
    ;;

  *)
    die "unknown verb: ${1:-}"
    ;;
esac
