wifi_ctrl/sta/
types.rs

1use super::{error, warn, Result};
2use serde::Serialize;
3use std::collections::HashMap;
4use std::fmt::Display;
5use std::str::FromStr;
6use tokio::net::UnixDatagram;
7
8#[derive(Serialize, Debug, Clone)]
9/// The result from scanning for networks.
10pub struct ScanResult {
11    pub mac: String,
12    pub frequency: String,
13    pub signal: isize,
14    pub flags: String,
15    pub name: String,
16}
17
18impl ScanResult {
19    pub fn vec_from_str(response: &str) -> Result<Vec<ScanResult>> {
20        let mut results = Vec::new();
21        let split = response.split('\n').skip(1);
22        for line in split {
23            let mut line_split = line.split_whitespace();
24            if let (Some(mac), Some(frequency), Some(signal), Some(flags)) = (
25                line_split.next(),
26                line_split.next(),
27                line_split.next(),
28                line_split.next(),
29            ) {
30                let mut name: Option<String> = None;
31                for text in line_split {
32                    match &mut name {
33                        Some(started) => {
34                            started.push(' ');
35                            started.push_str(text);
36                        }
37                        None => {
38                            name = Some(text.to_string());
39                        }
40                    }
41                }
42                if let Some(name) = name {
43                    if let Ok(signal) = isize::from_str(signal) {
44                        let scan_result = ScanResult {
45                            mac: mac.to_string(),
46                            frequency: frequency.to_string(),
47                            signal,
48                            flags: flags.to_string(),
49                            name,
50                        };
51                        results.push(scan_result);
52                    } else {
53                        warn!("Invalid string for signal: {signal}");
54                    }
55                }
56            }
57        }
58        Ok(results)
59    }
60}
61
62#[derive(Serialize, Debug, Clone)]
63/// A known WiFi network.
64pub struct NetworkResult {
65    pub network_id: usize,
66    pub ssid: String,
67    pub flags: String,
68}
69
70impl NetworkResult {
71    pub async fn vec_from_str(
72        response: &str,
73        socket: &mut UnixDatagram,
74    ) -> Result<Vec<NetworkResult>> {
75        let mut buffer = [0; 256];
76        let mut results = Vec::new();
77        let split = response.split('\n').skip(1);
78        for line in split {
79            let mut line_split = line.split_whitespace();
80            if let Some(network_id) = line_split.next() {
81                let cmd = format!("GET_NETWORK {network_id} ssid");
82                let bytes = cmd.into_bytes();
83                socket.send(&bytes).await?;
84                let n = socket.recv(&mut buffer).await?;
85                let ssid = std::str::from_utf8(&buffer[..n])?.trim_matches('\"');
86                if let Ok(network_id) = usize::from_str(network_id) {
87                    if let Some(flags) = line_split.last() {
88                        results.push(NetworkResult {
89                            flags: flags.into(),
90                            ssid: ssid.into(),
91                            network_id,
92                        })
93                    }
94                } else {
95                    warn!("Invalid network_id: {network_id}")
96                }
97            }
98        }
99        Ok(results)
100    }
101}
102
103/// A HashMap of what is returned when running `wpa_cli status`.
104pub type Status = HashMap<String, String>;
105
106pub(crate) fn parse_status(response: &str) -> Result<Status> {
107    use config::{Config, File, FileFormat};
108    let config = Config::builder()
109        .add_source(File::from_str(response, FileFormat::Ini))
110        .build()
111        .map_err(|e| error::Error::ParsingWifiStatus {
112            e,
113            s: response.into(),
114        })?;
115    Ok(config.try_deserialize::<HashMap<String, String>>().unwrap())
116}
117
118#[derive(Debug)]
119/// Key management types for WiFi networks (eg: WPA-PSK, WPA-EAP, etc). In theory, more than one may
120/// be configured, but I believe `wpa_supplicant` defaults to all of them if omitted. Therefore, in
121/// practice, this is mostly important for setting `key_mgmt` to `None` for an open network.
122pub enum KeyMgmt {
123    None,
124    WpaPsk,
125    WpaEap,
126    IEEE8021X,
127}
128
129impl Display for KeyMgmt {
130    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
131        let str = match self {
132            KeyMgmt::None => "NONE".to_string(),
133            KeyMgmt::WpaPsk => "WPA-PSK".to_string(),
134            KeyMgmt::WpaEap => "WPA-EAP".to_string(),
135            KeyMgmt::IEEE8021X => "IEEE8021X".to_string(),
136        };
137        write!(f, "{}", str)
138    }
139}