#!/usr/bin/env bash
DW_THEME_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
if [[ -f "$DW_THEME_DIR/Theme.sh" ]]; then source "$DW_THEME_DIR/Theme.sh"
elif [[ -f "$DW_THEME_DIR/../share/dw-freigabe/Theme.sh" ]]; then source "$DW_THEME_DIR/../share/dw-freigabe/Theme.sh"
else C_RESET=""; C_BOLD=""; C_INFO=""; C_GOLD=""; C_OK=""; C_WARN=""; C_DANGER=""; C_MUTED=""; fi
# DW Freigabe-Assistent 1.1.1
# Geführtes Einrichten einer Samba-Freigabe für das Heimnetz. Standard ist ein Probelauf.
# Änderungen nur in einer eigenen Include-Datei (dw-freigabe.conf) und einem markierten Block in der smb.conf.
# Keine Netzwerkverbindung ins Internet, keine Telemetrie. MIT-Lizenz, (c) 2026 Michael Bergfeld, DoMeZos-Ware
set -u
export LC_ALL=C.UTF-8 2>/dev/null || export LC_ALL=C

VERSION="1.1.1"
PROG="dw-freigabe"

AUSFUEHREN=0; JA=0; SCHREIBEN=0; RECHTE_KORR=0; OHNE_DIALOG=0; MIT_ANMELDUNG=0; SICHERUNG_ZURUECK=0
OPT_ORDNER=""; OPT_NAME=""; OPT_USER=""; OPT_NETZ=""; OPT_CONF=""
FEHLER=""; ANTWORT=""; PRIV_AUTO_ROLLBACK=0
TMPDIRS=()

# ---------------------------------------------------------------- Pfade
# Standard: die echten Systemdateien. Mit --conf DATEI wird eine andere smb.conf verwendet (Tests, Probe an einer Kopie);
# Include-Datei, Sicherungen und Protokoll liegen dann im selben Ordner wie diese Datei.
SMB_CONF="/etc/samba/smb.conf"
DW_CONF="/etc/samba/dw-freigabe.conf"
BACKUP_DIR="/var/backups/dw-freigabe"
LOG_FILE="${XDG_STATE_HOME:-${HOME:-/tmp}/.local/state}/dw-freigabe/protokoll.log"
INCLUDE_LINE=""
MARK_START="# >>> dw-freigabe (Beginn) - nicht von Hand ändern, Rückbau: dw-freigabe rueckbau"
MARK_END="# <<< dw-freigabe (Ende)"

# shellcheck disable=SC2329
cleanup() { local d; for d in "${TMPDIRS[@]+"${TMPDIRS[@]}"}"; do rm -rf -- "$d"; done; }
trap cleanup EXIT

usage() {
cat <<'EOF'
DW Freigabe-Assistent 1.1.1 - Samba-Freigabe für das Heimnetz geführt einrichten

Aufruf:
  dw-freigabe [Befehl] [Optionen]

Befehle:
  einrichten           Freigabe einrichten. Ohne --ordner: geführt mit Fragen im Terminal.
                       Mit --ordner PFAD --name NAME --benutzer BENUTZER: ohne Fragen.
  status               Freigaben, Include-Block und Sicherungen anzeigen
  testen               Konfiguration prüfen (testparm, wenn vorhanden auch smbclient)
  firewall             Hinweise zu ufw/firewalld und zum Heimnetz
  rueckbau             Freigabe(n) entfernen und die smb.conf in den alten Zustand bringen

Optionen:
  --ausfuehren         Änderungen wirklich durchführen (ohne diese Option nur Probelauf)
  --ja                 Rückfragen je Schritt automatisch mit Ja beantworten (nur mit --ausfuehren)
  --ordner PFAD        zu teilender Ordner (vollständiger Pfad)
  --name NAME          Freigabename (Buchstaben, Ziffern, _ und -, bis 30 Zeichen)
  --benutzer NAME      Linux-Benutzer, der zugreifen darf (bekommt ein eigenes Samba-Passwort)
  --schreiben          Schreibzugriff erlauben (Standard: nur lesen)
  --netz LISTE         erlaubte Heimnetze, z. B. 192.168.178.0/24 oder 192.168.178. (Standard: erkanntes Netz)
  --rechte-korrigieren Schreibrecht für "Andere" (o+w) am Ordner entfernen, falls gesetzt
  --conf DATEI         andere smb.conf verwenden (Test an einer Kopie; Include-Datei und Sicherungen liegen daneben)
  --mit-anmeldung      bei "testen": zusätzlich Anmeldung mit smbclient versuchen (Passwortabfrage durch smbclient)
  --sicherung-zurueck  bei "rueckbau": zusätzlich die älteste Sicherung der smb.conf wiederherstellen
  --ohne-dialog        keinen zenity-Dialog zur Ordnerwahl verwenden
  --version            Version anzeigen
  -h, --help, --hilfe  diese Hilfe

Beispiele:
  dw-freigabe einrichten
  dw-freigabe einrichten --ordner /srv/fotos --name Fotos --benutzer anna --netz 192.168.178.0/24
  dw-freigabe einrichten --ordner /srv/fotos --name Fotos --benutzer anna --ausfuehren
  dw-freigabe testen --name Fotos
  dw-freigabe rueckbau --name Fotos --ausfuehren

Sicherheit:
  Der Probelauf ist Standard und zeigt die Änderungen an der smb.conf als Diff. Gastzugriff ist immer aus,
  Rechte 777 werden nie gesetzt, Root wird nur für den jeweiligen Einzelschritt über sudo angefordert.
  Vor jeder Änderung wird die smb.conf gesichert. Das Samba-Passwort wird nie vom Programm gelesen,
  gespeichert oder protokolliert, sondern von smbpasswd direkt im Terminal abgefragt.
  Ausführliche Hinweise: NUTZUNG.txt (unter /usr/share/doc/dw-freigabe/) und "man dw-freigabe".
EOF
}

# ---------------------------------------------------------------- Ausgabe und Eingabe
gui_verfuegbar() {
  [ "$OHNE_DIALOG" -eq 0 ] && hat zenity && { [ -n "${DISPLAY:-}" ] || [ -n "${WAYLAND_DISPLAY:-}" ]; } && [ -t 0 ] && [ -t 1 ]
}
use_color() { [ -t 1 ] && [ -z "${NO_COLOR:-}" ]; }
say() {
  if [[ "$*" == "=== "* ]]; then
    printf '%s%s%s%s\n' "$C_INFO" "$C_BOLD" "$*" "$C_RESET"
  else
    printf '%s\n' "$*"
  fi
}
pfad_gold() {
  printf '%s%s%s%s' "$C_GOLD" "$C_BOLD" "$1" "$C_RESET"
}
warn() { printf '%sWarnung: %s%s\n' "$C_WARN" "$*" "$C_RESET" >&2; }
err()  { printf '%sFehler: %s%s\n' "$C_DANGER" "$*" "$C_RESET" >&2; }
die()  { err "$*"; exit 1; }
usage_fehler() { err "$*"; printf 'Hilfe: %s --hilfe\n' "$PROG" >&2; exit 2; }

frage() { # PROMPT [STANDARD] -> ANTWORT
  local p="$1" d="${2:-}" a="" beispiel=""
  case "$p" in
    *"Pfad des Ordners"*) beispiel="/srv/meine-freigabe";;
    Freigabename) beispiel="MeinShare";;
    Benutzer) beispiel="anna";;
    *"Netz"*) beispiel="192.168.178.0/24";;
  esac
  if [ -n "$d" ]; then printf '%s [%s]: ' "$p" "$d" >&2; else printf '%s: ' "$p" >&2; fi
  [ -n "$beispiel" ] && printf '%s[Beispiel: %s]%s ' "$C_MUTED" "$beispiel" "$C_RESET" >&2
  IFS= read -r a || [ -n "$a" ] || { printf '\n' >&2; err "Eingabe beendet, Abbruch. Es wurde nichts geändert."; exit 1; }
  ANTWORT="${a:-$d}"
}

frage_jn() { # PROMPT STANDARD(j|n)
  local a="" d="$2" hint="[j/N]"
  [ "$d" = "j" ] && hint="[J/n]"
  printf '%s %s: ' "$1" "$hint" >&2
  IFS= read -r a || [ -n "$a" ] || { printf '\n' >&2; err "Eingabe beendet, Abbruch. Es wurde nichts geändert."; exit 1; }
  a="${a,,}"; [ -z "$a" ] && a="$d"
  case "$a" in j|ja|y|yes) return 0;; *) return 1;; esac
}

protokoll() {
  mkdir -p -- "${LOG_FILE%/*}" 2>/dev/null || return 0
  printf '%s %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*" >>"$LOG_FILE" 2>/dev/null || true
}

hat() { command -v "$1" >/dev/null 2>&1; }

# Führt GENAU EINEN Befehl mit Root-Rechten aus (sudo je Schritt).
priv() {
  if [ "$(id -u)" -eq 0 ]; then "$@"; return $?; fi
  hat sudo || { err "sudo fehlt. Bitte als Root ausführen oder sudo installieren."; return 1; }
  printf '%s%s⚠ Root-Rechte für diesen einen Schritt:%s\n' "$C_DANGER" "$C_BOLD" "$C_RESET" >&2
  printf '%s  Befehl: sudo' "$C_MUTED" >&2
  local a
  for a in "$@"; do printf ' %q' "$a" >&2; done
  printf '%s\n' "$C_RESET" >&2
  if [ "$JA" -ne 1 ] && [ "$PRIV_AUTO_ROLLBACK" -ne 1 ]; then
    frage_jn "Diesen Root-Befehl ausführen?" n || { warn "Root-Befehl abgebrochen."; return 1; }
  fi
  sudo -- "$@"
}

mktmp() {
  local d; d="$(mktemp -d)" || die "Temporärer Ordner konnte nicht angelegt werden."
  TMPDIRS+=("$d"); printf '%s' "$d"
}

zeitstempel() { printf '%s-%s' "$(date +%Y%m%d-%H%M%S)" "$$"; }

setze_pfade() {
  if [ -n "$OPT_CONF" ]; then
    local d
    case "$OPT_CONF" in /*) ;; *) OPT_CONF="$PWD/$OPT_CONF";; esac
    d="${OPT_CONF%/*}"
    SMB_CONF="$OPT_CONF"
    DW_CONF="$d/dw-freigabe.conf"
    BACKUP_DIR="$d/dw-freigabe-sicherung"
    LOG_FILE="$d/dw-freigabe.log"
  fi
  INCLUDE_LINE="include = $DW_CONF"
}

# ---------------------------------------------------------------- Prüfungen der Eingaben
pruefe_name() {
  local n="$1"
  if ! [[ "$n" =~ ^[A-Za-z0-9_-]{1,30}$ ]]; then
    FEHLER="Freigabename: 1 bis 30 Zeichen aus Buchstaben (ohne Umlaute), Ziffern, Unterstrich und Bindestrich."; return 1
  fi
  case "${n,,}" in
    global|homes|printers|print|ipc|admin|netlogon|profiles) FEHLER="Der Name '$n' ist in Samba reserviert."; return 1;;
  esac
  return 0
}

conf_hat_freigabe() { [ -f "$DW_CONF" ] && grep -qxF -- "# >>> dw-freigabe Freigabe: $1" "$DW_CONF"; }
liste_freigaben() { [ -f "$DW_CONF" ] && sed -n 's/^# >>> dw-freigabe Freigabe: //p' "$DW_CONF"; }
smb_hat_include() { [ -f "$SMB_CONF" ] && grep -qxF -- "$MARK_START" "$SMB_CONF"; }

name_frei() { # NAME -> 0 wenn weder in dw-freigabe.conf noch in der smb.conf vorhanden
  if conf_hat_freigabe "$1"; then
    FEHLER="Eine Freigabe namens '$1' besteht bereits. Erst entfernen: $PROG rueckbau --name $1 --ausfuehren"; return 1
  fi
  if [ -f "$SMB_CONF" ] && grep -Eiq "^[[:space:]]*\\[$1\\][[:space:]]*\$" "$SMB_CONF"; then
    FEHLER="In der smb.conf gibt es schon einen Abschnitt [$1]. Bitte einen anderen Namen wählen."; return 1
  fi
  return 0
}

ORDNER_REAL=""
pruefe_ordner() {
  local p="$1" r re='[^[:alnum:] ._+@,=~:/-]'
  ORDNER_REAL=""
  [ -n "$p" ] || { FEHLER="Kein Ordner angegeben."; return 1; }
  case "$p" in
    /*) ;;
    *) FEHLER="Bitte den vollständigen Pfad angeben (beginnt mit /)."; return 1;;
  esac
  if [[ "$p" == *[[:cntrl:]]* ]]; then FEHLER="Der Pfad enthält Steuerzeichen."; return 1; fi
  [ -d "$p" ] || { FEHLER="Ordner nicht gefunden: $p"; return 1; }
  r="$(realpath -e -- "$p" 2>/dev/null)" || { FEHLER="Pfad kann nicht aufgelöst werden: $p"; return 1; }
  if [[ "$r" =~ $re ]]; then
    FEHLER="Der Pfad enthält Sonderzeichen, die in der Samba-Konfiguration Probleme machen (erlaubt: Buchstaben, Ziffern, Leerzeichen und . _ + @ , = ~ : / -)."; return 1
  fi
  case "$r" in
    /|/home|/var|/opt|/srv|/mnt|/media|/tmp|/run|/boot|/dev|/proc|/sys|/etc|/usr|/bin|/sbin|/lib|/lib32|/lib64|/libx32|/root)
      FEHLER="'$r' ist ein Systemordner und darf nicht freigegeben werden. Bitte einen eigenen Unterordner wählen."; return 1;;
    /etc/*|/proc/*|/sys/*|/dev/*|/boot/*|/bin/*|/sbin/*|/lib/*|/lib32/*|/lib64/*|/libx32/*|/usr/*|/root/*|/run/*)
      FEHLER="'$r' liegt in einem Systemordner und darf nicht freigegeben werden."; return 1;;
  esac
  ORDNER_REAL="$r"
  return 0
}

BENUTZER_UID=""; BENUTZER_HOME=""
pruefe_benutzer() {
  local u="$1" line
  if ! [[ "$u" =~ ^[a-z_][a-z0-9_.-]{0,31}$ ]]; then
    FEHLER="Benutzername ungültig (Kleinbuchstaben, Ziffern, _ . -)."; return 1
  fi
  [ "$u" != "root" ] || { FEHLER="Der Benutzer root darf nicht für Samba-Freigaben verwendet werden."; return 1; }
  line="$(getent passwd -- "$u" 2>/dev/null)" || { FEHLER="Benutzer '$u' existiert auf diesem System nicht (Anlegen z. B. mit: sudo adduser $u)."; return 1; }
  IFS=: read -r _ _ BENUTZER_UID _ _ BENUTZER_HOME _ <<<"$line"
  [ "${BENUTZER_UID:-0}" != "0" ] || { FEHLER="Benutzer mit UID 0 sind nicht erlaubt."; return 1; }
  return 0
}

# Ist der Netz-Eintrag ein privates Heimnetz? (a.b.c. | a.b.c.d | a.b.c.d/n)
netz_eintrag_ok() {
  local e="$1" a b c d n cnt x
  local -a o
  if [[ "$e" =~ ^([0-9]{1,3}\.){1,3}$ ]]; then
    IFS=. read -ra o <<<"${e%.}"
    cnt="${#o[@]}"
    for x in "${o[@]}"; do [ $((10#$x)) -le 255 ] || return 1; done
    a=$((10#${o[0]})); b=$((10#${o[1]:-0})); n=$((8 * cnt))
  elif [[ "$e" =~ ^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})(/([0-9]{1,2}))?$ ]]; then
    a=$((10#${BASH_REMATCH[1]})); b=$((10#${BASH_REMATCH[2]})); c=$((10#${BASH_REMATCH[3]})); d=$((10#${BASH_REMATCH[4]}))
    n=$((10#${BASH_REMATCH[6]:-32}))
    [ "$a" -le 255 ] && [ "$b" -le 255 ] && [ "$c" -le 255 ] && [ "$d" -le 255 ] || return 1
    [ "$n" -ge 1 ] && [ "$n" -le 32 ] || return 1
  else
    return 1
  fi
  if [ "$a" -eq 10 ] && [ "$n" -ge 8 ]; then return 0; fi
  if [ "$a" -eq 172 ] && [ "$b" -ge 16 ] && [ "$b" -le 31 ] && [ "$n" -ge 12 ]; then return 0; fi
  if [ "$a" -eq 192 ] && [ "$b" -eq 168 ] && [ "$n" -ge 16 ]; then return 0; fi
  if [ "$a" -eq 169 ] && [ "$b" -eq 254 ] && [ "$n" -ge 16 ]; then return 0; fi
  if [ "$a" -eq 127 ] && [ "$n" -ge 8 ]; then return 0; fi
  return 1
}

NETZE=""
pruefe_netze() { # Liste (Komma/Leerzeichen) -> NETZE (Leerzeichen getrennt)
  local l="${1//,/ }" e out=""
  [ -n "${l// /}" ] || { FEHLER="Kein Netz angegeben."; return 1; }
  for e in $l; do
    if ! netz_eintrag_ok "$e"; then
      FEHLER="Netz '$e' ist kein privates Heimnetz (erlaubt: 10.x, 172.16-31.x, 192.168.x, jeweils als 192.168.178. oder 192.168.178.0/24). Freigaben ins Internet sind nicht vorgesehen."; return 1
    fi
    out="$out $e"
  done
  NETZE="${out# }"
  return 0
}

DETECT_NETZ=""; DETECT_IP=""; DETECT_DEV=""
erkenne_netz() {
  DETECT_NETZ=""; DETECT_IP=""; DETECT_DEV=""
  hat ip || return 1
  local line dev cidr ip n a b c d mask net
  line="$(ip -4 -o addr show scope global 2>/dev/null | head -n1)" || return 1
  [ -n "$line" ] || return 1
  dev="$(awk '{print $2}' <<<"$line")"
  cidr="$(awk '{for(i=1;i<=NF;i++) if($i=="inet"){print $(i+1)}}' <<<"$line")"
  [[ "$cidr" =~ ^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})/([0-9]{1,2})$ ]] || return 1
  a=$((10#${BASH_REMATCH[1]})); b=$((10#${BASH_REMATCH[2]})); c=$((10#${BASH_REMATCH[3]})); d=$((10#${BASH_REMATCH[4]})); n=$((10#${BASH_REMATCH[5]}))
  [ "$n" -ge 1 ] && [ "$n" -le 32 ] || return 1
  ip=$(( (a<<24) + (b<<16) + (c<<8) + d ))
  mask=$(( (0xFFFFFFFF << (32 - n)) & 0xFFFFFFFF ))
  net=$(( ip & mask ))
  DETECT_NETZ="$(( (net>>24)&255 )).$(( (net>>16)&255 )).$(( (net>>8)&255 )).$(( net&255 ))/$n"
  DETECT_IP="$a.$b.$c.$d"; DETECT_DEV="$dev"
  netz_eintrag_ok "$DETECT_NETZ" || { DETECT_NETZ=""; return 1; }
  return 0
}

# ---------------------------------------------------------------- Ordnerrechte
RECHT_R=0; RECHT_W=0; ORD_OWNER=""; ORD_GROUP=""; ORD_MODE=""; ORD_WELTSCHREIB=0
analysiere_ordner() { # BENUTZER ORDNER
  local u="$1" d="$2" st mode bits
  st="$(stat -c '%U %G %a' -- "$d" 2>/dev/null)" || { RECHT_R=0; RECHT_W=0; return 1; }
  read -r ORD_OWNER ORD_GROUP ORD_MODE <<<"$st"
  mode=$((8#$ORD_MODE))
  if [ "$ORD_OWNER" = "$u" ]; then bits=$(( (mode >> 6) & 7 ))
  elif id -nG -- "$u" 2>/dev/null | tr ' ' '\n' | grep -qxF -- "$ORD_GROUP"; then bits=$(( (mode >> 3) & 7 ))
  else bits=$(( mode & 7 )); fi
  RECHT_R=0; RECHT_W=0
  [ $(( bits & 5 )) -eq 5 ] && RECHT_R=1
  [ $(( bits & 3 )) -eq 3 ] && RECHT_W=1
  ORD_WELTSCHREIB=0
  [ $(( mode & 2 )) -ne 0 ] && ORD_WELTSCHREIB=1
  return 0
}

# ---------------------------------------------------------------- Konfigurationsdateien
entferne_freigabe() { # NAME DATEI -> stdout ohne Block
  NAME_X="$1" awk 'BEGIN{n=ENVIRON["NAME_X"]; s="# >>> dw-freigabe Freigabe: " n; e="# <<< dw-freigabe Freigabe: " n; skip=0}
    $0==s{skip=1} skip==0{print} $0==e{skip=0}' "$2"
}

entferne_include() { # DATEI -> stdout ohne Include-Block
  MS="$MARK_START" ME="$MARK_END" awk 'BEGIN{s=ENVIRON["MS"]; e=ENVIRON["ME"]; skip=0}
    $0==s{skip=1} skip==0{print} $0==e{skip=0}' "$1"
}

feld_aus_block() { # NAME SCHLUESSEL -> Wert
  NAME_X="$1" KEY_X="$2" awk 'BEGIN{n=ENVIRON["NAME_X"]; k=ENVIRON["KEY_X"]; s="# >>> dw-freigabe Freigabe: " n; e="# <<< dw-freigabe Freigabe: " n}
    $0==s{inb=1; next} $0==e{inb=0}
    inb { line=$0; sub(/^[[:space:]]*/,"",line); if (index(line, k) == 1) { v=substr(line, length(k)+1); sub(/^[[:space:]]*=[[:space:]]*/,"",v); print v; exit } }' "$DW_CONF"
}

baue_block() { # NAME ORDNER USER SCHREIBEN(0|1) NETZE
  local name="$1" pfad="$2" user="$3" schreiben="$4" netze="$5" ro="yes"
  [ "$schreiben" = "1" ] && ro="no"
  printf '# >>> dw-freigabe Freigabe: %s\n' "$name"
  printf '[%s]\n' "$name"
  printf '   comment = Freigabe %s (%s)\n' "$name" "$PROG"
  printf '   path = %s\n' "$pfad"
  printf '   browseable = yes\n'
  printf '   guest ok = no\n'
  printf '   read only = %s\n' "$ro"
  printf '   valid users = %s\n' "$user"
  printf '   hosts allow = 127.0.0.1 %s\n' "$netze"
  printf '   hosts deny = ALL\n'
  printf '   create mask = 0660\n'
  printf '   directory mask = 0770\n'
  printf '# <<< dw-freigabe Freigabe: %s\n' "$name"
}

baue_neue_dw() { # ZIELDATEI
  {
    if [ -f "$DW_CONF" ]; then cat -- "$DW_CONF"; else printf '# Von %s verwaltet. Nicht von Hand ändern; Rückbau mit: %s rueckbau\n' "$PROG" "$PROG"; fi
    baue_block "$NAME" "$ORDNER" "$USER_SMB" "$SCHREIB_FLAG" "$NETZ_LISTE"
  } >"$1"
}

baue_neue_smb() { # ZIELDATEI ; nutzt INCLUDE_LINE (oder $2 als abweichende Zeile)
  local zeile="${2:-$INCLUDE_LINE}"
  {
    if [ -f "$SMB_CONF" ]; then cat -- "$SMB_CONF"; [ -z "$(tail -c1 -- "$SMB_CONF")" ] || printf '\n'; fi
    if ! smb_hat_include; then printf '%s\n%s\n%s\n' "$MARK_START" "$zeile" "$MARK_END"; fi
  } >"$1"
}

TESTPARM_OUT=""
teste_konfig() { # DATEI -> 0 ok, 1 Fehler, 2 testparm fehlt
  local rc
  hat testparm || return 2
  TESTPARM_OUT="$(testparm -s "$1" 2>&1 >/dev/null)"; rc=$?
  if [ "$rc" -eq 0 ] && grep -Eiq 'unknown parameter|unbekannter parameter|ignoring unknown' <<<"$TESTPARM_OUT"; then rc=1; fi
  return "$rc"
}

pruefe_vorhandene_konfig() { # beendet das Programm, wenn die vorhandene smb.conf schon Fehler hat
  local rc
  teste_konfig "$SMB_CONF"; rc=$?
  if [ "$rc" -eq 1 ]; then
    err "Die vorhandene smb.conf hat schon Fehler (testparm). Bitte zuerst beheben:"
    printf '%s\n' "$TESTPARM_OUT" >&2
    exit 1
  fi
  return 0
}

dienst_name() {
  local d
  hat systemctl || return 1
  for d in smbd smb; do
    if systemctl list-unit-files "$d.service" 2>/dev/null | grep -q "^$d.service"; then printf '%s' "$d"; return 0; fi
  done
  return 1
}

# ---------------------------------------------------------------- Hinweise
firewall_hinweis() { # NETZE
  local n="${1:-192.168.178.0/24}" first
  first="${n%% *}"
  say "Firewall (nur Hinweis, das Programm ändert nichts an der Firewall):"
  if hat ufw; then
    say "  ufw gefunden. Zugriff nur aus dem Heimnetz erlauben:"
    say "    sudo ufw allow from $first to any app Samba"
    say "    sudo ufw status verbose"
  fi
  if hat firewall-cmd; then
    say "  firewalld gefunden. Samba nur in der Zone 'home' freigeben:"
    say "    sudo firewall-cmd --permanent --zone=home --add-service=samba"
    say "    sudo firewall-cmd --permanent --zone=home --add-source=$first"
    say "    sudo firewall-cmd --reload"
  fi
  if ! hat ufw && ! hat firewall-cmd; then
    say "  Weder ufw noch firewalld gefunden. Falls Sie eine andere Firewall nutzen: TCP 445 (und 139) nur aus $first erlauben."
  fi
  say "  Wichtig: Die Ports 139 und 445 NIE im Router ins Internet weiterleiten."
}

vorschlag_global() { # Schnittstelle
  local dev="${1:-eth0}"
  say "Vorschlag für den Abschnitt [global] in der smb.conf (wird NICHT automatisch geschrieben):"
  say "    interfaces = lo $dev"
  say "    bind interfaces only = yes"
  say "    map to guest = Never"
  say "  Damit hört Samba nur auf dem Heimnetz-Anschluss und gibt Gästen nichts frei."
}

# ---------------------------------------------------------------- Vorprüfungen und Plan
NAME=""; ORDNER=""; USER_SMB=""; SCHREIB_FLAG=0; NETZ_LISTE=""; WARNUNGEN=0; SAMBA_OK=1

vorpruefung_samba() { # 0 ok; 1 blockierend für die Ausführung
  local rc=0
  if ! hat testparm; then
    warn "Hinweis: Samba-Paket fehlt. Installieren Sie es mit: sudo apt install samba"; rc=1
  fi
  if [ ! -f "$SMB_CONF" ]; then
    warn "Keine smb.conf gefunden: $SMB_CONF"; rc=1
  elif [ ! -r "$SMB_CONF" ]; then
    warn "smb.conf ist nicht lesbar: $SMB_CONF"; rc=1
  fi
  return "$rc"
}

zeige_diff() {
  local t alt="$SMB_CONF" kand
  t="$(mktmp)"
  [ -f "$alt" ] || alt=/dev/null
  baue_neue_smb "$t/smb.neu"
  baue_neue_dw "$t/dw.neu"
  say ""
  say "=== Änderung an $SMB_CONF (Diff) ==="
  if cmp -s "$alt" "$t/smb.neu"; then
    say "(keine Änderung: Include-Block ist schon vorhanden)"
  else
    diff -u --label "$SMB_CONF (vorher)" --label "$SMB_CONF (nachher)" "$alt" "$t/smb.neu"
  fi
  say ""
  if [ -f "$DW_CONF" ]; then
    say "=== Änderung an $DW_CONF (Diff) ==="
    diff -u --label "$DW_CONF (vorher)" --label "$DW_CONF (nachher)" "$DW_CONF" "$t/dw.neu"
  else
    say "=== Neue Datei $DW_CONF ==="
    cat -- "$t/dw.neu"
  fi
  # Neue Konfiguration an einer Kopie mit testparm prüfen
  if [ "$SAMBA_OK" -eq 1 ] && hat testparm; then
    kand="$t/kand"; mkdir -p -- "$kand"
    baue_neue_smb "$kand/smb.conf" "include = $kand/dw-freigabe.conf"
    cp -- "$t/dw.neu" "$kand/dw-freigabe.conf"
    say ""
    if teste_konfig "$kand/smb.conf"; then
      say "Prüfung der neuen Konfiguration (testparm, an einer Kopie): in Ordnung."
    else
      say "Prüfung der neuen Konfiguration (testparm, an einer Kopie): FEHLER"
      printf '%s\n' "$TESTPARM_OUT"
      WARNUNGEN=$((WARNUNGEN+1))
    fi
  fi
}

zeige_plan() {
  local ro="nur lesen" sub
  [ "$SCHREIB_FLAG" = "1" ] && ro="lesen und schreiben"
  say ""
  say "=== Plan ==="
  say "Freigabename:   $NAME"
  say "Ordner:         $ORDNER"
  say "Benutzer:       $USER_SMB (eigenes Samba-Passwort, Anmeldung erforderlich)"
  say "Zugriff:        $ro"
  say "Gastzugriff:    nein"
  say "Erlaubte Netze: 127.0.0.1 $NETZ_LISTE (alle anderen Adressen abgelehnt)"
  if analysiere_ordner "$USER_SMB" "$ORDNER"; then
    say "Ordnerrechte:   $ORD_OWNER:$ORD_GROUP, Modus $ORD_MODE (Lesen für $USER_SMB: $( [ "$RECHT_R" = 1 ] && echo ja || echo nein ), Schreiben: $( [ "$RECHT_W" = 1 ] && echo ja || echo nein ); ACLs nicht berücksichtigt)"
    if [ "$ORD_WELTSCHREIB" = "1" ]; then
      warn "Der Ordner ist für alle Benutzer beschreibbar (Modus $ORD_MODE). Mit --rechte-korrigieren wird o+w entfernt."
      WARNUNGEN=$((WARNUNGEN+1))
    fi
    if [ "$RECHT_R" != "1" ]; then
      warn "$USER_SMB darf den Ordner nach Besitzer/Gruppe/Modus nicht lesen. Beispiel zum Anpassen: sudo setfacl -m u:$USER_SMB:rX -- '$ORDNER' (oder Gruppe/Besitzer ändern)."
      WARNUNGEN=$((WARNUNGEN+1))
    fi
    if [ "$SCHREIB_FLAG" = "1" ] && [ "$RECHT_W" != "1" ]; then
      warn "Schreibzugriff gewünscht, aber $USER_SMB darf im Ordner nach Besitzer/Gruppe/Modus nicht schreiben. Beispiel: sudo setfacl -m u:$USER_SMB:rwX -- '$ORDNER'. Es wird NIE 777 gesetzt."
      WARNUNGEN=$((WARNUNGEN+1))
    fi
  fi
  sub="$(find "$ORDNER" -maxdepth 3 -type d -perm -o+w ! -perm -1000 -print -quit 2>/dev/null)"
  if [ -n "$sub" ]; then
    warn "Unterordner mit Schreibrecht für alle gefunden (z. B. $sub). Diese werden nicht verändert; Hinweis: chmod o-w auf diesen Ordnern."
    WARNUNGEN=$((WARNUNGEN+1))
  fi
  if [ -n "$BENUTZER_HOME" ] && [ "$ORDNER" = "$BENUTZER_HOME" ]; then
    warn "Sie geben das ganze Home-Verzeichnis frei (enthält versteckte Ordner wie .ssh). Besser einen Unterordner wählen."
    WARNUNGEN=$((WARNUNGEN+1))
  fi
  if [ -f "$SMB_CONF" ] && grep -Eiq '^[[:space:]]*(guest ok|public)[[:space:]]*=[[:space:]]*(yes|true|1)' "$SMB_CONF"; then
    warn "Ihre smb.conf enthält bereits Gastfreigaben. Diese werden nicht verändert; bitte selbst prüfen."
    WARNUNGEN=$((WARNUNGEN+1))
  fi
  say ""
  say "Fünf Einrichtungsphasen bei --ausfuehren (jeder Root-Befehl einzeln mit Rückfrage):"
  say "  1. Sicherung der smb.conf nach $BACKUP_DIR/"
  say "  2. Freigabe in die eigene Datei $DW_CONF schreiben"
  if smb_hat_include; then
    say "  3. Include-Zeile in der smb.conf ist schon vorhanden (keine Änderung)"
  else
    say "  3. Include-Block am Ende der smb.conf ergänzen:  $INCLUDE_LINE"
  fi
  say "  4. Konfiguration mit testparm prüfen (bei Fehler automatisch zurückgenommen) und Ordnerrechte prüfen"
  if [ "$RECHTE_KORR" = "1" ] && [ "$ORD_WELTSCHREIB" = "1" ]; then
    say "     Ordnerrecht o+w entfernen (chmod o-w)"
  else
    say "     Ordnerrechte bleiben unverändert"
  fi
  say "  5. Samba-Benutzer prüfen, bei Bedarf Passwort setzen und Konfiguration neu laden"
  zeige_diff
  say ""
}

nachher_hinweise() {
  local ziel="${DETECT_IP:-<IP-des-Rechners>}"
  say ""
  printf '%s%s╔══ Einrichtung abgeschlossen ═════════════════════╗%s\n' "$C_INFO" "$C_BOLD" "$C_RESET"
  say "  Name:      $NAME"
  say "  Pfad:      $ORDNER"
  printf '  Adresse:   %s%s%s%s\n' "$C_GOLD" "$C_BOLD" "smb://$ziel/$NAME" "$C_RESET"
  printf '%s╚═══════════════════════════════════════════════════╝%s\n' "$C_INFO" "$C_RESET"
  say "=== So greifen Sie zu ==="
  say "  Windows:  $(pfad_gold "\\\\$ziel\\$NAME")   (Anmeldung als $USER_SMB mit dem Samba-Passwort)"
  say "  Linux:    $(pfad_gold "smb://$ziel/$NAME")"
  say "  Android:  Dateimanager mit SMB-Unterstützung, Server $ziel, Freigabe $NAME"
  say ""
  firewall_hinweis "$NETZ_LISTE"
  say ""
  vorschlag_global "${DETECT_DEV:-eth0}"
  say ""
  say "Test: $PROG testen --name $NAME      Rückbau: $PROG rueckbau --name $NAME --ausfuehren"
  printf '%sSamba-Passwort entfernen: sudo smbpasswd -x %s%s\n' "$C_MUTED" "$USER_SMB" "$C_RESET"
  if gui_verfuegbar; then
    zenity --info --title="Freigabe erfolgreich eingerichtet" \
      --text="Ihre Freigabe ist aktiv:

Windows: \\\\${ziel}\\${NAME}
Linux/Mac: smb://${ziel}/${NAME}

Anmeldung als ${USER_SMB} mit dem Samba-Passwort." >/dev/null 2>&1 || true
  fi
}

# ---------------------------------------------------------------- Einrichten
schritt() { # NR TITEL -> 0 ausführen, 1 überspringen/abbrechen
  say ""
  printf '%s%s╔══ Schritt %s von 5: %s ══╗%s\n' "$C_INFO" "$C_BOLD" "$1" "$2" "$C_RESET"
  printf '%s[%s/5]%s %s\n' "$C_GOLD" "$1" "$C_RESET" "$2"
  [ "$JA" -eq 1 ] && return 0
  frage_jn "Diesen Schritt jetzt ausführen?" n
}
schritt_ok() {
  printf '%s✓ %s%s\n' "$C_OK" "$*" "$C_RESET"
  printf '%s╚══════════════════════════════════════════════════════╝%s\n' "$C_INFO" "$C_RESET"
}
schritt_fail() {
  printf '%s✗ %s%s\n' "$C_DANGER" "$*" "$C_RESET"
  printf '%s╚══════════════════════════════════════════════════════╝%s\n' "$C_DANGER" "$C_RESET"
}

rollback_einrichten() { # BACKUP_SMB BACKUP_DW(Datei oder leer) TS
  warn "Konfigurationsfehler oder Abbruch: Änderungen werden zurückgenommen."
  PRIV_AUTO_ROLLBACK=1
  priv cp -p -- "$1" "$SMB_CONF" || err "Zurücksetzen der smb.conf fehlgeschlagen. Sicherung: $1"
  if [ -n "$2" ]; then
    priv cp -p -- "$2" "$DW_CONF" || err "Zurücksetzen von $DW_CONF fehlgeschlagen. Sicherung: $2"
  elif [ -f "$DW_CONF" ]; then
    priv mv -- "$DW_CONF" "$BACKUP_DIR/dw-freigabe.conf.fehlgeschlagen.$3" || err "Konnte $DW_CONF nicht beiseite legen."
  fi
  PRIV_AUTO_ROLLBACK=0
  protokoll "ROLLBACK (Freigabe $NAME)"
}

wende_an() {
  local ts bk_smb="" bk_dw="" tmp mode neu dienst
  if [ "$SAMBA_OK" -ne 1 ]; then die "Samba ist nicht (vollständig) vorhanden. Es wird nichts geändert."; fi
  pruefe_vorhandene_konfig
  ts="$(zeitstempel)"
  tmp="$(mktmp)"
  say "Ausführung. Es wird vor jedem Schritt nachgefragt (außer mit --ja)."

  # 1 Sicherung
  schritt 1 "smb.conf sichern nach $BACKUP_DIR/smb.conf.$ts" || { say "Abgebrochen. Es wurde nichts geändert."; exit 1; }
  priv mkdir -p -- "$BACKUP_DIR" || die "Sicherungsordner konnte nicht angelegt werden."
  bk_smb="$BACKUP_DIR/smb.conf.$ts"
  priv cp -p -- "$SMB_CONF" "$bk_smb" || die "Sicherung der smb.conf fehlgeschlagen. Es wurde nichts geändert."
  if [ -f "$DW_CONF" ]; then
    bk_dw="$BACKUP_DIR/dw-freigabe.conf.$ts"
    priv cp -p -- "$DW_CONF" "$bk_dw" || die "Sicherung von $DW_CONF fehlgeschlagen."
  fi
  protokoll "Sicherung: $bk_smb"
  schritt_ok "smb.conf gesichert nach $bk_smb"

  # 2 Include-Datei
  schritt 2 "Freigabe '$NAME' in $DW_CONF schreiben" || { say "Abgebrochen nach der Sicherung. Keine Konfiguration geändert."; exit 1; }
  baue_neue_dw "$tmp/dw-freigabe.conf" || die "Temporäre Datei konnte nicht geschrieben werden."
  priv install -m 0644 -- "$tmp/dw-freigabe.conf" "$DW_CONF" || { schritt_fail "Schreiben von $DW_CONF fehlgeschlagen."; die "Schreiben von $DW_CONF fehlgeschlagen."; }
  protokoll "Freigabe '$NAME' ($ORDNER, Benutzer $USER_SMB, schreiben=$SCHREIB_FLAG) in $DW_CONF geschrieben"
  schritt_ok "Freigabe in $DW_CONF geschrieben"

  # 3 Include-Block in der smb.conf
  if smb_hat_include; then
    say ""
    printf '%sSchritt 3 bereits erledigt, übersprungen ✓%s\n' "$C_OK" "$C_RESET"
  else
    schritt 3 "Include-Block in $SMB_CONF ergänzen" || { rollback_einrichten "$bk_smb" "$bk_dw" "$ts"; say "Abgebrochen und zurückgenommen."; exit 1; }
    mode="$(stat -c '%a' -- "$SMB_CONF")"
    baue_neue_smb "$tmp/smb.conf" || die "Temporäre Datei konnte nicht geschrieben werden."
    priv install -m "$mode" -- "$tmp/smb.conf" "$SMB_CONF" || { rollback_einrichten "$bk_smb" "$bk_dw" "$ts"; schritt_fail "Schreiben der smb.conf fehlgeschlagen."; die "Schreiben der smb.conf fehlgeschlagen."; }
    protokoll "Include-Block in $SMB_CONF ergänzt"
    schritt_ok "Include-Block in $SMB_CONF ergänzt"
  fi

  # 4 Prüfung und optionale Rechtekorrektur
  schritt 4 "Konfiguration und Ordnerrechte prüfen" || { rollback_einrichten "$bk_smb" "$bk_dw" "$ts"; schritt_fail "Prüfung abgebrochen und Konfiguration zurückgenommen."; exit 1; }
  teste_konfig "$SMB_CONF"; neu=$?
  if [ "$neu" -eq 1 ]; then
    printf '%s\n' "$TESTPARM_OUT" >&2
    rollback_einrichten "$bk_smb" "$bk_dw" "$ts"
    schritt_fail "testparm meldet einen Konfigurationsfehler."
    die "testparm meldet Fehler in der neuen Konfiguration. Alles wurde zurückgenommen."
  elif [ "$neu" -eq 2 ]; then
    warn "testparm nicht vorhanden, Konfiguration konnte nicht geprüft werden."
  else
    say "testparm: Konfiguration in Ordnung."
  fi

  if [ "$RECHTE_KORR" = "1" ] && [ "$ORD_WELTSCHREIB" = "1" ]; then
    if priv chmod o-w -- "$ORDNER"; then say "o+w entfernt."; protokoll "chmod o-w $ORDNER"; else schritt_fail "chmod o-w fehlgeschlagen."; die "Ordnerrecht konnte nicht korrigiert werden."; fi
  else
    say "Ordnerrechte bereits passend oder Korrektur nicht angefordert; übersprungen ✓"
  fi
  schritt_ok "Konfiguration geprüft; Ordnerrechte berücksichtigt"

  # 5 Samba-Benutzer und Konfiguration neu laden
  schritt 5 "Samba-Benutzer prüfen und Konfiguration neu laden" || { schritt_fail "Abgebrochen; Freigabe ist bereits konfiguriert."; exit 1; }
  if ! hat pdbedit || ! hat smbpasswd; then
    warn "pdbedit/smbpasswd nicht gefunden. Passwort später setzen mit: sudo smbpasswd -a $USER_SMB"
  elif priv pdbedit -L | cut -d: -f1 | grep -qxF -- "$USER_SMB"; then
    printf '%sSamba-Benutzer bereits eingerichtet, Passwortschritt übersprungen ✓%s\n' "$C_OK" "$C_RESET"
    say "Samba-Benutzer '$USER_SMB' ist bereits eingerichtet (Passwort bleibt unverändert; ändern mit: sudo smbpasswd $USER_SMB)."
  elif [ -t 0 ] && { [ "$JA" -eq 1 ] || frage_jn "Jetzt für '$USER_SMB' ein Samba-Passwort anlegen (smbpasswd -a)? Die Eingabe erfolgt direkt bei smbpasswd" j; }; then
    say "smbpasswd fragt jetzt zweimal nach dem neuen Samba-Passwort (es wird nicht angezeigt)."
    if priv smbpasswd -a "$USER_SMB"; then protokoll "smbpasswd -a $USER_SMB"; else warn "smbpasswd fehlgeschlagen. Später: sudo smbpasswd -a $USER_SMB"; fi
  else
    say "Hinweis: Samba-Passwort noch nicht gesetzt. Bitte ausführen: sudo smbpasswd -a $USER_SMB"
  fi

  dienst="$(dienst_name || true)"
  if hat smbcontrol && priv smbcontrol all reload-config >/dev/null; then
    say "Samba hat die Konfiguration neu geladen."
  else
    say "Samba läuft vermutlich nicht. Starten und beim Systemstart aktivieren:  sudo systemctl enable --now ${dienst:-smbd}"
  fi
  schritt_ok "Samba-Benutzer geprüft und Konfiguration neu geladen oder Startanweisung angezeigt"
  protokoll "Einrichtung abgeschlossen (Freigabe $NAME)"
  say ""
  say "Fertig. Freigabe '$NAME' ist eingerichtet. Sicherung: $bk_smb"
  nachher_hinweise
}

abschluss_plan() {
  zeige_plan
  if [ "$AUSFUEHREN" -ne 1 ]; then
    say "PROBELAUF: Es wurde nichts geändert."
    say "Zum Durchführen denselben Aufruf mit --ausfuehren wiederholen, gleichwertig:"
    local cmd; cmd="$PROG einrichten --ordner $(printf '%q' "$ORDNER") --name $(printf '%q' "$NAME") --benutzer $(printf '%q' "$USER_SMB") --netz $(printf '%q' "$NETZ_LISTE")"
    [ "$SCHREIB_FLAG" = "1" ] && cmd="$cmd --schreiben"
    [ "$RECHTE_KORR" = "1" ] && cmd="$cmd --rechte-korrigieren"
    [ -n "$OPT_CONF" ] && cmd="$cmd --conf $(printf '%q' "$OPT_CONF")"
    say "  $cmd --ausfuehren"
    say ""
    firewall_hinweis "$NETZ_LISTE"
    return 0
  fi
  wende_an
}

sammle_und_pruefe() { # nutzt OPT_*; setzt NAME ORDNER USER_SMB SCHREIB_FLAG NETZ_LISTE
  local netz
  pruefe_ordner "$OPT_ORDNER" || usage_fehler "$FEHLER"
  ORDNER="$ORDNER_REAL"
  NAME="$OPT_NAME"
  if [ -z "$NAME" ]; then
    NAME="$(basename -- "$ORDNER" | tr -c 'A-Za-z0-9_-' '_' | cut -c1-30)"
    NAME="${NAME%_}"; [ -n "$NAME" ] || NAME="Freigabe"
  fi
  pruefe_name "$NAME" || usage_fehler "$FEHLER"
  name_frei "$NAME" || usage_fehler "$FEHLER"
  USER_SMB="$OPT_USER"
  if [ -z "$USER_SMB" ]; then
    USER_SMB="${SUDO_USER:-$(id -un)}"
    [ "$USER_SMB" != "root" ] || usage_fehler "--benutzer fehlt (als root gibt es keinen Standard)."
  fi
  pruefe_benutzer "$USER_SMB" || usage_fehler "$FEHLER"
  SCHREIB_FLAG="$SCHREIBEN"
  erkenne_netz || true
  netz="$OPT_NETZ"
  [ -n "$netz" ] || netz="$DETECT_NETZ"
  [ -n "$netz" ] || usage_fehler "Heimnetz konnte nicht erkannt werden. Bitte --netz angeben, z. B. --netz 192.168.178.0/24."
  pruefe_netze "$netz" || usage_fehler "$FEHLER"
  NETZ_LISTE="$NETZE"
  vorpruefung_samba || SAMBA_OK=0
  [ "$SAMBA_OK" -eq 1 ] && pruefe_vorhandene_konfig
  return 0
}

# ---------------------------------------------------------------- Geführter Dialog
waehle_ordner() {
  local z
  if gui_verfuegbar; then
    if z="$(zenity --file-selection --directory --title='Ordner für die Freigabe wählen' 2>/dev/null)" && [ -n "$z" ]; then
      ANTWORT="$z"; return 0
    fi
    say "Im Dialog wurde kein Ordner gewählt. Bitte hier eingeben."
  fi
  frage "Vollständiger Pfad des Ordners, der freigegeben werden soll"
}

zenity_formular() {
  local vorschlag="$1" raw name ord rechte
  raw="$(zenity --forms --title="DW Freigabe-Assistent" \
    --text="Ordner im Heimnetzwerk freigeben" \
    --add-entry="Freigabename:" \
    --add-entry="Ordnerpfad:" \
    --add-combo="Rechte:" --combo-values="Nur Lesen (Gäste erlaubt)|Schreibzugriff (Passwort erforderlich)" \
    --separator='|' 2>/dev/null)" || return 1
  IFS='|' read -r name ord rechte <<<"$raw"
  [ -n "$name" ] || name="$vorschlag"
  OPT_NAME="${name:-$vorschlag}"
  if [ -z "$ord" ] && gui_verfuegbar; then
    ord="$(zenity --file-selection --directory --title='Ordner für die Freigabe wählen' 2>/dev/null)" || true
  fi
  ANTWORT="$ord"
  case "$rechte" in *Schreib*) SCHREIBEN=1; SCHREIB_FLAG=1;; *) SCHREIB_FLAG=0;; esac
  [ -n "$ANTWORT" ]
}

cmd_assistent() {
  local vorschlag bvor formular_ok=0
  printf '%s%s=== %s %s ===%s\n' "$C_INFO" "$C_BOLD" "DW Freigabe-Assistent" "$VERSION" "$C_RESET"
  say "Dieser Assistent richtet eine Samba-Freigabe für Ihr Heimnetz ein."
  if [ "$AUSFUEHREN" -eq 1 ]; then
    say "Modus: AUSFÜHREN. Änderungen werden nach Ihren Antworten und je Schritt nach Rückfrage durchgeführt."
  else
    say "Modus: PROBELAUF. Es wird nichts geändert; am Ende sehen Sie den Plan und den Diff. (Zum Durchführen: $PROG einrichten --ausfuehren)"
  fi
  say ""
  vorschlag="Freigabe"
  if gui_verfuegbar; then
    say "Formular: Freigabename, Ordner und Rechte in einem Fenster."
    if zenity_formular "$vorschlag"; then
      if pruefe_ordner "$ANTWORT"; then
        ORDNER="$ORDNER_REAL"
        vorschlag="$(basename -- "$ORDNER" | tr -c 'A-Za-z0-9_-' '_' | cut -c1-30)"; vorschlag="${vorschlag%_}"; [ -n "$vorschlag" ] || vorschlag="Freigabe"
        if [ -n "${OPT_NAME:-}" ] && pruefe_name "$OPT_NAME" && name_frei "$OPT_NAME"; then
          NAME="$OPT_NAME"; formular_ok=1
        fi
      else
        err "$FEHLER"
      fi
    fi
  fi
  if [ "$formular_ok" -eq 0 ]; then
  say "Frage 1 von 5: Welcher Ordner soll freigegeben werden?"
  while :; do
    waehle_ordner
    if pruefe_ordner "$ANTWORT"; then ORDNER="$ORDNER_REAL"; break; fi
    err "$FEHLER"
  done
  vorschlag="$(basename -- "$ORDNER" | tr -c 'A-Za-z0-9_-' '_' | cut -c1-30)"; vorschlag="${vorschlag%_}"; [ -n "$vorschlag" ] || vorschlag="Freigabe"
  say ""; say "Frage 2 von 5: Unter welchem Namen erscheint die Freigabe im Netz?"
  while :; do
    frage "Freigabename" "$vorschlag"
    if pruefe_name "$ANTWORT" && name_frei "$ANTWORT"; then NAME="$ANTWORT"; break; fi
    err "$FEHLER"
  done
  fi
  bvor="${SUDO_USER:-$(id -un)}"; [ "$bvor" = "root" ] && bvor=""
  say ""; say "Frage 3 von 5: Welcher Linux-Benutzer darf zugreifen?"
  say "  Dieser Benutzer bekommt ein eigenes Samba-Passwort (smbpasswd). Gäste haben keinen Zugriff."
  while :; do
    frage "Benutzer" "$bvor"
    if pruefe_benutzer "$ANTWORT"; then USER_SMB="$ANTWORT"; break; fi
    err "$FEHLER"
  done
  if [ "$formular_ok" -eq 0 ]; then
  say ""; say "Frage 4 von 5: Darf der Benutzer auch schreiben und löschen?"
  say "  Standard ist nur lesen. Schreibzugriff erst aktivieren, wenn nötig."
  if [ "$SCHREIBEN" -eq 1 ] || frage_jn "Schreibzugriff erlauben?" n; then SCHREIB_FLAG=1; else SCHREIB_FLAG=0; fi
  fi
  erkenne_netz || true
  say ""; say "Frage 5 von 5: Aus welchem Netz darf zugegriffen werden?"
  if [ -n "$DETECT_NETZ" ]; then say "  Erkanntes Heimnetz: $DETECT_NETZ (Anschluss ${DETECT_DEV:-?})"; fi
  while :; do
    if [ -n "$OPT_NETZ" ]; then ANTWORT="$OPT_NETZ"; OPT_NETZ=""; else frage "Erlaubtes Netz" "$DETECT_NETZ"; fi
    if pruefe_netze "$ANTWORT"; then NETZ_LISTE="$NETZE"; break; fi
    err "$FEHLER"
  done
  analysiere_ordner "$USER_SMB" "$ORDNER" || true
  if [ "${ORD_WELTSCHREIB:-0}" = "1" ] && [ "$RECHTE_KORR" -eq 0 ]; then
    if frage_jn "Der Ordner ist für alle beschreibbar (Modus $ORD_MODE). Das Schreibrecht für 'Andere' entfernen?" j; then RECHTE_KORR=1; fi
  fi
  vorpruefung_samba || SAMBA_OK=0
  [ "$SAMBA_OK" -eq 1 ] && pruefe_vorhandene_konfig
  abschluss_plan
}

cmd_einrichten() {
  if [ -z "$OPT_ORDNER" ]; then
    cmd_assistent
  else
    sammle_und_pruefe
    abschluss_plan
  fi
}

# ---------------------------------------------------------------- Status, Test, Firewall
cmd_status() {
  local n d any=0 f p u r
  say "DW Freigabe-Assistent $VERSION - Status"
  if hat testparm; then say "Samba (testparm): vorhanden"; else say "Samba (testparm): FEHLT (sudo apt install samba)"; fi
  if [ -z "$OPT_CONF" ] && d="$(dienst_name)"; then
    say "Dienst $d: $(systemctl is-active "$d" 2>/dev/null || true)"
  fi
  if [ -f "$SMB_CONF" ]; then say "smb.conf: $SMB_CONF"; else say "smb.conf: nicht gefunden ($SMB_CONF)"; fi
  if smb_hat_include; then say "Include-Block: vorhanden"; else say "Include-Block: nicht vorhanden"; fi
  if [ -f "$DW_CONF" ]; then say "Include-Datei: $DW_CONF"; else say "Include-Datei: nicht vorhanden"; fi
  say ""
  say "Freigaben dieses Programms:"
  while IFS= read -r n; do
    [ -n "$n" ] || continue
    any=1
    p="$(feld_aus_block "$n" path)"; u="$(feld_aus_block "$n" "valid users")"; r="$(feld_aus_block "$n" "read only")"
    say "  $n: Ordner $p, Benutzer $u, $( [ "$r" = "no" ] && echo "lesen und schreiben" || echo "nur lesen" )"
  done < <(liste_freigaben)
  [ "$any" -eq 1 ] || say "  (keine)"
  say ""
  say "Sicherungen in $BACKUP_DIR:"
  if [ -d "$BACKUP_DIR" ] && compgen -G "$BACKUP_DIR/*" >/dev/null; then
    for f in "$BACKUP_DIR"/*; do say "  ${f##*/}"; done
  else
    say "  (keine)"
  fi
  say "Protokoll: $LOG_FILE"
}

cmd_testen() {
  local n pfad usr bad=0 out rc
  local -a namen=()
  say "DW Freigabe-Assistent $VERSION - Test"
  teste_konfig "$SMB_CONF"; rc=$?
  case "$rc" in
    0) say "OK     testparm: smb.conf ist in Ordnung.";;
    2) say "HINWEIS testparm fehlt, Konfigurationsprüfung nicht möglich."; bad=1;;
    *) say "FEHLER testparm meldet Probleme:"; printf '%s\n' "$TESTPARM_OUT"; bad=1;;
  esac
  if [ -n "$OPT_NAME" ]; then namen=("$OPT_NAME"); else while IFS= read -r n; do [ -n "$n" ] && namen+=("$n"); done < <(liste_freigaben); fi
  if [ "${#namen[@]}" -eq 0 ]; then say "HINWEIS Keine Freigabe dieses Programms vorhanden."; exit "$bad"; fi
  for n in "${namen[@]}"; do
    if ! conf_hat_freigabe "$n"; then say "FEHLER [$n] Freigabe nicht gefunden."; bad=1; continue; fi
    pfad="$(feld_aus_block "$n" path)"; usr="$(feld_aus_block "$n" "valid users")"
    if [ -d "$pfad" ]; then say "OK     [$n] Ordner vorhanden: $pfad"; else say "FEHLER [$n] Ordner fehlt: $pfad"; bad=1; fi
    if [ "$(feld_aus_block "$n" "guest ok")" = "no" ]; then say "OK     [$n] Gastzugriff ist aus."; else say "FEHLER [$n] Gastzugriff nicht ausdrücklich ausgeschaltet."; bad=1; fi
    if [ -d "$pfad" ] && [ -n "$(find "$pfad" -maxdepth 0 -perm -o+w 2>/dev/null)" ]; then say "FEHLER [$n] Ordner ist für alle beschreibbar. Abhilfe: chmod o-w -- '$pfad'"; bad=1; else say "OK     [$n] Ordner ist nicht für alle beschreibbar."; fi
    if hat smbclient && [ -z "$OPT_CONF" ]; then
      if out="$(smbclient -N -L //127.0.0.1 2>&1)" && grep -qw -- "$n" <<<"$out"; then
        say "OK     [$n] Freigabe wird vom Samba-Dienst aufgelistet."
      else
        say "FEHLER [$n] Freigabe wird nicht aufgelistet. Läuft der Dienst (sudo systemctl status smbd)? Wurde die Konfiguration neu geladen?"; bad=1
      fi
      if smbclient -N "//127.0.0.1/$n" -c ls >/dev/null 2>&1; then
        say "FEHLER [$n] Anonymer Zugriff ist möglich, das darf nicht sein."; bad=1
      else
        say "OK     [$n] Anonymer Zugriff wird abgelehnt."
      fi
      if [ "$MIT_ANMELDUNG" -eq 1 ]; then
        say "Anmeldung als $usr (smbclient fragt das Passwort ab) ..."
        if smbclient "//127.0.0.1/$n" -U "$usr" -c ls >/dev/null; then say "OK     [$n] Anmeldung als $usr erfolgreich."; else say "FEHLER [$n] Anmeldung als $usr fehlgeschlagen (Passwort mit smbpasswd -a gesetzt?)."; bad=1; fi
      fi
    elif [ -n "$OPT_CONF" ]; then
      say "HINWEIS [$n] Mit --conf wird kein Verbindungstest gegen den laufenden Dienst gemacht."
    else
      say "HINWEIS smbclient nicht installiert (sudo apt install smbclient); Verbindungstest übersprungen."
    fi
  done
  exit "$bad"
}

cmd_firewall() {
  local net="$OPT_NETZ"
  if [ -z "$net" ]; then erkenne_netz || true; net="$DETECT_NETZ"; fi
  if [ -n "$net" ]; then pruefe_netze "$net" || usage_fehler "$FEHLER"; net="$NETZE"; fi
  firewall_hinweis "$net"
  say ""
  vorschlag_global "${DETECT_DEV:-eth0}"
}

# ---------------------------------------------------------------- Rückbau
cmd_rueckbau() {
  local n ts tmp rest oldest bk alle=0
  local -a namen=()
  if [ ! -f "$DW_CONF" ] && [ "$SICHERUNG_ZURUECK" -eq 0 ] && ! smb_hat_include; then say "Es gibt nichts zurückzubauen (keine $DW_CONF)."; exit 0; fi
  if [ -n "$OPT_NAME" ]; then
    pruefe_name "$OPT_NAME" || usage_fehler "$FEHLER"
    conf_hat_freigabe "$OPT_NAME" || usage_fehler "Freigabe '$OPT_NAME' nicht gefunden."
    namen=("$OPT_NAME")
    rest=0; while IFS= read -r n; do [ "$n" = "$OPT_NAME" ] || rest=$((rest+1)); done < <(liste_freigaben)
    [ "$rest" -gt 0 ] || alle=1
  else
    while IFS= read -r n; do [ -n "$n" ] && namen+=("$n"); done < <(liste_freigaben)
    alle=1
  fi
  say "=== Rückbau ==="
  if [ "$alle" -eq 1 ]; then
    say "Entfernt wird: alle Freigaben dieses Programms (${namen[*]:-keine}), der Include-Block in der smb.conf und die Datei $DW_CONF (wird nach $BACKUP_DIR verschoben, nicht gelöscht)."
  else
    say "Entfernt wird: Freigabe '${namen[0]}' aus $DW_CONF. Die anderen bleiben."
  fi
  [ "$SICHERUNG_ZURUECK" -eq 1 ] && say "Zusätzlich: Die älteste Sicherung der smb.conf wird wiederhergestellt (der aktuelle Stand wird vorher gesichert)."
  say "Nicht verändert: der freigegebene Ordner samt Inhalt und Rechten, Samba-Benutzer und -Passwörter, Firewall."
  if [ "$AUSFUEHREN" -ne 1 ]; then
    if [ "$alle" -eq 1 ] && [ -f "$SMB_CONF" ] && smb_hat_include; then
      tmp="$(mktmp)"; entferne_include "$SMB_CONF" >"$tmp/smb.neu"
      say ""; say "=== Änderung an $SMB_CONF (Diff) ==="
      diff -u --label "$SMB_CONF (vorher)" --label "$SMB_CONF (nachher)" "$SMB_CONF" "$tmp/smb.neu"
    fi
    say ""; say "PROBELAUF: Es wurde nichts geändert. Zum Durchführen: $PROG rueckbau ${OPT_NAME:+--name $OPT_NAME }--ausfuehren"
    exit 0
  fi
  hat testparm || warn "testparm fehlt, die Prüfung nach dem Rückbau entfällt."
  ts="$(zeitstempel)"; tmp="$(mktmp)"
  schritt 1 "smb.conf und $DW_CONF sichern nach $BACKUP_DIR" || { say "Abgebrochen. Nichts geändert."; exit 1; }
  priv mkdir -p -- "$BACKUP_DIR" || die "Sicherungsordner konnte nicht angelegt werden."
  if [ -f "$SMB_CONF" ]; then priv cp -p -- "$SMB_CONF" "$BACKUP_DIR/smb.conf.$ts" || die "Sicherung fehlgeschlagen."; fi
  if [ -f "$DW_CONF" ]; then priv cp -p -- "$DW_CONF" "$BACKUP_DIR/dw-freigabe.conf.$ts" || die "Sicherung fehlgeschlagen."; fi
  protokoll "Rückbau: Sicherung $ts"

  if [ "$alle" -eq 1 ]; then
    schritt 2 "Include-Block aus der smb.conf entfernen und $DW_CONF beiseite legen" || { say "Abgebrochen nach der Sicherung."; exit 1; }
    if [ "$SICHERUNG_ZURUECK" -eq 1 ]; then
      oldest=""
      for bk in "$BACKUP_DIR"/smb.conf.*; do [ -f "$bk" ] || continue; oldest="$bk"; break; done
      [ -n "$oldest" ] || die "Keine Sicherung der smb.conf gefunden."
      priv cp -p -- "$oldest" "$SMB_CONF" || die "Wiederherstellen der Sicherung fehlgeschlagen."
      say "smb.conf aus Sicherung wiederhergestellt: ${oldest##*/}"
    elif smb_hat_include; then
      entferne_include "$SMB_CONF" >"$tmp/smb.conf" || die "Verarbeitung der smb.conf fehlgeschlagen."
      priv install -m "$(stat -c '%a' -- "$SMB_CONF")" -- "$tmp/smb.conf" "$SMB_CONF" || die "Schreiben der smb.conf fehlgeschlagen. Sicherung: $BACKUP_DIR/smb.conf.$ts"
      say "Include-Block entfernt."
    else
      say "Kein Include-Block in der smb.conf gefunden."
    fi
    if [ -f "$DW_CONF" ]; then
      priv mv -- "$DW_CONF" "$BACKUP_DIR/dw-freigabe.conf.entfernt.$ts" || die "Konnte $DW_CONF nicht beiseite legen."
      say "$DW_CONF verschoben nach $BACKUP_DIR/dw-freigabe.conf.entfernt.$ts"
    fi
  else
    schritt 2 "Freigabe '${namen[0]}' aus $DW_CONF entfernen" || { say "Abgebrochen nach der Sicherung."; exit 1; }
    entferne_freigabe "${namen[0]}" "$DW_CONF" >"$tmp/dw-freigabe.conf" || die "Verarbeitung fehlgeschlagen."
    priv install -m 0644 -- "$tmp/dw-freigabe.conf" "$DW_CONF" || die "Schreiben von $DW_CONF fehlgeschlagen."
    say "Freigabe '${namen[0]}' entfernt."
  fi
  protokoll "Rückbau ausgeführt (${namen[*]:-alle})"
  if teste_konfig "$SMB_CONF"; then say "testparm: Konfiguration in Ordnung."; else
    case "$?" in 2) ;; *) warn "testparm meldet Probleme:"; printf '%s\n' "$TESTPARM_OUT" >&2;; esac
  fi
  if hat smbcontrol && priv smbcontrol all reload-config >/dev/null 2>&1; then say "Samba hat die Konfiguration neu geladen."; else say "Samba läuft nicht oder wurde nicht neu geladen. Bei Bedarf: sudo systemctl reload smbd"; fi
  say ""
  say "Fertig. Weitere Aufräumschritte (nur Hinweise, werden nicht automatisch ausgeführt):"
  printf '%s  Samba-Passwort eines Benutzers entfernen:  sudo smbpasswd -x BENUTZER%s\n' "$C_MUTED" "$C_RESET"
  say "  Firewall-Regeln für Samba prüfen und ggf. entfernen (ufw status, firewall-cmd --list-all)"
  say "  Der freigegebene Ordner und seine Daten wurden nicht angetastet."
}

# ---------------------------------------------------------------- Start
CMD=""
if [ $# -gt 0 ]; then
  case "$1" in -*) ;; *) CMD="$1"; shift;; esac
fi
while [ $# -gt 0 ]; do
  case "$1" in
    --ausfuehren) AUSFUEHREN=1;;
    --ja) JA=1;;
    --schreiben) SCHREIBEN=1;;
    --rechte-korrigieren) RECHTE_KORR=1;;
    --mit-anmeldung) MIT_ANMELDUNG=1;;
    --sicherung-zurueck) SICHERUNG_ZURUECK=1;;
    --ohne-dialog) OHNE_DIALOG=1;;
    --ordner|--name|--benutzer|--netz|--conf)
      [ $# -ge 2 ] || usage_fehler "Option $1 braucht einen Wert."
      case "$1" in
        --ordner) OPT_ORDNER="$2";; --name) OPT_NAME="$2";; --benutzer) OPT_USER="$2";; --netz) OPT_NETZ="$2";; --conf) OPT_CONF="$2";;
      esac
      shift;;
    --version) say "DW Freigabe-Assistent $VERSION"; exit 0;;
    -h|--help|--hilfe) usage; exit 0;;
    *) usage_fehler "Unbekannte Option: $1";;
  esac
  shift
done
setze_pfade

case "$CMD" in
  "") usage;;
  einrichten|assistent) cmd_einrichten;;
  status) cmd_status;;
  testen) cmd_testen;;
  firewall) cmd_firewall;;
  rueckbau) cmd_rueckbau;;
  hilfe|help) usage;;
  *) usage_fehler "Unbekannter Befehl: $CMD";;
esac
exit 0
