#!/bin/bash
# vphone_jb_setup.sh — First-boot JB finalization script.
#
# Deployed to /cores/ during cfw_install_jb.sh.
# Runs automatically via LaunchDaemon on first normal boot.
# Idempotent — safe to re-run on subsequent boots.
#
# Logs to /var/log/vphone_jb_setup.log for host-side monitoring
# via vphoned file browser.

set -uo pipefail

LOG="/var/log/vphone_jb_setup.log"
DONE_MARKER="/var/mobile/.vphone_jb_setup_done"

export TERM=xterm-256color
export DEBIAN_FRONTEND=noninteractive
P=""
for d in \
    /var/jb/usr/bin /var/jb/bin /var/jb/sbin /var/jb/usr/sbin \
    /iosbinpack64/bin /iosbinpack64/usr/bin /iosbinpack64/sbin /iosbinpack64/usr/sbin \
    /usr/bin /usr/sbin /bin /sbin; do
    [ -d "$d" ] && P="$P:$d"
done
export PATH="${P#:}"

# Redirect all output (stdout+stderr) through tee to the log + console.
exec > >(tee -a "$LOG") 2>&1

# NOTE: log() does NOT pipe through tee — stdout is already tee'd to $LOG by the
# exec above, so a second tee here would double every timestamped line.
log() { echo "[$(date '+%H:%M:%S')] $*"; }
die() { log "FATAL: $*"; exit 1; }

log "=== vphone_jb_setup.sh starting ==="
log "PATH=$PATH"

# ── Check done marker ────────────────────────────────────────
if [ -f "$DONE_MARKER" ]; then
    log "Already completed (marker exists), exiting."
    exit 0
fi

# ── Find boot manifest hash ─────────────────────────────────
BOOT_HASH=""
for d in /private/preboot/*/; do
    b="${d%/}"; b="${b##*/}"
    if [ "${#b}" = 96 ]; then
        BOOT_HASH="$b"
        break
    fi
done
[ -n "$BOOT_HASH" ] || die "Could not find 96-char boot manifest hash"
log "Boot hash: $BOOT_HASH"

JB_TARGET="/private/preboot/$BOOT_HASH/jb-vphone/procursus"
[ -d "$JB_TARGET" ] || die "Procursus not found at $JB_TARGET"

# ═══════════ 0/7 REPLACE LAUNCHCTL ═════════════════════════════
# Procursus launchctl crashes (missing _launch_active_user_switch symbol).
# iosbinpack64's launchctl talks to launchd fine and always exits 0,
# which is enough for dpkg postinst/prerm script compatibility.
log "[0/8] Linking iosbinpack64 launchctl into procursus..."
IOSBINPACK_LAUNCHCTL=""
for p in /iosbinpack64/bin/launchctl /iosbinpack64/usr/bin/launchctl; do
    [ -f "$p" ] && IOSBINPACK_LAUNCHCTL="$p" && break
done

if [ -n "$IOSBINPACK_LAUNCHCTL" ]; then
    if [ -f "$JB_TARGET/usr/bin/launchctl" ] && [ ! -L "$JB_TARGET/usr/bin/launchctl" ] && [ ! -f "$JB_TARGET/usr/bin/launchctl.procursus" ]; then
        mv "$JB_TARGET/usr/bin/launchctl" "$JB_TARGET/usr/bin/launchctl.procursus"
        log "  procursus original saved as launchctl.procursus"
    fi
    ln -sf "$IOSBINPACK_LAUNCHCTL" "$JB_TARGET/usr/bin/launchctl"
    mkdir -p "$JB_TARGET/bin"
    ln -sf "$IOSBINPACK_LAUNCHCTL" "$JB_TARGET/bin/launchctl"
    log "  linked usr/bin/launchctl + bin/launchctl -> $IOSBINPACK_LAUNCHCTL"
else
    log "  WARNING: iosbinpack64 launchctl not found"
fi

# ═══════════ 1/7 SYMLINK /var/jb ═════════════════════════════
log "[1/8] Creating /private/var/jb symlink..."
CURRENT_LINK=$(readlink /private/var/jb 2>/dev/null || true)
if [ "$CURRENT_LINK" = "$JB_TARGET" ]; then
    log "  Symlink already correct"
else
    ln -sf "$JB_TARGET" /private/var/jb
    log "  /var/jb -> $JB_TARGET"
fi

# ═══════════ 2/7 FIX OWNERSHIP / PERMISSIONS ═════════════════
log "[2/8] Fixing mobile Library ownership..."
mkdir -p /var/jb/var/mobile/Library/Preferences
mkdir -p /var/jb/Library/MobileSubstrate/DynamicLibraries
chown -R 501:501 /var/jb/var/mobile/Library
chmod 0755 /var/jb/var/mobile/Library
chown -R 501:501 /var/jb/var/mobile/Library/Preferences
chmod 0755 /var/jb/var/mobile/Library/Preferences
chown 0:0 /var/jb/Library /var/jb/Library/MobileSubstrate /var/jb/Library/MobileSubstrate/DynamicLibraries
chmod 0755 /var/jb/Library /var/jb/Library/MobileSubstrate /var/jb/Library/MobileSubstrate/DynamicLibraries
log "  Ownership set"

log "[2a/8] Preparing dropbear host keys..."
mkdir -p /var/dropbear
DROPBEARKEY=""
for p in /iosbinpack64/usr/local/bin/dropbearkey /iosbinpack64/usr/local/dropbearkey /var/jb/usr/bin/dropbearkey; do
    [ -x "$p" ] && DROPBEARKEY="$p" && break
done
if [ -n "$DROPBEARKEY" ]; then
    [ -f /var/dropbear/dropbear_rsa_host_key ] || "$DROPBEARKEY" -t rsa -f /var/dropbear/dropbear_rsa_host_key >/dev/null
    [ -f /var/dropbear/dropbear_ecdsa_host_key ] || "$DROPBEARKEY" -t ecdsa -f /var/dropbear/dropbear_ecdsa_host_key >/dev/null
    if [ -f /var/dropbear/dropbear_rsa_host_key ] && [ -f /var/dropbear/dropbear_ecdsa_host_key ]; then
        if chmod 0600 /var/dropbear/dropbear_rsa_host_key /var/dropbear/dropbear_ecdsa_host_key; then
            log "  dropbear host keys ready"
        else
            log "  WARNING: dropbear host key chmod failed"
        fi
    else
        log "  WARNING: dropbear host key generation incomplete"
    fi
else
    log "  WARNING: dropbearkey not found"
fi

# ═══════════ 3/7 RUN prep_bootstrap.sh ═══════════════════════
log "[3/8] Running prep_bootstrap.sh..."
if [ -f /var/jb/prep_bootstrap.sh ]; then
    NO_PASSWORD_PROMPT=1 /var/jb/prep_bootstrap.sh || log "  prep_bootstrap.sh exited with $?"
    log "  prep_bootstrap.sh completed"
else
    log "  prep_bootstrap.sh already ran (deleted itself), skipping"
fi

# Re-discover PATH after prep_bootstrap
P=""
for d in \
    /var/jb/usr/bin /var/jb/bin /var/jb/sbin /var/jb/usr/sbin \
    /iosbinpack64/bin /iosbinpack64/usr/bin /iosbinpack64/sbin /iosbinpack64/usr/sbin \
    /usr/bin /usr/sbin /bin /sbin; do
    [ -d "$d" ] && P="$P:$d"
done
export PATH="${P#:}"
log "  PATH=$PATH"

# ═══════════ 4/7 CREATE MARKER FILES ═════════════════════════
log "[4/8] Creating marker files..."
for marker in .procursus_strapped .installed_dopamine; do
    if [ -f "/var/jb/$marker" ]; then
        log "  $marker already exists"
    else
        : > "/var/jb/$marker"
        chown 0:0 "/var/jb/$marker"
        chmod 0644 "/var/jb/$marker"
        log "  $marker created"
    fi
done

# ═══════════ 5/7 INSTALL SILEO ═══════════════════════════════
log "[5/8] Installing Sileo..."
SILEO_DEB_PATH="/private/preboot/$BOOT_HASH/org.coolstar.sileo_2.5.1_iphoneos-arm64.deb"

if dpkg -s org.coolstar.sileo >/dev/null 2>&1; then
    log "  Sileo already installed"
else
    if [ -f "$SILEO_DEB_PATH" ]; then
        dpkg -i "$SILEO_DEB_PATH" || log "  dpkg -i sileo exited with $?"
        log "  Sileo installed"
    else
        log "  WARNING: Sileo deb not found at $SILEO_DEB_PATH"
    fi
fi

# ═══════════ 5b/8 INSTALL EXTRA DEBS ═════════════════════════
log "[5b/8] Installing extra debs..."
DEBS_DIR="/private/preboot/$BOOT_HASH/debs"
if [ -d "$DEBS_DIR" ]; then
    to_install=()
    for deb in "$DEBS_DIR"/*.deb; do
        [ -f "$deb" ] || continue
        name="$(basename "$deb")"
        pkg="$(dpkg-deb -f "$deb" Package 2>/dev/null)"
        ver="$(dpkg-deb -f "$deb" Version 2>/dev/null)"
        if [ -z "$pkg" ]; then
            log "  WARNING: cannot read Package field from $name, will install anyway"
            to_install+=("$deb")
            continue
        fi
        cur="$(dpkg-query -W -f='${Version}' "$pkg" 2>/dev/null)"
        if [ -n "$cur" ] && dpkg --compare-versions "$cur" ge "$ver" 2>/dev/null; then
            log "  $pkg $cur already installed (>= $ver), skipping"
        else
            to_install+=("$deb")
        fi
    done
    if [ "${#to_install[@]}" -gt 0 ]; then
        names="$(for d in "${to_install[@]}"; do basename "$d"; done | tr '\n' ' ')"
        log "  Installing ${#to_install[@]} deb(s): $names"
        if dpkg -i "${to_install[@]}"; then
            log "  Extra debs installed"
        else
            rc=$?
            log "  WARNING: dpkg -i exited with $rc (unmet external deps are non-fatal)"
        fi
    else
        log "  All extra debs already installed, nothing to do"
    fi
else
    log "  No extra debs staged"
fi

uicache -a 2>/dev/null || true
log "  uicache refreshed"

# ═══════════ 6/7 APT SETUP ══════════════════════════════════
log "[6/8] Running apt setup..."

# Determine apt sources directory
HAVOC_LIST="/var/jb/etc/apt/sources.list.d/havoc.list"
if [ -d /etc/apt/sources.list.d ] && [ ! -d /var/jb/etc/apt/sources.list.d ]; then
    HAVOC_LIST="/etc/apt/sources.list.d/havoc.list"
fi

if ! grep -rIl 'havoc.app' /etc/apt /var/jb/etc/apt 2>/dev/null | grep -q .; then
    mkdir -p "$(dirname "$HAVOC_LIST")"
    printf '%s\n' 'deb https://havoc.app/ ./' > "$HAVOC_LIST"
    log "  Havoc source added: $HAVOC_LIST"
else
    log "  Havoc source already present"
fi

FRIDA_LIST="$(dirname "$HAVOC_LIST")/frida.list"
if ! grep -rIl 'build.frida.re' /etc/apt /var/jb/etc/apt 2>/dev/null | grep -q .; then
    mkdir -p "$(dirname "$FRIDA_LIST")"
    printf '%s\n' 'deb https://build.frida.re/ ./' > "$FRIDA_LIST"
    log "  Frida source added: $FRIDA_LIST"
else
    log "  Frida source already present"
fi

apt-get -o Acquire::AllowInsecureRepositories=true \
    -o Acquire::AllowDowngradeToInsecureRepositories=true \
    update -qq 2>&1 || log "  apt update exited with $?"
log "  apt update done"

apt-get -o APT::Get::AllowUnauthenticated=true \
    install -y -qq libkrw0-tfp0 2>/dev/null || true
log "  libkrw0-tfp0 done"

apt-get -o APT::Get::AllowUnauthenticated=true \
    upgrade -y -qq 2>/dev/null || true
log "  apt upgrade done"

# ═══════════ 7/7 INSTALL TROLLSTORE LITE ═════════════════════
log "[7/8] Installing TrollStore Lite..."
TROLLSTORE_READY=0
if dpkg -s com.opa334.trollstorelite >/dev/null 2>&1; then
    log "  TrollStore Lite already installed"
    TROLLSTORE_READY=1
else
    apt-get -o APT::Get::AllowUnauthenticated=true \
        install -y -qq com.opa334.trollstorelite 2>&1
    trollstore_rc=$?
    if [ "$trollstore_rc" -ne 0 ]; then
        log "  WARNING: TrollStore Lite apt install failed with exit code $trollstore_rc"
    else
        if dpkg -s com.opa334.trollstorelite >/dev/null 2>&1; then
            log "  TrollStore Lite installed"
            TROLLSTORE_READY=1
        else
            log "  WARNING: TrollStore Lite install completed without registering package"
        fi
    fi
fi

uicache -a 2>/dev/null || true
log "  uicache refreshed"

# iOS 27: -[LSApplicationWorkspace registerApplicationDictionary:] (what uicache -a
# uses) is a deprecated no-op stub on 27, so uicache cannot register JB apps. Register
# them via the containerized LS API instead (needs the lsd embedded-reg gate patch from
# cfw_patch_lsd_embedded_reg). The 27 gate is the /cores/vpregister PRESENCE: it is
# deployed only on a 27 base (cfw_install_jb.sh gates the deploy on the mounted
# SystemVersion.plist), so a 26.x/18.x base has no /cores/vpregister and skips this.
# Do NOT re-add a guest-side `sw_vers` version check here — the hybrid guest's sw_vers
# does not reliably report the 27 userland version at first boot, which silently
# skipped this step and left Sileo unregistered.
if [ -x /cores/vpregister ]; then
    log "  Registering JB apps via containerized LS API (iOS 27 path)..."
    /cores/vpregister 2>&1 | while IFS= read -r vpr_line; do log "    $vpr_line"; done
fi

# ═══════════ 8/8 SHELL PROFILES FOR SSH ═══════════════════════
log "[8/8] Setting up shell profiles for SSH..."
# .bashrc  — non-login interactive shells (dropbear default)
# .bash_profile — login shells (some SSH configurations)
# Both source /var/jb/etc/profile to get the full JB PATH.
for profile in /var/root/.bashrc /var/root/.bash_profile; do
    if [ ! -f "$profile" ]; then
        printf '%s\n' '# Source JB environment' '[ -r /var/jb/etc/profile ] && . /var/jb/etc/profile' > "$profile"
        log "  $profile created"
    else
        log "  $profile already exists, skipping"
    fi
done

# ═══════════ DONE ════════════════════════════════════════════
if [ "$TROLLSTORE_READY" = "1" ]; then
    : > "$DONE_MARKER"
    log "=== vphone_jb_setup.sh complete ==="
else
    log "=== vphone_jb_setup.sh core steps complete; TrollStore Lite still pending, marker not written ==="
fi
