#!/usr/bin/env bash # ============================================================================= # NetworkManager Captive Portal Auto-Detector # ============================================================================= # Erstellt von Pascal Bouquet am 16.09.2025 # Aktualisiert am 18.09.2025 # # Dieses Skript ist freie Software: Es kann frei verwendet, bearbeitet und # verbreitet werden. Es wird keine Garantie für die Funktionsfähigekeit übernommen. # ============================================================================= # ============================================================================= # KONFIGURATION # ============================================================================= LOG_FILE="/var/log/nm-captive.log" LOG_LEVEL="ERROR" # NONE, DEBUG, INFO, WARNING, ERROR CHECK_INTERVAL=300 # 5 Minuten in Sekunden MAX_CHECKS=12 # Maximal 1 Stunde lang prüfen (12 * 5min) # Test-URLs für Captive Portal Erkennung TEST_URLS=( "http://captive.apple.com/hotspot-detect.html" "http://connectivitycheck.gstatic.com/generate_204" ) # ============================================================================= # FUNKTIONEN # ============================================================================= log() { local level=$1 local message=$2 # Kein Logging wenn LOG_LEVEL=NONE if [ "$LOG_LEVEL" = "NONE" ]; then return fi # Log-Level Filterung case $LOG_LEVEL in "DEBUG") echo "$(date '+%Y-%m-%d %H:%M:%S') - $level - $message" >> "$LOG_FILE" ;; "INFO") if [[ "$level" != "DEBUG" ]]; then echo "$(date '+%Y-%m-%d %H:%M:%S') - $level - $message" >> "$LOG_FILE" fi ;; "WARNING") if [[ "$level" == "WARNING" || "$level" == "ERROR" ]]; then echo "$(date '+%Y-%m-%d %H:%M:%S') - $level - $message" >> "$LOG_FILE" fi ;; "ERROR") if [[ "$level" == "ERROR" ]]; then echo "$(date '+%Y-%m-%d %H:%M:%S') - $level - $message" >> "$LOG_FILE" fi ;; esac } get_user_environment() { local user=$1 local user_id=$(id -u "$user") # Finde den Desktop-Prozess und übernehme dessen Environment local pid=$(pgrep -u "$user" -n plasmashell || pgrep -u "$user" -n gnome-shell || pgrep -u "$user" -n kwin_wayland || echo "") if [ -n "$pid" ] && [ -f "/proc/$pid/environ" ]; then log "DEBUG" "Using environment from process $pid" # Extrahiere alle benötigten Environment-Variablen tr '\0' '\n' < "/proc/$pid/environ" | grep -E "(WAYLAND|DISPLAY|XDG|DBUS|USER|HOME|PATH)" else # Fallback: Basis-Environment setzen log "DEBUG" "Using fallback environment" echo "USER=$user" echo "HOME=/home/$user" echo "XDG_RUNTIME_DIR=/run/user/$user_id" echo "DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$user_id/bus" # Versuche, den echten Display zu finden if [ -S "/run/user/$user_id/wayland-0" ]; then echo "WAYLAND_DISPLAY=wayland-0" echo "XDG_SESSION_TYPE=wayland" elif [ -S "/tmp/.X11-unix/X0" ]; then echo "DISPLAY=:0" else echo "DISPLAY=:0" fi fi } get_user_session() { local user="" local display="" local session_type="" # Finde den aktuellen GUI-Benutzer user=$(who | grep -E "(tty[0-9]|pts/)" | awk '{print $1}' | head -n1) log "DEBUG" "who found user: $user" if [ -n "$user" ]; then local user_id=$(id -u "$user") # Prüfe auf Wayland if [ -S "/run/user/$user_id/wayland-0" ]; then session_type="wayland" display="wayland-0" log "DEBUG" "Wayland socket detected" elif [ -S "/tmp/.X11-unix/X0" ]; then session_type="x11" display=":0" log "DEBUG" "X11 socket detected" else # Fallback basierend auf Prozessen if pgrep -u "$user" kwin_wayland >/dev/null; then session_type="wayland" display="wayland-0" log "DEBUG" "Wayland process detected" else session_type="x11" display=":0" log "DEBUG" "Assuming X11" fi fi fi echo "$user $display $session_type" } check_captive_portal() { local captive_detected=false for url in "${TEST_URLS[@]}"; do log "DEBUG" "Testing URL: $url" if [[ "$url" == *"apple.com"* ]]; then local response=$(curl -s -L --connect-timeout 5 "$url") if [ $? -ne 0 ]; then log "DEBUG" "Network error for $url" captive_detected=true break elif ! echo "$response" | grep -qi "success"; then log "DEBUG" "Apple check failed - no 'Success' in response" captive_detected=true break fi elif [[ "$url" == *"google"* || "$url" == *"gstatic"* ]]; then local http_code=$(curl -s -o /dev/null -w "%{http_code}" --connect-timeout 5 "$url") if [ $? -ne 0 ]; then log "DEBUG" "Network error for $url" captive_detected=true break elif [ "$http_code" != "204" ]; then log "DEBUG" "Google check failed - HTTP $http_code instead of 204" captive_detected=true break fi fi done if [ "$captive_detected" = true ]; then log "INFO" "Captive portal detected" return 1 else log "INFO" "All connectivity checks passed - no captive portal" return 0 fi } start_firefox() { local user=$1 local display=$2 local session_type=$3 log "INFO" "Attempting to start Firefox for user: $user, display: $display, type: $session_type" # Holle die komplette Environment des Users local user_env=$(get_user_environment "$user") log "DEBUG" "User environment: $user_env" # Baue den sudo-Befehl mit der kompletten Environment local env_vars="" while IFS= read -r line; do if [ -n "$line" ]; then env_vars="$env_vars $line" fi done <<< "$user_env" # Starte Firefox mit der kompletten Environment log "DEBUG" "Starting Firefox with full user environment" sudo -u "$user" $env_vars firefox --new-window "http://detectportal.firefox.com/canonical.html" & local pid=$! sleep 1 if ps -p $pid >/dev/null 2>&1; then log "INFO" "Firefox successfully started with PID: $pid" return 0 else log "ERROR" "Firefox process died immediately after start" return 1 fi } start_periodic_checks() { local interface="$1" local user="$2" local display="$3" local session_type="$4" local check_count=0 log "INFO" "Starting periodic checks for $interface (every ${CHECK_INTERVAL}s)" while [ $check_count -lt $MAX_CHECKS ]; do sleep $CHECK_INTERVAL ((check_count++)) log "DEBUG" "Periodic check $check_count/$MAX_CHECKS for $interface" if ! check_captive_portal; then log "INFO" "Captive portal still active - re-opening browser" if start_firefox "$user" "$display" "$session_type"; then log "INFO" "Firefox re-opened successfully" else log "ERROR" "Failed to re-open Firefox" fi else log "INFO" "Captive portal resolved - stopping periodic checks" break fi done log "INFO" "Periodic checks completed for $interface" } # ============================================================================= # HAUPTPROGRAMM # ============================================================================= log "INFO" "=== Dispatch triggered: $1 $2 ===" if [[ "$1" == wl* ]] && [ "$2" = "up" ]; then sleep 3 if ! check_captive_portal; then log "INFO" "CAPTIVE PORTAL DETECTED! Opening browser..." read user display session_type <<< $(get_user_session) if [ -n "$user" ] && [ -n "$display" ]; then log "INFO" "Found user: $user, display: $display, type: $session_type" # Starte Firefox if start_firefox "$user" "$display" "$session_type"; then log "INFO" "Firefox successfully launched" # STARTE PERIODISCHE CHECKS IM HINTERGRUND log "INFO" "Starting periodic checks in background..." start_periodic_checks "$1" "$user" "$display" "$session_type" & else log "ERROR" "Failed to start Firefox" fi else log "WARNING" "No local user or display found for GUI access" fi else log "INFO" "No captive portal detected on connection start" fi elif [[ "$1" == wl* ]] && [ "$2" = "down" ]]; then log "INFO" "WLAN interface $1 disconnected" fi log "INFO" "=== Completed ==="