# Annexe : Noyau - wifi.py

## Rôle et utilité

Le fichier `core/wifi.py` est responsable de la gestion des réseaux sans fil. En l'absence d'outils lourds comme NetworkManager, il s'interface directement avec l'outil système bas niveau `wpa_supplicant` pour scanner les réseaux, s'y connecter et vérifier le statut de la connexion.

## Implémentation technique

- Détection matérielle : La méthode `_detect_iface()` lit directement le dossier Linux `/sys/class/net` pour chercher une interface réseau dont le nom commence par `wl` (ex: `wlan0` ou `wlp2s0`), s'assurant que le Wi-Fi fonctionnera indépendamment du nom donné par le noyau Linux.
- Le Scanner (Parsing de lignes) : La méthode `scan_networks()` exécute la commande `wpa_cli scan` puis `wpa_cli scan_results`. La vraie difficulté réside dans le fait de découper la chaîne de caractères renvoyée (séparée par des tabulations) pour extraire le nom du réseau (SSID) et sa puissance de signal (en dBm), tout en filtrant les réseaux cachés ou invalides.
- L'injection de configuration : La méthode `connect_network()` construit de toutes pièces une configuration temporaire en exécutant une suite de commandes `add_network`, `set_network`, puis `enable_network` via `wpa_cli`. Elle supporte aussi bien les réseaux ouverts (`key_mgmt=NONE`) que les réseaux protégés par mot de passe.

## Pistes de modification

- Support de l'EAP-PEAP (Réseaux d'entreprise) : Actuellement, le script gère les mots de passes simples (WPA/WPA2-PSK). Si GrimOS devait être utilisé dans une université ou une entreprise, il faudrait modifier la méthode `connect_network` pour injecter des paramètres `identity` et `password` via la norme 802.1x.
- Mémorisation des réseaux multiples : Le script pourrait lire le fichier `/etc/wpa_supplicant/wpa_supplicant.conf` en python pour lister tous les réseaux déjà enregistrés et proposer à l'utilisateur de les "Oublier" via un menu dédié dans l'interface.

## Code Source

    import subprocess
    import os

    class WifiManager:
        def __init__(self, desktop):
            self.desktop = desktop
            self.iface = self._detect_iface()

        def _detect_iface(self):
            try:
                net_path = "/sys/class/net"
                if os.path.exists(net_path):
                    for iface in os.listdir(net_path):
                        if iface.startswith("wl"):
                            return iface
            except:
                pass
            return None

        def scan_networks(self, sudo_pwd):
            if not self.iface:
                return []

            try:
                # Déclenchement du scan via wpa_cli (car wpa_supplicant tourne déjà)
                subprocess.run(['sudo', '-S', '/sbin/wpa_cli', '-i', self.iface, 'scan'], input=sudo_pwd + '\n', text=True, capture_output=True)

                import time
                time.sleep(5) # Laisser le temps au scan complet de s'effectuer (souvent 3 à 5 secondes)

                # Récupération des résultats
                res = subprocess.run(['sudo', '-S', '/sbin/wpa_cli', '-i', self.iface, 'scan_results'], input=sudo_pwd + '\n', text=True, capture_output=True)

                # DEBUG : Sauvegarder la sortie brute pour analyse
                try:
                    with open('/tmp/wifi_debug.txt', 'w') as f:
                        f.write(res.stdout)
                except:
                    pass

                networks = {}
                lines = res.stdout.strip().split('\n')
                # Format attendu : bssid / frequency / signal level / flags / ssid (séparés par des tabulations)
                for line in lines[1:]:
                    parts = line.split('\t')
                    if len(parts) >= 5:
                        signal = parts[2]
                        ssid = parts[4]

                        if ssid and not ssid.startswith('\\x00'):
                            try:
                                # Ne garder que le meilleur signal par SSID
                                sig_val = float(signal)
                                if ssid not in networks or networks[ssid]['signal'] < sig_val:
                                    networks[ssid] = {'ssid': ssid, 'signal': sig_val}
                            except:
                                pass

                valid_nets = sorted(networks.values(), key=lambda x: x['signal'], reverse=True)
                return valid_nets
            except Exception as e:
                print("Scan error:", e)
                return []

        def connect_network(self, sudo_pwd, ssid, psk):
            if not self.iface:
                return False, "Aucune interface Wi-Fi détectée"

            try:
                def run_wpa_cmd(args):
                    cmd = ['sudo', '-S', '/sbin/wpa_cli', '-i', self.iface] + args
                    res = subprocess.run(cmd, input=sudo_pwd + '\n', text=True, capture_output=True)
                    return res.stdout.strip()

                out = run_wpa_cmd(['add_network'])
                # Output of add_network should be the network ID (integer as string)
                # but sometimes it has leading/trailing debug info. Let's just grab the last line.
                lines = out.split('\n')
                net_id = lines[-1].strip()

                if not net_id.isdigit():
                    return False, f"Erreur de création de réseau ({out})"

                run_wpa_cmd(['set_network', net_id, 'ssid', f'"{ssid}"'])
                if psk:
                    run_wpa_cmd(['set_network', net_id, 'psk', f'"{psk}"'])
                else:
                    run_wpa_cmd(['set_network', net_id, 'key_mgmt', 'NONE'])

                run_wpa_cmd(['enable_network', net_id])
                run_wpa_cmd(['save_config'])
                run_wpa_cmd(['select_network', net_id])

                return True, "Connexion initiée."
            except Exception as e:
                return False, str(e)

        def get_current_status(self, sudo_pwd):
            if not self.iface:
                return None, None

            try:
                res = subprocess.run(['sudo', '-S', '/sbin/wpa_cli', '-i', self.iface, 'status'], input=sudo_pwd + '\n', text=True, capture_output=True)

                ssid = None
                ip = None
                wpa_state = None
                for line in res.stdout.split('\n'):
                    if line.startswith('ssid='):
                        ssid = line.split('=', 1)[1]
                    elif line.startswith('ip_address='):
                        ip = line.split('=', 1)[1]
                    elif line.startswith('wpa_state='):
                        wpa_state = line.split('=', 1)[1]

                if wpa_state == 'COMPLETED':
                    return ssid, ip
            except:
                pass
            return None, None