#!/usr/bin/env bash
# inventaire_linux.sh — Agent Vigie (FactualRisk) — inventaire en LECTURE SEULE
# N'installe rien, ne modifie rien, n'envoie rien sur le réseau. Relisez le résultat avant envoi.
#   bash inventaire_linux.sh [nom_public_du_serveur] > parc-$(hostname).csv
# Distributions : Debian, Ubuntu (dpkg) ; RHEL, Rocky, Alma, Fedora (rpm).
# Colonnes : Editeur;Produit;Version;Hôte;Exposé;Origine;Paquet
#   Exposé=écoute   → écoute publique constatée (ss), oui = déclaré, non = local uniquement
#   Origine=debian  → paquet de distribution
#   Paquet=debian/<codename>/<paquet_source>/<version_installée>
#                   → confronté automatiquement au suivi de sécurité Debian
set -u
HOSTN="${1:-}"
CODENAME="$(. /etc/os-release 2>/dev/null; echo "${VERSION_CODENAME:-}")"
DISTRO="$(. /etc/os-release 2>/dev/null; echo "${ID:-}")"
echo "Editeur;Produit;Version;Hôte;Exposé;Origine;Paquet"
v(){ "$@" 2>&1 | head -1; }
emit(){ [[ -n "$3" ]] && echo "$1;$2;$3;$4;$5;$6;${7:-}"; }
HAS_DPKG=0; command -v dpkg-query >/dev/null && HAS_DPKG=1
# équivalences de noms de paquets Debian → RPM
rpmname(){ case "$1" in apache2) echo httpd;; bind9) echo bind;; exim4-base) echo exim;; mariadb-server-core) echo mariadb-server;; *) echo "$1";; esac; }
installed(){
  if (( HAS_DPKG )); then dpkg-query -W -f='${db:Status-Abbrev}' "$1" 2>/dev/null | grep -q '^ii'
  else command -v rpm >/dev/null && rpm -q "$(rpmname "$1")" >/dev/null 2>&1; fi
}
# version amont (sans epoch ni révision) : 1:9.2p1-2+deb12u3 → 9.2p1
up(){
  if (( HAS_DPKG )); then dpkg-query -W -f='${Version}' "$1" 2>/dev/null | sed -E 's/^[0-9]+://; s/[-+~].*$//'
  else rpm -q --qf '%{VERSION}' "$(rpmname "$1")" 2>/dev/null; fi
}
# référence pour le tracker : debian/trixie/<source>/<version complète>
ref(){
  [[ "$DISTRO" == "debian" && -n "$CODENAME" ]] || return 0
  local s ver
  s="$(dpkg-query -W -f='${source:Package}' "$1" 2>/dev/null)"; ver="$(dpkg-query -W -f='${Version}' "$1" 2>/dev/null)"
  [[ -n "$s" && -n "$ver" ]] && echo "debian/$CODENAME/$s/$ver"
}
# Exposition réelle d'un démon : « oui » s'il écoute sur une adresse non locale,
# « non » s'il n'écoute que sur 127.0.0.1/::1 ou un socket, défaut sinon.
SS="$(ss -H -ltnp 2>/dev/null; ss -H -lunp 2>/dev/null)"
expo(){ # expo <processus> <défaut>
  local lines pub=0 loc=0 a
  lines="$(echo "$SS" | grep -F "\"$1\"" || true)"
  [[ -z "$lines" ]] && { echo "$2"; return; }
  while read -r a; do
    case "$a" in
      127.*|\[::1\]*|::1:*|localhost*) loc=1 ;;
      *) pub=1 ;;
    esac
  done < <(echo "$lines" | awk '{print $4}')
  if (( pub )); then echo "écoute"; elif (( loc )); then echo "non"; else echo "$2"; fi
}
deb(){ # deb <paquet> <éditeur> <produit> <hôte> <exposé_défaut> [processus]
  installed "$1" || return 0
  local ex="$5" full=""; (( HAS_DPKG )) && full="$(dpkg-query -W -f='${Version}' "$1" 2>/dev/null)"
  [[ -n "${6:-}" ]] && ex="$(expo "$6" "$5")"
  if [[ "$full" == *pgdg* ]]; then   # dépôt PGDG : pas Debian, le tracker Debian ne fait pas autorité
    emit "$2" "$3" "$(up "$1")" "$4" "$ex" "editeur" ""
  elif [[ "$DISTRO" == "debian" ]]; then
    emit "$2" "$3" "$(up "$1")" "$4" "$ex" "debian" "$(ref "$1")"
  else                                  # ubuntu, rhel, rocky… : paquet de distribution
    emit "$2" "$3" "$(up "$1")" "$4" "$ex" "${DISTRO:-distro}" ""
  fi
}

deb openssh-server "OpenSSH"    "OpenSSH"    ""       "oui" sshd
deb openssl        "OpenSSL"    "OpenSSL"    ""       "non"
deb sudo           "sudo"       "sudo"       ""       "non"
deb python3        "Python"     "Python"     ""       "non"
deb nginx          "F5"         "nginx"      ""       "oui" nginx
deb apache2        "Apache"     "httpd"      ""       "oui" apache2
deb fail2ban       "Fail2ban"   "fail2ban"   ""       "non"
deb exim4-base     "Exim"       "Exim"       ""       "oui" exim4
deb postfix        "Postfix"    "Postfix"    ""       "oui" master
deb dovecot-core   "Dovecot"    "Dovecot"    ""       "oui" dovecot
deb bind9          "ISC"        "BIND"       ""       "oui" named
# PHP : paquet versionné (php8.4-cli…) ; sinon binaire
PHPPKG=""; (( HAS_DPKG )) && PHPPKG="$(dpkg-query -W -f='${Package}\n' 'php[0-9]*-cli' 2>/dev/null | head -1)"
if [[ -n "$PHPPKG" ]] && installed "$PHPPKG"; then deb "$PHPPKG" "PHP" "PHP" "" "non"
elif command -v php >/dev/null; then emit "PHP" "PHP" "$(v php -v | awk '{print $2}' | sed -E 's/[-+~].*$//')" "" "non" "editeur"; fi
# PostgreSQL
PGPKG=""; (( HAS_DPKG )) && PGPKG="$(dpkg-query -W -f='${Package}\n' 'postgresql-[0-9]*' 2>/dev/null | grep -E '^postgresql-[0-9]+$' | head -1)"
(( HAS_DPKG )) || { installed postgresql-server && deb postgresql-server "PostgreSQL" "PostgreSQL" "" "non" postgres; }
[[ -n "$PGPKG" ]] && installed "$PGPKG" && deb "$PGPKG" "PostgreSQL" "PostgreSQL" "" "non" postgres
# MariaDB ou MySQL — distinguer, ne jamais confondre
if installed mariadb-server; then deb mariadb-server "MariaDB" "MariaDB Server" "" "non" mariadbd
elif installed mariadb-server-core; then deb mariadb-server-core "MariaDB" "MariaDB Server" "" "non" mariadbd
elif command -v mariadbd >/dev/null || (command -v mysqld >/dev/null && mysqld --version 2>&1 | grep -qi mariadb); then
  emit "MariaDB" "MariaDB Server" "$( (mariadbd --version 2>/dev/null || mysqld --version 2>&1) | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1)" "" "non" "editeur"
elif command -v mysqld >/dev/null; then
  emit "Oracle" "MySQL" "$(v mysqld --version | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1)" "" "non" "editeur"
fi
# Logiciels installés hors distribution
command -v caddy >/dev/null && emit "Caddy"    "Caddy"    "$(v caddy version | awk '{print $1}' | sed 's/^v//')" "$HOSTN" "$(expo caddy oui)" "editeur"
command -v node  >/dev/null && emit "Node.js"  "Node.js"  "$(v node -v | sed 's/^v//')" "" "$(expo node non)" "editeur"
command -v cscli >/dev/null && emit "CrowdSec" "CrowdSec" "$(cscli version 2>&1 | awk -F': *' '/^version/{print $2; exit}' | sed -E 's/^v//; s/-.*$//')" "" "non" "editeur"
# Plesk : fichier de version (fiable) puis commande
PLESKV=""
[[ -r /usr/local/psa/version ]] && PLESKV="$(grep -oE '^[0-9]+(\.[0-9]+)+' /usr/local/psa/version | head -1)"
[[ -z "$PLESKV" ]] && command -v plesk >/dev/null && PLESKV="$(plesk version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+(\.[0-9]+)?' | head -1)"
emit "Plesk" "Plesk Obsidian" "$PLESKV" "" "oui" "editeur"
python3 - <<'PY' 2>/dev/null
try:
    import gunicorn; print(f"Gunicorn;gunicorn;{gunicorn.__version__};;non;editeur;")
except Exception: pass
PY
exit 0
