#!/usr/bin/env bash
set -Eeuo pipefail

readonly SERVICE_USER="mailperch"
readonly CONFIG_DIR="/etc/mailperch"
readonly CONFIG_PATH="${CONFIG_DIR}/config.json"
readonly BINARY_PATH="/usr/local/bin/mailperch-connector"
readonly SERVICE_PATH="/etc/systemd/system/mailperch-connector.service"

fail() { printf 'Mailperch install failed: %s\n' "$1" >&2; exit 1; }
usage() {
    printf 'Usage: curl -fsSL https://get.mailperch.app/install | bash -s -- --token TOKEN [--api-url URL]\n'
}

claim_token=""
api_url="https://console.mailperch.app"
while [[ $# -gt 0 ]]; do
    case "$1" in
        --token)
            [[ -n ${2:-} ]] || fail "--token requires a value"
            claim_token=$2
            shift 2
            ;;
        --api-url)
            [[ -n ${2:-} ]] || fail "--api-url requires a value"
            api_url=${2%/}
            shift 2
            ;;
        --help|-h)
            usage
            exit 0
            ;;
        *) fail "unknown argument: $1" ;;
    esac
done

[[ -n ${claim_token} ]] || fail "--token is required"
[[ ${api_url} =~ ^https://[^[:space:]]+$ ]] || fail "--api-url must be an HTTPS URL"
[[ ${EUID} -eq 0 ]] || fail "run this installer as root"
[[ -r /etc/os-release ]] || fail "cannot identify the operating system"
. /etc/os-release
[[ ${ID:-} == "almalinux" && ${VERSION_ID%%.*} == "9" ]] || fail "POC supports AlmaLinux 9 only"
[[ $(uname -m) == "x86_64" ]] || fail "POC supports x86_64 servers only"
command -v systemctl >/dev/null || fail "systemd is required"
[[ -x /usr/local/cpanel/cpanel ]] || fail "cPanel/WHM was not detected"
if ! systemctl is-active --quiet cpanel-dovecot && ! systemctl is-active --quiet dovecot; then
    fail "Dovecot is not active"
fi
systemctl is-active --quiet exim || fail "Exim is not active"
command -v curl >/dev/null || fail "curl is required"
command -v sha256sum >/dev/null || fail "sha256sum is required"

if [[ -x ${BINARY_PATH} && -r ${CONFIG_PATH} ]]; then
    systemctl enable --now mailperch-connector.service
    printf 'Mailperch Connector is already installed; the existing identity was preserved.\n'
    "${BINARY_PATH}" doctor --config "${CONFIG_PATH}"
    exit 0
fi

tmp_dir=$(mktemp -d /tmp/mailperch-install.XXXXXX)
trap 'rm -rf -- "$tmp_dir"' EXIT
bootstrap_path="${tmp_dir}/bootstrap.env"
bootstrap_curl_config="${tmp_dir}/bootstrap.curl"
printf 'header = "Authorization: Bearer %s"\nheader = "Accept: text/plain"\n' "${claim_token}" >"${bootstrap_curl_config}"
chmod 0600 "${bootstrap_curl_config}"

curl --config "${bootstrap_curl_config}" \
    --fail --silent --show-error \
    --retry 2 \
    --connect-timeout 10 \
    --max-time 30 \
    --request POST \
    --output "${bootstrap_path}" \
    "${api_url}/api/v1/installations/bootstrap"

mail_domain=""
configured_api_url=""
reverb_url=""
reverb_key=""
mail_host=""
binary_url=""
binary_sha256=""
while IFS='=' read -r key value; do
    [[ -n ${key} ]] || continue
    case "${key}" in
        MAILPERCH_DOMAIN) mail_domain=${value} ;;
        MAILPERCH_API_URL) configured_api_url=${value} ;;
        MAILPERCH_REVERB_URL) reverb_url=${value} ;;
        MAILPERCH_REVERB_KEY) reverb_key=${value} ;;
        MAILPERCH_MAIL_HOST) mail_host=${value} ;;
        MAILPERCH_BINARY_URL) binary_url=${value} ;;
        MAILPERCH_BINARY_SHA256) binary_sha256=${value} ;;
        *) fail "bootstrap response contained an unknown setting" ;;
    esac
done <"${bootstrap_path}"

host_pattern='^[A-Za-z0-9]([A-Za-z0-9.-]*[A-Za-z0-9])?$'
[[ ${mail_domain} =~ ${host_pattern} ]] || fail "bootstrap returned an invalid email domain"
[[ ${mail_host} =~ ${host_pattern} ]] || fail "bootstrap returned an invalid mail hostname"
[[ ${configured_api_url} =~ ^https://[^[:space:]]+$ ]] || fail "bootstrap returned an invalid API URL"
[[ ${reverb_url} =~ ^wss://[^[:space:]]+$ ]] || fail "bootstrap returned an invalid Reverb URL"
[[ ${reverb_key} =~ ^[A-Za-z0-9_-]{1,255}$ ]] || fail "bootstrap returned an invalid Reverb key"
[[ ${binary_url} =~ ^https://[^[:space:]]+$ ]] || fail "bootstrap returned an invalid binary URL"
[[ ${binary_sha256} =~ ^[a-f0-9]{64}$ ]] || fail "bootstrap returned an invalid binary checksum"

curl --fail --silent --show-error --location \
    --retry 2 \
    --connect-timeout 10 \
    --max-time 120 \
    --output "${tmp_dir}/mailperch-connector" \
    "${binary_url}"
printf '%s  %s\n' "${binary_sha256}" "${tmp_dir}/mailperch-connector" \
    | sha256sum --check --status \
    || fail "binary checksum mismatch"
chmod 0755 "${tmp_dir}/mailperch-connector"
"${tmp_dir}/mailperch-connector" version >/dev/null || fail "connector binary is not executable on this server"

if ! id "${SERVICE_USER}" >/dev/null 2>&1; then
    useradd --system --home-dir /var/lib/mailperch --create-home --shell /sbin/nologin "${SERVICE_USER}"
fi
install -m 0755 "${tmp_dir}/mailperch-connector" "${BINARY_PATH}"
install -d -m 0700 -o "${SERVICE_USER}" -g "${SERVICE_USER}" "${CONFIG_DIR}"

printf '%s\n' "${claim_token}" | "${BINARY_PATH}" register \
    --config "${CONFIG_PATH}" \
    --domain "${mail_domain}" \
    --api-url "${configured_api_url}" \
    --reverb-url "${reverb_url}" \
    --reverb-key "${reverb_key}" \
    --imap-address "${mail_host}:993" \
    --smtp-address "${mail_host}:465" \
    --tls-server-name "${mail_host}"
unset claim_token
chown "${SERVICE_USER}:${SERVICE_USER}" "${CONFIG_PATH}"
chmod 0600 "${CONFIG_PATH}"

install -m 0644 /dev/stdin "${SERVICE_PATH}" <<'UNIT'
[Unit]
Description=Mailperch Connector
After=network-online.target dovecot.service exim.service
Wants=network-online.target

[Service]
Type=simple
User=mailperch
Group=mailperch
ExecStart=/usr/local/bin/mailperch-connector run --config /etc/mailperch/config.json
Restart=always
RestartSec=5s
NoNewPrivileges=true
PrivateTmp=true
PrivateDevices=true
ProtectSystem=strict
ProtectHome=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
LockPersonality=true
MemoryDenyWriteExecute=true
RestrictSUIDSGID=true
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6

[Install]
WantedBy=multi-user.target
UNIT

systemctl daemon-reload
systemctl enable --now mailperch-connector.service
systemctl is-active --quiet mailperch-connector.service || fail "connector service did not start"
"${BINARY_PATH}" doctor --config "${CONFIG_PATH}"
printf 'Mailperch Connector installed successfully. cPanel, Exim and Dovecot were not modified.\n'
