use crate::SNIFFNET_LOWERCASE; use crate::gui::types::conf::{CONF, Conf}; use crate::gui::types::message::Message; use crate::networking::types::capture_context::CaptureSourcePicklist; use crate::utils::formatted_strings::APP_VERSION; use clap::Parser; use iced::{Task, window}; #[derive(Parser, Debug)] #[command( name = SNIFFNET_LOWERCASE, bin_name = SNIFFNET_LOWERCASE, version = APP_VERSION, about = "Application to comfortably monitor your network traffic" )] pub(crate) struct Args { /// Start sniffing packets from the supplied network adapter #[arg(short, long, value_name = "NAME", default_missing_value = CONF.device.device_name.as_str(), num_args = 0..=1)] adapter: Option, /// Print the path to the configuration file #[arg(short, long, exclusive = true)] config_path: bool, #[cfg(all(windows, not(debug_assertions)))] /// Show the logs (stdout and stderr) of the most recent application run #[arg(short, long, exclusive = true)] logs: bool, /// Restore default settings #[arg(short, long, exclusive = true)] restore_default: bool, } impl Args { /// Handle and return CLI arguments pub fn handle() -> Self { let args = Args::parse(); #[cfg(all(windows, not(debug_assertions)))] if let Some(logs_file) = crate::utils::formatted_strings::get_logs_file_path() { if args.logs { std::process::Command::new("explorer") .arg(logs_file) .spawn() .unwrap() .wait() .unwrap_or_default(); std::process::exit(0); } else { // truncate logs file let _ = std::fs::OpenOptions::new() .write(true) .truncate(true) .open(logs_file); } } if args.restore_default { if Conf::default().store().is_ok() { println!("Restored default settings"); std::process::exit(0); } else { eprintln!("Could not restore default settings"); std::process::exit(1); } } if args.config_path { if let Ok(config_path) = confy::get_configuration_file_path(SNIFFNET_LOWERCASE, Conf::FILE_NAME) { println!("{}", config_path.display()); std::process::exit(0); } else { eprintln!("Could not retrieve configuration file path"); std::process::exit(1); } } args } pub fn get_boot_task_chain(&self) -> Task { let mut boot_task_chain = window::latest().map(Message::StartApp); if let Some(adapter) = self.adapter.clone() { boot_task_chain = boot_task_chain .chain(Task::done(Message::SetCaptureSource( CaptureSourcePicklist::Device, ))) .chain(Task::done(Message::DeviceSelection(adapter))) .chain(Task::done(Message::Start)); } boot_task_chain } } #[cfg(test)] mod tests { use serial_test::serial; use std::collections::HashSet; use crate::gui::pages::types::running_page::RunningPage; use crate::gui::pages::types::settings_page::SettingsPage; use crate::gui::styles::types::gradient_type::GradientType; use crate::gui::types::conf::Conf; use crate::gui::types::config_window::ConfigWindow; use crate::gui::types::export_pcap::ExportPcap; use crate::gui::types::favorite::FavoriteKey; use crate::gui::types::filters::Filters; use crate::gui::types::settings::Settings; use crate::networking::types::capture_context::CaptureSourcePicklist; use crate::networking::types::config_device::ConfigDevice; use crate::networking::types::data_representation::DataRepr; use crate::networking::types::service::Service; use crate::notifications::types::notifications::Notifications; use crate::report::types::sort_type::SortType; use crate::{Language, Sniffer, StyleType}; #[test] #[serial] fn test_restore_default_configs() { // initial configs stored are the default ones assert_eq!(Conf::load(), Conf::default()); let modified_conf = Conf { settings: Settings { color_gradient: GradientType::Wild, language: Language::ZH, scale_factor: 0.65, mmdb_country: "countrymmdb".to_string(), mmdb_asn: "asnmmdb".to_string(), style_path: format!( "{}/resources/themes/catppuccin.toml", env!("CARGO_MANIFEST_DIR") ), notifications: Notifications { volume: 100, data_notification: Default::default(), favorite_notification: Default::default(), remote_notifications: Default::default(), ip_blacklist_notification: Default::default(), }, style: StyleType::DraculaDark, ip_blacklist: "some-path".to_string(), favorites: HashSet::from([FavoriteKey::Service(Service::Name("https"))]), }, device: ConfigDevice { device_name: "hey-hey".to_string(), }, window: ConfigWindow::new((452.0, 870.0), (440.0, 99.0), (20.0, 20.0)), capture_source_picklist: CaptureSourcePicklist::File, report_sort_type: SortType::Ascending, host_sort_type: SortType::Descending, service_sort_type: SortType::Neutral, program_sort_type: SortType::Neutral, filters: Filters { bpf: "tcp".to_string(), expanded: true, }, import_pcap_path: "whole_day.pcapng".to_string(), export_pcap: ExportPcap { enabled: true, file_name: "sniffnet.pcap".to_string(), directory: "home".to_string(), }, last_opened_setting: SettingsPage::General, last_opened_page: RunningPage::Inspect, data_repr: DataRepr::Packets, }; // we want to be sure that modified config is different from defaults assert_ne!(Conf::default(), modified_conf); //store modified configs modified_conf.store().unwrap(); // assert they've been stored assert_eq!(Conf::load(), modified_conf); // restore defaults Conf::default().store().unwrap(); // assert that defaults are stored assert_eq!(Conf::load(), Conf::default()); // only needed because it will delete config files via its Drop implementation Sniffer::new(Conf::default()); } }