#!/bin/sh
# What the .deb and the .rpm do on install and removal, in one place (#298).
#
# Installed as /usr/libexec/promptguard-shadow/package-hooks and run by each
# format's own scripts, which only translate their arguments: dpkg says
# `configure` / `remove`, rpm says how many copies will remain. Both call
# `configure` after every install or upgrade, and `remove` only on a real
# removal, never on an upgrade.
#
# Nothing here may fail the package operation. A machine where a step fails
# must still be able to install or remove the package; each failure leaves
# steering off (the service refuses to steer an agent it can't verify), which
# fails open to the PAC and tool routes, and says so.

GROUP=promptguard-shadow
AGENT=/usr/bin/pgshadow
UNIT=promptguard-shadow-steering.service
TABLE=promptguard_shadow

systemd_running() { [ -d /run/systemd/system ]; }

configure() {
  # The agent's own connections are exempt from steering by this group
  # (SECURITY.md, "Linux steering service"). It must have no members.
  if ! getent group "$GROUP" >/dev/null 2>&1; then
    groupadd --system "$GROUP" || echo "PromptGuard: could not create group $GROUP; Network capture stays off." >&2
  fi
  # A group someone added members to would hand them the exemption: take it
  # off the agent, including what an earlier install set (the service, which
  # is restarted below, then refuses to steer too).
  if [ -n "$(getent group "$GROUP" | cut -d: -f4)" ]; then
    echo "PromptGuard: group $GROUP has members; Network capture stays off until it has none." >&2
    chgrp root "$AGENT" && chmod 0755 "$AGENT" || echo "PromptGuard: could not take $GROUP off $AGENT." >&2
  # chgrp clears the setgid bit, so the mode comes second.
  elif chgrp "$GROUP" "$AGENT" && chmod 2755 "$AGENT"; then :; else
    echo "PromptGuard: could not make $AGENT setgid $GROUP; Network capture stays off." >&2
  fi
  if systemd_running; then
    systemctl daemon-reload || true
    systemctl enable "$UNIT" >/dev/null 2>&1 || true
    # A restart, not a start: the service verifies the agent binary once at
    # start, and an upgrade has just replaced it.
    systemctl restart "$UNIT" || echo "PromptGuard: the steering service did not start; see 'journalctl -u $UNIT'." >&2
  fi
}

# Undo, as each user, what the app set up for them: their proxy settings, the
# CA in their browsers' store, their agent and its state. A logged-in user's
# own session bus carries the GNOME settings change; for anyone else a private
# bus does (dconf writes their settings file either way).
remove_for_users() {
  getent passwd | while IFS=: read -r name _ uid _ _ home _; do
    [ "$uid" -ge 1000 ] 2>/dev/null || continue
    [ -d "$home/.promptguard" ] || [ -d "$home/.mitmproxy" ] || continue
    echo "PromptGuard: reversing interception for $name..."
    bus="/run/user/$uid/bus"
    if [ -S "$bus" ]; then
      runuser -u "$name" -- env HOME="$home" XDG_RUNTIME_DIR="/run/user/$uid" \
        DBUS_SESSION_BUS_ADDRESS="unix:path=$bus" "$AGENT" uninstall
    elif command -v dbus-run-session >/dev/null 2>&1; then
      runuser -u "$name" -- env HOME="$home" dbus-run-session -- "$AGENT" uninstall
    else
      runuser -u "$name" -- env HOME="$home" "$AGENT" uninstall
    fi || echo "PromptGuard: teardown for $name reported a problem; see above for what remains."
  done
}

remove() {
  if systemd_running; then
    # Stopping runs the unit's ExecStopPost, which deletes the table.
    systemctl disable --now "$UNIT" >/dev/null 2>&1 || true
  fi
  # And in case the service never ran under systemd at all.
  nft delete table inet "$TABLE" >/dev/null 2>&1 || true
  if [ -x "$AGENT" ]; then
    remove_for_users
    # Root's own state, and the system trust store if the CA was put there.
    "$AGENT" uninstall || echo "PromptGuard: teardown reported a problem; see above for what remains."
  else
    echo "PromptGuard: could not find $AGENT, so interception was NOT reversed; run 'pgshadow uninstall' as each user." >&2
  fi
  # The group stays, as Debian's policy has it for system groups: files the
  # agent wrote carry its GID, and a deleted group's GID could be handed to a
  # new group with members.
}

case "${1:-}" in
  configure) configure ;;
  remove) remove ;;
  *) echo "usage: package-hooks configure|remove" >&2 ;;
esac
exit 0
