use std::io::Write;
use serde::Serialize;
use crate::types::{ApMap, HandshakeMap, ProbeMap};
#[derive(Serialize)]
struct ApExport {
essid: String,
bssid: String,
channel: u8,
frequency: u16,
signal_dbm: i8,
noise_dbm: Option<i8>,
encryption: String,
wpa3: bool,
pmf: bool,
wifi_generation: Option<u8>,
channel_width: u16,
vendor: String,
beacons: u64,
data: u64,
clients: Vec<ClientExport>,
handshakes: u32,
pmkid: bool,
last_seen_secs: u64,
first_seen_secs: u64,
deauth_sent: u32,
security_score: u8,
}
#[derive(Serialize)]
struct ClientExport {
mac: String,
signal_dbm: i8,
vendor: String,
data_count: u64,
probed_ssids: Vec<String>,
last_seen_secs: u64,
is_randomized: bool,
}
pub fn export_aps_csv(ap_map: &ApMap, path: &str) -> Result<usize, String> {
let map = ap_map.lock().unwrap();
let mut file = std::fs::File::create(path).map_err(|e| format!("{}", e))?;
writeln!(file,
"ESSID,BSSID,Channel,Frequency,Signal_dBm,Noise_dBm,Encryption,WPA3,PMF,WiFi_Gen,Width_MHz,Vendor,Beacons,Data,Clients,Handshakes,PMKID,Security_Score,First_Seen_Secs,Last_Seen_Secs"
).map_err(|e| format!("{}", e))?;
let count = map.len();
for ap in map.values() {
let noise = ap.noise_dbm.map(|n| n.to_string()).unwrap_or_default();
let wifi_gen = ap.wifi_generation.map(|g| g.to_string()).unwrap_or_else(|| "-".to_string());
writeln!(file,
"\"{}\",{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{},{}",
ap.essid.replace('"', "\"\""),
ap.bssid, ap.channel, ap.frequency_mhz, ap.signal_dbm, noise,
ap.encryption.display, ap.encryption.wpa3_sae, ap.encryption.pmf_required,
wifi_gen, ap.channel_width, ap.vendor,
ap.beacon_count, ap.data_count, ap.clients.len(),
ap.handshakes, ap.pmkid_captured, ap.encryption.security_score(),
ap.uptime_secs(), ap.age_secs()
).map_err(|e| format!("{}", e))?;
}
Ok(count)
}
pub fn export_aps_json(ap_map: &ApMap, path: &str) -> Result<usize, String> {
let map = ap_map.lock().unwrap();
let exports: Vec<ApExport> = map.values().map(|ap| {
let clients: Vec<ClientExport> = ap.clients.values().map(|c| {
ClientExport {
mac: c.mac.clone(),
signal_dbm: c.signal_dbm,
vendor: c.vendor.clone(),
data_count: c.data_count,
probed_ssids: c.probed_ssids.clone(),
last_seen_secs: c.age_secs(),
is_randomized: c.is_randomized,
}
}).collect();
ApExport {
essid: ap.essid.clone(),
bssid: ap.bssid.clone(),
channel: ap.channel,
frequency: ap.frequency_mhz,
signal_dbm: ap.signal_dbm,
noise_dbm: ap.noise_dbm,
encryption: ap.encryption.display.clone(),
wpa3: ap.encryption.wpa3_sae,
pmf: ap.encryption.pmf_required,
wifi_generation: ap.wifi_generation,
channel_width: ap.channel_width,
vendor: ap.vendor.clone(),
beacons: ap.beacon_count,
data: ap.data_count,
clients,
handshakes: ap.handshakes,
pmkid: ap.pmkid_captured,
last_seen_secs: ap.age_secs(),
first_seen_secs: ap.uptime_secs(),
deauth_sent: ap.deauth_sent,
security_score: ap.encryption.security_score(),
}
}).collect();
let count = exports.len();
let json = serde_json::to_string_pretty(&exports).map_err(|e| format!("{}", e))?;
std::fs::write(path, json).map_err(|e| format!("{}", e))?;
Ok(count)
}
pub fn export_probes_csv(probe_map: &ProbeMap, path: &str) -> Result<usize, String> {
let pm = probe_map.lock().unwrap();
let mut file = std::fs::File::create(path).map_err(|e| format!("{}", e))?;
writeln!(file, "Client_MAC,Probed_SSIDs").map_err(|e| format!("{}", e))?;
let count = pm.len();
for (mac, ssids) in pm.iter() {
let ssid_list: Vec<&String> = ssids.iter().collect();
writeln!(file, "{},\"{}\"", mac, ssid_list.iter().map(|s| s.as_str()).collect::<Vec<_>>().join(";"))
.map_err(|e| format!("{}", e))?;
}
Ok(count)
}
pub fn export_pmkid_hashcat(handshake_map: &HandshakeMap, path: &str) -> Result<usize, String> {
let hs_map = handshake_map.lock().unwrap();
let mut file = std::fs::File::create(path).map_err(|e| format!("{}", e))?;
let mut count = 0;
for (key, hs) in hs_map.iter() {
if let Some(ref pmkid) = hs.pmkid {
let pmkid_hex: String = pmkid.iter().map(|b| format!("{:02x}", b)).collect();
let bssid_clean = key.bssid.replace(':', "");
let client_clean = key.client.replace(':', "");
let essid_hex: String = hs.essid.bytes().map(|b| format!("{:02x}", b)).collect();
writeln!(file, "{}*{}*{}*{}", pmkid_hex, bssid_clean, client_clean, essid_hex)
.map_err(|e| format!("{}", e))?;
count += 1;
}
}
Ok(count)
}
pub fn export_wigle_csv(ap_map: &ApMap, path: &str) -> Result<usize, String> {
let map = ap_map.lock().unwrap();
let mut file = std::fs::File::create(path).map_err(|e| format!("{}", e))?;
writeln!(file, "WigleWifi-1.4,appRelease=wifiscan,model=linux,release=0.4.0,device=wifiscan,display=TUI,board=custom,brand=wifiscan")
.map_err(|e| format!("{}", e))?;
writeln!(file, "MAC,SSID,AuthMode,FirstSeen,Channel,RSSI,CurrentLatitude,CurrentLongitude,AltitudeMeters,AccuracyMeters,Type")
.map_err(|e| format!("{}", e))?;
let count = map.len();
let now = chrono::Local::now().format("%Y-%m-%d %H:%M:%S").to_string();
for ap in map.values() {
let auth = &ap.encryption.display;
writeln!(file, "{},\"{}\",{},{},{},{},0.0,0.0,0,0,WIFI",
ap.bssid, ap.essid.replace('"', "\"\""), auth, now, ap.channel, ap.signal_dbm)
.map_err(|e| format!("{}", e))?;
}
Ok(count)
}