#!/bin/sh
# Opérations d'administration demandées par Réglages (mot de passe demandé).
#   admin-outil nettoyer
#   admin-outil formater /dev/sdX exfat|fat32|ntfs|ext4 NOM   (disque entier, une partition)
#   admin-outil table /dev/sdX gpt|dos
#   admin-outil partition-creer /dev/sdX TAILLE_MO|max FORMAT NOM
#   admin-outil partition-supprimer /dev/sdX1
#   admin-outil partition-formater /dev/sdX1 FORMAT NOM
#   admin-outil compte-creer LOGIN admin|standard "Nom"     (mot de passe lu sur l'entrée)
#   admin-outil compte-mdp LOGIN                             (mot de passe lu sur l'entrée)
#   admin-outil compte-role LOGIN admin|standard
#   admin-outil compte-supprimer LOGIN garder|effacer
#   admin-outil imprimante-ajouter IDENT URI "Nom affiché"
#   admin-outil imprimante-retirer IDENT
set -u
export LC_ALL=C.UTF-8 PATH=/usr/sbin:/usr/bin:/sbin:/bin DEBIAN_FRONTEND=noninteractive
[ "$(id -u)" = 0 ] || { echo "À lancer en administrateur." >&2; exit 1; }
refus() { echo "$*" >&2; exit 1; }

# ------------------------------------------------------------- disques
nettoyer_nom() { n="$(printf %s "$1" | tr -cd 'A-Za-z0-9_-' | cut -c1-11)"; echo "${n:-DISQUE}"; }

# Disque entier auquel appartient une partition
disque_de() {
    case "$1" in /dev/*) ;; *) refus "Partition refusée : $1" ;; esac
    [ -b "$1" ] || refus "Partition introuvable."
    parent="$(lsblk -dno PKNAME "$1" 2>/dev/null | tr -d ' ')"
    [ -n "$parent" ] || refus "Ce n'est pas une partition."
    echo "/dev/$parent"
}

# Garde-fous : un vrai disque entier, jamais celui qui porte le système en
# cours (ni la clé USB d'installation d'une session d'essai).
verifier_disque() {
    case "$1" in
        /dev/sd[a-z]|/dev/sd[a-z][a-z]|/dev/nvme[0-9]n[0-9]|/dev/nvme[0-9][0-9]n[0-9]|/dev/mmcblk[0-9]|/dev/vd[a-z]) ;;
        *) refus "Disque refusé : $1" ;;
    esac
    [ -b "$1" ] || refus "Disque introuvable."
    [ "$(lsblk -dno TYPE "$1" 2>/dev/null | tr -d ' ')" = disk ] || refus "Ce n'est pas un disque entier."
    [ "$(lsblk -dno RO "$1" 2>/dev/null | tr -d ' ')" = 0 ] || refus "Ce disque est protégé en écriture."
    if lsblk -nro MOUNTPOINT "$1" | grep -Eq '^(/|/boot|/boot/efi|/home|/usr|/var|\[SWAP\]|/cdrom|/rofs|/run/live.*|/run/initramfs.*)$'; then
        refus "Ce disque est utilisé par le système : opération refusée."
    fi
    # Seconde vérification, indépendante des points de montage affichés : le
    # disque ne doit porter aucun des systèmes de fichiers du système en cours.
    siens=" $(lsblk -nro MAJ:MIN "$1" | tr '\n' ' ')"
    for dossier in / /boot /boot/efi /home /usr /var /cdrom; do
        [ -e "$dossier" ] || continue
        d="$(stat -c %d "$dossier" 2>/dev/null)" || continue
        numero="$(( (d >> 8) & 0xfff )):$(( (d & 0xff) | ((d >> 12) & 0xfff00) ))"
        case "$siens" in *" $numero "*) refus "Ce disque est utilisé par le système : opération refusée." ;; esac
    done
    # Session d'essai : le support d'installation n'apparaît pas toujours monté
    if [ -r /proc/cmdline ] && grep -qw boot=casper /proc/cmdline; then
        support="$(findmnt -no SOURCE /cdrom 2>/dev/null)"
        if [ -n "$support" ] && [ "/dev/$(lsblk -dno PKNAME "$support" 2>/dev/null | tr -d ' ')" = "$1" -o "$support" = "$1" ]; then
            refus "C'est le support d'installation de P2i OS : opération refusée."
        fi
    fi
}

liberer() {  # démonte toutes les partitions du disque
    for p in $(lsblk -nrpo NAME "$1" | tail -n +2); do
        umount "$p" 2>/dev/null || true
        grep -q "^$p " /proc/mounts && refus "Le disque est utilisé : fermez les fichiers ouverts."
    done
    return 0
}

relire() { partprobe "$1" 2>/dev/null || blockdev --rereadpt "$1" 2>/dev/null || true; udevadm settle 2>/dev/null || true; sleep 1; }

type_partition() {  # type_partition gpt|dos SYSTEME
    if [ "$1" = gpt ]; then
        case "$2" in ext4) echo L ;; *) echo EBD0A0A2-B9E5-4433-87C0-68B6B72699C7 ;; esac
    else
        case "$2" in ext4) echo 83 ;; fat32) echo c ;; *) echo 7 ;; esac
    fi
}

creer_systeme() {  # creer_systeme PARTITION SYSTEME NOM
    case "$2" in
        exfat) mkfs.exfat -L "$3" "$1" ;;
        fat32) mkfs.vfat -F 32 -n "$(echo "$3" | tr a-z A-Z)" "$1" ;;
        ntfs)  mkfs.ntfs -Q -L "$3" "$1" ;;
        ext4)  mkfs.ext4 -q -F -L "$3" -E root_owner="${PKEXEC_UID:-0}:${PKEXEC_UID:-0}" "$1" ;;
        *) refus "Format inconnu." ;;
    esac >/dev/null 2>&1 || refus "Formatage impossible."
    udevadm settle 2>/dev/null || true
}

case "${1:-}" in
nettoyer)
    apt-get -y autoremove --purge
    apt-get clean
    journalctl --vacuum-size=100M >/dev/null 2>&1 || true
    ;;

formater)
    # Efface tout le disque et y crée une seule partition, prête à l'emploi.
    disque="${2:-}"; systeme="${3:-}"; nom="$(nettoyer_nom "${4:-DISQUE}")"
    verifier_disque "$disque"
    liberer "$disque"
    wipefs -a "$disque" >/dev/null || refus "Effacement impossible (support protégé en écriture ?)."
    printf 'label: dos\n,,%s\n' "$(type_partition dos "$systeme")" | sfdisk -q --wipe always --wipe-partitions always "$disque" || refus "Création de la partition impossible."
    relire "$disque"
    part="$(lsblk -nrpo NAME "$disque" | sed -n 2p)"
    [ -b "$part" ] || refus "Partition introuvable après création."
    creer_systeme "$part" "$systeme" "$nom"
    ;;

table)
    # Nouvelle table de partitions vide (tout le contenu du disque est perdu).
    disque="${2:-}"; genre="${3:-gpt}"
    case "$genre" in gpt|dos) ;; *) refus "Type de table inconnu." ;; esac
    verifier_disque "$disque"
    liberer "$disque"
    for p in $(lsblk -nrpo NAME "$disque" | tail -n +2); do wipefs -a "$p" >/dev/null 2>&1 || true; done
    wipefs -a "$disque" >/dev/null || refus "Effacement impossible (support protégé en écriture ?)."
    printf 'label: %s\n' "$genre" | sfdisk -q --wipe always "$disque" || refus "Création de la table impossible."
    relire "$disque"
    ;;

partition-creer)
    # Ajoute une partition dans l'espace libre qui suit la dernière.
    disque="${2:-}"; taille="${3:-max}"; systeme="${4:-}"; nom="$(nettoyer_nom "${5:-DISQUE}")"
    verifier_disque "$disque"
    genre="$(blkid -p -o value -s PTTYPE "$disque" 2>/dev/null)"
    if [ -z "$genre" ]; then
        genre=gpt
        printf 'label: gpt\n' | sfdisk -q --wipe always "$disque" || refus "Création de la table impossible."
    fi
    avant="$(lsblk -nrpo NAME "$disque" | tail -n +2 | sort)"
    case "$taille" in
        max) ligne=",,$(type_partition "$genre" "$systeme")" ;;
        *[!0-9]*|"") refus "Taille incorrecte." ;;
        *) ligne=",${taille}M,$(type_partition "$genre" "$systeme")" ;;
    esac
    printf '%s\n' "$ligne" | sfdisk -q --append --no-reread --wipe-partitions always "$disque" || refus "Pas assez d'espace libre sur le disque."
    relire "$disque"
    apres="$(lsblk -nrpo NAME "$disque" | tail -n +2 | sort)"
    part="$(printf '%s\n%s\n' "$avant" "$apres" | grep -v '^$' | sort | uniq -u | head -1)"
    [ -b "$part" ] || refus "Partition introuvable après création."
    wipefs -a "$part" >/dev/null 2>&1 || true
    creer_systeme "$part" "$systeme" "$nom"
    ;;

partition-supprimer)
    part="${2:-}"
    disque="$(disque_de "$part")"
    verifier_disque "$disque"
    numero="$(cat "/sys/class/block/$(basename "$part")/partition" 2>/dev/null)"
    [ -n "$numero" ] || refus "Partition introuvable."
    umount "$part" 2>/dev/null || true
    grep -q "^$part " /proc/mounts && refus "Cette partition est utilisée : fermez les fichiers ouverts."
    wipefs -a "$part" >/dev/null 2>&1 || true
    sfdisk -q --delete --no-reread "$disque" "$numero" || refus "Suppression impossible."
    relire "$disque"
    ;;

partition-formater)
    part="${2:-}"; systeme="${3:-}"; nom="$(nettoyer_nom "${4:-DISQUE}")"
    disque="$(disque_de "$part")"
    verifier_disque "$disque"
    umount "$part" 2>/dev/null || true
    grep -q "^$part " /proc/mounts && refus "Cette partition est utilisée : fermez les fichiers ouverts."
    wipefs -a "$part" >/dev/null || refus "Effacement impossible."
    creer_systeme "$part" "$systeme" "$nom"
    ;;

compte-creer|compte-mdp|compte-role|compte-supprimer)
    action="$1"; login="${2:-}"
    printf %s "$login" | grep -Eq '^[a-z][a-z0-9_-]{0,30}$' || refus "Identifiant incorrect : lettres minuscules et chiffres seulement."
    est_admin() { id -nG "$1" 2>/dev/null | tr ' ' '\n' | grep -qx sudo; }
    nombre_admins() { n=0; for u in $(getent group sudo | cut -d: -f4 | tr ',' ' '); do id "$u" >/dev/null 2>&1 && n=$((n + 1)); done; echo "$n"; }
    if [ "$action" != compte-creer ]; then
        uid="$(id -u "$login" 2>/dev/null)" || refus "Ce compte n'existe pas."
        [ "$uid" -ge 1000 ] && [ "$uid" -lt 60000 ] || refus "Ce compte appartient au système."
    fi
    case "$action" in
    compte-creer)
        id "$login" >/dev/null 2>&1 && refus "Un compte porte déjà cet identifiant."
        IFS= read -r motdepasse || true
        [ -n "$motdepasse" ] || refus "Mot de passe vide."
        groupes=""
        for g in plugdev cdrom dip audio video scanner lpadmin; do getent group "$g" >/dev/null && groupes="$groupes,$g"; done
        [ "${3:-standard}" = admin ] && groupes="$groupes,sudo,adm"
        useradd -m -s /bin/bash -c "${4:-$login}" ${groupes:+-G "${groupes#,}"} "$login" || refus "Création du compte impossible."
        printf '%s:%s\n' "$login" "$motdepasse" | chpasswd || { userdel -r "$login" 2>/dev/null; refus "Mot de passe refusé (trop court ou trop simple)."; }
        ;;
    compte-mdp)
        IFS= read -r motdepasse || true
        [ -n "$motdepasse" ] || refus "Mot de passe vide."
        printf '%s:%s\n' "$login" "$motdepasse" | chpasswd || refus "Mot de passe refusé (trop court ou trop simple)."
        ;;
    compte-role)
        if [ "${3:-}" = admin ]; then
            usermod -aG sudo,adm "$login" || refus "Changement impossible."
        else
            if est_admin "$login" && [ "$(nombre_admins)" -le 1 ]; then refus "C'est le seul compte administrateur du poste."; fi
            gpasswd -d "$login" sudo >/dev/null 2>&1 || true
            gpasswd -d "$login" adm >/dev/null 2>&1 || true
        fi
        ;;
    compte-supprimer)
        [ "$uid" = "${PKEXEC_UID:-}" ] && refus "Ce compte est celui de la session en cours."
        if est_admin "$login" && [ "$(nombre_admins)" -le 1 ]; then refus "C'est le seul compte administrateur du poste."; fi
        loginctl terminate-user "$login" >/dev/null 2>&1 || true
        if [ "${3:-garder}" = effacer ]; then userdel -r "$login" 2>/dev/null || userdel "$login" || refus "Suppression impossible."
        else userdel "$login" || refus "Suppression impossible."; fi
        ;;
    esac
    ;;

imprimante-ajouter)
    ident="$(printf %s "${2:-}" | tr -cd 'A-Za-z0-9_-')"; uri="${3:-}"
    [ -n "$ident" ] || refus "Nom d'imprimante vide."
    case "$uri" in ipp://*|ipps://*|http://*|https://*|socket://*|lpd://*) ;; *) refus "Adresse d'imprimante refusée." ;; esac
    lpadmin -p "$ident" -E -v "$uri" -m everywhere -D "${4:-$ident}" || refus "Ajout impossible."
    cupsenable "$ident" 2>/dev/null; cupsaccept "$ident" 2>/dev/null
    [ -n "$(lpstat -d 2>/dev/null | grep -o ': .*')" ] || lpadmin -d "$ident"
    ;;

imprimante-retirer)
    ident="$(printf %s "${2:-}" | tr -cd 'A-Za-z0-9_.-')"
    [ -n "$ident" ] || refus "Nom d'imprimante vide."
    lpadmin -x "$ident"
    ;;

*) echo "usage : admin-outil nettoyer | formater | table | partition-creer | partition-supprimer | partition-formater | imprimante-ajouter | imprimante-retirer" >&2; exit 2 ;;
esac
