#!/usr/bin/env bash
# Sysop-only updater. Public release key is pinned in this script at build time.
set -Eeuo pipefail
DEST=$(cd -- "$(dirname -- "$0")" && pwd -P)
PLATFORM=__PLATFORM__
CURRENT=$(cat "$DEST/release.version" 2>/dev/null || printf '1.6.0')
BASE=https://scores.a-net.online/updates
for tool in curl openssl sha256sum; do command -v "$tool" >/dev/null || { echo "Missing update tool: $tool" >&2;exit 1; };done
TMP=$(mktemp -d "${TMPDIR:-/tmp}/pumpko-update.XXXXXXXX")
GATE=0
cleanup(){ rm -rf -- "$TMP";if ((GATE));then rm -f -- "$DEST/.pumpko-maintenance";fi; }
trap cleanup EXIT
cat > "$TMP/public.pem" <<'KEY'
__PUBLIC_KEY__
KEY
fetch(){ curl --fail --silent --show-error --proto '=https' --tlsv1.2 --connect-timeout 8 --max-time 120 --max-filesize "$3" --output "$2" "$1"; }
echo "PUMPKO $CURRENT - checking $PLATFORM updates..."
fetch "$BASE/pumpko/$PLATFORM.txt" "$TMP/release.txt" 8192
fetch "$BASE/pumpko/$PLATFORM.sig" "$TMP/release.sig" 512
openssl dgst -sha256 -verify "$TMP/public.pem" -signature "$TMP/release.sig" "$TMP/release.txt" >/dev/null || { echo 'Release signature rejected. Nothing installed.' >&2;exit 1; }
# Strict flat manifest; never source remote text or interpolate it into commands.
declare -A F
while IFS='=' read -r key value; do
 [[ "$key" =~ ^(game|platform|version|url|size|sha256|notes)$ && ! -v F[$key] ]] || { echo 'Invalid manifest.' >&2;exit 1; }
 F[$key]=$value
done < "$TMP/release.txt"
[[ ${#F[@]} == 7 && ${F[game]} == pumpko && ${F[platform]} == "$PLATFORM" && ${F[version]} =~ ^[0-9]+\.[0-9]+\.[0-9]+$ && ${F[size]} =~ ^[0-9]+$ && ${F[sha256]} =~ ^[0-9a-f]{64}$ && ${F[url]} == "$BASE/packages/"* ]] || { echo 'Invalid release fields.' >&2;exit 1; }
[[ ${F[url]} =~ ^https://scores\.a-net\.online/updates/packages/[A-Za-z0-9._-]+$ && ${F[size]} -gt 0 && ${F[size]} -le 104857600 ]] || exit 1
newer(){ local a b c x y z;IFS=. read -r a b c <<< "$1";IFS=. read -r x y z <<< "$2";((10#$a>10#$x || (10#$a==10#$x && (10#$b>10#$y || (10#$b==10#$y && 10#$c>10#$z))))); }
if ! newer "${F[version]}" "$CURRENT";then echo "Up to date ($CURRENT).";exit 0;fi
echo "Available: ${F[version]}";printf '%s\n' "${F[notes]}"
[[ ${1:-} != --check ]] || exit 0
read -r -p 'Download and install this update? Y/N [N]: ' answer
[[ "$answer" == Y || "$answer" == y ]] || { echo 'Cancelled.';exit 0; }
fetch "${F[url]}" "$TMP/update.sh" "${F[size]}"
[[ $(wc -c < "$TMP/update.sh") == "${F[size]}" ]] || { echo 'Download size mismatch.' >&2;exit 1; }
printf '%s  %s\n' "${F[sha256]}" "$TMP/update.sh" | sha256sum --check --status || { echo 'Download hash mismatch.' >&2;exit 1; }
# Atomic ownership of the maintenance marker. Check after gating new sessions.
(set -o noclobber;printf 'Sysop update in progress\n' > "$DEST/.pumpko-maintenance") 2>/dev/null || { echo 'Another update or maintenance operation is active.' >&2;exit 1; }
GATE=1
for p in /proc/[0-9]*/comm;do
 name=$(cat "$p" 2>/dev/null || :)
 if [[ "$name" == PUMPKO || "$name" == pumpko ]];then echo 'A PUMPKO session is active. Nothing replaced; retry when callers finish.';exit 1;fi
done
bash "$TMP/update.sh" --update "$DEST"
echo "Update to ${F[version]} complete."
