#!/bin/bash
# Installe uniquement la cle publique et la source APT PHP. Aucun paquet installe.
set -Eeuo pipefail
export LC_ALL=C

DEBIAN_FILE=/etc/debian_version
OS_RELEASE_FILE=/etc/os-release
APT_ETC=/etc/apt
HTTPS_METHOD=/usr/lib/apt/methods/https
APT_HELPER=/usr/lib/apt/apt-helper
KEYRING=/usr/share/keyrings/stackx-php-archive-keyring.gpg
BACKUP_ROOT=/var/backups/stackx-php-repo
LOCK_FILE=/run/lock/stackx-php-repo.lock
MIRROR=https://mirrors.scalarx.net/stackx-php
FINGERPRINT=AF97ED98AACE4CE129A03B6E2C9736B593BABF7F

die() { printf 'ERREUR : %s\n' "$*" >&2; exit 1; }
require_root() { [[ $EUID -eq 0 ]] || die 'Lancer ce script avec root (ou sudo bash).'; }
usage() {
    cat <<'EOF'
Usage : bash add-stackx-php-repo.sh [--dry-run]

Ajoute la source signee StackX PHP pour Debian 9/10/11 amd64.
Une source trusted=yes geree par l'ancien script est migree avec sauvegarde.
--dry-run : affiche les changements prevus, sans modifier le systeme.
La cle est limitee a cette source par signed-by. Aucun apt-key add global.
Aucun paquet installe, aucune mise a jour ni aucun service redemarre.
EOF
}

fetch() {
    env -u APT_CONFIG "$APT_HELPER" \
        -o Acquire::https::Verify-Peer=true -o Acquire::https::Verify-Host=true \
        -o Acquire::https::Timeout=60 -o Acquire::Retries=1 \
        download-file "$1" "$2"
}

main() (
    local dry_run=0 major suite os_id='' line file source_file entry legacy count=0
    local release_suite release_codename
    local work='' key_tmp='' source_tmp='' backup_dir='' key_changed=1 source_changed=1
    case "${1:-}" in
        --help|-h) usage; exit 0 ;;
        --dry-run) dry_run=1; shift ;;
    esac
    [[ $# -eq 0 ]] || { usage >&2; exit 2; }
    [[ -r $DEBIAN_FILE && -r $OS_RELEASE_FILE ]] || die 'Identification Debian indisponible.'
    major=$(<"$DEBIAN_FILE")
    case "$major" in
        9|9.*) suite=stretch ;;
        10|10.*) suite=buster ;;
        11|11.*) suite=bullseye ;;
        *) die "Version Debian non concernee : $major (seules 9, 10 et 11 sont acceptees)." ;;
    esac
    while IFS= read -r line || [[ -n $line ]]; do
        case "$line" in ID=*) os_id=${line#ID=} ;; esac
    done < "$OS_RELEASE_FILE"
    [[ $os_id == debian || $os_id == '"debian"' || $os_id == "'debian'" ]] || die 'Ce systeme ne se declare pas comme Debian.'
    [[ $(dpkg --print-architecture) == amd64 ]] || die 'Depot reserve a amd64.'
    [[ -d $APT_ETC && -x $HTTPS_METHOD && -x $APT_HELPER ]] || die 'Configuration APT ou transport HTTPS absent.'
    require_root
    for file in gpg gpgv awk cmp mktemp install mkdir cp mv chmod date rm flock; do
        command -v "$file" >/dev/null 2>&1 || die "Commande necessaire absente : $file. Aucun paquet installe automatiquement."
    done
    source_file=$APT_ETC/sources.list.d/stackx-php.list
    entry="deb [arch=amd64 signed-by=$KEYRING] $MIRROR/$suite/repo/ ./"
    legacy="deb [arch=amd64 trusted=yes] $MIRROR/$suite/repo/ ./"
    for file in "$source_file" "$KEYRING"; do
        [[ ! -L $file && ( ! -e $file || -f $file ) ]] || die "Fichier non ordinaire ou lien symbolique : $file."
    done
    [[ ! -L $APT_ETC/sources.list.d && ! -L ${KEYRING%/*} ]] || die 'Repertoire de destination symbolique : modification refusee.'

    if (( ! dry_run )); then
        [[ ! -L $LOCK_FILE ]] || die 'Verrou symbolique refuse.'
        exec 9>"$LOCK_FILE"
        flock -n 9 || die 'Une configuration StackX PHP est deja en cours.'
    fi
    shopt -s nullglob
    for file in "$APT_ETC/sources.list" "$APT_ETC/sources.list.d/"*.list "$APT_ETC/sources.list.d/"*.sources; do
        [[ -f $file ]] || continue
        [[ -r $file ]] || die "Impossible de lire $file."
        while IFS= read -r line || [[ -n $line ]]; do
            line=${line%%#*}
            line="${line#"${line%%[![:space:]]*}"}"
            line="${line%"${line##*[![:space:]]}"}"
            [[ -n $line ]] || continue
            if [[ $file == "$source_file" ]]; then
                [[ $line == "$entry" || $line == "$legacy" ]] || die "Configuration personnalisee dans $file : fichier conserve."
                count=$((count+1))
                (( count <= 1 )) || die 'Plusieurs sources dans le fichier gere : modification refusee.'
            elif [[ $line == *mirrors.scalarx.net/stackx-php* ]]; then
                die "Depot deja defini dans $file : verifier cette definition pour eviter un doublon."
            fi
        done < "$file"
    done
    if (( dry_run )); then
        printf 'SIMULATION : verification de la cle %s et de la signature du depot.\n' "$FINGERPRINT"
        printf 'Cle : %s\nSource : %s\n%s\n' "$KEYRING" "$source_file" "$entry"
        printf 'Sauvegarde des fichiers remplaces sous %s. Aucun changement effectue.\n' "$BACKUP_ROOT"
        exit 0
    fi

    work=$(mktemp -d)
    chmod 0700 "$work"
    trap '[[ -z $key_tmp ]] || rm -f -- "$key_tmp"; [[ -z $source_tmp ]] || rm -f -- "$source_tmp"; [[ -z $work ]] || rm -rf -- "$work"' EXIT
    mkdir -m 0700 "$work/gnupg"
    fetch "$MIRROR/stackx-repository.asc" "$work/key.asc"
    gpg --batch --no-options --homedir "$work/gnupg" --import-options import-minimal --import "$work/key.asc" >"$work/import.log" 2>&1 || die 'Cle publique illisible.'
    [[ -z $(gpg --batch --no-options --homedir "$work/gnupg" --with-colons --list-secret-keys 2>/dev/null) ]] || die 'Le fichier fourni contient une cle privee : refuse.'
    gpg --batch --no-options --homedir "$work/gnupg" --with-colons --fingerprint --list-keys >"$work/keys.txt"
    awk -F: -v expected="$FINGERPRINT" -v now="$(date +%s)" '
      $1=="pub" { n++; need=1; if ($2=="r" || $2=="e" || ($7!="" && $7!="0" && $7+0<=now+0)) bad=1 }
      $1=="fpr" && need { if ($10!=expected) bad=1; got++; need=0 }
      END { exit !(n==1 && got==1 && !bad) }
    ' "$work/keys.txt" || die 'Empreinte, validite ou nombre de cles inattendu.'
    gpg --batch --no-options --homedir "$work/gnupg" --export-options export-minimal --export "$FINGERPRINT" >"$work/key.gpg"
    [[ -s $work/key.gpg ]] || die 'Export de cle vide.'
    fetch "$MIRROR/$suite/repo/InRelease" "$work/InRelease"
    gpgv --homedir "$work/gnupg" --keyring "$work/key.gpg" --output "$work/Release" "$work/InRelease" >"$work/verify.log" 2>&1 || die 'Signature du depot invalide ou indisponible. Source existante conservee.'
    release_suite=$(awk '/^Suite: / {print $2}' "$work/Release")
    release_codename=$(awk '/^Codename: / {print $2}' "$work/Release")
    # Le depot Bullseye publie utilise Suite: bullseye-local et Codename: bullseye.
    if [[ $suite == bullseye && $release_suite == bullseye-local ]]; then
        [[ $release_codename == bullseye ]] || die 'Codename du depot Bullseye inattendu.'
    else
        [[ $release_suite == "$suite" ]] || die 'La signature ne correspond pas a la distribution attendue.'
        [[ -z $release_codename || $release_codename == "$suite" ]] || die 'Codename du depot inattendu.'
    fi
    printf '%s\n' '# Depot PHP StackX pour Debian 9/10/11 - ajoute par add-stackx-php-repo.sh' \
        '# Signature obligatoire ; cle limitee a cette source.' "$entry" >"$work/stackx-php.list"
    if [[ -f $KEYRING ]] && cmp -s "$KEYRING" "$work/key.gpg"; then key_changed=0; fi
    if [[ -f $source_file ]] && cmp -s "$source_file" "$work/stackx-php.list"; then source_changed=0; fi
    if (( ! key_changed && ! source_changed )); then
        printf 'OK : depot %s deja configure, cle et signature verifiees. Aucun changement.\n' "$suite"
        exit 0
    fi
    if [[ -f $source_file || -f $KEYRING ]]; then
        backup_dir=$BACKUP_ROOT/$(date -u +%Y%m%dT%H%M%SZ)-$$
        mkdir -p -- "$backup_dir"
        [[ ! -f $source_file ]] || cp -p -- "$source_file" "$backup_dir/stackx-php.list"
        [[ ! -f $KEYRING ]] || cp -p -- "$KEYRING" "$backup_dir/stackx-php-archive-keyring.gpg"
        printf 'Sauvegarde : %s\n' "$backup_dir"
    fi
    mkdir -p -- "${KEYRING%/*}" "$APT_ETC/sources.list.d"
    # Installer la cle avant de rendre la verification obligatoire sur la source.
    if (( key_changed )); then
        key_tmp=$(mktemp "${KEYRING%/*}/.stackx-php.XXXXXX")
        install -m 0644 "$work/key.gpg" "$key_tmp"
        mv -fT -- "$key_tmp" "$KEYRING"
        key_tmp=''
    fi
    if (( source_changed )); then
        source_tmp=$(mktemp "$APT_ETC/sources.list.d/.stackx-php.XXXXXX")
        install -m 0644 "$work/stackx-php.list" "$source_tmp"
        mv -fT -- "$source_tmp" "$source_file"
        source_tmp=''
    fi
    printf 'OK : depot %s configure avec la cle StackX Repository.\n' "$suite"
    printf 'Aucun paquet installe ou mis a jour, aucun service redemarre.\n'
    printf '\nA lancer ensuite :\n  apt update\n  apt upgrade\n'
)

if [[ ${BASH_SOURCE[0]} == "$0" ]]; then main "$@"; fi
