// shell.exec("{networksetup,airport}") goes brrrr use crate::connectors::types::{ Connects, ListsNetworkInterfaces, ListsNetworks, Network, NetworkInterface, }; use crate::connectors::utils::call_exec; use plist; use regex::Regex; use serde::Deserialize; #[derive(Deserialize)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] pub struct AirportNetwork { #[serde(rename = "SSID_STR")] ssid: String, bssid: String, channel: u8, rssi: i8, } impl From for Network { fn from(net: AirportNetwork) -> Network { Network { ssid: net.ssid.clone(), bssid: Some(net.bssid.clone()), channel: Some(net.channel.clone()), rssi: Some(net.rssi.clone()), ..Default::default() } } } pub struct MacOSConnector {} impl MacOSConnector { fn call_airport(&self, args: Vec<&str>) -> Result, String> { let mut args = args.clone(); args.push("--xml"); call_exec( "airport", "/System/Library/PrivateFrameworks/Apple80211.framework/Versions/Current/Resources/airport", args) } fn call_networksetup(&self, args: Vec<&str>) -> Result, String> { call_exec("networksetup", "/usr/sbin/networksetup", args) } } impl Connects for MacOSConnector { fn connect_to_network( &self, iface: &NetworkInterface, net: &Network, psk: Option<&str>, ) -> Result { let mut args = vec!["-setairportnetwork", &iface.machine_name, &net.ssid]; if let Some(p) = psk { args.push(p); } match self.call_networksetup(args) { Ok(_) => Ok(true), Err(e) => Err(e), } } } impl ListsNetworks for MacOSConnector { fn list_networks(&self, _iface: &NetworkInterface) -> Result, String> { // this should use the specific network interface, but airport won't let us specify it let output = self .call_airport(vec!["-s"]) .expect("airport idk what actually"); let res: Vec = plist::from_bytes(&output).expect("airport returned shit"); let networks = res.into_iter().map(|net| Network::from(net)).collect(); Ok(networks) } } impl ListsNetworkInterfaces for MacOSConnector { fn list_network_interfaces(&self) -> Result, String> { // order returns both names let output = String::from_utf8( self.call_networksetup(vec!["-listnetworkserviceorder"]) .expect("networksetup idk what actually"), ) .expect("networksetup returned non-utf8 chars???"); if output.len() == 0 { return Err("networksetup returned nothing. Is the Wi-Fi turned on?".to_string()); } lazy_static! { static ref RE: Regex = Regex::new( r"(?x) \((?P\d+|\*)\)\ (?P[^\n]+)\n \(Hardware\ Port:\ .+?,\ Device:\ (?P[a-z]+\d+)\) " ) .unwrap(); } let mut ifaces: Vec = vec![]; for iface in RE.captures_iter(&output) { ifaces.push(NetworkInterface { enabled: &iface["order"] != "*", machine_name: iface["unix"].to_string(), human_name: Some(iface["human"].to_string()), }); } Ok(ifaces) } }