From 3109455ea07e74619f67f3a0bd17220a758223ee Mon Sep 17 00:00:00 2001 From: PaulVua Date: Wed, 20 May 2026 11:23:10 +0200 Subject: [PATCH] =?UTF-8?q?v1.9.3:=20Fix=20wifi=5Fconnect=20(escaping=20sh?= =?UTF-8?q?ell=20+=20URL)=20+=20log=20d=C3=A9di=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bugs corrigés: - launcher.php passait SSID/PASS au shell sans escapeshellarg(): un mot de passe avec $/&/;/espace cassait silencieusement la commande avant que nmcli ne soit appelé. Cause probable des retours clients "ça bloque au cliquer sur Se connecter". - wifi.html n'encodait pas SSID/PASS dans l'URL: caractères &/+/= corrompaient la requête. Observabilité: - Nouveau fichier logs/wifi_connect.log avec timestamps stricts - launcher.php log la requête entrante (IP, longueurs SSID/PASS) - connexion.sh: fonction log_wc(), snapshots NM avant/après, capture stdout+stderr nmcli, code retour explicite, fallback SSID dérivé du serial si deviceName indisponible. Co-Authored-By: Claude Opus 4.7 (1M context) --- VERSION | 2 +- changelog.json | 18 ++++++++ connexion.sh | 113 +++++++++++++++++++++++++++++++--------------- html/launcher.php | 24 ++++++++-- html/wifi.html | 2 +- 5 files changed, 118 insertions(+), 41 deletions(-) diff --git a/VERSION b/VERSION index 8fdcf38..77fee73 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.9.2 +1.9.3 diff --git a/changelog.json b/changelog.json index 91365a9..049ef8b 100644 --- a/changelog.json +++ b/changelog.json @@ -1,5 +1,23 @@ { "versions": [ + { + "version": "1.9.3", + "date": "2026-05-20", + "changes": { + "features": [ + "Nouveau log dédié logs/wifi_connect.log (timestamps stricts) tracant chaque étape d'une tentative de connexion WiFi depuis le mode hotspot: réception de la requête PHP, état NetworkManager avant/après, code de retour et sortie complète de nmcli, fallback hotspot si échec" + ], + "improvements": [ + "connexion.sh: réécrit avec fonction log_wc() horodatée, snapshots NM avant/après, capture stdout+stderr de nmcli, validation des arguments d'entrée, busy timeout SQLite, fallback SSID dérivé du serial RPi" + ], + "fixes": [ + "Correction critique du flow wifi_connect: launcher.php n'échappait pas les arguments SSID/PASS passés en shell (pas d'escapeshellarg). Les SSID avec espaces ou les mots de passe contenant $, &, ;, espaces, etc. étaient corrompus avant d'atteindre nmcli, faisant échouer silencieusement la connexion (retour client: 'ça bloque au moment de cliquer sur se connecter')", + "wifi.html: ajout de encodeURIComponent() sur SSID et PASS dans l'URL de wifi_connect (caractères &, +, =, # cassaient la requête côté serveur)" + ], + "compatibility": [] + }, + "notes": "Corrige le bug le plus probable derrière les retours clients de connexion WiFi qui échoue sans message d'erreur. Le nouveau log wifi_connect.log permet désormais de diagnostiquer précisément un échec (à fournir lors de tout futur retour client). Log tronqué automatiquement chaque nuit comme les autres logs." + }, { "version": "1.9.2", "date": "2026-05-20", diff --git a/connexion.sh b/connexion.sh index d72d945..6bba582 100755 --- a/connexion.sh +++ b/connexion.sh @@ -1,51 +1,92 @@ #!/bin/bash -echo "-------" -echo "Start connexion shell script at $(date)" +# Connect wlan0 to a client WiFi network, replacing the local hotspot. +# Called by launcher.php (wifi_connect action) with: $1=SSID $2=PASSWORD +# All output is appended to logs/wifi_connect.log by the caller's redirect, +# but we also write timestamped lines here to make the log self-contained. -# Get deviceName from database for hotspot SSID -DEVICE_NAME=$(sqlite3 /var/www/nebuleair_pro_4g/sqlite/sensors.db "SELECT value FROM config_table WHERE key='deviceName'") -echo "Device Name: $DEVICE_NAME" +WIFI_LOG="/var/www/nebuleair_pro_4g/logs/wifi_connect.log" +DB="/var/www/nebuleair_pro_4g/sqlite/sensors.db" -# Find and disable any active hotspot connection -echo "Disable Hotspot..." -# Get all wireless connections that are currently active (excludes the target WiFi) -ACTIVE_HOTSPOT=$(nmcli -t -f NAME,TYPE,DEVICE connection show --active | grep ':802-11-wireless:wlan0' | cut -d: -f1) +log_wc() { + local ts + ts=$(date '+%Y-%m-%d %H:%M:%S') + echo "[$ts] [connexion.sh] $*" +} -if [ -n "$ACTIVE_HOTSPOT" ]; then - echo "Disabling hotspot connection: $ACTIVE_HOTSPOT" - sudo nmcli connection down "$ACTIVE_HOTSPOT" -else - echo "No active hotspot found" +log_wc "================================================================" +log_wc "=== connexion.sh started ===" +log_wc "PID=$$ user=$(whoami)" +log_wc "SSID='$1' (len=${#1})" +log_wc "PASS=[HIDDEN] (len=${#2})" + +if [ -z "$1" ]; then + log_wc "ERROR: empty SSID, aborting" + exit 1 +fi +if [ -z "$2" ]; then + log_wc "ERROR: empty PASSWORD, aborting" + exit 1 fi +# Get deviceName for the hotspot fallback SSID (with busy timeout against lock) +DEVICE_NAME=$(sqlite3 -cmd ".timeout 5000" "$DB" "SELECT value FROM config_table WHERE key='deviceName'") +log_wc "deviceName from DB: '$DEVICE_NAME'" + +# Fallback SSID derived from RPi serial if DB read failed or value is empty +if [ -z "$DEVICE_NAME" ]; then + serial_number=$(cat /proc/cpuinfo | grep Serial | awk '{print substr($3, length($3) - 7)}') + DEVICE_NAME="nebuleair-pro-$serial_number" + log_wc "WARN: deviceName empty in DB, fallback to '$DEVICE_NAME'" +fi + +# Snapshot NM state BEFORE we touch anything (helps debug) +log_wc "--- NetworkManager state BEFORE ---" +log_wc "wlan0 state: $(nmcli -g GENERAL.STATE device show wlan0 2>&1)" +log_wc "wlan0 connection: $(nmcli -g GENERAL.CONNECTION device show wlan0 2>&1)" +log_wc "active connections:" +nmcli -t -f NAME,TYPE,DEVICE connection show --active 2>&1 | while read -r line; do log_wc " $line"; done + +# Find and bring down any active wireless connection on wlan0 (the hotspot) +ACTIVE_HOTSPOT=$(nmcli -t -f NAME,TYPE,DEVICE connection show --active | grep ':802-11-wireless:wlan0' | cut -d: -f1) +if [ -n "$ACTIVE_HOTSPOT" ]; then + log_wc "Disabling active wireless connection on wlan0: '$ACTIVE_HOTSPOT'" + DOWN_OUT=$(sudo nmcli connection down "$ACTIVE_HOTSPOT" 2>&1) + DOWN_RC=$? + log_wc "nmcli connection down rc=$DOWN_RC out: $DOWN_OUT" +else + log_wc "No active wireless connection on wlan0 (nothing to bring down)" +fi + +log_wc "Sleeping 5s to let NM release wlan0..." sleep 5 -echo "Start connection with:" -echo "SSID: $1" -echo "Password: [HIDDEN]" -sudo nmcli device wifi connect "$1" password "$2" +log_wc "--- Attempting nmcli wifi connect ---" +log_wc "Command: sudo nmcli device wifi connect '$1' password [HIDDEN]" +NMCLI_OUT=$(sudo nmcli device wifi connect "$1" password "$2" 2>&1) +NMCLI_RC=$? +log_wc "nmcli exit code: $NMCLI_RC" +log_wc "nmcli output: $NMCLI_OUT" -# Check if connection is successful -if [ $? -eq 0 ]; then - echo "Connection to $1 is successful" +# Snapshot NM state AFTER attempt +log_wc "--- NetworkManager state AFTER ---" +log_wc "wlan0 state: $(nmcli -g GENERAL.STATE device show wlan0 2>&1)" +log_wc "wlan0 connection: $(nmcli -g GENERAL.CONNECTION device show wlan0 2>&1)" - # Update SQLite to reflect connected status - sqlite3 /var/www/nebuleair_pro_4g/sqlite/sensors.db "UPDATE config_table SET value='connected' WHERE key='WIFI_status'" - echo "Updated database: WIFI_status = connected" +if [ $NMCLI_RC -eq 0 ]; then + log_wc "SUCCESS: connected to '$1'" + sqlite3 -cmd ".timeout 5000" "$DB" "UPDATE config_table SET value='connected' WHERE key='WIFI_status'" + log_wc "DB updated: WIFI_status = connected" else - echo "Connection to $1 failed" - echo "Restarting hotspot..." + log_wc "FAILURE: connection to '$1' failed (rc=$NMCLI_RC), restarting hotspot..." - # Recreate hotspot with current deviceName as SSID - sudo nmcli device wifi hotspot ifname wlan0 ssid "$DEVICE_NAME" password nebuleaircfg + # Recreate hotspot with deviceName (or fallback) as SSID + HS_OUT=$(sudo nmcli device wifi hotspot ifname wlan0 ssid "$DEVICE_NAME" password nebuleaircfg 2>&1) + HS_RC=$? + log_wc "nmcli hotspot rc=$HS_RC out: $HS_OUT" - # Update SQLite to reflect hotspot mode - sqlite3 /var/www/nebuleair_pro_4g/sqlite/sensors.db "UPDATE config_table SET value='hotspot' WHERE key='WIFI_status'" - echo "Updated database: WIFI_status = hotspot" - echo "Hotspot restarted with SSID: $DEVICE_NAME" + sqlite3 -cmd ".timeout 5000" "$DB" "UPDATE config_table SET value='hotspot' WHERE key='WIFI_status'" + log_wc "DB updated: WIFI_status = hotspot" + log_wc "Hotspot restarted with SSID: '$DEVICE_NAME'" fi -echo "End connexion shell script" -echo "-------" - - +log_wc "=== connexion.sh end ===" diff --git a/html/launcher.php b/html/launcher.php index e4a8cd2..9967d87 100755 --- a/html/launcher.php +++ b/html/launcher.php @@ -1268,6 +1268,17 @@ if ($type == "wifi_connect") { $SSID=$_GET['SSID']; $PASS=$_GET['pass']; + // Dedicated WiFi connect log (separate from app.log for easier debugging) + $wifi_log = '/var/www/nebuleair_pro_4g/logs/wifi_connect.log'; + $log_wc = function($msg) use ($wifi_log) { + $ts = date('Y-m-d H:i:s'); + @file_put_contents($wifi_log, "[$ts] [launcher.php] $msg\n", FILE_APPEND); + }; + $log_wc("=== wifi_connect request received ==="); + $log_wc("SSID='" . $SSID . "' (len=" . strlen($SSID) . ")"); + $log_wc("PASS=[HIDDEN] (len=" . strlen($PASS) . ")"); + $log_wc("client_ip=" . ($_SERVER['REMOTE_ADDR'] ?? '?')); + // Get device name and hostname for instructions try { $db = new PDO("sqlite:$database_path"); @@ -1279,13 +1290,20 @@ if ($type == "wifi_connect") { $db = null; } catch (PDOException $e) { $deviceName = 'NebuleAir'; + $log_wc("WARN: PDO error reading deviceName: " . $e->getMessage()); } $hostname = trim(shell_exec('hostname 2>/dev/null')) ?: 'aircarto'; + $log_wc("deviceName='$deviceName' hostname='$hostname'"); - // Launch connection script in background + // Launch connection script in background. + // CRITICAL: escapeshellarg() each argument so SSIDs with spaces or passwords + // with special characters ($, &, ;, etc.) are passed correctly to bash. $script_path = '/var/www/nebuleair_pro_4g/connexion.sh'; - $log_file = '/var/www/nebuleair_pro_4g/logs/app.log'; - shell_exec("$script_path $SSID $PASS >> $log_file 2>&1 &"); + $ssid_arg = escapeshellarg($SSID); + $pass_arg = escapeshellarg($PASS); + $cmd = "$script_path $ssid_arg $pass_arg >> $wifi_log 2>&1 &"; + $log_wc("Launching: $script_path (background, output -> wifi_connect.log)"); + shell_exec($cmd); // Return JSON response with instructions header('Content-Type: application/json'); diff --git a/html/wifi.html b/html/wifi.html index c762635..2b33cd5 100755 --- a/html/wifi.html +++ b/html/wifi.html @@ -261,7 +261,7 @@ function wifi_connect(SSID, PASS){ if (myModal) { myModal.hide(); } $.ajax({ - url: 'launcher.php?type=wifi_connect&SSID='+SSID+'&pass='+PASS, + url: 'launcher.php?type=wifi_connect&SSID='+encodeURIComponent(SSID)+'&pass='+encodeURIComponent(PASS), dataType: 'json', method: 'GET', success: function(response) {