diff --git a/src/cli/mod.rs b/src/cli/mod.rs index b0bb2c62..ce2eaf62 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -126,8 +126,10 @@ mod tests { 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(), }, device: ConfigDevice { device_name: "hey-hey".to_string(), diff --git a/src/gui/pages/thumbnail_page.rs b/src/gui/pages/thumbnail_page.rs index f7e0184b..3ff28859 100644 --- a/src/gui/pages/thumbnail_page.rs +++ b/src/gui/pages/thumbnail_page.rs @@ -11,11 +11,12 @@ use crate::gui::styles::style_constants::FONT_SIZE_FOOTER; use crate::gui::styles::types::style_type::StyleType; use crate::gui::types::message::Message; use crate::networking::types::data_representation::DataRepr; -use crate::networking::types::host::{Host, ThumbnailHost}; +use crate::networking::types::host::ThumbnailHost; use crate::networking::types::info_traffic::InfoTraffic; use crate::report::get_report_entries::{get_host_entries, get_service_entries}; use crate::report::types::sort_type::SortType; use crate::translations::types::language::Language; +use crate::utils::formatted_strings::clip_text; const MAX_ENTRIES: usize = 4; const MAX_CHARS_HOST: usize = 26; @@ -95,12 +96,9 @@ fn host_col<'a>( let mut thumbnail_hosts = Vec::new(); for (host, data_info_host) in &hosts { - let text = host_text(host); - let country = host.country; - let thumbnail_host = ThumbnailHost { - country, - text: text.clone(), - }; + let thumbnail_host = ThumbnailHost::from_host(host, MAX_CHARS_HOST); + let country = thumbnail_host.country; + let text = thumbnail_host.text.clone(); if thumbnail_hosts.contains(&thumbnail_host) { continue; @@ -139,120 +137,3 @@ fn service_col<'a>( } service_col } - -fn host_text(host: &Host) -> String { - clip_text(host.to_host_thumbnail_string(), MAX_CHARS_HOST) -} - -fn clip_text(text: &str, max_chars: usize) -> String { - let text = text.trim(); - let chars = text.chars().collect::>(); - let tot_len = chars.len(); - let slice_len = min(max_chars, tot_len); - - let suspensions = if tot_len > max_chars { "…" } else { "" }; - let slice = if tot_len > max_chars { - &chars[..slice_len - 2] - } else { - &chars[..slice_len] - } - .iter() - .collect::(); - - [slice.trim(), suspensions].concat() -} - -#[cfg(test)] -mod tests { - use crate::gui::pages::thumbnail_page::{ - MAX_CHARS_HOST, MAX_CHARS_SERVICE, clip_text, host_text, - }; - use crate::networking::types::asn::Asn; - use crate::networking::types::host::Host; - - fn host_for_tests(domain: &str, asn: &str) -> Host { - Host { - domain: domain.to_string(), - asn: Asn { - name: asn.to_string(), - code: "512".to_string(), - }, - country: Default::default(), - } - } - - #[test] - fn test_clip_text() { - assert_eq!( - clip_text("iphone-di-doofenshmirtz.local", MAX_CHARS_HOST), - "iphone-di-doofenshmirtz.…" - ); - assert_eq!(clip_text("github.com", MAX_CHARS_HOST), "github.com"); - - assert_eq!(clip_text("https6789012", MAX_CHARS_SERVICE), "https6789012"); - assert_eq!( - clip_text("https67890123", MAX_CHARS_SERVICE), - "https67890123" - ); - assert_eq!( - clip_text("https678901234", MAX_CHARS_SERVICE), - "https678901…" - ); - assert_eq!( - clip_text("https6789012345", MAX_CHARS_SERVICE), - "https678901…" - ); - - assert_eq!( - clip_text("protocol with space", MAX_CHARS_SERVICE), - "protocol wi…" - ); - assert_eq!( - clip_text("protocol90 23456", MAX_CHARS_SERVICE), - "protocol90…" - ); - - assert_eq!( - clip_text(" \n\t sniffnet.net ", MAX_CHARS_HOST), - "sniffnet.net" - ); - assert_eq!( - clip_text(" protocol90 23456 \n ", MAX_CHARS_SERVICE), - "protocol90…" - ); - assert_eq!( - clip_text(" protocol90 23456 ", MAX_CHARS_HOST), - "protocol90 23456" - ); - } - - #[test] - fn test_host_text() { - let host = host_for_tests("iphone-di-doofenshmirtz.local", "AS1234"); - assert_eq!(host_text(&host), "iphone-di-doofenshmirtz.…"); - - let host = host_for_tests("", ""); - assert_eq!(host_text(&host), ""); - - let host = host_for_tests("192.168.1.113", "AS1234"); - assert_eq!(host_text(&host), "AS1234"); - - let host = host_for_tests("192.168.1.113", ""); - assert_eq!(host_text(&host), "192.168.1.113"); - - let host = host_for_tests("", "FASTLY"); - assert_eq!(host_text(&host), "FASTLY"); - - let host = host_for_tests("::", "GOOGLE"); - assert_eq!(host_text(&host), "GOOGLE"); - - let host = host_for_tests("::f", "AKAMAI-TECHNOLOGIES-INCORPORATED"); - assert_eq!(host_text(&host), "AKAMAI-TECHNOLOGIES-INCO…"); - - let host = host_for_tests("::g", "GOOGLE"); - assert_eq!(host_text(&host), "::g"); - - let host = host_for_tests(" ", "GOOGLE"); - assert_eq!(host_text(&host), "GOOGLE"); - } -} diff --git a/src/gui/sniffer.rs b/src/gui/sniffer.rs index 596f05b9..7d02125e 100644 --- a/src/gui/sniffer.rs +++ b/src/gui/sniffer.rs @@ -1366,7 +1366,7 @@ mod tests { DataThresholdExceeded, LoggedNotification, }; use crate::notifications::types::notifications::{ - DataNotification, FavoriteNotification, Notification, Notifications, + DataNotification, Notification, Notifications, SimpleNotification, }; use crate::notifications::types::sound::Sound; use crate::report::types::sort_type::SortType; @@ -1874,14 +1874,24 @@ mod tests { previous_threshold: 800_000, }; - let fav_notification_init = FavoriteNotification { - notify_on_favorite: false, + let fav_notification_init = SimpleNotification { + is_active: false, + sound: Sound::Pop, + }; + + let fav_notification_new = SimpleNotification { + is_active: true, + sound: Sound::Pop, + }; + + let blacklist_notification_init = SimpleNotification { + is_active: false, sound: Sound::Swhoosh, }; - let fav_notification_new = FavoriteNotification { - notify_on_favorite: true, - sound: Sound::Pop, + let blacklist_notification_new = SimpleNotification { + is_active: true, + sound: Sound::Gulp, }; // initial default state @@ -1894,6 +1904,14 @@ mod tests { sniffer.conf.settings.notifications.favorite_notification, fav_notification_init ); + assert_eq!( + sniffer + .conf + .settings + .notifications + .ip_blacklist_notification, + blacklist_notification_init + ); // change volume sniffer.update(Message::ChangeVolume(95)); @@ -1907,14 +1925,13 @@ mod tests { sniffer.conf.settings.notifications.favorite_notification, fav_notification_init, ); - assert_eq!( - sniffer.conf.settings.notifications.data_notification, - bytes_notification_init - ); - assert_eq!( - sniffer.conf.settings.notifications.favorite_notification, - fav_notification_init + sniffer + .conf + .settings + .notifications + .ip_blacklist_notification, + blacklist_notification_init ); // Toggle on bytes notifications @@ -1952,6 +1969,14 @@ mod tests { sniffer.conf.settings.notifications.favorite_notification, fav_notification_init, ); + assert_eq!( + sniffer + .conf + .settings + .notifications + .ip_blacklist_notification, + blacklist_notification_init, + ); // change favorite notifications sniffer.update(Message::UpdateNotificationSettings( @@ -1976,6 +2001,21 @@ mod tests { sniffer.conf.settings.notifications.favorite_notification, fav_notification_new ); + + // change favorite notifications + sniffer.update(Message::UpdateNotificationSettings( + Notification::IpBlacklist(blacklist_notification_new), + true, + )); + + assert_eq!( + sniffer + .conf + .settings + .notifications + .ip_blacklist_notification, + blacklist_notification_new, + ); } #[test] @@ -2129,6 +2169,7 @@ mod tests { sniffer.update(Message::SetPcapImport("/test.pcap".to_string())); sniffer.update(Message::ChangeRunningPage(RunningPage::Notifications)); sniffer.update(Message::DataReprSelection(DataRepr::Bits)); + sniffer.update(Message::LoadIpBlacklist("blacklist_file.csv".to_string())); // force saving configs by quitting the app sniffer.welcome = Some((false, 0)); @@ -2153,11 +2194,10 @@ mod tests { ), notifications: Notifications { volume: 100, - data_notification: Default::default(), - favorite_notification: Default::default(), - remote_notifications: Default::default(), + ..Notifications::default() }, style: StyleType::DraculaDark, + ip_blacklist: "blacklist_file.csv".to_string(), }, window: ConfigWindow::new((1000.0, 999.0), (-5.0, 277.5), (20.0, 20.0)), device: ConfigDevice::default(), diff --git a/src/networking/types/host.rs b/src/networking/types/host.rs index 4f4abb00..903293b2 100644 --- a/src/networking/types/host.rs +++ b/src/networking/types/host.rs @@ -1,6 +1,7 @@ use crate::countries::types::country::Country; use crate::networking::types::asn::Asn; use crate::networking::types::data_info_host::DataInfoHost; +use crate::utils::formatted_strings::clip_text; use std::net::IpAddr; /// Struct to represent a network host @@ -26,17 +27,6 @@ impl Host { ret_val } - /// Used in the thumbnail - pub fn to_host_thumbnail_string(&self) -> &str { - let domain = &self.domain; - let asn = &self.asn.name; - if asn.is_empty() || (!domain.trim().is_empty() && domain.parse::().is_err()) { - domain - } else { - asn - } - } - /// Used in the blacklist notifications pub fn to_host_blacklist_string(&self) -> String { let domain = &self.domain; @@ -74,6 +64,24 @@ pub struct ThumbnailHost { pub text: String, } +impl ThumbnailHost { + /// Constructor from a Host + pub fn from_host(host: &Host, max_text_chars: usize) -> Self { + let domain = &host.domain; + let asn = &host.asn.name; + let unclipped = + if asn.is_empty() || (!domain.trim().is_empty() && domain.parse::().is_err()) { + domain + } else { + asn + }; + Self { + country: host.country.clone(), + text: clip_text(unclipped, max_text_chars), + } + } +} + #[derive(Clone, Debug)] pub struct HostMessage { pub host: Host, @@ -81,3 +89,56 @@ pub struct HostMessage { pub address_to_lookup: IpAddr, pub rdns: String, } + +#[cfg(test)] +mod tests { + use crate::networking::types::asn::Asn; + use crate::networking::types::host::{Host, ThumbnailHost}; + + fn host_for_tests(domain: &str, asn: &str) -> Host { + Host { + domain: domain.to_string(), + asn: Asn { + name: asn.to_string(), + code: "512".to_string(), + }, + country: Default::default(), + } + } + + #[test] + fn test_thumbnail_host_text() { + let host = host_for_tests("iphone-di-doofenshmirtz.local", "AS1234"); + assert_eq!( + ThumbnailHost::from_host(&host, 26).text, + "iphone-di-doofenshmirtz.…" + ); + + let host = host_for_tests("", ""); + assert_eq!(ThumbnailHost::from_host(&host, 26).text, ""); + + let host = host_for_tests("192.168.1.113", "AS1234"); + assert_eq!(ThumbnailHost::from_host(&host, 26).text, "AS1234"); + + let host = host_for_tests("192.168.1.113", ""); + assert_eq!(ThumbnailHost::from_host(&host, 26).text, "192.168.1.113"); + + let host = host_for_tests("", "FASTLY"); + assert_eq!(ThumbnailHost::from_host(&host, 26).text, "FASTLY"); + + let host = host_for_tests("::", "GOOGLE"); + assert_eq!(ThumbnailHost::from_host(&host, 26).text, "GOOGLE"); + + let host = host_for_tests("::f", "AKAMAI-TECHNOLOGIES-INCORPORATED"); + assert_eq!( + ThumbnailHost::from_host(&host, 26).text, + "AKAMAI-TECHNOLOGIES-INCO…" + ); + + let host = host_for_tests("::g", "GOOGLE"); + assert_eq!(ThumbnailHost::from_host(&host, 26).text, "::g"); + + let host = host_for_tests(" ", "GOOGLE"); + assert_eq!(ThumbnailHost::from_host(&host, 26).text, "GOOGLE"); + } +} diff --git a/src/utils/formatted_strings.rs b/src/utils/formatted_strings.rs index 8dcabc23..24510ed0 100644 --- a/src/utils/formatted_strings.rs +++ b/src/utils/formatted_strings.rs @@ -137,6 +137,24 @@ pub fn redirect_stdout_stderr_to_file() None } +pub fn clip_text(text: &str, max_chars: usize) -> String { + let text = text.trim(); + let chars = text.chars().collect::>(); + let tot_len = chars.len(); + let slice_len = min(max_chars, tot_len); + + let suspensions = if tot_len > max_chars { "…" } else { "" }; + let slice = if tot_len > max_chars { + &chars[..slice_len - 2] + } else { + &chars[..slice_len] + } + .iter() + .collect::(); + + [slice.trim(), suspensions].concat() +} + #[cfg(test)] mod tests { use super::*; @@ -209,4 +227,34 @@ mod tests { assert_eq!(f("..."), ".."); assert_eq!(f("no_dots_in_this"), "no_dots_in_this"); } + + #[test] + fn test_clip_text() { + assert_eq!( + clip_text("iphone-di-doofenshmirtz.local", 26), + "iphone-di-doofenshmirtz.…" + ); + assert_eq!(clip_text("github.com", 26), "github.com"); + + assert_eq!(clip_text("https6789012", 13), "https6789012"); + assert_eq!(clip_text("https67890123", 13), "https67890123"); + assert_eq!(clip_text("https678901234", 13), "https678901…"); + assert_eq!(clip_text("https6789012345", 13), "https678901…"); + + assert_eq!(clip_text("protocol with space", 13), "protocol wi…"); + assert_eq!(clip_text("protocol90 23456", 13), "protocol90…"); + + assert_eq!( + clip_text(" \n\t sniffnet.net ", 26), + "sniffnet.net" + ); + assert_eq!( + clip_text(" protocol90 23456 \n ", 12), + "protocol90…" + ); + assert_eq!( + clip_text(" protocol90 23456 ", 26), + "protocol90 23456" + ); + } }