use std::collections::HashMap;
use zbus::Connection;
use crate::Result;
use crate::api::models::{ConnectionError, Network};
use crate::dbus::{NMAccessPointProxy, NMDeviceProxy, NMProxy, NMWirelessProxy};
use crate::monitoring::info::current_ssid;
use crate::types::constants::{device_type, security_flags, wifi_mode};
use crate::util::utils::{
decode_ssid_or_empty, decode_ssid_or_hidden, for_each_access_point,
get_ip_addresses_from_active_connection,
};
pub(crate) async fn scan_networks(conn: &Connection) -> Result<()> {
let nm = NMProxy::new(conn).await?;
let devices = nm.get_devices().await?;
for dp in devices {
let d_proxy = NMDeviceProxy::builder(conn)
.path(dp.clone())?
.build()
.await?;
let dev_type = d_proxy
.device_type()
.await
.map_err(|e| ConnectionError::DbusOperation {
context: format!(
"failed to get device type for {} during Wi-Fi scan",
dp.as_str()
),
source: e,
})?;
if dev_type != device_type::WIFI {
continue;
}
let wifi = NMWirelessProxy::builder(conn)
.path(dp.clone())?
.build()
.await?;
let opts = std::collections::HashMap::new();
wifi.request_scan(opts)
.await
.map_err(|e| ConnectionError::DbusOperation {
context: format!("failed to request Wi-Fi scan on device {}", dp.as_str()),
source: e,
})?;
}
Ok(())
}
pub(crate) async fn list_networks(conn: &Connection) -> Result<Vec<Network>> {
let mut networks: HashMap<(String, u32), Network> = HashMap::new();
let all_networks = for_each_access_point(conn, |ap| {
Box::pin(async move {
let ssid_bytes = ap.ssid().await?;
let ssid = decode_ssid_or_hidden(&ssid_bytes);
let strength = ap.strength().await?;
let bssid = ap.hw_address().await?;
let flags = ap.flags().await?;
let wpa = ap.wpa_flags().await?;
let rsn = ap.rsn_flags().await?;
let frequency = ap.frequency().await?;
let secured = (flags & security_flags::WEP) != 0 || wpa != 0 || rsn != 0;
let is_psk = (wpa & security_flags::PSK) != 0 || (rsn & security_flags::PSK) != 0;
let is_eap = (wpa & security_flags::EAP) != 0 || (rsn & security_flags::EAP) != 0;
let is_hotspot = ap.mode().await.unwrap_or(0) == wifi_mode::AP;
let network = Network {
device: String::new(),
ssid: ssid.to_string(),
bssid: Some(bssid),
strength: Some(strength),
frequency: Some(frequency),
secured,
is_psk,
is_eap,
is_hotspot,
ip4_address: None,
ip6_address: None,
};
Ok(Some((ssid, frequency, network)))
})
})
.await?;
for (ssid, frequency, new_net) in all_networks {
networks
.entry((ssid.to_string(), frequency))
.and_modify(|n| n.merge_ap(&new_net))
.or_insert(new_net);
}
Ok(networks.into_values().collect())
}
pub(crate) async fn current_network(conn: &Connection) -> Result<Option<Network>> {
let current_ssid = match current_ssid(conn).await {
Some(ssid) => ssid,
None => return Ok(None),
};
let nm = NMProxy::new(conn).await?;
let devices = nm.get_devices().await?;
for dev_path in devices {
let dev = NMDeviceProxy::builder(conn)
.path(dev_path.clone())?
.build()
.await?;
if dev.device_type().await? != device_type::WIFI {
continue;
}
let wifi = NMWirelessProxy::builder(conn)
.path(dev_path.clone())?
.build()
.await?;
let ap_path = wifi.active_access_point().await?;
if ap_path.as_str() == "/" {
continue;
}
let ap = NMAccessPointProxy::builder(conn)
.path(ap_path)?
.build()
.await?;
let ssid_bytes = ap.ssid().await?;
let ssid = decode_ssid_or_empty(&ssid_bytes);
if ssid != current_ssid {
continue;
}
let strength = ap.strength().await?;
let bssid = ap.hw_address().await?;
let flags = ap.flags().await?;
let wpa = ap.wpa_flags().await?;
let rsn = ap.rsn_flags().await?;
let frequency = ap.frequency().await?;
let secured = (flags & security_flags::WEP) != 0 || wpa != 0 || rsn != 0;
let is_psk = (wpa & security_flags::PSK) != 0 || (rsn & security_flags::PSK) != 0;
let is_eap = (wpa & security_flags::EAP) != 0 || (rsn & security_flags::EAP) != 0;
let is_hotspot = ap.mode().await.unwrap_or(0) == wifi_mode::AP;
let interface = dev.interface().await.unwrap_or_default();
let (ip4_address, ip6_address) = if let Ok(active_conn_path) = dev.active_connection().await
{
if active_conn_path.as_str() != "/" {
get_ip_addresses_from_active_connection(conn, &active_conn_path).await
} else {
(None, None)
}
} else {
(None, None)
};
return Ok(Some(Network {
device: interface,
ssid: ssid.to_string(),
bssid: Some(bssid),
strength: Some(strength),
frequency: Some(frequency),
secured,
is_psk,
is_eap,
is_hotspot,
ip4_address,
ip6_address,
}));
}
Ok(None)
}