#!/bin/bash
# Pre-uninstallation script for WarpSend.

set -euo pipefail

HOME_DIR="${WARPSEND_SPK_HOME_DIR:-/var/packages/WarpSend/home}"
PACKAGE_DIR="${WARPSEND_SPK_PACKAGE_DIR:-/var/packages/WarpSend/target}"
NGINX_CONF="${WARPSEND_SPK_NGINX_CONF:-/usr/local/etc/nginx/conf.d/dsm.WarpSend.conf}"
CONFIG_DIR="${HOME_DIR}/.config/warpsend"
CONFIG_FILE="${CONFIG_DIR}/config.toml"
CLI_BINARY="${PACKAGE_DIR}/bin/warpsend"
PID_FILE="${HOME_DIR}/warpsend.pid"
LOCK_FILE="${HOME_DIR}/supervisor.lock"
LOG_FILE="${HOME_DIR}/warpsend.log"
SETUP_TOKEN_FILE="${HOME_DIR}/setup-token"
BIN_DIR="${HOME_DIR}/bin"

# Best-effort server-side endpoint unpair + token revocation. Delegate to the
# bundled CLI instead of reimplementing config parsing and HTTP with DSM curl.
# On DSM 7.3 the system curl uses c-ares and can time out resolving the same
# cloud hostname the Rust agent/CLI resolves successfully; the old script hid
# that failure and left a permanent offline endpoint in the dashboard.
revoke_cloud_endpoint() {
  [ -f "$CONFIG_FILE" ] || return 0
  [ -r "$CONFIG_FILE" ] || return 0
  [ -x "$CLI_BINARY" ] || {
    echo "WarpSend preuninst: bundled CLI missing; cloud unpair skipped" >&2
    return 0
  }

  # `warpsend unpair` reads this exact package-home config, posts the
  # endpoint-token-authenticated /api/endpoints/unpair request, and clears the
  # binding keys. Keep it non-fatal so an offline NAS can still uninstall; its
  # warning remains visible in the package lifecycle log instead of disappearing.
  if ! HOME="$HOME_DIR" WARPSEND_CONFIG_DIR="$HOME_DIR" "$CLI_BINARY" unpair; then
    echo "WarpSend preuninst: bundled CLI unpair failed; continuing local removal" >&2
  fi
}

# Wipe local pairing credentials so a subsequent reinstall enters first-pair
# mode and prompts the user instead of silently reusing the now-revoked
# token. DSM keeps /var/packages/<pkg>/home/ across uninstall/reinstall by
# design (it's where user data lives), so without this the next install
# starts up with the old endpoint_id + endpoint_token in config.toml.
wipe_local_credentials() {
  # Erase the whole config dir — config.toml (endpoint_id, endpoint_token,
  # org_id, owner_user_id, EPK pinned key) and sessions.json (browser cookies
  # the agent web UI persisted). Machine identity (ed25519/x25519) lives in
  # the same file but it's intentional that uninstall regenerates these too:
  # the package is being removed, the cloud-side endpoint just got unpaired,
  # there's no scenario where the next install wants to inherit the old keys.
  rm -rf "$CONFIG_DIR" 2>/dev/null || true

  # Managed runtime artifacts live beside .config because the SPK sets
  # WARPSEND_CONFIG_DIR=/var/packages/WarpSend/home. Fresh uninstall must be
  # truly clean; upgrades intentionally skip this function.
  rm -rf "$BIN_DIR" 2>/dev/null || true
  rm -f "$PID_FILE" "$LOCK_FILE" "$LOG_FILE" "$SETUP_TOKEN_FILE" 2>/dev/null || true
}

# Best-effort removal of the persistent "WarpSend-udp-buffer" boot task
# (created via the dashboard's admin-authorized speed optimization). DSM 7
# runs lifecycle scripts as the package user, so this normally lacks the
# root that `synowebapi --exec` needs and quietly no-ops — the task then
# stays behind, which is harmless (it only raises two kernel ceilings at
# boot) and the optimization dialog tells the admin it is visible and
# removable in DSM Task Scheduler at any time. Documented in SECURITY.md.
remove_udp_boot_task() {
  [ "$(id -u)" = "0" ] || return 0
  [ -x /usr/syno/bin/synowebapi ] || return 0
  /usr/syno/bin/synowebapi --exec api=SYNO.Core.EventScheduler method=delete \
    version=1 task_name="WarpSend-udp-buffer" >/dev/null 2>&1 || true
}

stop_running_supervisor() {
  local pid=""
  # Prefer the supervisor lock, but ONLY if it names a live process. A stale
  # lock (crashed supervisor, recycled PID) must not shadow the live PID in
  # warpsend.pid: the old code committed to whatever the lock held, so a dead
  # lock PID skipped the kill block below (its kill -0 fails) and left the real
  # daemon running. Fall through to warpsend.pid when the lock is stale.
  if [ -s "$LOCK_FILE" ]; then
    local lock_pid
    lock_pid=$(cat "$LOCK_FILE" 2>/dev/null || true)
    if [ -n "$lock_pid" ] && kill -0 "$lock_pid" 2>/dev/null; then
      pid="$lock_pid"
    fi
  fi
  if [ -z "$pid" ] && [ -s "$PID_FILE" ]; then
    local file_pid
    file_pid=$(cat "$PID_FILE" 2>/dev/null || true)
    if [ -n "$file_pid" ] && kill -0 "$file_pid" 2>/dev/null; then
      pid="$file_pid"
    fi
  fi

  if [ -n "$pid" ] && kill -0 "$pid" 2>/dev/null; then
    kill "$pid" 2>/dev/null || true
    for _ in $(seq 1 10); do
      kill -0 "$pid" 2>/dev/null || break
      sleep 1
    done
    if kill -0 "$pid" 2>/dev/null; then
      kill -9 "$pid" 2>/dev/null || true
    fi
  fi
}

# DSM 7 (at least 7.3 / x86_64 v1000 build 86009) does NOT pass argv to
# preuninst — `$1` is empty. The action comes via $SYNOPKG_PKG_STATUS env
# var ("UNINSTALL"/"UPGRADE", uppercase). Without this fallback both arms
# below silently no-op: cloud-side unpair never happens (token stays valid
# for ~30 days, defeats the security posture documented in revoke_cloud_endpoint
# above) and wipe_local_credentials never runs (DSM's @apphome persists
# across uninstall, so the next install resurrects the previous pairing).
ACTION="${1:-${SYNOPKG_PKG_STATUS:-}}"
ACTION="$(printf '%s' "$ACTION" | tr '[:upper:]' '[:lower:]')"

case "$ACTION" in
  uninstall)
    # Notify cloud BEFORE we kill the daemon — the daemon's own process
    # is what holds the outgoing signaling WebSocket; once we kill it the
    # cloud marks the endpoint offline either way, but calling the unpair
    # while the daemon is still up matches the "authenticated user asked
    # to log out" audit signature rather than a stale-reconnect event.
    revoke_cloud_endpoint

    stop_running_supervisor

    # Always wipe local creds and managed slots — even if cloud unpair failed
    # (network down), the next install should be a fresh pair and a fresh
    # manifest download rather than inheriting stale runtime state.
    wipe_local_credentials

    remove_udp_boot_task

    # Remove legacy nginx reverse proxy config if this script happens to run
    # with enough privileges. Current DSM 7 package flow uses the direct
    # package UI launcher and does not install this config anymore.
    if [ -f "${NGINX_CONF}" ]; then
      rm -f "${NGINX_CONF}" 2>/dev/null || true
      nginx -s reload 2>/dev/null || true
    fi
    ;;
  upgrade)
    # Upgrading: handled by start-stop-status; never revoke the cloud
    # token here (would log the user out of their own cloud account
    # mid-upgrade) and never wipe local credentials (would force a
    # re-pair on every upgrade).
    ;;
  *)
    # DSM 7 always sets SYNOPKG_PKG_STATUS (UNINSTALL/UPGRADE); reaching here
    # means neither argv nor the env var told us the operation. Do NOT guess
    # "uninstall" — wiping a live pairing during an unknown phase is worse than
    # a no-op. Log it so a silent skip (cloud token left valid, creds left on
    # disk) is at least diagnosable in the DSM package log.
    echo "WarpSend preuninst: unrecognized action '${ACTION}' (SYNOPKG_PKG_STATUS='${SYNOPKG_PKG_STATUS:-}') — skipping cleanup" >&2
    ;;
esac

exit 0
