"]
edition = "2024"
description = "Application to comfortably monitor your network traffic"
@@ -37,7 +37,7 @@ strip = true
#═══════════════════════════════════════════════════════════════════════════════════════════════════════════════════════
[dependencies]
-pcap = "2.2.0"
+pcap = "2.3.0"
etherparse = "0.18.0"
chrono = { version = "0.4.41", default-features = false, features = ["clock"] }
plotters = { version = "0.3.7", default-features = false, features = ["area_series", "line_series"] }
@@ -46,27 +46,28 @@ plotters-iced = "0.11.0"
maxminddb = "0.26.0"
confy = "1.0.0"
serde = { version = "1.0.219", default-features = false, features = ["derive"] }
-rodio = { version = "0.20.1", default-features = false, features = ["mp3"] }
+rodio = { version = "0.21.1", default-features = false, features = ["mp3", "playback"] }
dns-lookup = "2.0.4"
-toml = "0.8.23"
+toml = "0.9.2"
ctrlc = { version = "3.4.7", features = ["termination"] }
-rfd = "0.15.3"
-phf = "0.11.3"
-phf_shared = "0.11.3"
+rfd = "0.15.4"
+phf = "0.12.1"
+phf_shared = "0.12.1"
splines = "5.0.0"
-clap = { version = "4.5.39", features = ["derive"] }
-tokio = { version = "1.45.1", features = ["macros"] }
-async-channel = "2.3.1"
+clap = { version = "4.5.41", features = ["derive"] }
+tokio = { version = "1.46.1", features = ["macros"] }
+async-channel = "2.5.0"
semver = "1.0"
+
[target.'cfg(windows)'.dependencies]
gag = "1.0.0"
[target.'cfg(not(target_arch = "powerpc64"))'.dependencies]
-reqwest = { version = "0.12.19", default-features = false, features = ["json", "rustls-tls"] }
+reqwest = { version = "0.12.22", default-features = false, features = ["json", "rustls-tls"] }
[target.'cfg(target_arch = "powerpc64")'.dependencies]
-reqwest = { version = "0.12.19", features = ["json"] }
+reqwest = { version = "0.12.22", features = ["json"] }
#───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
@@ -78,8 +79,8 @@ serial_test = { version = "3.2.0", default-features = false }
#───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
[build-dependencies]
-phf_codegen = "0.11.3"
-phf_shared = "0.11.3"
+phf_codegen = "0.12.1"
+phf_shared = "0.12.1"
rustrict = { version = "0.7.35", default-features = false, features = ["censor"] }
[target."cfg(windows)".build-dependencies]
diff --git a/Dockerfile b/Dockerfile
index ad96da93..9bfbf416 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,4 +1,4 @@
-FROM rust:1.85-slim AS builder
+FROM rust:1.88-slim AS builder
# Install build dependencies for both X11 and Wayland
RUN apt-get update && apt-get install -y \
diff --git a/README.md b/README.md
index d8bb4c9a..524e1d0f 100644
--- a/README.md
+++ b/README.md
@@ -12,7 +12,7 @@
Cross-platform. Intuitive. Reliable.
Translated in:
-🇨🇳 🇩🇪 🇫🇷 🇷🇺 🇵🇹 🇪🇦 🇮🇹 🇵🇱 [+ 12 more languages](https://github.com/GyulyVGC/sniffnet/issues/60)
+🇨🇳 🇩🇪 🇫🇷 🇷🇺 🇵🇹 🇪🇦 🇮🇹 🇵🇱 [+ 14 more languages](https://github.com/GyulyVGC/sniffnet/issues/60)
@@ -25,7 +25,7 @@
-
+
@@ -77,14 +77,14 @@ ## Features
- 📖 view overall **statistics** about your Internet traffic
- 📈 view **real-time charts** about traffic intensity
- 📌 keep an eye on your network even when the application is **minimized**
-- 📁 **export** comprehensive capture reports as **PCAP files**
+- 📁 **import** and **export** comprehensive capture reports as **PCAP files**
- 🔎 identify **6000+ upper layer services**, protocols, trojans, and worms
- 🌐 find out **domain name** and **ASN** of the hosts you are exchanging traffic with
- 🏠 identify connections in your **local network**
-- 🌍 get information about the country of remote hosts (**IP geolocation**)
+- 🌍 discover the **geographical location** of remote hosts
- ⭐ save your **favorite** network hosts
- 🕵️♂️ search and **inspect** each of your network connections in real time
-- 🔉 set **custom notifications** to inform you when defined network events occur
+- 🔉 set custom **notifications** to inform you when defined network events occur
- 🎨 choose the **style** that fits you the most, including custom themes support
- ...and more!
@@ -136,6 +136,7 @@ ## Acknowledgements
- A big shout-out to [all the contributors](https://github.com/GyulyVGC/sniffnet/blob/main/CONTRIBUTORS.md) of Sniffnet!
- The graphical user interface has been realized with [iced](https://github.com/iced-rs/iced), a cross-platform GUI library for Rust focused on simplicity and type-safety
- IP geolocation and ASN data are provided by [MaxMind](https://www.maxmind.com)
+- [Sniffnet](https://ads.fund/token/0xadfc251f8ef00ceaeca2b5c1882dabe5db0833df) project is supported by ADS.FUND
- Last but not least, thanks to [every single stargazer](https://github.com/GyulyVGC/sniffnet/stargazers): all forms of support made it possible to keep improving Sniffnet!
@@ -149,5 +150,5 @@ ## Stay in the loop
-
+
\ No newline at end of file
diff --git a/ROADMAP.md b/ROADMAP.md
index 244f3b2b..cb3f68ac 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -8,5 +8,5 @@ # Roadmap
and embodying the final step of the transition from a personal toy-project to one of the most renowned network analysers available.
-
+
diff --git a/adsfund.json b/adsfund.json
new file mode 100644
index 00000000..d4efce2a
--- /dev/null
+++ b/adsfund.json
@@ -0,0 +1,8 @@
+{
+ "info": "This is verification file for ads.fund project",
+ "project": {
+ "name": "Sniffnet",
+ "walletAddress": "0x8Ab8C652c2689C2111fA12e05FB91002BF1D84e3",
+ "tokenAddress": "0xadfc251f8ef00ceaeca2b5c1882dabe5db0833df"
+ }
+}
\ No newline at end of file
diff --git a/build.rs b/build.rs
index 8ded898c..92d90c9d 100644
--- a/build.rs
+++ b/build.rs
@@ -1,6 +1,7 @@
#[cfg(windows)]
extern crate winres;
+use std::borrow::Cow;
use std::env;
use std::fs::File;
use std::io::{BufRead, BufReader, BufWriter, Write};
@@ -42,14 +43,14 @@ fn build_services_phf() {
let line = line_res.unwrap();
let mut parts = line.split('\t');
// we want to panic if one of the service names is invalid
- let val = get_valid_service_fmt_const(parts.next().unwrap());
+ let val = Cow::Owned(get_valid_service_fmt_const(parts.next().unwrap()));
// we want to panic if port is not a u16, or protocol is not TCP or UDP
let key = get_valid_service_query(parts.next().unwrap());
assert!(parts.next().is_none());
- services_map.entry(key, &val);
+ services_map.entry(key, val);
num_entries += 1;
}
- assert_eq!(num_entries, 12078);
+ assert_eq!(num_entries, 12084);
writeln!(
&mut output,
diff --git a/resources/DB/GeoLite2-ASN.mmdb b/resources/DB/GeoLite2-ASN.mmdb
index 8b359ecc..bb64f735 100644
Binary files a/resources/DB/GeoLite2-ASN.mmdb and b/resources/DB/GeoLite2-ASN.mmdb differ
diff --git a/resources/DB/GeoLite2-Country.mmdb b/resources/DB/GeoLite2-Country.mmdb
index f32738e6..98d59b77 100644
Binary files a/resources/DB/GeoLite2-Country.mmdb and b/resources/DB/GeoLite2-Country.mmdb differ
diff --git a/resources/countries_flags/4x3/zz-bogon.svg b/resources/countries_flags/4x3/zz-bogon.svg
index 65c95a2a..32028e5c 100644
--- a/resources/countries_flags/4x3/zz-bogon.svg
+++ b/resources/countries_flags/4x3/zz-bogon.svg
@@ -1,28 +1 @@
-
-
-
-
+
\ No newline at end of file
diff --git a/resources/fonts/full/subset_characters.txt b/resources/fonts/full/subset_characters.txt
index ab57c2de..33c02891 100644
--- a/resources/fonts/full/subset_characters.txt
+++ b/resources/fonts/full/subset_characters.txt
@@ -112,6 +112,7 @@ z
è
é
ê
+ë
ì
í
î
diff --git a/resources/fonts/subset/icons.ttf b/resources/fonts/subset/icons.ttf
index 23974b82..a77400c6 100644
Binary files a/resources/fonts/subset/icons.ttf and b/resources/fonts/subset/icons.ttf differ
diff --git a/resources/fonts/subset/sarasa-mono-sc-bold.subset.ttf b/resources/fonts/subset/sarasa-mono-sc-bold.subset.ttf
index cd3d53a4..0e28e1ab 100644
Binary files a/resources/fonts/subset/sarasa-mono-sc-bold.subset.ttf and b/resources/fonts/subset/sarasa-mono-sc-bold.subset.ttf differ
diff --git a/resources/fonts/subset/sarasa-mono-sc-regular.subset.ttf b/resources/fonts/subset/sarasa-mono-sc-regular.subset.ttf
index 8ed245cf..f33f1e57 100644
Binary files a/resources/fonts/subset/sarasa-mono-sc-regular.subset.ttf and b/resources/fonts/subset/sarasa-mono-sc-regular.subset.ttf differ
diff --git a/resources/repository/badges/bluesky.svg b/resources/repository/badges/bluesky.svg
index 2cea3a45..ab6fbf93 100644
--- a/resources/repository/badges/bluesky.svg
+++ b/resources/repository/badges/bluesky.svg
@@ -1,9 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/resources/repository/badges/linkedin.svg b/resources/repository/badges/linkedin.svg
index fc97389b..61c67aa5 100644
--- a/resources/repository/badges/linkedin.svg
+++ b/resources/repository/badges/linkedin.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/resources/repository/badges/reddit.svg b/resources/repository/badges/reddit.svg
index c8f98aff..41feebfb 100644
--- a/resources/repository/badges/reddit.svg
+++ b/resources/repository/badges/reddit.svg
@@ -1,25 +1 @@
-
-
-
-
+
\ No newline at end of file
diff --git a/resources/repository/badges/telegram.svg b/resources/repository/badges/telegram.svg
index 0f316ae7..21d2bdfd 100644
--- a/resources/repository/badges/telegram.svg
+++ b/resources/repository/badges/telegram.svg
@@ -1,6 +1 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/resources/repository/badges/x.svg b/resources/repository/badges/x.svg
index 49d5cc8b..23b141b5 100644
--- a/resources/repository/badges/x.svg
+++ b/resources/repository/badges/x.svg
@@ -1,24 +1 @@
-
-
-
-
+
\ No newline at end of file
diff --git a/resources/repository/hr.png b/resources/repository/hr.png
index dc98d317..c74f0328 100644
Binary files a/resources/repository/hr.png and b/resources/repository/hr.png differ
diff --git a/resources/repository/old/hr_1.png b/resources/repository/old/hr_1.png
new file mode 100644
index 00000000..dc98d317
Binary files /dev/null and b/resources/repository/old/hr_1.png differ
diff --git a/resources/repository/pages/catppuccin.png b/resources/repository/pages/catppuccin.png
deleted file mode 100644
index 962f6951..00000000
Binary files a/resources/repository/pages/catppuccin.png and /dev/null differ
diff --git a/resources/repository/pages/deep_cosmos.png b/resources/repository/pages/deep_cosmos.png
new file mode 100644
index 00000000..942edb7d
Binary files /dev/null and b/resources/repository/pages/deep_cosmos.png differ
diff --git a/resources/repository/pages/inspect.png b/resources/repository/pages/inspect.png
index 544ca43c..9e666cd7 100644
Binary files a/resources/repository/pages/inspect.png and b/resources/repository/pages/inspect.png differ
diff --git a/resources/repository/pages/notifications.png b/resources/repository/pages/notifications.png
index ebe3ee24..ab1235b0 100644
Binary files a/resources/repository/pages/notifications.png and b/resources/repository/pages/notifications.png differ
diff --git a/resources/repository/pages/overview.png b/resources/repository/pages/overview.png
index 25bb095d..9dc334c2 100644
Binary files a/resources/repository/pages/overview.png and b/resources/repository/pages/overview.png differ
diff --git a/resources/repository/pages/thumbnail.png b/resources/repository/pages/thumbnail.png
index fb33a249..c1847c66 100644
Binary files a/resources/repository/pages/thumbnail.png and b/resources/repository/pages/thumbnail.png differ
diff --git a/resources/repository/roadmap.png b/resources/repository/roadmap.png
index ce4d52d3..123a0546 100644
Binary files a/resources/repository/roadmap.png and b/resources/repository/roadmap.png differ
diff --git a/services.txt b/services.txt
index 68e24b1e..502ba5d6 100644
--- a/services.txt
+++ b/services.txt
@@ -4930,6 +4930,7 @@ f5-globalsite 2792/tcp
f5-globalsite 2792/udp
initlsmsad 2793/tcp
initlsmsad 2793/udp
+urp 2794/tcp
livestats 2795/tcp
livestats 2795/udp
ac-tech 2796/tcp
@@ -9185,6 +9186,7 @@ fis 5912/tcp
fis 5912/udp
ads-c 5913/tcp
ads-c 5913/udp
+ipsdtls 5914/tcp
teamviewer 5938/tcp
indy 5963/tcp
indy 5963/udp
@@ -9489,6 +9491,7 @@ scup-disc 6315/udp
abb-escp 6316/tcp
abb-escp 6316/udp
nav-data-cmd 6317/tcp
+iona-data 6318/tcp
repsvc 6320/tcp
repsvc 6320/udp
emp-server1 6321/tcp
@@ -10149,6 +10152,7 @@ dell-eql-asm 7569/tcp
aries-kfinder 7570/tcp
aries-kfinder 7570/udp
coherence 7574/tcp
+wtmi-panel 7575/tcp
sun-lm 7588/tcp
sun-lm 7588/udp
qaz 7597/tcp
@@ -10473,6 +10477,7 @@ pando-sec 8276/udp
synapse-nhttp 8280/tcp
synapse-nhttp 8280/udp
libelle 8282/tcp
+winbox 8291/tcp
blp3 8292/tcp
blp3 8292/udp
hiperscan-id 8293/tcp
@@ -11737,6 +11742,7 @@ flexlm9 27009/tcp
flex-lm 27009/udp
flexlm10 27010/tcp
halflife 27015/udp
+chlenix 27016/tcp
mongod 27017/tcp
mongod 27018/tcp
mongod 27019/tcp
diff --git a/src/chart/manage_chart_data.rs b/src/chart/manage_chart_data.rs
index 391091bf..27558ec3 100644
--- a/src/chart/manage_chart_data.rs
+++ b/src/chart/manage_chart_data.rs
@@ -4,11 +4,11 @@
use crate::networking::types::info_traffic::InfoTraffic;
impl TrafficChart {
- pub fn update_charts_data(&mut self, info_traffic: &InfoTraffic, no_more_packets: bool) {
+ pub fn update_charts_data(&mut self, info_traffic_msg: &InfoTraffic, no_more_packets: bool) {
self.no_more_packets = no_more_packets;
if self.ticks == 0 {
- self.first_packet_timestamp = info_traffic.last_packet_timestamp;
+ self.first_packet_timestamp = info_traffic_msg.last_packet_timestamp;
}
#[allow(clippy::cast_precision_loss)]
@@ -16,16 +16,13 @@ pub fn update_charts_data(&mut self, info_traffic: &InfoTraffic, no_more_packets
self.ticks += 1;
#[allow(clippy::cast_precision_loss)]
- let out_bytes_entry =
- -1.0 * (info_traffic.tot_out_bytes - info_traffic.tot_out_bytes_prev) as f32;
+ let out_bytes_entry = -(info_traffic_msg.tot_data_info.outgoing_bytes() as f32);
#[allow(clippy::cast_precision_loss)]
- let in_bytes_entry = (info_traffic.tot_in_bytes - info_traffic.tot_in_bytes_prev) as f32;
+ let in_bytes_entry = info_traffic_msg.tot_data_info.incoming_bytes() as f32;
#[allow(clippy::cast_precision_loss)]
- let out_packets_entry =
- -1.0 * (info_traffic.tot_out_packets - info_traffic.tot_out_packets_prev) as f32;
+ let out_packets_entry = -(info_traffic_msg.tot_data_info.outgoing_packets() as f32);
#[allow(clippy::cast_precision_loss)]
- let in_packets_entry =
- (info_traffic.tot_in_packets - info_traffic.tot_in_packets_prev) as f32;
+ let in_packets_entry = info_traffic_msg.tot_data_info.incoming_packets() as f32;
let out_bytes_point = (tot_seconds, out_bytes_entry);
let in_bytes_point = (tot_seconds, in_bytes_entry);
@@ -144,8 +141,8 @@ pub struct ChartSeries {
}
fn reduce_all_time_data(all_time: &mut Vec<(f32, f32)>) {
- // bisect data until we have less than 300 points
- while all_time.len() > 300 {
+ // bisect data until we have less than 150 points
+ while all_time.len() > 150 {
let mut new_vec = Vec::new();
all_time.iter().enumerate().for_each(|(i, (x, y))| {
if i % 2 == 0 {
@@ -158,11 +155,64 @@ fn reduce_all_time_data(all_time: &mut Vec<(f32, f32)>) {
}
}
+// impl TrafficChart {
+// use crate::gui::styles::types::style_type::StyleType;
+// use crate::translations::types::language::Language;
+// use std::io::Read;
+// pub fn sample_for_screenshot() -> Self {
+// let get_rand = |delta: f32| {
+// let mut f = std::fs::File::open("/dev/urandom").unwrap();
+// let mut buf = [0u8; 1];
+// f.read_exact(&mut buf).unwrap();
+// let x = buf[0];
+// x as f32 / 255.0 * 2.0 * delta - delta
+// };
+//
+// let mut chart = TrafficChart::new(StyleType::default(), Language::default());
+//
+// chart.ticks = 5 * 60 - 2;
+// let x_range = chart.ticks - 30..chart.ticks;
+//
+// let in_base = 35_000.0;
+// let in_delta = 7_000.0;
+// let out_base = -15_000.0;
+// let out_delta = 3_000.0;
+//
+// chart.in_bytes.spline = Spline::from_vec(
+// x_range
+// .clone()
+// .map(|x| {
+// Key::new(
+// x as f32,
+// in_base + get_rand(in_delta),
+// Interpolation::Cosine,
+// )
+// })
+// .collect(),
+// );
+// chart.out_bytes.spline = Spline::from_vec(
+// x_range
+// .map(|x| {
+// Key::new(
+// x as f32,
+// out_base + get_rand(out_delta),
+// Interpolation::Cosine,
+// )
+// })
+// .collect(),
+// );
+// chart.min_bytes = get_min(&chart.out_bytes);
+// chart.max_bytes = get_max(&chart.in_bytes);
+// chart
+// }
+// }
+
#[cfg(test)]
mod tests {
use splines::{Interpolation, Key, Spline};
use crate::chart::manage_chart_data::{ChartSeries, get_max, get_min};
+ use crate::networking::types::data_info::DataInfo;
use crate::utils::types::timestamp::Timestamp;
use crate::{ChartType, InfoTraffic, Language, StyleType, TrafficChart};
@@ -248,8 +298,7 @@ fn test_chart_data_updates() {
spline: received_spl,
all_time: vec![],
};
- let tot_sent = 1000 * 28 + 500;
- let tot_received = 21000 * 28 + 1000;
+ let tot_data_info = DataInfo::new_for_tests(4444, 3333, 2222, 1111);
let mut traffic_chart = TrafficChart {
ticks: 29,
out_bytes: sent.clone(),
@@ -271,15 +320,8 @@ fn test_chart_data_updates() {
let mut info_traffic = InfoTraffic {
all_bytes: 0,
all_packets: 0,
- tot_out_bytes: tot_sent + 1111,
- tot_in_bytes: tot_received + 2222,
- tot_out_packets: tot_sent + 3333,
- tot_in_packets: tot_received + 4444,
+ tot_data_info,
dropped_packets: 0,
- tot_out_bytes_prev: tot_sent,
- tot_in_bytes_prev: tot_received,
- tot_out_packets_prev: tot_sent,
- tot_in_packets_prev: tot_received,
..Default::default()
};
@@ -291,12 +333,6 @@ fn test_chart_data_updates() {
assert_eq!(get_min(&traffic_chart.out_packets), -3333.0);
assert_eq!(get_max(&traffic_chart.in_bytes), 21000.0);
- // prev values aren't updated here anymore: manually set them
- info_traffic.tot_out_bytes_prev = info_traffic.tot_out_bytes;
- info_traffic.tot_in_bytes_prev = info_traffic.tot_in_bytes;
- info_traffic.tot_out_packets_prev = info_traffic.tot_out_packets;
- info_traffic.tot_in_packets_prev = info_traffic.tot_in_packets;
-
let mut sent_bytes = sent.clone();
sent_bytes
.spline
@@ -337,17 +373,9 @@ fn test_chart_data_updates() {
received_bytes.spline.keys()
);
- info_traffic.tot_out_bytes += 99;
- info_traffic.tot_in_packets += 990;
- info_traffic.tot_in_bytes += 2;
+ info_traffic.tot_data_info = DataInfo::new_for_tests(990, 1, 2, 99);
traffic_chart.update_charts_data(&info_traffic, false);
- info_traffic.tot_out_bytes_prev = info_traffic.tot_out_bytes;
- info_traffic.tot_in_bytes_prev = info_traffic.tot_in_bytes;
- info_traffic.tot_out_packets_prev = info_traffic.tot_out_packets;
- info_traffic.tot_in_packets_prev = info_traffic.tot_in_packets;
- info_traffic.tot_out_bytes += 77;
- info_traffic.tot_in_packets += 1;
- info_traffic.tot_out_packets += 220;
+ info_traffic.tot_data_info = DataInfo::new_for_tests(1, 220, 0, 77);
traffic_chart.update_charts_data(&info_traffic, false);
sent_bytes.spline.remove(0);
@@ -370,7 +398,7 @@ fn test_chart_data_updates() {
sent_packets.spline.remove(0);
sent_packets
.spline
- .add(Key::new(30.0, 0.0, Interpolation::Cosine));
+ .add(Key::new(30.0, -1.0, Interpolation::Cosine));
sent_packets
.spline
.add(Key::new(31.0, -220.0, Interpolation::Cosine));
diff --git a/src/chart/types/chart_type.rs b/src/chart/types/chart_type.rs
index 29b308ee..0e339435 100644
--- a/src/chart/types/chart_type.rs
+++ b/src/chart/types/chart_type.rs
@@ -1,8 +1,9 @@
use crate::Language;
use crate::translations::translations::{bytes_translation, packets_translation};
+use serde::{Deserialize, Serialize};
/// Enum representing the possible kind of chart displayed.
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum ChartType {
Packets,
Bytes,
diff --git a/src/chart/types/traffic_chart.rs b/src/chart/types/traffic_chart.rs
index e89f9a2d..835ff54c 100644
--- a/src/chart/types/traffic_chart.rs
+++ b/src/chart/types/traffic_chart.rs
@@ -331,8 +331,8 @@ fn build_chart(
}
}
-const PTS: usize = 300;
fn sample_spline(spline: &Spline) -> Vec<(f32, f32)> {
+ let pts = spline.len() * 10; // 10 samples per key
let mut ret_val = Vec::new();
let len = spline.len();
let first_x = spline
@@ -344,8 +344,8 @@ fn sample_spline(spline: &Spline) -> Vec<(f32, f32)> {
.unwrap_or(&Key::new(0.0, 0.0, Interpolation::Cosine))
.t;
#[allow(clippy::cast_precision_loss)]
- let delta = (last_x - first_x) / (PTS as f32 - 1.0);
- for i in 0..PTS {
+ let delta = (last_x - first_x) / (pts as f32 - 1.0);
+ for i in 0..pts {
#[allow(clippy::cast_precision_loss)]
let x = first_x + delta * i as f32;
let p = spline.clamped_sample(x).unwrap_or_default();
@@ -358,7 +358,7 @@ fn sample_spline(spline: &Spline) -> Vec<(f32, f32)> {
mod tests {
use splines::{Interpolation, Key, Spline};
- use crate::chart::types::traffic_chart::{PTS, sample_spline};
+ use crate::chart::types::traffic_chart::sample_spline;
#[test]
fn test_spline_samples() {
@@ -401,14 +401,15 @@ fn test_spline_samples() {
let eps = 0.001;
+ let pts = spline.len() * 10;
let samples = sample_spline(&spline);
- assert_eq!(samples.len(), PTS);
+ assert_eq!(samples.len(), pts);
let delta = samples[1].0 - samples[0].0;
assert_eq!(samples[0].0, 0.0);
assert_eq!(samples[0].1, -500.0);
- for i in 0..PTS - 1 {
+ for i in 0..pts - 1 {
assert_eq!(
(samples[i + 1].0 * 10_000.0 - samples[i].0 * 10_000.0).round() / 10_000.0,
(delta * 10_000.0).round() / 10_000.0
@@ -417,7 +418,7 @@ fn test_spline_samples() {
assert!(samples[i].1 >= -1000.0 - eps);
assert!(samples[i + 1].1 < samples[i].1 + eps);
}
- assert_eq!(samples[PTS - 1].0, 28.0);
- assert_eq!(samples[PTS - 1].1, -1000.0);
+ assert_eq!(samples[pts - 1].0, 28.0);
+ assert_eq!(samples[pts - 1].1, -1000.0);
}
}
diff --git a/src/cli/mod.rs b/src/cli/mod.rs
index 14e23f9f..0256167c 100644
--- a/src/cli/mod.rs
+++ b/src/cli/mod.rs
@@ -98,8 +98,7 @@ fn test_restore_default_configs() {
),
notifications: Notifications {
volume: 100,
- packets_notification: Default::default(),
- bytes_notification: Default::default(),
+ data_notification: Default::default(),
favorite_notification: Default::default(),
},
style: StyleType::Custom(ExtraStyles::DraculaDark),
diff --git a/src/countries/country_utils.rs b/src/countries/country_utils.rs
index 465bc6b1..cefc3b31 100644
--- a/src/countries/country_utils.rs
+++ b/src/countries/country_utils.rs
@@ -8,14 +8,15 @@
AD, AE, AF, AG, AI, AL, AM, AO, AQ, AR, AS, AT, AU, AW, AX, AZ, BA, BB, BD, BE, BF, BG, BH, BI,
BJ, BM, BN, BO, BOGON, BR, BROADCAST, BS, BT, BV, BW, BY, BZ, CA, CC, CD, CF, CG, CH, CI, CK,
CL, CM, CN, CO, COMPUTER, CR, CU, CV, CW, CX, CY, CZ, DE, DJ, DK, DM, DO, DZ, EC, EE, EG, EH,
- ER, ES, ET, FI, FJ, FK, FLAGS_WIDTH_BIG, FLAGS_WIDTH_SMALL, FM, FO, FR, GA, GB, GD, GE, GG, GH,
- GI, GL, GM, GN, GQ, GR, GS, GT, GU, GW, GY, HK, HN, HOME, HR, HT, HU, ID, IE, IL, IM, IN, IO,
- IQ, IR, IS, IT, JE, JM, JO, JP, KE, KG, KH, KI, KM, KN, KP, KR, KW, KY, KZ, LA, LB, LC, LI, LK,
- LR, LS, LT, LU, LV, LY, MA, MC, MD, ME, MG, MH, MK, ML, MM, MN, MO, MP, MR, MS, MT, MU,
- MULTICAST, MV, MW, MX, MY, MZ, NA, NC, NE, NF, NG, NI, NL, NO, NP, NR, NU, NZ, OM, PA, PE, PF,
- PG, PH, PK, PL, PN, PR, PS, PT, PW, PY, QA, RO, RS, RU, RW, SA, SB, SC, SD, SE, SG, SH, SI, SK,
- SL, SM, SN, SO, SR, SS, ST, SV, SX, SY, SZ, TC, TD, TF, TG, TH, TJ, TK, TL, TM, TN, TO, TR, TT,
- TV, TW, TZ, UA, UG, UNKNOWN, US, UY, UZ, VA, VC, VE, VG, VI, VN, VU, WS, YE, ZA, ZM, ZW,
+ ER, ES, ET, FI, FJ, FK, FLAGS_HEIGHT_BIG, FLAGS_WIDTH_BIG, FLAGS_WIDTH_SMALL, FM, FO, FR, GA,
+ GB, GD, GE, GG, GH, GI, GL, GM, GN, GQ, GR, GS, GT, GU, GW, GY, HK, HN, HOME, HR, HT, HU, ID,
+ IE, IL, IM, IN, IO, IQ, IR, IS, IT, JE, JM, JO, JP, KE, KG, KH, KI, KM, KN, KP, KR, KW, KY, KZ,
+ LA, LB, LC, LI, LK, LR, LS, LT, LU, LV, LY, MA, MC, MD, ME, MG, MH, MK, ML, MM, MN, MO, MP, MR,
+ MS, MT, MU, MULTICAST, MV, MW, MX, MY, MZ, NA, NC, NE, NF, NG, NI, NL, NO, NP, NR, NU, NZ, OM,
+ PA, PE, PF, PG, PH, PK, PL, PN, PR, PS, PT, PW, PY, QA, RO, RS, RU, RW, SA, SB, SC, SD, SE, SG,
+ SH, SI, SK, SL, SM, SN, SO, SR, SS, ST, SV, SX, SY, SZ, TC, TD, TF, TG, TH, TJ, TK, TL, TM, TN,
+ TO, TR, TT, TV, TW, TZ, UA, UG, UNKNOWN, US, UY, UZ, VA, VC, VE, VG, VI, VN, VU, WS, YE, ZA,
+ ZM, ZW,
};
use crate::countries::types::country::Country;
use crate::gui::styles::container::ContainerType;
@@ -384,7 +385,7 @@ pub fn get_computer_tooltip<'a>(
)))
.class(SvgType::AdaptColor)
.width(FLAGS_WIDTH_BIG)
- .height(FLAGS_WIDTH_BIG * 0.75);
+ .height(FLAGS_HEIGHT_BIG);
let tooltip = match (is_my_address, is_local, is_bogon, traffic_type) {
(true, _, _, _) => your_network_adapter_translation(language).to_string(),
diff --git a/src/countries/flags_pictures.rs b/src/countries/flags_pictures.rs
index f13feced..4f6c9b72 100644
--- a/src/countries/flags_pictures.rs
+++ b/src/countries/flags_pictures.rs
@@ -1,6 +1,8 @@
pub const FLAGS_WIDTH_SMALL: f32 = 20.0;
pub const FLAGS_WIDTH_BIG: f32 = 37.5;
+pub const FLAGS_HEIGHT_BIG: f32 = FLAGS_WIDTH_BIG * 3.0 / 4.0;
+
pub const AD: &[u8] = include_bytes!("../../resources/countries_flags/4x3/ad.svg");
pub const AE: &[u8] = include_bytes!("../../resources/countries_flags/4x3/ae.svg");
pub const AF: &[u8] = include_bytes!("../../resources/countries_flags/4x3/af.svg");
diff --git a/src/gui/pages/inspect_page.rs b/src/gui/pages/inspect_page.rs
index c92f8d61..0274f63a 100644
--- a/src/gui/pages/inspect_page.rs
+++ b/src/gui/pages/inspect_page.rs
@@ -554,10 +554,9 @@ fn get_agglomerates_row<'a>(
) -> Row<'a, Message, StyleType> {
let tot_packets = tot.tot_packets();
let tot_bytes = tot.tot_bytes();
- let width = ReportCol::FILTER_COLUMNS_WIDTH;
- let (in_length, out_length) = get_bars_length(width, chart_type, &tot, &tot);
- let bars = get_bars(in_length, out_length);
+ let (in_length, out_length) = get_bars_length(chart_type, &tot, &tot);
+ let bars = get_bars(in_length, out_length).width(ReportCol::FILTER_COLUMNS_WIDTH);
let bytes_col = Column::new()
.align_x(Alignment::Center)
@@ -640,7 +639,7 @@ mod tests {
#[test]
fn test_table_titles_display_and_tooltip_values_for_each_language() {
// check glyph len when adding new language...
- assert_eq!(Language::ALL.len(), 21);
+ assert_eq!(Language::ALL.len(), 22);
for report_col in ReportCol::ALL {
for language in Language::ALL {
let (title, title_small, tooltip_val) =
diff --git a/src/gui/pages/notifications_page.rs b/src/gui/pages/notifications_page.rs
index b29f9e3e..0249de8d 100644
--- a/src/gui/pages/notifications_page.rs
+++ b/src/gui/pages/notifications_page.rs
@@ -1,34 +1,41 @@
-use iced::Length::FillPortion;
-use iced::widget::scrollable::Direction;
-use iced::widget::text::LineHeight;
-use iced::widget::tooltip::Position;
-use iced::widget::{Column, Container, Row, Scrollable, Text, Tooltip};
-use iced::widget::{Space, button, vertical_space};
-use iced::{Alignment, Font, Length};
-use std::fmt::Write;
-
-use crate::countries::country_utils::get_flag_tooltip;
+use crate::chart::types::chart_type::ChartType;
+use crate::countries::country_utils::get_computer_tooltip;
+use crate::countries::flags_pictures::FLAGS_HEIGHT_BIG;
use crate::gui::components::header::get_button_settings;
use crate::gui::components::tab::get_pages_tabs;
use crate::gui::components::types::my_modal::MyModal;
+use crate::gui::pages::overview_page::{get_bars, get_bars_length, host_bar, service_bar};
use crate::gui::pages::types::settings_page::SettingsPage;
use crate::gui::styles::container::ContainerType;
use crate::gui::styles::scrollbar::ScrollbarType;
use crate::gui::styles::style_constants::FONT_SIZE_FOOTER;
use crate::gui::styles::text::TextType;
use crate::gui::types::message::Message;
+use crate::networking::types::data_info::DataInfo;
+use crate::networking::types::data_info_host::DataInfoHost;
+use crate::networking::types::host::Host;
+use crate::networking::types::service::Service;
+use crate::networking::types::traffic_type::TrafficType;
use crate::notifications::types::logged_notification::{
- BytesThresholdExceeded, FavoriteTransmitted, LoggedNotification, PacketsThresholdExceeded,
+ DataThresholdExceeded, FavoriteTransmitted, LoggedNotification,
};
+use crate::report::types::sort_type::SortType;
use crate::translations::translations::{
- bytes_exceeded_translation, bytes_exceeded_value_translation, clear_all_translation,
- favorite_transmitted_translation, incoming_translation, no_notifications_received_translation,
- no_notifications_set_translation, only_last_30_translation, outgoing_translation,
- packets_exceeded_translation, packets_exceeded_value_translation, per_second_translation,
+ bytes_exceeded_translation, clear_all_translation, favorite_transmitted_translation,
+ no_notifications_received_translation, no_notifications_set_translation,
+ only_last_30_translation, packets_exceeded_translation, per_second_translation,
threshold_translation,
};
use crate::utils::types::icon::Icon;
use crate::{ByteMultiple, ConfigSettings, Language, RunningPage, Sniffer, StyleType};
+use iced::Length::FillPortion;
+use iced::widget::scrollable::Direction;
+use iced::widget::text::LineHeight;
+use iced::widget::tooltip::Position;
+use iced::widget::{Column, Container, Row, Rule, Scrollable, Text, Tooltip, horizontal_space};
+use iced::widget::{Space, button, vertical_space};
+use iced::{Alignment, Font, Length, Padding};
+use std::cmp::max;
/// Computes the body of gui notifications page
pub fn notifications_page(sniffer: &Sniffer) -> Container {
@@ -53,30 +60,29 @@ pub fn notifications_page(sniffer: &Sniffer) -> Container {
sniffer.unread_notifications,
);
- tab_and_body = tab_and_body.push(tabs).push(Space::with_height(15));
+ tab_and_body = tab_and_body.push(tabs);
- if notifications.packets_notification.threshold.is_none()
- && notifications.bytes_notification.threshold.is_none()
+ if notifications.data_notification.threshold.is_none()
&& !notifications.favorite_notification.notify_on_favorite
- && sniffer.logged_notifications.is_empty()
+ && sniffer.logged_notifications.0.is_empty()
{
let body = body_no_notifications_set(font, language);
tab_and_body = tab_and_body.push(body);
- } else if sniffer.logged_notifications.is_empty() {
+ } else if sniffer.logged_notifications.0.is_empty() {
let body = body_no_notifications_received(font, language, &sniffer.dots_pulse.0);
tab_and_body = tab_and_body.push(body);
} else {
let logged_notifications = logged_notifications(sniffer);
let body_row = Row::new()
- .width(Length::Fill)
+ .spacing(10)
+ .padding(Padding::new(10.0).bottom(0))
.push(
- Container::new(if sniffer.logged_notifications.len() < 30 {
+ Container::new(if sniffer.logged_notifications.0.len() < 30 {
Text::new("")
} else {
Text::new(only_last_30_translation(language)).font(font)
})
- .padding(10)
- .width(Length::Fill)
+ .width(150)
.height(Length::Fill)
.align_x(Alignment::Center)
.align_y(Alignment::Center),
@@ -87,7 +93,7 @@ pub fn notifications_page(sniffer: &Sniffer) -> Container {
))
.push(
Container::new(get_button_clear_all(font, language))
- .width(Length::Fill)
+ .width(150)
.height(Length::Fill)
.align_x(Alignment::Center)
.align_y(Alignment::Center),
@@ -138,40 +144,35 @@ fn body_no_notifications_received(
.push(Space::with_height(FillPortion(2)))
}
-fn packets_notification_log<'a>(
- logged_notification: PacketsThresholdExceeded,
+fn data_notification_log<'a>(
+ logged_notification: &DataThresholdExceeded,
+ first_entry_data_info: DataInfo,
language: Language,
font: Font,
) -> Container<'a, Message, StyleType> {
+ let chart_type = logged_notification.chart_type;
+ let data_string = if chart_type == ChartType::Bytes {
+ ByteMultiple::formatted_string(logged_notification.threshold.into())
+ } else {
+ logged_notification.threshold.to_string()
+ };
+ let icon = if chart_type == ChartType::Packets {
+ Icon::PacketsThreshold
+ } else {
+ Icon::BytesThreshold
+ }
+ .to_text()
+ .size(80)
+ .line_height(LineHeight::Relative(1.0));
let threshold_str = format!(
- "{}: {} {}",
+ "{}: {data_string} {}",
threshold_translation(language),
- logged_notification.threshold,
per_second_translation(language)
);
- let mut incoming_str = " - ".to_string();
- incoming_str.push_str(incoming_translation(language));
- incoming_str.push_str(": ");
- incoming_str.push_str(&logged_notification.incoming.to_string());
- let mut outgoing_str = " - ".to_string();
- outgoing_str.push_str(outgoing_translation(language));
- outgoing_str.push_str(": ");
- outgoing_str.push_str(&logged_notification.outgoing.to_string());
let content = Row::new()
.align_y(Alignment::Center)
- .height(Length::Fill)
.spacing(30)
- .push(
- Tooltip::new(
- Icon::PacketsThreshold
- .to_text()
- .size(80)
- .line_height(LineHeight::Relative(1.0)),
- Text::new(packets_exceeded_translation(language)).font(font),
- Position::FollowCursor,
- )
- .class(ContainerType::Tooltip),
- )
+ .push(icon)
.push(
Column::new()
.spacing(7)
@@ -180,12 +181,16 @@ fn packets_notification_log<'a>(
Row::new()
.spacing(8)
.push(Icon::Clock.to_text())
- .push(Text::new(logged_notification.timestamp).font(font)),
+ .push(Text::new(logged_notification.timestamp.clone()).font(font)),
)
.push(
- Text::new(packets_exceeded_translation(language))
- .class(TextType::Title)
- .font(font),
+ Text::new(if chart_type == ChartType::Bytes {
+ bytes_exceeded_translation(language)
+ } else {
+ packets_exceeded_translation(language)
+ })
+ .class(TextType::Title)
+ .font(font),
)
.push(
Text::new(threshold_str)
@@ -194,149 +199,50 @@ fn packets_notification_log<'a>(
.font(font),
),
)
- .push(
- Column::new()
- .spacing(7)
- .push(
- Text::new(packets_exceeded_value_translation(
- language,
- logged_notification.incoming + logged_notification.outgoing,
- ))
- .font(font),
- )
- .push(Text::new(incoming_str).font(font))
- .push(Text::new(outgoing_str).font(font)),
- );
- Container::new(content)
- .height(120)
- .width(800)
- .padding(10)
- .class(ContainerType::BorderedRound)
-}
-
-fn bytes_notification_log<'a>(
- logged_notification: BytesThresholdExceeded,
- language: Language,
- font: Font,
-) -> Container<'a, Message, StyleType> {
- let mut threshold_str = threshold_translation(language).to_string();
- threshold_str.push_str(": ");
- threshold_str.push_str(&ByteMultiple::formatted_string(
- (logged_notification.threshold).into(),
- ));
-
- let _ = write!(threshold_str, " {}", per_second_translation(language));
- let mut incoming_str = " - ".to_string();
- incoming_str.push_str(incoming_translation(language));
- incoming_str.push_str(": ");
- incoming_str.push_str(&ByteMultiple::formatted_string(u128::from(
- logged_notification.incoming,
- )));
- let mut outgoing_str = " - ".to_string();
- outgoing_str.push_str(outgoing_translation(language));
- outgoing_str.push_str(": ");
- outgoing_str.push_str(&ByteMultiple::formatted_string(u128::from(
- logged_notification.outgoing,
- )));
- let content = Row::new()
- .spacing(30)
- .align_y(Alignment::Center)
- .height(Length::Fill)
- .push(
- Tooltip::new(
- Icon::BytesThreshold
- .to_text()
- .size(80)
- .line_height(LineHeight::Relative(1.0)),
- Text::new(bytes_exceeded_translation(language)).font(font),
- Position::FollowCursor,
- )
- .class(ContainerType::Tooltip),
- )
- .push(
- Column::new()
- .spacing(7)
- .width(250)
- .push(
- Row::new()
- .spacing(8)
- .push(Icon::Clock.to_text())
- .push(Text::new(logged_notification.timestamp).font(font)),
- )
- .push(
- Text::new(bytes_exceeded_translation(language))
- .class(TextType::Title)
- .font(font),
- )
- .push(
- Text::new(threshold_str)
- .size(FONT_SIZE_FOOTER)
- .class(TextType::Subtitle)
- .font(font),
- ),
- )
- .push(
- Column::new()
- .spacing(7)
- .push(
- Text::new(bytes_exceeded_value_translation(
- language,
- &ByteMultiple::formatted_string(u128::from(
- logged_notification.incoming + logged_notification.outgoing,
- )),
- ))
- .font(font),
- )
- .push(Text::new(incoming_str).font(font))
- .push(Text::new(outgoing_str).font(font)),
- );
- Container::new(content)
- .height(120)
- .width(800)
- .padding(10)
+ .push(threshold_bar(
+ logged_notification,
+ first_entry_data_info,
+ language,
+ font,
+ ));
+ let content_and_extra = Column::new()
+ .spacing(10)
+ .push(content)
+ .push(button_expand(
+ logged_notification.id,
+ logged_notification.is_expanded,
+ ))
+ .push_maybe(data_notification_extra(logged_notification, font, language));
+ Container::new(content_and_extra)
+ .width(Length::Fill)
+ .padding(15)
.class(ContainerType::BorderedRound)
}
fn favorite_notification_log<'a>(
- logged_notification: FavoriteTransmitted,
+ logged_notification: &FavoriteTransmitted,
+ first_entry_data_info: DataInfo,
+ chart_type: ChartType,
language: Language,
font: Font,
) -> Container<'a, Message, StyleType> {
- let country = logged_notification.host.country;
- let asn = &logged_notification.host.asn;
-
- let mut domain_asn_str = logged_notification.host.domain;
- if !asn.name.is_empty() {
- let _ = write!(domain_asn_str, " - {}", asn.name);
- }
-
- let row_flag_details = Row::new()
- .align_y(Alignment::Center)
- .spacing(5)
- .push(get_flag_tooltip(
- country,
- &logged_notification.data_info_host,
- language,
- font,
- false,
- ))
- .push(Text::new(domain_asn_str).font(font));
+ let host_bar = host_bar(
+ &logged_notification.host,
+ &logged_notification.data_info_host,
+ chart_type,
+ first_entry_data_info,
+ font,
+ language,
+ );
let content = Row::new()
.spacing(30)
.align_y(Alignment::Center)
- .height(Length::Fill)
.push(
- Tooltip::new(
- Icon::Star
- .to_text()
- .size(80)
- .class(TextType::Starred)
- .line_height(LineHeight::Relative(1.0)),
- Text::new(favorite_transmitted_translation(language)).font(font),
- Position::FollowCursor,
- )
- .class(ContainerType::Tooltip),
+ Icon::Star
+ .to_text()
+ .size(80)
+ .line_height(LineHeight::Relative(1.0)),
)
.push(
Column::new()
@@ -346,7 +252,7 @@ fn favorite_notification_log<'a>(
Row::new()
.spacing(8)
.push(Icon::Clock.to_text())
- .push(Text::new(logged_notification.timestamp).font(font)),
+ .push(Text::new(logged_notification.timestamp.clone()).font(font)),
)
.push(
Text::new(favorite_transmitted_translation(language))
@@ -354,16 +260,11 @@ fn favorite_notification_log<'a>(
.font(font),
),
)
- .push(
- Column::new()
- .spacing(7)
- .width(Length::Fill)
- .push(row_flag_details),
- );
+ .push(host_bar);
+
Container::new(content)
- .height(120)
- .width(800)
- .padding(10)
+ .width(Length::Fill)
+ .padding(15)
.class(ContainerType::BorderedRound)
}
@@ -393,25 +294,165 @@ fn logged_notifications<'a>(sniffer: &Sniffer) -> Column<'a, Message, StyleType>
let ConfigSettings {
style, language, ..
} = sniffer.configs.settings;
+ let chart_type = sniffer.traffic_chart.chart_type;
let font = style.get_extension().font;
let mut ret_val = Column::new()
- .width(830)
- .padding(5)
+ .padding(Padding::ZERO.right(15))
.spacing(10)
.align_x(Alignment::Center);
- for logged_notification in &sniffer.logged_notifications {
+ let first_entry_data_info = sniffer
+ .logged_notifications
+ .0
+ .iter()
+ .map(LoggedNotification::data_info)
+ .max_by(|d1, d2| d1.compare(d2, SortType::Ascending, chart_type))
+ .unwrap_or_default();
+
+ for logged_notification in &sniffer.logged_notifications.0 {
ret_val = ret_val.push(match logged_notification {
- LoggedNotification::PacketsThresholdExceeded(packet_threshold_exceeded) => {
- packets_notification_log(packet_threshold_exceeded.clone(), language, font)
- }
- LoggedNotification::BytesThresholdExceeded(byte_threshold_exceeded) => {
- bytes_notification_log(byte_threshold_exceeded.clone(), language, font)
+ LoggedNotification::DataThresholdExceeded(data_threshold_exceeded) => {
+ data_notification_log(
+ data_threshold_exceeded,
+ first_entry_data_info,
+ language,
+ font,
+ )
}
LoggedNotification::FavoriteTransmitted(favorite_transmitted) => {
- favorite_notification_log(favorite_transmitted.clone(), language, font)
+ favorite_notification_log(
+ favorite_transmitted,
+ first_entry_data_info,
+ chart_type,
+ language,
+ font,
+ )
}
});
}
ret_val
}
+
+fn threshold_bar<'a>(
+ logged_notification: &DataThresholdExceeded,
+ first_entry_data_info: DataInfo,
+ language: Language,
+ font: Font,
+) -> Row<'a, Message, StyleType> {
+ let chart_type = logged_notification.chart_type;
+ let data_info = logged_notification.data_info;
+ let (incoming_bar_len, outgoing_bar_len) =
+ get_bars_length(chart_type, &first_entry_data_info, &data_info);
+
+ Row::new()
+ .align_y(Alignment::Center)
+ .spacing(5)
+ .push(get_computer_tooltip(
+ true,
+ true,
+ None,
+ TrafficType::Unicast,
+ language,
+ font,
+ ))
+ .push(
+ Column::new()
+ .spacing(1)
+ .push(
+ Row::new().push(horizontal_space()).push(
+ Text::new(if chart_type.eq(&ChartType::Packets) {
+ data_info.tot_packets().to_string()
+ } else {
+ ByteMultiple::formatted_string(data_info.tot_bytes())
+ })
+ .font(font),
+ ),
+ )
+ .push(get_bars(incoming_bar_len, outgoing_bar_len)),
+ )
+}
+
+fn button_expand<'a>(
+ notification_id: usize,
+ is_expanded: bool,
+) -> Container<'a, Message, StyleType> {
+ let button = button(
+ if is_expanded {
+ Icon::SortAscending
+ } else {
+ Icon::SortDescending
+ }
+ .to_text()
+ .size(11)
+ .align_x(Alignment::Center)
+ .align_y(Alignment::Center),
+ )
+ .padding(Padding::ZERO.top(if is_expanded { 0 } else { 2 }))
+ .width(25)
+ .height(25)
+ .on_press(Message::ExpandNotification(notification_id, !is_expanded));
+
+ Container::new(button)
+ .padding(Padding::ZERO.left(395))
+ .align_y(Alignment::Center)
+}
+
+fn data_notification_extra<'a>(
+ logged_notification: &DataThresholdExceeded,
+ font: Font,
+ language: Language,
+) -> Option> {
+ let max_entries = max(
+ logged_notification.hosts.len(),
+ logged_notification.services.len(),
+ );
+ if !logged_notification.is_expanded || max_entries == 0 {
+ return None;
+ }
+ let spacing = 10.0;
+ #[allow(clippy::cast_precision_loss)]
+ let height = (FLAGS_HEIGHT_BIG + spacing) * max_entries as f32;
+
+ let mut hosts_col = Column::new().spacing(spacing).width(Length::FillPortion(5));
+ let first_data_info_host = logged_notification
+ .hosts
+ .first()
+ .unwrap_or(&(Host::default(), DataInfoHost::default()))
+ .1
+ .data_info;
+ for (host, data_info_host) in &logged_notification.hosts {
+ let host_bar = host_bar(
+ host,
+ data_info_host,
+ logged_notification.chart_type,
+ first_data_info_host,
+ font,
+ language,
+ );
+ hosts_col = hosts_col.push(host_bar);
+ }
+
+ let mut services_col = Column::new().spacing(spacing).width(Length::FillPortion(2));
+ let first_data_info_service = logged_notification
+ .services
+ .first()
+ .unwrap_or(&(Service::default(), DataInfo::default()))
+ .1;
+ for (service, data_info) in &logged_notification.services {
+ let service_bar = service_bar(
+ service,
+ data_info,
+ logged_notification.chart_type,
+ first_data_info_service,
+ font,
+ );
+ services_col = services_col.push(service_bar);
+ }
+
+ Some(
+ Row::new()
+ .push(hosts_col)
+ .push(Container::new(Rule::vertical(30)).height(height))
+ .push(services_col),
+ )
+}
diff --git a/src/gui/pages/overview_page.rs b/src/gui/pages/overview_page.rs
index 134239df..9f8341e2 100644
--- a/src/gui/pages/overview_page.rs
+++ b/src/gui/pages/overview_page.rs
@@ -5,7 +5,7 @@
use crate::chart::types::donut_chart::donut_chart;
use crate::countries::country_utils::get_flag_tooltip;
-use crate::countries::flags_pictures::FLAGS_WIDTH_BIG;
+use crate::countries::flags_pictures::{FLAGS_HEIGHT_BIG, FLAGS_WIDTH_BIG};
use crate::gui::components::tab::get_pages_tabs;
use crate::gui::sniffer::Sniffer;
use crate::gui::styles::button::ButtonType;
@@ -18,8 +18,10 @@
use crate::gui::types::message::Message;
use crate::networking::types::capture_context::CaptureSource;
use crate::networking::types::data_info::DataInfo;
+use crate::networking::types::data_info_host::DataInfoHost;
use crate::networking::types::filters::Filters;
use crate::networking::types::host::Host;
+use crate::networking::types::service::Service;
use crate::report::get_report_entries::{get_host_entries, get_service_entries};
use crate::report::types::search_parameters::SearchParameters;
use crate::report::types::sort_type::SortType;
@@ -68,7 +70,7 @@ pub fn overview_page(sniffer: &Sniffer) -> Container {
} else {
// NO pcap error detected
let observed = sniffer.info_traffic.all_packets;
- let filtered = sniffer.info_traffic.tot_out_packets + sniffer.info_traffic.tot_in_packets;
+ let filtered = sniffer.info_traffic.tot_data_info.tot_packets();
match (observed, filtered) {
(0, 0) => {
@@ -217,33 +219,37 @@ fn body_pcap_error<'a>(
}
fn row_report<'a>(sniffer: &Sniffer) -> Row<'a, Message, StyleType> {
- let col_host = col_host(840.0, sniffer);
- let col_service = col_service(250.0, sniffer);
+ let col_host = col_host(sniffer);
+ let col_service = col_service(sniffer);
Row::new()
.spacing(10)
.push(
Container::new(col_host)
+ .width(Length::FillPortion(5))
.height(Length::Fill)
.padding(Padding::new(10.0).top(0).bottom(5))
.class(ContainerType::BorderedRound),
)
.push(
Container::new(col_service)
+ .width(Length::FillPortion(2))
.height(Length::Fill)
.padding(Padding::new(10.0).top(0).bottom(5))
.class(ContainerType::BorderedRound),
)
}
-fn col_host<'a>(width: f32, sniffer: &Sniffer) -> Column<'a, Message, StyleType> {
+fn col_host<'a>(sniffer: &Sniffer) -> Column<'a, Message, StyleType> {
let ConfigSettings {
style, language, ..
} = sniffer.configs.settings;
let font = style.get_extension().font;
let chart_type = sniffer.traffic_chart.chart_type;
- let mut scroll_host = Column::new().width(width).align_x(Alignment::Center);
+ let mut scroll_host = Column::new()
+ .padding(Padding::ZERO.right(11.0))
+ .align_x(Alignment::Center);
let entries = get_host_entries(&sniffer.info_traffic, chart_type, sniffer.host_sort_type);
let first_entry_data_info = entries
.iter()
@@ -252,52 +258,21 @@ fn col_host<'a>(width: f32, sniffer: &Sniffer) -> Column<'a, Message, StyleType>
.unwrap_or_default();
for (host, data_info_host) in &entries {
- let (incoming_bar_len, outgoing_bar_len) = get_bars_length(
- width * 0.86,
- chart_type,
- &first_entry_data_info,
- &data_info_host.data_info,
- );
-
let star_button = get_star_button(data_info_host.is_favorite, host.clone());
- let host_bar = Column::new()
- .width(width)
- .spacing(1)
- .push(
- Row::new()
- .push(Text::new(host.domain.clone()).font(font))
- .push(
- Text::new(if host.asn.name.is_empty() {
- String::new()
- } else {
- format!(" - {}", host.asn.name)
- })
- .font(font),
- )
- .push(horizontal_space())
- .push(
- Text::new(if chart_type.eq(&ChartType::Packets) {
- data_info_host.data_info.tot_packets().to_string()
- } else {
- ByteMultiple::formatted_string(data_info_host.data_info.tot_bytes())
- })
- .font(font),
- ),
- )
- .push(get_bars(incoming_bar_len, outgoing_bar_len));
+ let host_bar = host_bar(
+ host,
+ data_info_host,
+ chart_type,
+ first_entry_data_info,
+ font,
+ language,
+ );
let content = Row::new()
.align_y(Alignment::Center)
.spacing(5)
.push(star_button)
- .push(get_flag_tooltip(
- host.country,
- data_info_host,
- language,
- font,
- false,
- ))
.push(host_bar);
scroll_host = scroll_host.push(
@@ -317,7 +292,6 @@ fn col_host<'a>(width: f32, sniffer: &Sniffer) -> Column<'a, Message, StyleType>
}
Column::new()
- .width(width + 11.0)
.push(
Row::new()
.height(45)
@@ -343,14 +317,16 @@ fn col_host<'a>(width: f32, sniffer: &Sniffer) -> Column<'a, Message, StyleType>
)
}
-fn col_service<'a>(width: f32, sniffer: &Sniffer) -> Column<'a, Message, StyleType> {
+fn col_service<'a>(sniffer: &Sniffer) -> Column<'a, Message, StyleType> {
let ConfigSettings {
style, language, ..
} = sniffer.configs.settings;
let font = style.get_extension().font;
let chart_type = sniffer.traffic_chart.chart_type;
- let mut scroll_service = Column::new().width(width).align_x(Alignment::Center);
+ let mut scroll_service = Column::new()
+ .padding(Padding::ZERO.right(11.0))
+ .align_x(Alignment::Center);
let entries = get_service_entries(&sniffer.info_traffic, chart_type, sniffer.service_sort_type);
let first_entry_data_info = entries
.iter()
@@ -359,30 +335,11 @@ fn col_service<'a>(width: f32, sniffer: &Sniffer) -> Column<'a, Message, StyleTy
.unwrap_or_default();
for (service, data_info) in &entries {
- let (incoming_bar_len, outgoing_bar_len) =
- get_bars_length(width * 0.88, chart_type, &first_entry_data_info, data_info);
-
- let content = Column::new()
- .spacing(1)
- .width(width)
- .push(
- Row::new()
- .push(Text::new(service.to_string()).font(font))
- .push(horizontal_space())
- .push(
- Text::new(if chart_type.eq(&ChartType::Packets) {
- data_info.tot_packets().to_string()
- } else {
- ByteMultiple::formatted_string(data_info.tot_bytes())
- })
- .font(font),
- ),
- )
- .push(get_bars(incoming_bar_len, outgoing_bar_len));
+ let content = service_bar(service, data_info, chart_type, first_entry_data_info, font);
scroll_service = scroll_service.push(
button(content)
- .padding(Padding::new(5.0).right(15).bottom(8).left(10))
+ .padding(Padding::new(5.0).right(15).left(10))
.on_press(Message::Search(SearchParameters::new_service_search(
service,
)))
@@ -399,7 +356,6 @@ fn col_service<'a>(width: f32, sniffer: &Sniffer) -> Column<'a, Message, StyleTy
}
Column::new()
- .width(width + 11.0)
.push(
Row::new()
.height(45)
@@ -425,6 +381,93 @@ fn col_service<'a>(width: f32, sniffer: &Sniffer) -> Column<'a, Message, StyleTy
)
}
+pub fn host_bar<'a>(
+ host: &Host,
+ data_info_host: &DataInfoHost,
+ chart_type: ChartType,
+ first_entry_data_info: DataInfo,
+ font: Font,
+ language: Language,
+) -> Row<'a, Message, StyleType> {
+ let (incoming_bar_len, outgoing_bar_len) = get_bars_length(
+ chart_type,
+ &first_entry_data_info,
+ &data_info_host.data_info,
+ );
+
+ Row::new()
+ .height(FLAGS_HEIGHT_BIG)
+ .align_y(Alignment::Center)
+ .spacing(5)
+ .push(get_flag_tooltip(
+ host.country,
+ data_info_host,
+ language,
+ font,
+ false,
+ ))
+ .push(
+ Column::new()
+ .spacing(1)
+ .push(
+ Row::new()
+ .push(Text::new(host.domain.clone()).font(font))
+ .push(
+ Text::new(if host.asn.name.is_empty() {
+ String::new()
+ } else {
+ format!(" - {}", host.asn.name)
+ })
+ .font(font),
+ )
+ .push(horizontal_space())
+ .push(
+ Text::new(if chart_type.eq(&ChartType::Packets) {
+ data_info_host.data_info.tot_packets().to_string()
+ } else {
+ ByteMultiple::formatted_string(data_info_host.data_info.tot_bytes())
+ })
+ .font(font),
+ ),
+ )
+ .push(get_bars(incoming_bar_len, outgoing_bar_len)),
+ )
+}
+
+pub fn service_bar<'a>(
+ service: &Service,
+ data_info: &DataInfo,
+ chart_type: ChartType,
+ first_entry_data_info: DataInfo,
+ font: Font,
+) -> Row<'a, Message, StyleType> {
+ let (incoming_bar_len, outgoing_bar_len) =
+ get_bars_length(chart_type, &first_entry_data_info, data_info);
+
+ Row::new()
+ .height(FLAGS_HEIGHT_BIG)
+ .align_y(Alignment::Center)
+ .spacing(5)
+ .push(
+ Column::new()
+ .spacing(1)
+ .push(
+ Row::new()
+ .push(Text::new(service.to_string()).font(font))
+ .push(horizontal_space())
+ .push(
+ Text::new(if chart_type.eq(&ChartType::Packets) {
+ data_info.tot_packets().to_string()
+ } else {
+ ByteMultiple::formatted_string(data_info.tot_bytes())
+ })
+ .font(font),
+ ),
+ )
+ .push(get_bars(incoming_bar_len, outgoing_bar_len)),
+ )
+}
+
fn col_info<'a>(sniffer: &Sniffer) -> Container<'a, Message, StyleType> {
let ConfigSettings {
style, language, ..
@@ -656,14 +699,13 @@ fn donut_legend_entry<'a>(
.push_maybe(tooltip)
}
-const MIN_BARS_LENGTH: f32 = 10.0;
+const MIN_BARS_LENGTH: f32 = 4.0;
pub fn get_bars_length(
- tot_width: f32,
chart_type: ChartType,
first_entry: &DataInfo,
data_info: &DataInfo,
-) -> (f32, f32) {
+) -> (u16, u16) {
let (in_val, out_val, first_entry_tot_val) = match chart_type {
ChartType::Packets => (
data_info.incoming_packets(),
@@ -679,11 +721,11 @@ pub fn get_bars_length(
let tot_val = in_val + out_val;
if tot_val == 0 {
- return (0.0, 0.0);
+ return (0, 0);
}
#[allow(clippy::cast_precision_loss)]
- let tot_len = tot_width * tot_val as f32 / first_entry_tot_val as f32;
+ let tot_len = 100.0 * tot_val as f32 / first_entry_tot_val as f32;
#[allow(clippy::cast_precision_loss)]
let (mut in_len, mut out_len) = (
tot_len * in_val as f32 / tot_val as f32,
@@ -720,29 +762,31 @@ pub fn get_bars_length(
}
}
- // cut to 3 significant digits
- in_len = (in_len * 1000.0).round() / 1000.0;
- out_len = (out_len * 1000.0).round() / 1000.0;
-
- (in_len, out_len)
+ #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
+ (in_len.round() as u16, out_len.round() as u16)
}
-pub fn get_bars<'a>(in_len: f32, out_len: f32) -> Row<'a, Message, StyleType> {
+pub fn get_bars<'a>(in_len: u16, out_len: u16) -> Row<'a, Message, StyleType> {
Row::new()
- .push(if in_len > 0.0 {
+ .push(if in_len > 0 {
Row::new()
- .width(in_len)
+ .width(Length::FillPortion(in_len))
.push(Rule::horizontal(1).class(RuleType::Incoming))
} else {
Row::new()
})
- .push(if out_len > 0.0 {
+ .push(if out_len > 0 {
Row::new()
- .width(out_len)
+ .width(Length::FillPortion(out_len))
.push(Rule::horizontal(1).class(RuleType::Outgoing))
} else {
Row::new()
})
+ .push(if in_len + out_len < 100 {
+ Row::new().width(Length::FillPortion(100 - in_len - out_len))
+ } else {
+ Row::new()
+ })
}
fn get_star_button<'a>(is_favorite: bool, host: Host) -> Button<'a, Message, StyleType> {
@@ -754,7 +798,7 @@ fn get_star_button<'a>(is_favorite: bool, host: Host) -> Button<'a, Message, Sty
.align_y(Alignment::Center),
)
.padding(0)
- .height(FLAGS_WIDTH_BIG * 0.75)
+ .height(FLAGS_HEIGHT_BIG)
.width(FLAGS_WIDTH_BIG)
.class(if is_favorite {
ButtonType::Starred
@@ -846,12 +890,12 @@ fn test_get_bars_length_simple() {
let first_entry = DataInfo::new_for_tests(50, 50, 150, 50);
let data_info = DataInfo::new_for_tests(25, 55, 165, 30);
assert_eq!(
- get_bars_length(200.0, ChartType::Packets, &first_entry, &data_info),
- (50.0, 110.0)
+ get_bars_length(ChartType::Packets, &first_entry, &data_info),
+ (25, 55)
);
assert_eq!(
- get_bars_length(200.0, ChartType::Bytes, &first_entry, &data_info),
- (165.0, 30.0)
+ get_bars_length(ChartType::Bytes, &first_entry, &data_info),
+ (83, 15)
);
}
@@ -860,22 +904,22 @@ fn test_get_bars_length_normalize_small_values() {
let first_entry = DataInfo::new_for_tests(50, 50, 150, 50);
let mut data_info = DataInfo::new_for_tests(2, 1, 1, 0);
assert_eq!(
- get_bars_length(200.0, ChartType::Packets, &first_entry, &data_info),
- (MIN_BARS_LENGTH / 2.0, MIN_BARS_LENGTH / 2.0)
+ get_bars_length(ChartType::Packets, &first_entry, &data_info),
+ (MIN_BARS_LENGTH as u16 / 2, MIN_BARS_LENGTH as u16 / 2)
);
assert_eq!(
- get_bars_length(200.0, ChartType::Bytes, &first_entry, &data_info),
- (MIN_BARS_LENGTH, 0.0)
+ get_bars_length(ChartType::Bytes, &first_entry, &data_info),
+ (MIN_BARS_LENGTH as u16, 0)
);
data_info = DataInfo::new_for_tests(0, 3, 0, 2);
assert_eq!(
- get_bars_length(200.0, ChartType::Packets, &first_entry, &data_info),
- (0.0, MIN_BARS_LENGTH)
+ get_bars_length(ChartType::Packets, &first_entry, &data_info),
+ (0, MIN_BARS_LENGTH as u16)
);
assert_eq!(
- get_bars_length(200.0, ChartType::Bytes, &first_entry, &data_info),
- (0.0, MIN_BARS_LENGTH)
+ get_bars_length(ChartType::Bytes, &first_entry, &data_info),
+ (0, MIN_BARS_LENGTH as u16)
);
}
@@ -885,141 +929,129 @@ fn test_get_bars_length_normalize_very_small_values() {
DataInfo::new_for_tests(u128::MAX / 2, u128::MAX / 2, u128::MAX / 2, u128::MAX / 2);
let mut data_info = DataInfo::new_for_tests(1, 1, 1, 1);
assert_eq!(
- get_bars_length(200.0, ChartType::Packets, &first_entry, &data_info),
- (MIN_BARS_LENGTH / 2.0, MIN_BARS_LENGTH / 2.0)
+ get_bars_length(ChartType::Packets, &first_entry, &data_info),
+ (MIN_BARS_LENGTH as u16 / 2, MIN_BARS_LENGTH as u16 / 2)
);
assert_eq!(
- get_bars_length(200.0, ChartType::Bytes, &first_entry, &data_info),
- (MIN_BARS_LENGTH / 2.0, MIN_BARS_LENGTH / 2.0)
+ get_bars_length(ChartType::Bytes, &first_entry, &data_info),
+ (MIN_BARS_LENGTH as u16 / 2, MIN_BARS_LENGTH as u16 / 2)
);
data_info = DataInfo::new_for_tests(0, 1, 0, 1);
assert_eq!(
- get_bars_length(200.0, ChartType::Packets, &first_entry, &data_info),
- (0.0, MIN_BARS_LENGTH)
+ get_bars_length(ChartType::Packets, &first_entry, &data_info),
+ (0, MIN_BARS_LENGTH as u16)
);
assert_eq!(
- get_bars_length(200.0, ChartType::Bytes, &first_entry, &data_info),
- (0.0, MIN_BARS_LENGTH)
+ get_bars_length(ChartType::Bytes, &first_entry, &data_info),
+ (0, MIN_BARS_LENGTH as u16)
);
data_info = DataInfo::new_for_tests(1, 0, 1, 0);
assert_eq!(
- get_bars_length(200.0, ChartType::Packets, &first_entry, &data_info),
- (MIN_BARS_LENGTH, 0.0)
+ get_bars_length(ChartType::Packets, &first_entry, &data_info),
+ (MIN_BARS_LENGTH as u16, 0)
);
assert_eq!(
- get_bars_length(200.0, ChartType::Bytes, &first_entry, &data_info),
- (MIN_BARS_LENGTH, 0.0)
+ get_bars_length(ChartType::Bytes, &first_entry, &data_info),
+ (MIN_BARS_LENGTH as u16, 0)
);
}
#[test]
fn test_get_bars_length_complex() {
- let first_entry = DataInfo::new_for_tests(350, 50, 12, 88);
+ let first_entry = DataInfo::new_for_tests(48, 7, 2, 12);
let mut data_info = DataInfo::new_for_tests(0, 9, 0, 10);
assert_eq!(
- get_bars_length(722.0, ChartType::Packets, &first_entry, &data_info),
- (0.0, 16.245)
+ get_bars_length(ChartType::Packets, &first_entry, &data_info),
+ (0, 16)
);
assert_eq!(
- get_bars_length(100.0, ChartType::Bytes, &first_entry, &data_info),
- (0.0, MIN_BARS_LENGTH)
+ get_bars_length(ChartType::Bytes, &first_entry, &data_info),
+ (0, 71)
);
data_info = DataInfo::new_for_tests(9, 0, 13, 0);
assert_eq!(
- get_bars_length(722.0, ChartType::Packets, &first_entry, &data_info),
- (16.245, 0.0)
+ get_bars_length(ChartType::Packets, &first_entry, &data_info),
+ (16, 0)
);
assert_eq!(
- get_bars_length(100.0, ChartType::Bytes, &first_entry, &data_info),
- (13.0, 0.0)
+ get_bars_length(ChartType::Bytes, &first_entry, &data_info),
+ (93, 0)
);
data_info = DataInfo::new_for_tests(4, 5, 6, 7);
assert_eq!(
- get_bars_length(722.0, ChartType::Packets, &first_entry, &data_info),
- (
- (1000.0_f32 * 16.245 * 4.0 / 9.0).round() / 1000.0,
- (1000.0_f32 * 16.245 * 5.0 / 9.0).round() / 1000.0
- )
+ get_bars_length(ChartType::Packets, &first_entry, &data_info),
+ (7, 9)
);
assert_eq!(
- get_bars_length(100.0, ChartType::Bytes, &first_entry, &data_info),
- (6.0, 7.0)
+ get_bars_length(ChartType::Bytes, &first_entry, &data_info),
+ (43, 50)
);
data_info = DataInfo::new_for_tests(5, 4, 7, 6);
assert_eq!(
- get_bars_length(722.0, ChartType::Packets, &first_entry, &data_info),
- (
- (1000.0_f32 * 16.245 * 5.0 / 9.0).round() / 1000.0,
- (1000.0_f32 * 16.245 * 4.0 / 9.0).round() / 1000.0
- )
+ get_bars_length(ChartType::Packets, &first_entry, &data_info),
+ (9, 7)
);
assert_eq!(
- get_bars_length(100.0, ChartType::Bytes, &first_entry, &data_info),
- (7.0, 6.0)
+ get_bars_length(ChartType::Bytes, &first_entry, &data_info),
+ (50, 43)
);
data_info = DataInfo::new_for_tests(1, 8, 1, 12);
assert_eq!(
- get_bars_length(722.0, ChartType::Packets, &first_entry, &data_info),
- (MIN_BARS_LENGTH / 2.0, 11.245)
+ get_bars_length(ChartType::Packets, &first_entry, &data_info),
+ (MIN_BARS_LENGTH as u16 / 2, 14)
);
assert_eq!(
- get_bars_length(100.0, ChartType::Bytes, &first_entry, &data_info),
- (MIN_BARS_LENGTH / 2.0, 8.0)
+ get_bars_length(ChartType::Bytes, &first_entry, &data_info),
+ (7, 86)
);
data_info = DataInfo::new_for_tests(8, 1, 12, 1);
assert_eq!(
- get_bars_length(722.0, ChartType::Packets, &first_entry, &data_info),
- (11.245, MIN_BARS_LENGTH / 2.0)
+ get_bars_length(ChartType::Packets, &first_entry, &data_info),
+ (14, MIN_BARS_LENGTH as u16 / 2)
);
assert_eq!(
- get_bars_length(100.0, ChartType::Bytes, &first_entry, &data_info),
- (8.0, MIN_BARS_LENGTH / 2.0)
+ get_bars_length(ChartType::Bytes, &first_entry, &data_info),
+ (86, 7)
);
data_info = DataInfo::new_for_tests(6, 1, 10, 1);
assert_eq!(
- get_bars_length(722.0, ChartType::Packets, &first_entry, &data_info),
- (
- 16.245 * 7.0 / 9.0 - MIN_BARS_LENGTH / 2.0,
- MIN_BARS_LENGTH / 2.0
- )
+ get_bars_length(ChartType::Packets, &first_entry, &data_info),
+ (11, MIN_BARS_LENGTH as u16 / 2)
);
assert_eq!(
- get_bars_length(100.0, ChartType::Bytes, &first_entry, &data_info),
- (6.0, MIN_BARS_LENGTH / 2.0)
+ get_bars_length(ChartType::Bytes, &first_entry, &data_info),
+ (71, 7)
);
data_info = DataInfo::new_for_tests(1, 6, 1, 9);
assert_eq!(
- get_bars_length(722.0, ChartType::Packets, &first_entry, &data_info),
- (
- MIN_BARS_LENGTH / 2.0,
- 16.245 * 7.0 / 9.0 - MIN_BARS_LENGTH / 2.0,
- )
+ get_bars_length(ChartType::Packets, &first_entry, &data_info),
+ (MIN_BARS_LENGTH as u16 / 2, 11,)
);
assert_eq!(
- get_bars_length(100.0, ChartType::Bytes, &first_entry, &data_info),
- (MIN_BARS_LENGTH / 2.0, MIN_BARS_LENGTH / 2.0)
+ get_bars_length(ChartType::Bytes, &first_entry, &data_info),
+ (7, 64)
);
data_info = DataInfo::new_for_tests(1, 6, 5, 5);
assert_eq!(
- get_bars_length(100.0, ChartType::Bytes, &first_entry, &data_info),
- (MIN_BARS_LENGTH / 2.0, MIN_BARS_LENGTH / 2.0)
+ get_bars_length(ChartType::Bytes, &first_entry, &data_info),
+ (36, 36)
);
data_info = DataInfo::new_for_tests(0, 0, 0, 0);
assert_eq!(
- get_bars_length(722.0, ChartType::Packets, &first_entry, &data_info),
- (0.0, 0.0,)
+ get_bars_length(ChartType::Packets, &first_entry, &data_info),
+ (0, 0)
);
assert_eq!(
- get_bars_length(100.0, ChartType::Bytes, &first_entry, &data_info),
- (0.0, 0.0)
+ get_bars_length(ChartType::Bytes, &first_entry, &data_info),
+ (0, 0)
);
}
}
diff --git a/src/gui/pages/settings_general_page.rs b/src/gui/pages/settings_general_page.rs
index 45496adc..f4b2aafc 100644
--- a/src/gui/pages/settings_general_page.rs
+++ b/src/gui/pages/settings_general_page.rs
@@ -3,7 +3,7 @@
use iced::widget::{
Column, Container, PickList, Row, Rule, Slider, Space, Text, Tooltip, button, vertical_space,
};
-use iced::{Alignment, Font, Length};
+use iced::{Alignment, Font, Length, Padding};
use crate::gui::components::button::{button_open_file, row_open_link_tooltip};
use crate::gui::components::tab::get_settings_tabs;
@@ -18,9 +18,9 @@
use crate::translations::translations::language_translation;
use crate::translations::translations_2::country_translation;
use crate::translations::translations_3::{
- learn_more_translation, mmdb_files_translation, params_not_editable_translation,
- zoom_translation,
+ mmdb_files_translation, params_not_editable_translation, zoom_translation,
};
+use crate::translations::translations_4::share_feedback_translation;
use crate::utils::formatted_strings::get_path_termination_string;
use crate::utils::types::file_info::FileInfo;
use crate::utils::types::icon::Icon;
@@ -133,7 +133,7 @@ fn language_picklist<'a>(language: Language, font: Font) -> Container<'a, Messag
.width(20)
.class(ButtonType::Alert),
row_open_link_tooltip(
- "The selected language is not\nfully updated to version 1.3",
+ "The selected language is not\nfully updated to version 1.4",
font,
),
Position::FollowCursor,
@@ -206,7 +206,7 @@ fn need_help<'a>(language: Language, font: Font) -> Container<'a, Message, Style
let content = Column::new()
.align_x(Alignment::Center)
.push(
- Text::new(learn_more_translation(language))
+ Text::new(share_feedback_translation(language))
.class(TextType::Subtitle)
.size(FONT_SIZE_SUBTITLE)
.font(font),
@@ -215,18 +215,18 @@ fn need_help<'a>(language: Language, font: Font) -> Container<'a, Message, Style
.push(
Tooltip::new(
button(
- Icon::Book
+ Icon::Feedback
.to_text()
.align_y(Alignment::Center)
.align_x(Alignment::Center)
- .size(22)
+ .size(20)
.line_height(LineHeight::Relative(1.0)),
)
- .on_press(Message::OpenWebPage(WebPage::Wiki))
- .padding(2)
+ .on_press(Message::OpenWebPage(WebPage::Issues))
+ .padding(Padding::new(2.0).top(5))
.height(40)
.width(60),
- row_open_link_tooltip("Wiki", font),
+ row_open_link_tooltip("GitHub Issues", font),
Position::Right,
)
.gap(5)
diff --git a/src/gui/pages/settings_notifications_page.rs b/src/gui/pages/settings_notifications_page.rs
index e7302926..6c10c1c8 100644
--- a/src/gui/pages/settings_notifications_page.rs
+++ b/src/gui/pages/settings_notifications_page.rs
@@ -1,8 +1,9 @@
use iced::widget::scrollable::Direction;
use iced::widget::{Button, Slider, horizontal_space};
use iced::widget::{Checkbox, Column, Container, Row, Scrollable, Space, Text, TextInput};
-use iced::{Alignment, Font, Length};
+use iced::{Alignment, Font, Length, Padding};
+use crate::chart::types::chart_type::ChartType;
use crate::gui::components::button::button_hide;
use crate::gui::components::tab::get_settings_tabs;
use crate::gui::pages::types::settings_page::SettingsPage;
@@ -14,14 +15,15 @@
use crate::gui::styles::types::gradient_type::GradientType;
use crate::gui::types::message::Message;
use crate::notifications::types::notifications::{
- BytesNotification, FavoriteNotification, Notification, PacketsNotification,
+ DataNotification, FavoriteNotification, Notification,
};
use crate::notifications::types::sound::Sound;
use crate::translations::translations::{
- bytes_exceeded_translation, favorite_transmitted_translation, notifications_title_translation,
- packets_exceeded_translation, per_second_translation, settings_translation, sound_translation,
- threshold_translation, volume_translation,
+ favorite_transmitted_translation, notifications_title_translation, per_second_translation,
+ settings_translation, sound_translation, threshold_translation, volume_translation,
};
+use crate::translations::translations_2::data_representation_translation;
+use crate::translations::translations_4::data_exceeded_translation;
use crate::utils::types::icon::Icon;
use crate::{ConfigSettings, Language, Sniffer, StyleType};
@@ -36,18 +38,12 @@ pub fn settings_notifications_page<'a>(sniffer: &Sniffer) -> Container<'a, Messa
let font = style.get_extension().font;
let font_headers = style.get_extension().font_headers;
- // Use thresholds that have not yet been applied, if available
- if let Some((temp_packets_notifications, temp_bytes_notifications)) =
- sniffer.timing_events.temp_thresholds()
- {
- notifications.packets_notification.threshold = temp_packets_notifications.threshold;
- notifications.packets_notification.previous_threshold =
- temp_packets_notifications.previous_threshold;
-
- notifications.bytes_notification.threshold = temp_bytes_notifications.threshold;
- notifications.bytes_notification.byte_multiple = temp_bytes_notifications.byte_multiple;
- notifications.bytes_notification.previous_threshold =
- temp_bytes_notifications.previous_threshold;
+ // Use threshold that has not yet been applied, if available
+ if let Some(temp_data_notification) = sniffer.timing_events.temp_threshold() {
+ notifications.data_notification.threshold = temp_data_notification.threshold;
+ notifications.data_notification.byte_multiple = temp_data_notification.byte_multiple;
+ notifications.data_notification.previous_threshold =
+ temp_data_notification.previous_threshold;
}
let mut content = Column::new()
@@ -82,13 +78,8 @@ pub fn settings_notifications_page<'a>(sniffer: &Sniffer) -> Container<'a, Messa
Column::new()
.align_x(Alignment::Center)
.width(Length::Fill)
- .push(get_packets_notify(
- notifications.packets_notification,
- language,
- font,
- ))
- .push(get_bytes_notify(
- notifications.bytes_notification,
+ .push(get_data_notify(
+ notifications.data_notification,
language,
font,
))
@@ -108,29 +99,29 @@ pub fn settings_notifications_page<'a>(sniffer: &Sniffer) -> Container<'a, Messa
.class(ContainerType::Modal)
}
-fn get_packets_notify<'a>(
- packets_notification: PacketsNotification,
+fn get_data_notify<'a>(
+ data_notification: DataNotification,
language: Language,
font: Font,
) -> Column<'a, Message, StyleType> {
let checkbox = Checkbox::new(
- packets_exceeded_translation(language),
- packets_notification.threshold.is_some(),
+ data_exceeded_translation(language),
+ data_notification.threshold.is_some(),
)
.on_toggle(move |toggled| {
if toggled {
Message::UpdateNotificationSettings(
- Notification::Packets(PacketsNotification {
- threshold: Some(packets_notification.previous_threshold),
- ..packets_notification
+ Notification::Data(DataNotification {
+ threshold: Some(data_notification.previous_threshold),
+ ..data_notification
}),
false,
)
} else {
Message::UpdateNotificationSettings(
- Notification::Packets(PacketsNotification {
+ Notification::Data(DataNotification {
threshold: None,
- ..packets_notification
+ ..data_notification
}),
false,
)
@@ -139,9 +130,9 @@ fn get_packets_notify<'a>(
.size(18)
.font(font);
- let mut ret_val = Column::new().spacing(10).push(checkbox);
+ let mut ret_val = Column::new().spacing(15).push(checkbox);
- if packets_notification.threshold.is_none() {
+ if data_notification.threshold.is_none() {
Column::new().padding(5).push(
Container::new(ret_val)
.padding(10)
@@ -149,62 +140,18 @@ fn get_packets_notify<'a>(
.class(ContainerType::BorderedRound),
)
} else {
- let input_row = input_group_packets(packets_notification, font, language);
- let sound_row = sound_buttons(Notification::Packets(packets_notification), font, language);
- ret_val = ret_val.push(input_row).push(sound_row);
- Column::new().padding(5).push(
- Container::new(ret_val)
- .padding(10)
- .width(700)
- .class(ContainerType::BorderedRound),
- )
- }
-}
-
-fn get_bytes_notify<'a>(
- bytes_notification: BytesNotification,
- language: Language,
- font: Font,
-) -> Column<'a, Message, StyleType> {
- let checkbox = Checkbox::new(
- bytes_exceeded_translation(language),
- bytes_notification.threshold.is_some(),
- )
- .on_toggle(move |toggled| {
- if toggled {
- Message::UpdateNotificationSettings(
- Notification::Bytes(BytesNotification {
- threshold: Some(bytes_notification.previous_threshold),
- ..bytes_notification
- }),
- false,
- )
- } else {
- Message::UpdateNotificationSettings(
- Notification::Bytes(BytesNotification {
- threshold: None,
- ..bytes_notification
- }),
- false,
- )
- }
- })
- .size(18)
- .font(font);
-
- let mut ret_val = Column::new().spacing(10).push(checkbox);
-
- if bytes_notification.threshold.is_none() {
- Column::new().padding(5).push(
- Container::new(ret_val)
- .padding(10)
- .width(700)
- .class(ContainerType::BorderedRound),
- )
- } else {
- let input_row = input_group_bytes(bytes_notification, font, language);
- let sound_row = sound_buttons(Notification::Bytes(bytes_notification), font, language);
- ret_val = ret_val.push(input_row).push(sound_row);
+ let data_representation_row = row_data_representation(
+ data_notification,
+ language,
+ font,
+ data_notification.chart_type,
+ );
+ let input_row = input_group_bytes(data_notification, font, language);
+ let sound_row = sound_buttons(Notification::Data(data_notification), font, language);
+ ret_val = ret_val
+ .push(sound_row)
+ .push(data_representation_row)
+ .push(input_row);
Column::new().padding(5).push(
Container::new(ret_val)
.padding(10)
@@ -236,7 +183,7 @@ fn get_favorite_notify<'a>(
.size(18)
.font(font);
- let mut ret_val = Column::new().spacing(10).push(checkbox);
+ let mut ret_val = Column::new().spacing(15).push(checkbox);
if favorite_notification.notify_on_favorite {
let sound_row = sound_buttons(
@@ -261,54 +208,8 @@ fn get_favorite_notify<'a>(
}
}
-fn input_group_packets<'a>(
- packets_notification: PacketsNotification,
- font: Font,
- language: Language,
-) -> Container<'a, Message, StyleType> {
- let curr_threshold_str = &packets_notification
- .threshold
- .unwrap_or_default()
- .to_string();
- let input_row = Row::new()
- .align_y(Alignment::Center)
- .spacing(5)
- .push(Space::with_width(45))
- .push(Text::new(format!("{}:", threshold_translation(language))).font(font))
- .push(
- TextInput::new(
- "0",
- if curr_threshold_str == "0" {
- ""
- } else {
- curr_threshold_str
- },
- )
- .on_input(move |value| {
- let packets_notification =
- PacketsNotification::from(&value, Some(packets_notification));
- Message::UpdateNotificationSettings(
- Notification::Packets(packets_notification),
- false,
- )
- })
- .padding([2, 5])
- .font(font)
- .width(100),
- )
- .push(
- Text::new(per_second_translation(language))
- .font(font)
- .align_y(Alignment::Center)
- .size(FONT_SIZE_FOOTER),
- );
- Container::new(input_row)
- .align_x(Alignment::Center)
- .align_y(Alignment::Center)
-}
-
fn input_group_bytes<'a>(
- bytes_notification: BytesNotification,
+ bytes_notification: DataNotification,
font: Font,
language: Language,
) -> Container<'a, Message, StyleType> {
@@ -324,15 +225,15 @@ fn input_group_bytes<'a>(
.push(
TextInput::new(
"0",
- if curr_threshold_str == "0" {
+ if curr_threshold_str.starts_with('0') {
""
} else {
&curr_threshold_str
},
)
.on_input(move |value| {
- let bytes_notification = BytesNotification::from(&value, Some(bytes_notification));
- Message::UpdateNotificationSettings(Notification::Bytes(bytes_notification), false)
+ let bytes_notification = DataNotification::from(&value, Some(bytes_notification));
+ Message::UpdateNotificationSettings(Notification::Data(bytes_notification), false)
})
.padding([2, 5])
.font(font)
@@ -396,12 +297,12 @@ fn sound_buttons<'a>(
language: Language,
) -> Row<'a, Message, StyleType> {
let current_sound = match notification {
- Notification::Packets(n) => n.sound,
- Notification::Bytes(n) => n.sound,
+ Notification::Data(n) => n.sound,
Notification::Favorite(n) => n.sound,
};
let mut ret_val = Row::new()
+ .width(Length::Shrink)
.align_y(Alignment::Center)
.spacing(5)
.push(Space::with_width(45))
@@ -410,28 +311,29 @@ fn sound_buttons<'a>(
for option in Sound::ALL {
let is_active = current_sound.eq(&option);
let message_value = match notification {
- Notification::Packets(n) => {
- Notification::Packets(PacketsNotification { sound: option, ..n })
- }
- Notification::Bytes(n) => Notification::Bytes(BytesNotification { sound: option, ..n }),
+ Notification::Data(n) => Notification::Data(DataNotification { sound: option, ..n }),
Notification::Favorite(n) => {
Notification::Favorite(FavoriteNotification { sound: option, ..n })
}
};
ret_val = ret_val.push(
- Button::new(option.get_text(font))
- .padding(0)
- .width(80)
- .height(25)
- .class(if is_active {
- ButtonType::BorderedRoundSelected
- } else {
- ButtonType::BorderedRound
- })
- .on_press(Message::UpdateNotificationSettings(
- message_value,
- option.ne(&Sound::None),
- )),
+ Button::new(
+ option
+ .get_text(font)
+ .align_x(Alignment::Center)
+ .align_y(Alignment::Center),
+ )
+ .padding(Padding::ZERO.left(15).right(15))
+ .height(25)
+ .class(if is_active {
+ ButtonType::BorderedRoundSelected
+ } else {
+ ButtonType::BorderedRound
+ })
+ .on_press(Message::UpdateNotificationSettings(
+ message_value,
+ option.ne(&Sound::None),
+ )),
);
}
ret_val
@@ -465,3 +367,45 @@ pub fn settings_header<'a>(
.width(Length::Fill)
.class(ContainerType::Gradient(color_gradient))
}
+
+fn row_data_representation<'a>(
+ data_notification: DataNotification,
+ language: Language,
+ font: Font,
+ chart_type: ChartType,
+) -> Row<'a, Message, StyleType> {
+ let mut ret_val = Row::new()
+ .width(Length::Shrink)
+ .align_y(Alignment::Center)
+ .spacing(5)
+ .push(Space::with_width(45))
+ .push(Text::new(format!("{}:", data_representation_translation(language))).font(font));
+
+ for option in ChartType::ALL {
+ let is_active = chart_type.eq(&option);
+ ret_val = ret_val.push(
+ Button::new(
+ Text::new(option.get_label(language).to_owned())
+ .size(FONT_SIZE_FOOTER)
+ .align_x(Alignment::Center)
+ .align_y(Alignment::Center)
+ .font(font),
+ )
+ .padding(Padding::ZERO.left(15).right(15))
+ .height(25)
+ .class(if is_active {
+ ButtonType::BorderedRoundSelected
+ } else {
+ ButtonType::BorderedRound
+ })
+ .on_press(Message::UpdateNotificationSettings(
+ Notification::Data(DataNotification {
+ chart_type: option,
+ ..data_notification
+ }),
+ false,
+ )),
+ );
+ }
+ ret_val
+}
diff --git a/src/gui/pages/thumbnail_page.rs b/src/gui/pages/thumbnail_page.rs
index 4800cac8..86a3ae61 100644
--- a/src/gui/pages/thumbnail_page.rs
+++ b/src/gui/pages/thumbnail_page.rs
@@ -27,7 +27,7 @@ pub fn thumbnail_page(sniffer: &Sniffer) -> Container {
let ConfigSettings { style, .. } = sniffer.configs.settings;
let font = style.get_extension().font;
- let filtered = sniffer.info_traffic.tot_out_packets + sniffer.info_traffic.tot_in_packets;
+ let filtered = sniffer.info_traffic.tot_data_info.tot_packets();
if filtered == 0 {
return Container::new(
@@ -69,9 +69,19 @@ pub fn thumbnail_page(sniffer: &Sniffer) -> Container {
.padding([5, 0])
.height(Length::Fill)
.align_y(Alignment::Start)
- .push(host_col(info_traffic, chart_type, font))
+ .push(host_col(
+ info_traffic,
+ chart_type,
+ font,
+ sniffer.host_sort_type,
+ ))
.push(Rule::vertical(10))
- .push(service_col(info_traffic, chart_type, font));
+ .push(service_col(
+ info_traffic,
+ chart_type,
+ font,
+ sniffer.service_sort_type,
+ ));
let content = Column::new()
.push(charts)
@@ -85,12 +95,13 @@ fn host_col<'a>(
info_traffic: &InfoTraffic,
chart_type: ChartType,
font: Font,
+ sort_type: SortType,
) -> Column<'a, Message, StyleType> {
let mut host_col = Column::new()
.padding([0, 5])
.spacing(3)
.width(Length::FillPortion(2));
- let hosts = get_host_entries(info_traffic, chart_type, SortType::Neutral);
+ let hosts = get_host_entries(info_traffic, chart_type, sort_type);
let mut thumbnail_hosts = Vec::new();
for (host, data_info_host) in &hosts {
@@ -127,9 +138,10 @@ fn service_col<'a>(
info_traffic: &InfoTraffic,
chart_type: ChartType,
font: Font,
+ sort_type: SortType,
) -> Column<'a, Message, StyleType> {
let mut service_col = Column::new().padding([0, 5]).spacing(3).width(Length::Fill);
- let services = get_service_entries(info_traffic, chart_type, SortType::Neutral);
+ let services = get_service_entries(info_traffic, chart_type, sort_type);
let n_entry = min(services.len(), MAX_ENTRIES);
for (service, _) in services.get(..n_entry).unwrap_or_default() {
service_col = service_col.push(
diff --git a/src/gui/sniffer.rs b/src/gui/sniffer.rs
index e40cd4d1..79f74228 100644
--- a/src/gui/sniffer.rs
+++ b/src/gui/sniffer.rs
@@ -46,19 +46,16 @@
use crate::networking::parse_packets::BackendTrafficMessage;
use crate::networking::parse_packets::parse_packets;
use crate::networking::types::capture_context::{CaptureContext, CaptureSource, MyPcapImport};
-use crate::networking::types::data_info_host::DataInfoHost;
use crate::networking::types::filters::Filters;
use crate::networking::types::host::{Host, HostMessage};
use crate::networking::types::host_data_states::HostDataStates;
-use crate::networking::types::info_traffic::InfoTrafficMessage;
+use crate::networking::types::info_traffic::InfoTraffic;
use crate::networking::types::ip_collection::AddressCollection;
use crate::networking::types::my_device::MyDevice;
use crate::networking::types::port_collection::PortCollection;
use crate::notifications::notify_and_log::notify_and_log;
use crate::notifications::types::logged_notification::LoggedNotification;
-use crate::notifications::types::notifications::{
- BytesNotification, Notification, PacketsNotification,
-};
+use crate::notifications::types::notifications::{DataNotification, Notification};
use crate::notifications::types::sound::{Sound, play};
use crate::report::get_report_entries::get_searched_entries;
use crate::report::types::report_sort_type::ReportSortType;
@@ -69,7 +66,7 @@
use crate::utils::error_logger::{ErrorLogger, Location};
use crate::utils::types::file_info::FileInfo;
use crate::utils::types::web_page::WebPage;
-use crate::{ConfigSettings, Configs, InfoTraffic, StyleType, TrafficChart, location};
+use crate::{ConfigSettings, Configs, StyleType, TrafficChart, location};
pub const FONT_FAMILY_NAME: &str = "Sarasa Mono SC for Sniffnet";
pub const ICON_FONT_FAMILY_NAME: &str = "Icons for Sniffnet";
@@ -88,8 +85,8 @@ pub struct Sniffer {
pub addresses_resolved: HashMap,
/// Collection of the favorite hosts
pub favorite_hosts: HashSet,
- /// Log of the received notifications
- pub logged_notifications: VecDeque,
+ /// Log of the displayed notifications, with the total number of notifications for this capture
+ pub logged_notifications: (VecDeque, usize),
/// Reports if a newer release of the software is available on GitHub
pub newer_release_available: Option,
/// Network device to be analyzed, or PCAP file to be imported
@@ -156,7 +153,7 @@ pub fn new(configs: Configs) -> Self {
info_traffic: InfoTraffic::default(),
addresses_resolved: HashMap::new(),
favorite_hosts: HashSet::new(),
- logged_notifications: VecDeque::new(),
+ logged_notifications: (VecDeque::new(), 0),
newer_release_available: None,
capture_source: CaptureSource::Device(device),
my_devices: Vec::new(),
@@ -365,7 +362,7 @@ pub fn update(&mut self, message: Message) -> Task {
self.configs.settings.notifications.volume = volume;
}
Message::ClearAllNotifications => {
- self.logged_notifications = VecDeque::new();
+ self.logged_notifications.0 = VecDeque::new();
self.modal = None;
}
Message::SwitchPage(next) => {
@@ -552,6 +549,17 @@ pub fn update(&mut self, message: Message) -> Task {
Message::Periodic => {
self.update_waiting_dots();
self.fetch_devices();
+ self.update_threshold();
+ }
+ Message::ExpandNotification(id, expand) => {
+ if let Some(n) = self
+ .logged_notifications
+ .0
+ .iter_mut()
+ .find(|n| n.id() == id)
+ {
+ n.expand(expand);
+ }
}
}
Task::none()
@@ -653,59 +661,45 @@ pub fn scale_factor(&self) -> f64 {
self.configs.settings.scale_factor
}
- /// Updates thresholds if they haven't been edited for a while
- fn update_thresholds(&mut self) {
+ /// Updates threshold if it hasn't been edited for a while
+ fn update_threshold(&mut self) {
// Ignore if just edited
- if let Some(temp_thresholds) = self.timing_events.threshold_adjust_expired_take() {
- // Apply the temporary thresholds to the actual config
+ if let Some(temp_threshold) = self.timing_events.threshold_adjust_expired_take() {
+ // Apply the temporary threshold to the actual config
self.configs
.settings
.notifications
- .packets_notification
- .threshold = temp_thresholds.0.threshold;
+ .data_notification
+ .threshold = temp_threshold.threshold;
self.configs
.settings
.notifications
- .packets_notification
- .previous_threshold = temp_thresholds.0.previous_threshold;
-
+ .data_notification
+ .byte_multiple = temp_threshold.byte_multiple;
self.configs
.settings
.notifications
- .bytes_notification
- .threshold = temp_thresholds.1.threshold;
- self.configs
- .settings
- .notifications
- .bytes_notification
- .byte_multiple = temp_thresholds.1.byte_multiple;
- self.configs
- .settings
- .notifications
- .bytes_notification
- .previous_threshold = temp_thresholds.1.previous_threshold;
+ .data_notification
+ .previous_threshold = temp_threshold.previous_threshold;
}
}
- fn refresh_data(&mut self, msg: InfoTrafficMessage, no_more_packets: bool) {
- self.info_traffic.refresh(msg, &self.favorite_hosts);
- self.update_thresholds();
- let info_traffic = &self.info_traffic;
- if info_traffic.tot_in_packets + info_traffic.tot_out_packets == 0 {
+ fn refresh_data(&mut self, mut msg: InfoTraffic, no_more_packets: bool) {
+ self.info_traffic.refresh(&mut msg);
+ if self.info_traffic.tot_data_info.tot_packets() == 0 {
return;
}
let emitted_notifications = notify_and_log(
&mut self.logged_notifications,
self.configs.settings.notifications,
- info_traffic,
+ &msg,
+ &self.favorite_hosts,
&self.capture_source,
);
- self.info_traffic.favorites_last_interval = HashSet::new();
if self.thumbnail || self.running_page.ne(&RunningPage::Notifications) {
self.unread_notifications += emitted_notifications;
}
- self.traffic_chart
- .update_charts_data(&self.info_traffic, no_more_packets);
+ self.traffic_chart.update_charts_data(&msg, no_more_packets);
if let CaptureSource::Device(device) = &self.capture_source {
let current_device_name = device.get_name().clone();
@@ -801,7 +795,7 @@ fn reset(&mut self) {
self.info_traffic = InfoTraffic::default();
self.addresses_resolved = HashMap::new();
self.favorite_hosts = HashSet::new();
- self.logged_notifications = VecDeque::new();
+ self.logged_notifications = (VecDeque::new(), 0);
self.pcap_error = None;
self.traffic_chart = TrafficChart::new(style, language);
self.report_sort_type = ReportSortType::default();
@@ -866,88 +860,57 @@ fn close_settings(&mut self) {
}
}
- /// Don't update adjustments to thresholds immediately:
- /// that is, sound and toggling thresholds on/off should be applied immediately
+ /// Don't update adjustments to threshold immediately:
+ /// that is, sound and toggling threshold on/off should be applied immediately
/// Threshold adjustments are saved in `self.timing_events.threshold_adjust` and then applied
/// after timeout
fn update_notifications_settings(&mut self, notification: Notification, emit_sound: bool) {
let notifications = self.configs.settings.notifications;
let sound = match notification {
- Notification::Packets(PacketsNotification {
- threshold,
- sound,
- previous_threshold,
- }) => {
- let mut temp_thresholds = self.get_temp_thresholds();
- // Check if adjustments have been made to thresholds
- if temp_thresholds.0.threshold != threshold
- || temp_thresholds.0.previous_threshold != previous_threshold
- {
- temp_thresholds.0 = PacketsNotification {
- threshold,
- sound,
- previous_threshold,
- };
- self.timing_events.threshold_adjust_now(temp_thresholds);
- }
- // If threshold is toggled, apply immediately
- if threshold.is_some() != notifications.packets_notification.threshold.is_some() {
- self.configs
- .settings
- .notifications
- .packets_notification
- .threshold = threshold;
- self.configs
- .settings
- .notifications
- .packets_notification
- .previous_threshold = previous_threshold;
- }
- // always update sound
- self.configs
- .settings
- .notifications
- .packets_notification
- .sound = sound;
- sound
- }
- Notification::Bytes(BytesNotification {
+ Notification::Data(DataNotification {
+ chart_type,
threshold,
byte_multiple,
sound,
previous_threshold,
}) => {
- let mut temp_thresholds = self.get_temp_thresholds();
- if temp_thresholds.1.threshold != threshold
- || temp_thresholds.1.byte_multiple != byte_multiple
- || temp_thresholds.1.previous_threshold != previous_threshold
+ let mut temp_threshold = self.get_temp_threshold();
+ if temp_threshold.threshold != threshold
+ || temp_threshold.byte_multiple != byte_multiple
+ || temp_threshold.previous_threshold != previous_threshold
{
- temp_thresholds.1 = BytesNotification {
+ temp_threshold = DataNotification {
+ chart_type,
threshold,
byte_multiple,
sound,
previous_threshold,
};
- self.timing_events.threshold_adjust_now(temp_thresholds);
+ self.timing_events.threshold_adjust_now(temp_threshold);
}
- if threshold.is_some() != notifications.bytes_notification.threshold.is_some() {
+ if threshold.is_some() != notifications.data_notification.threshold.is_some() {
self.configs
.settings
.notifications
- .bytes_notification
+ .data_notification
.threshold = threshold;
self.configs
.settings
.notifications
- .bytes_notification
+ .data_notification
.byte_multiple = byte_multiple;
self.configs
.settings
.notifications
- .bytes_notification
+ .data_notification
.previous_threshold = previous_threshold;
}
- self.configs.settings.notifications.bytes_notification.sound = sound;
+ self.configs.settings.notifications.data_notification.sound = sound;
+ self.configs
+ .settings
+ .notifications
+ .data_notification
+ .chart_type = chart_type;
sound
}
Notification::Favorite(favorite_notification) => {
@@ -960,16 +923,13 @@ fn update_notifications_settings(&mut self, notification: Notification, emit_sou
}
}
- /// Returns thresholds in `timing_events.threshold_adjust` or copy of current thresholds
- fn get_temp_thresholds(&self) -> (PacketsNotification, BytesNotification) {
- if let Some(temp_thresholds) = self.timing_events.temp_thresholds() {
- temp_thresholds
+ /// Returns threshold in `timing_events.threshold_adjust` or copy of current threshold
+ fn get_temp_threshold(&self) -> DataNotification {
+ if let Some(temp_threshold) = self.timing_events.temp_threshold() {
+ temp_threshold
} else {
let notifications = self.configs.settings.notifications;
- (
- notifications.packets_notification,
- notifications.bytes_notification,
- )
+ notifications.data_notification
}
}
@@ -989,7 +949,7 @@ fn switch_page(&mut self, next: bool) {
true,
) => {
// Running with no overlays
- if self.info_traffic.tot_out_packets + self.info_traffic.tot_in_packets > 0 {
+ if self.info_traffic.tot_data_info.tot_packets() > 0 {
// Running with no overlays and some packets filtered
self.running_page = if next {
self.running_page.next()
@@ -1061,7 +1021,7 @@ fn quit_wrapper(&mut self) -> Task {
fn shortcut_ctrl_d(&mut self) -> Task {
if self.running_page.eq(&RunningPage::Notifications)
- && !self.logged_notifications.is_empty()
+ && !self.logged_notifications.0.is_empty()
{
return Task::done(Message::ShowModal(MyModal::ClearAll));
}
@@ -1100,11 +1060,7 @@ async fn open_file(old_file: String, file_info: FileInfo, language: Language) ->
fn handle_new_host(&mut self, host_msg: HostMessage) {
let HostMessage {
host,
- other_data,
- is_loopback,
- is_local,
- is_bogon,
- traffic_type,
+ data_info_host,
address_to_lookup,
rdns,
} = host_msg;
@@ -1112,32 +1068,16 @@ fn handle_new_host(&mut self, host_msg: HostMessage) {
self.info_traffic
.hosts
.entry(host.clone())
- .and_modify(|data_info_host| {
- data_info_host.data_info.refresh(other_data);
- data_info_host.is_loopback = is_loopback;
- data_info_host.is_local = is_local;
- data_info_host.is_bogon = is_bogon;
- data_info_host.traffic_type = traffic_type;
+ .and_modify(|d| {
+ d.refresh(&data_info_host);
})
- .or_insert_with(|| DataInfoHost {
- data_info: other_data,
- is_favorite: false,
- is_loopback,
- is_local,
- is_bogon,
- traffic_type,
- });
+ .or_insert(data_info_host);
self.addresses_resolved
.insert(address_to_lookup, (rdns, host.clone()));
// update host data states including the new host
self.host_data_states.data.update(&host);
-
- // check if the newly resolved host was featured in the favorites (possible in case of already existing host)
- if self.favorite_hosts.contains(&host) {
- self.info_traffic.favorites_last_interval.insert(host);
- }
}
fn register_sigint_handler() -> Task {
@@ -1172,13 +1112,14 @@ mod tests {
use crate::gui::styles::types::gradient_type::GradientType;
use crate::gui::types::message::Message;
use crate::gui::types::timing_events::TimingEvents;
+ use crate::networking::types::data_info::DataInfo;
use crate::networking::types::host::Host;
- use crate::networking::types::info_traffic::InfoTrafficMessage;
+ use crate::networking::types::traffic_direction::TrafficDirection;
use crate::notifications::types::logged_notification::{
- LoggedNotification, PacketsThresholdExceeded,
+ DataThresholdExceeded, LoggedNotification,
};
use crate::notifications::types::notifications::{
- BytesNotification, FavoriteNotification, Notification, Notifications, PacketsNotification,
+ DataNotification, FavoriteNotification, Notification, Notifications,
};
use crate::notifications::types::sound::Sound;
use crate::report::types::report_col::ReportCol;
@@ -1708,67 +1649,44 @@ fn expire_notifications_timeout(sniffer: &mut Sniffer) {
std::thread::sleep(Duration::from_millis(
TimingEvents::TIMEOUT_THRESHOLD_ADJUST + 5,
));
- // Thresholds adjustments won't be updated if `info_traffic.tot_in_packets`
+ // Threshold adjustments won't be updated if `info_traffic.tot_in_packets`
// and `info_traffic.tot_out_packets` are both `0`.
- sniffer.info_traffic.tot_in_packets = 1;
+ sniffer
+ .info_traffic
+ .tot_data_info
+ .add_packet(0, TrafficDirection::Outgoing);
- // Simulate a tick to apply the settings
- sniffer.update(Message::TickRun(
- 0,
- InfoTrafficMessage::default(),
- vec![],
- false,
- ));
+ // Simulate an update to apply the settings
+ sniffer.update(Message::Periodic);
}
let mut sniffer = Sniffer::new(Configs::default());
- let packets_notification_init = PacketsNotification {
- threshold: None,
- sound: Sound::Gulp,
- previous_threshold: 750,
- };
-
- let packets_notification_toggle_on = PacketsNotification {
- threshold: Some(750),
- sound: Sound::Gulp,
- previous_threshold: 750,
- };
-
- let packets_notification_adjusted_threshold_sound_off = PacketsNotification {
- threshold: Some(1122),
- sound: Sound::None,
- previous_threshold: 1122,
- };
-
- // Used for comparing that sound is applied right away, but not threshold adjustment
- let packets_notification_sound_off_only = PacketsNotification {
- threshold: Some(750),
- sound: Sound::None,
- previous_threshold: 750,
- };
-
- let bytes_notification_init = BytesNotification {
+ let bytes_notification_init = DataNotification {
+ chart_type: ChartType::Bytes,
threshold: None,
byte_multiple: ByteMultiple::KB,
sound: Sound::Pop,
previous_threshold: 800000,
};
- let bytes_notification_toggled_on = BytesNotification {
+ let bytes_notification_toggled_on = DataNotification {
+ chart_type: ChartType::Bytes,
threshold: Some(800_000),
byte_multiple: ByteMultiple::GB,
sound: Sound::Pop,
previous_threshold: 800_000,
};
- let bytes_notification_adjusted_threshold_sound_off = BytesNotification {
+ let bytes_notification_adjusted_threshold_sound_off = DataNotification {
+ chart_type: ChartType::Bytes,
threshold: Some(3),
byte_multiple: ByteMultiple::KB,
sound: Sound::None,
previous_threshold: 3,
};
- let bytes_notification_sound_off_only = BytesNotification {
+ let bytes_notification_sound_off_only = DataNotification {
+ chart_type: ChartType::Bytes,
threshold: Some(800_000),
byte_multiple: ByteMultiple::GB,
sound: Sound::None,
@@ -1789,11 +1707,7 @@ fn expire_notifications_timeout(sniffer: &mut Sniffer) {
assert_eq!(sniffer.configs.settings.notifications.volume, 60);
assert_eq!(sniffer.configs.settings.notifications.volume, 60);
assert_eq!(
- sniffer.configs.settings.notifications.packets_notification,
- packets_notification_init
- );
- assert_eq!(
- sniffer.configs.settings.notifications.bytes_notification,
+ sniffer.configs.settings.notifications.data_notification,
bytes_notification_init
);
assert_eq!(
@@ -1806,11 +1720,7 @@ fn expire_notifications_timeout(sniffer: &mut Sniffer) {
assert_eq!(sniffer.configs.settings.notifications.volume, 95);
assert_eq!(
- sniffer.configs.settings.notifications.packets_notification,
- packets_notification_init,
- );
- assert_eq!(
- sniffer.configs.settings.notifications.bytes_notification,
+ sniffer.configs.settings.notifications.data_notification,
bytes_notification_init,
);
assert_eq!(
@@ -1818,38 +1728,8 @@ fn expire_notifications_timeout(sniffer: &mut Sniffer) {
fav_notification_init,
);
- sniffer.update(Message::UpdateNotificationSettings(
- Notification::Packets(packets_notification_toggle_on),
- false,
- ));
-
- // Verify that toggling threshold is applied immediately
assert_eq!(
- sniffer.configs.settings.notifications.packets_notification,
- packets_notification_toggle_on,
- );
-
- sniffer.update(Message::UpdateNotificationSettings(
- Notification::Packets(packets_notification_adjusted_threshold_sound_off),
- false,
- ));
-
- // Verify thresholds are not applied before timeout expires,
- // and rest is applied immediately
- assert_eq!(
- sniffer.configs.settings.notifications.packets_notification,
- packets_notification_sound_off_only,
- );
-
- expire_notifications_timeout(&mut sniffer);
-
- assert_eq!(sniffer.configs.settings.notifications.volume, 95);
- assert_eq!(
- sniffer.configs.settings.notifications.packets_notification,
- packets_notification_adjusted_threshold_sound_off,
- );
- assert_eq!(
- sniffer.configs.settings.notifications.bytes_notification,
+ sniffer.configs.settings.notifications.data_notification,
bytes_notification_init
);
assert_eq!(
@@ -1859,25 +1739,25 @@ fn expire_notifications_timeout(sniffer: &mut Sniffer) {
// Toggle on bytes notifications
sniffer.update(Message::UpdateNotificationSettings(
- Notification::Bytes(bytes_notification_toggled_on),
+ Notification::Data(bytes_notification_toggled_on),
true,
));
// Verify that toggling threshold is applied immediately
assert_eq!(
- sniffer.configs.settings.notifications.bytes_notification,
+ sniffer.configs.settings.notifications.data_notification,
bytes_notification_toggled_on,
);
sniffer.update(Message::UpdateNotificationSettings(
- Notification::Bytes(bytes_notification_adjusted_threshold_sound_off),
+ Notification::Data(bytes_notification_adjusted_threshold_sound_off),
true,
));
- // Verify adjusted thresholds are not applied before timeout expires,
+ // Verify adjusted threshold is not applied before timeout expires,
// and rest is applied immediately
assert_eq!(
- sniffer.configs.settings.notifications.bytes_notification,
+ sniffer.configs.settings.notifications.data_notification,
bytes_notification_sound_off_only,
);
@@ -1885,11 +1765,7 @@ fn expire_notifications_timeout(sniffer: &mut Sniffer) {
assert_eq!(sniffer.configs.settings.notifications.volume, 95);
assert_eq!(
- sniffer.configs.settings.notifications.packets_notification,
- packets_notification_adjusted_threshold_sound_off
- );
- assert_eq!(
- sniffer.configs.settings.notifications.bytes_notification,
+ sniffer.configs.settings.notifications.data_notification,
bytes_notification_adjusted_threshold_sound_off
);
assert_eq!(
@@ -1903,7 +1779,7 @@ fn expire_notifications_timeout(sniffer: &mut Sniffer) {
true,
));
- // Verify thresholds are not applied before timeout expires,
+ // Verify threshold is not applied before timeout expires,
// and rest is applied immediately
assert_eq!(
sniffer.configs.settings.notifications.favorite_notification,
@@ -1913,11 +1789,7 @@ fn expire_notifications_timeout(sniffer: &mut Sniffer) {
// And the rest is intact
assert_eq!(sniffer.configs.settings.notifications.volume, 95);
assert_eq!(
- sniffer.configs.settings.notifications.packets_notification,
- packets_notification_adjusted_threshold_sound_off
- );
- assert_eq!(
- sniffer.configs.settings.notifications.bytes_notification,
+ sniffer.configs.settings.notifications.data_notification,
bytes_notification_adjusted_threshold_sound_off
);
assert_eq!(
@@ -1930,23 +1802,27 @@ fn expire_notifications_timeout(sniffer: &mut Sniffer) {
#[parallel] // needed to not collide with other tests generating configs files
fn test_clear_all_notifications() {
let mut sniffer = Sniffer::new(Configs::default());
- sniffer.logged_notifications =
- VecDeque::from([LoggedNotification::PacketsThresholdExceeded(
- PacketsThresholdExceeded {
+ sniffer.logged_notifications.0 =
+ VecDeque::from([LoggedNotification::DataThresholdExceeded(
+ DataThresholdExceeded {
+ id: 1,
+ chart_type: ChartType::Packets,
threshold: 0,
- incoming: 0,
- outgoing: 0,
+ data_info: DataInfo::default(),
timestamp: "".to_string(),
+ services: Vec::new(),
+ hosts: Vec::new(),
+ is_expanded: false,
},
)]);
assert_eq!(sniffer.modal, None);
sniffer.update(Message::ShowModal(MyModal::ClearAll));
assert_eq!(sniffer.modal, Some(MyModal::ClearAll));
- assert_eq!(sniffer.logged_notifications.len(), 1);
+ assert_eq!(sniffer.logged_notifications.0.len(), 1);
sniffer.update(Message::ClearAllNotifications);
assert_eq!(sniffer.modal, None);
- assert_eq!(sniffer.logged_notifications.len(), 0);
+ assert_eq!(sniffer.logged_notifications.0.len(), 0);
}
#[test]
@@ -1988,7 +1864,10 @@ fn test_correctly_switch_running_and_settings_pages() {
assert_eq!(sniffer.running_page, RunningPage::Overview);
assert_eq!(sniffer.settings_page, None);
// switch with closed setting and some packets received => change running page
- sniffer.info_traffic.tot_in_packets += 1;
+ sniffer
+ .info_traffic
+ .tot_data_info
+ .add_packet(0, TrafficDirection::Outgoing);
sniffer.update(Message::SwitchPage(true));
assert_eq!(sniffer.running_page, RunningPage::Inspect);
assert_eq!(sniffer.settings_page, None);
@@ -2032,8 +1911,7 @@ fn test_config_settings() {
style_path: "".to_string(),
notifications: Notifications {
volume: 60,
- packets_notification: Default::default(),
- bytes_notification: Default::default(),
+ data_notification: Default::default(),
favorite_notification: Default::default()
},
style: StyleType::Custom(ExtraStyles::A11yDark)
@@ -2074,8 +1952,7 @@ fn test_config_settings() {
),
notifications: Notifications {
volume: 100,
- packets_notification: Default::default(),
- bytes_notification: Default::default(),
+ data_notification: Default::default(),
favorite_notification: Default::default()
},
style: StyleType::Custom(ExtraStyles::DraculaDark)
diff --git a/src/gui/styles/text.rs b/src/gui/styles/text.rs
index c21e4807..917b3949 100644
--- a/src/gui/styles/text.rs
+++ b/src/gui/styles/text.rs
@@ -18,7 +18,6 @@ pub enum TextType {
Subtitle,
Danger,
Sponsor,
- Starred,
}
/// Returns a formatted caption followed by subtitle, new line, tab, and desc
@@ -76,7 +75,6 @@ pub fn highlight(style: &StyleType, element: TextType) -> Color {
TextType::Outgoing => colors.outgoing,
TextType::Danger | TextType::Sponsor => ext.red_alert_color,
TextType::Standard => colors.text_body,
- TextType::Starred => colors.starred,
}
}
diff --git a/src/gui/types/message.rs b/src/gui/types/message.rs
index 0e833d1d..847c2cb4 100644
--- a/src/gui/types/message.rs
+++ b/src/gui/types/message.rs
@@ -6,7 +6,7 @@
use crate::gui::pages::types::settings_page::SettingsPage;
use crate::gui::styles::types::gradient_type::GradientType;
use crate::networking::types::host::{Host, HostMessage};
-use crate::networking::types::info_traffic::InfoTrafficMessage;
+use crate::networking::types::info_traffic::InfoTraffic;
use crate::notifications::types::notifications::Notification;
use crate::report::types::search_parameters::SearchParameters;
use crate::report::types::sort_type::SortType;
@@ -20,7 +20,7 @@ pub enum Message {
/// Run tasks to initialize the app
StartApp(Option),
/// Sent by the backend parsing packets; includes the capture id, new data, new hosts batched data, and whether an offline capture has finished
- TickRun(usize, InfoTrafficMessage, Vec, bool),
+ TickRun(usize, InfoTraffic, Vec, bool),
/// Select network device
DeviceSelection(String),
/// Select IP filter
@@ -65,7 +65,7 @@ pub enum Message {
ChangeRunningPage(RunningPage),
/// Select language
LanguageSelection(Language),
- /// Set packets notification
+ /// Set notification settings
UpdateNotificationSettings(Notification, bool),
/// Clear all received notifications
ClearAllNotifications,
@@ -133,4 +133,6 @@ pub enum Message {
OfflineGap(usize, u32),
/// Emitted every second to repeat certain tasks (such as fetching the network devices)
Periodic,
+ /// Expand or collapse the given logged notification
+ ExpandNotification(usize, bool),
}
diff --git a/src/gui/types/timing_events.rs b/src/gui/types/timing_events.rs
index 7d9ce5e2..dd4a52ea 100644
--- a/src/gui/types/timing_events.rs
+++ b/src/gui/types/timing_events.rs
@@ -2,7 +2,7 @@
use std::ops::Sub;
use std::time::Duration;
-use crate::notifications::types::notifications::{BytesNotification, PacketsNotification};
+use crate::notifications::types::notifications::DataNotification;
pub struct TimingEvents {
/// Instant of the last window focus
@@ -13,12 +13,9 @@ pub struct TimingEvents {
thumbnail_enter: std::time::Instant,
/// Instant of the last click on the thumbnail window
thumbnail_click: std::time::Instant,
- /// Instant of the last adjust of notifications settings thresholds and storage of these
- /// thresholds while editing
- threshold_adjust: (
- std::time::Instant,
- Option<(PacketsNotification, BytesNotification)>,
- ),
+ /// Instant of the last adjust of notifications settings threshold and storage of this
+ /// threshold while editing
+ threshold_adjust: (std::time::Instant, Option),
}
impl TimingEvents {
@@ -66,18 +63,13 @@ pub fn was_just_thumbnail_click(&self) -> bool {
< Duration::from_millis(TimingEvents::TIMEOUT_THUMBNAIL_CLICK)
}
- pub fn threshold_adjust_now(
- &mut self,
- temp_thresholds: (PacketsNotification, BytesNotification),
- ) {
+ pub fn threshold_adjust_now(&mut self, temp_threshold: DataNotification) {
self.threshold_adjust.0 = std::time::Instant::now();
- self.threshold_adjust.1 = Some(temp_thresholds);
+ self.threshold_adjust.1 = Some(temp_threshold);
}
- /// If timeout has expired, take temporary thresholds
- pub fn threshold_adjust_expired_take(
- &mut self,
- ) -> Option<(PacketsNotification, BytesNotification)> {
+ /// If timeout has expired, take temporary threshold
+ pub fn threshold_adjust_expired_take(&mut self) -> Option {
if self.threshold_adjust.0.elapsed()
> Duration::from_millis(TimingEvents::TIMEOUT_THRESHOLD_ADJUST)
{
@@ -87,7 +79,7 @@ pub fn threshold_adjust_expired_take(
}
}
- pub fn temp_thresholds(&self) -> Option<(PacketsNotification, BytesNotification)> {
+ pub fn temp_threshold(&self) -> Option {
self.threshold_adjust.1
}
}
diff --git a/src/networking/manage_packets.rs b/src/networking/manage_packets.rs
index 685a333b..90037db6 100644
--- a/src/networking/manage_packets.rs
+++ b/src/networking/manage_packets.rs
@@ -1,18 +1,16 @@
use std::collections::HashMap;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
-use std::sync::{Arc, Mutex};
use etherparse::{EtherType, LaxPacketHeaders, LinkHeader, NetHeaders, TransportHeader};
use pcap::Address;
-use crate::networking::parse_packets::AddressesResolutionState;
use crate::networking::types::address_port_pair::AddressPortPair;
use crate::networking::types::arp_type::ArpType;
use crate::networking::types::bogon::is_bogon;
use crate::networking::types::capture_context::CaptureSource;
use crate::networking::types::icmp_type::{IcmpType, IcmpTypeV4, IcmpTypeV6};
use crate::networking::types::info_address_port_pair::InfoAddressPortPair;
-use crate::networking::types::info_traffic::InfoTrafficMessage;
+use crate::networking::types::info_traffic::InfoTraffic;
use crate::networking::types::packet_filters_fields::PacketFiltersFields;
use crate::networking::types::service::Service;
use crate::networking::types::service_query::ServiceQuery;
@@ -250,16 +248,14 @@ pub fn get_service(
}
/// Function to insert the source and destination of a packet into the map containing the analyzed traffic
-#[allow(clippy::too_many_arguments)]
pub fn modify_or_insert_in_map(
- info_traffic_msg: &mut InfoTrafficMessage,
+ info_traffic_msg: &mut InfoTraffic,
key: &AddressPortPair,
cs: &CaptureSource,
mac_addresses: (Option, Option),
icmp_type: IcmpType,
arp_type: ArpType,
exchanged_bytes: u128,
- resolutions_state: &Arc>,
) -> (TrafficDirection, Service) {
let mut traffic_direction = TrafficDirection::default();
let mut service = Service::Unknown;
@@ -280,16 +276,6 @@ pub fn modify_or_insert_in_map(
);
// determine upper layer service
service = get_service(key, traffic_direction, my_interface_addresses);
- // consider all hosts as potential favorites, then they'll be filtered in InfoTraffic::refresh
- if let Some(host) = resolutions_state
- .lock()
- .unwrap()
- .addresses_resolved
- .get(&get_address_to_lookup(key, traffic_direction))
- .cloned()
- {
- info_traffic_msg.potential_favorites.insert(host);
- }
}
let timestamp = info_traffic_msg.last_packet_timestamp;
@@ -1566,7 +1552,7 @@ fn test_get_service_unknown() {
#[test]
fn test_all_services_map_key_and_values_are_valid() {
- assert_eq!(SERVICES.len(), 12078);
+ assert_eq!(SERVICES.len(), 12084);
let mut distinct_services = HashSet::new();
for (sq, s) in &SERVICES {
// only tcp or udp
@@ -1587,7 +1573,7 @@ fn test_all_services_map_key_and_values_are_valid() {
// just to count and verify number of distinct services
distinct_services.insert(name.to_string());
}
- assert_eq!(distinct_services.len(), 6450);
+ assert_eq!(distinct_services.len(), 6456);
}
#[test]
diff --git a/src/networking/parse_packets.rs b/src/networking/parse_packets.rs
index b0686818..b18ce3a3 100644
--- a/src/networking/parse_packets.rs
+++ b/src/networking/parse_packets.rs
@@ -17,7 +17,7 @@
use crate::networking::types::filters::Filters;
use crate::networking::types::host::{Host, HostMessage};
use crate::networking::types::icmp_type::IcmpType;
-use crate::networking::types::info_traffic::InfoTrafficMessage;
+use crate::networking::types::info_traffic::InfoTraffic;
use crate::networking::types::my_link_type::MyLinkType;
use crate::networking::types::packet_filters_fields::PacketFiltersFields;
use crate::networking::types::traffic_direction::TrafficDirection;
@@ -48,7 +48,7 @@ pub fn parse_packets(
let my_link_type = capture_context.my_link_type();
let (mut cap, mut savefile) = capture_context.consume();
- let mut info_traffic_msg = InfoTrafficMessage::default();
+ let mut info_traffic_msg = InfoTraffic::default();
let resolutions_state = Arc::new(Mutex::new(AddressesResolutionState::default()));
// list of newly resolved hosts to be sent (batched to avoid UI updates too often)
let new_hosts_to_send = Arc::new(Mutex::new(Vec::new()));
@@ -152,10 +152,11 @@ pub fn parse_packets(
icmp_type,
arp_type,
exchanged_bytes,
- &resolutions_state,
);
- info_traffic_msg.add_packet(exchanged_bytes, traffic_direction);
+ info_traffic_msg
+ .tot_data_info
+ .add_packet(exchanged_bytes, traffic_direction);
// check the rDNS status of this address and act accordingly
let address_to_lookup = get_address_to_lookup(&key, traffic_direction);
@@ -389,13 +390,18 @@ fn reverse_dns_lookup(
.insert(address_to_lookup, new_host.clone());
drop(resolutions_lock);
- let msg_data = HostMessage {
- host: new_host,
- other_data,
- is_loopback,
+ let data_info_host = DataInfoHost {
+ data_info: other_data,
+ is_favorite: false,
is_local,
is_bogon,
+ is_loopback,
traffic_type,
+ };
+
+ let msg_data = HostMessage {
+ host: new_host,
+ data_info_host,
address_to_lookup,
rdns,
};
@@ -414,14 +420,14 @@ pub struct AddressesResolutionState {
#[allow(clippy::large_enum_variant)]
pub enum BackendTrafficMessage {
- TickRun(usize, InfoTrafficMessage, Vec, bool),
+ TickRun(usize, InfoTraffic, Vec, bool),
PendingHosts(usize, Vec),
OfflineGap(usize, u32),
}
fn maybe_send_tick_run_live(
cap_id: usize,
- info_traffic_msg: &mut InfoTrafficMessage,
+ info_traffic_msg: &mut InfoTraffic,
new_hosts_to_send: &Arc>>,
cs: &mut CaptureSource,
first_packet_ticks: &mut Option,
@@ -432,7 +438,7 @@ fn maybe_send_tick_run_live(
first_packet_ticks.and_then(|i| i.checked_add(Duration::from_millis(1000)));
let _ = tx.send_blocking(BackendTrafficMessage::TickRun(
cap_id,
- info_traffic_msg.take_but_leave_timestamp(),
+ info_traffic_msg.take_but_leave_something(),
new_hosts_to_send.lock().unwrap().drain(..).collect(),
false,
));
@@ -447,7 +453,7 @@ fn maybe_send_tick_run_live(
fn maybe_send_tick_run_offline(
cap_id: usize,
- info_traffic_msg: &mut InfoTrafficMessage,
+ info_traffic_msg: &mut InfoTraffic,
new_hosts_to_send: &Arc>>,
next_packet_timestamp: Timestamp,
tx: &Sender,
@@ -460,7 +466,7 @@ fn maybe_send_tick_run_offline(
next_packet_timestamp.secs() - info_traffic_msg.last_packet_timestamp.secs();
let _ = tx.send_blocking(BackendTrafficMessage::TickRun(
cap_id,
- info_traffic_msg.take_but_leave_timestamp(),
+ info_traffic_msg.take_but_leave_something(),
new_hosts_to_send.lock().unwrap().drain(..).collect(),
false,
));
diff --git a/src/networking/types/data_info.rs b/src/networking/types/data_info.rs
index 2b64d734..5c5d99af 100644
--- a/src/networking/types/data_info.rs
+++ b/src/networking/types/data_info.rs
@@ -8,7 +8,7 @@
/// Amount of exchanged data (packets and bytes) incoming and outgoing, with the timestamp of the latest occurrence
// data fields are private to make them only editable via the provided methods: needed to correctly refresh timestamps
-#[derive(Clone, Copy, Debug)]
+#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub struct DataInfo {
/// Incoming packets
incoming_packets: u128,
@@ -47,6 +47,13 @@ pub fn tot_bytes(&self) -> u128 {
self.incoming_bytes + self.outgoing_bytes
}
+ pub fn tot_data(&self, chart_type: ChartType) -> u128 {
+ match chart_type {
+ ChartType::Packets => self.tot_packets(),
+ ChartType::Bytes => self.tot_bytes(),
+ }
+ }
+
pub fn add_packet(&mut self, bytes: u128, traffic_direction: TrafficDirection) {
if traffic_direction.eq(&TrafficDirection::Outgoing) {
self.outgoing_packets += 1;
diff --git a/src/networking/types/data_info_host.rs b/src/networking/types/data_info_host.rs
index 0603206b..4b63d8f9 100644
--- a/src/networking/types/data_info_host.rs
+++ b/src/networking/types/data_info_host.rs
@@ -4,7 +4,7 @@
use crate::networking::types::traffic_type::TrafficType;
/// Host-related information.
-#[derive(Clone, Copy, Default, Debug)]
+#[derive(Clone, Copy, Default, Debug, Eq, PartialEq, Hash)]
pub struct DataInfoHost {
/// Incoming and outgoing packets and bytes
pub data_info: DataInfo,
diff --git a/src/networking/types/host.rs b/src/networking/types/host.rs
index 6684e8ae..370d41e2 100644
--- a/src/networking/types/host.rs
+++ b/src/networking/types/host.rs
@@ -1,7 +1,6 @@
use crate::countries::types::country::Country;
use crate::networking::types::asn::Asn;
-use crate::networking::types::data_info::DataInfo;
-use crate::networking::types::traffic_type::TrafficType;
+use crate::networking::types::data_info_host::DataInfoHost;
use std::net::IpAddr;
/// Struct to represent a network host
@@ -30,11 +29,7 @@ pub struct ThumbnailHost {
#[derive(Clone, Debug)]
pub struct HostMessage {
pub host: Host,
- pub other_data: DataInfo,
- pub is_loopback: bool,
- pub is_local: bool,
- pub is_bogon: Option<&'static str>,
- pub traffic_type: TrafficType,
+ pub data_info_host: DataInfoHost,
pub address_to_lookup: IpAddr,
pub rdns: String,
}
diff --git a/src/networking/types/info_traffic.rs b/src/networking/types/info_traffic.rs
index cfd84143..976e78c7 100644
--- a/src/networking/types/info_traffic.rs
+++ b/src/networking/types/info_traffic.rs
@@ -5,21 +5,14 @@
use crate::networking::types::data_info_host::DataInfoHost;
use crate::networking::types::host::Host;
use crate::networking::types::info_address_port_pair::InfoAddressPortPair;
-use crate::networking::types::traffic_direction::TrafficDirection;
use crate::utils::types::timestamp::Timestamp;
-use std::collections::{HashMap, HashSet};
+use std::collections::HashMap;
/// Struct containing overall traffic statistics and data.
-#[derive(Debug, Default)]
+#[derive(Debug, Default, Clone)]
pub struct InfoTraffic {
- /// Total amount of filtered bytes received.
- pub tot_in_bytes: u128,
- /// Total amount of filtered bytes sent.
- pub tot_out_bytes: u128,
- /// Total amount of filtered packets received.
- pub tot_in_packets: u128,
- /// Total amount of filtered packets sent.
- pub tot_out_packets: u128,
+ /// Total amount of exchanged data
+ pub tot_data_info: DataInfo,
/// Total packets including those not filtered
pub all_packets: u128,
/// Total bytes including those not filtered
@@ -28,139 +21,77 @@ pub struct InfoTraffic {
pub dropped_packets: u32,
/// Timestamp of the latest parsed packet
pub last_packet_timestamp: Timestamp,
- /// Total sent bytes filtered before the current time interval
- pub tot_out_bytes_prev: u128,
- /// Total received bytes filtered before the current time interval
- pub tot_in_bytes_prev: u128,
- /// Total sent packets filtered before the current time interval
- pub tot_out_packets_prev: u128,
- /// Total received packets filtered before the current time interval
- pub tot_in_packets_prev: u128,
/// Map of the filtered traffic
pub map: HashMap,
/// Map of the upper layer services with their data info
pub services: HashMap,
/// Map of the hosts with their data info
pub hosts: HashMap,
- /// Collection of favorite hosts that exchanged data in the last interval
- pub favorites_last_interval: HashSet,
}
impl InfoTraffic {
- pub fn refresh(&mut self, msg: InfoTrafficMessage, favorites: &HashSet) {
- self.tot_out_bytes_prev = self.tot_out_bytes;
- self.tot_in_bytes_prev = self.tot_in_bytes;
- self.tot_out_packets_prev = self.tot_out_packets;
- self.tot_in_packets_prev = self.tot_in_packets;
+ pub fn refresh(&mut self, msg: &mut InfoTraffic) {
+ self.tot_data_info.refresh(msg.tot_data_info);
- self.tot_in_bytes += msg.tot_in_bytes;
- self.tot_out_bytes += msg.tot_out_bytes;
- self.tot_in_packets += msg.tot_in_packets;
- self.tot_out_packets += msg.tot_out_packets;
self.all_packets += msg.all_packets;
self.all_bytes += msg.all_bytes;
self.dropped_packets = msg.dropped_packets;
// it can happen they're equal due to dis-alignments in the PCAP timestamp
if self.last_packet_timestamp.secs() == msg.last_packet_timestamp.secs() {
- self.last_packet_timestamp.add_secs(1);
- } else {
- self.last_packet_timestamp = msg.last_packet_timestamp;
+ msg.last_packet_timestamp.add_secs(1);
}
+ self.last_packet_timestamp = msg.last_packet_timestamp;
- for (key, value) in msg.map {
+ for (key, value) in &msg.map {
self.map
- .entry(key)
- .and_modify(|x| x.refresh(&value))
- .or_insert(value);
- }
-
- for (key, value) in msg.services {
- self.services
- .entry(key)
+ .entry(*key)
.and_modify(|x| x.refresh(value))
- .or_insert(value);
+ .or_insert_with(|| value.clone());
}
- for (key, value) in msg.hosts {
+ for (key, value) in &msg.services {
+ self.services
+ .entry(*key)
+ .and_modify(|x| x.refresh(*value))
+ .or_insert(*value);
+ }
+
+ for (key, value) in &msg.hosts {
self.hosts
- .entry(key)
- .and_modify(|x| x.refresh(&value))
- .or_insert(value);
+ .entry(key.clone())
+ .and_modify(|x| x.refresh(value))
+ .or_insert(*value);
}
-
- self.favorites_last_interval = msg
- .potential_favorites
- .into_iter()
- .filter(|h| favorites.contains(h))
- .collect();
}
pub fn get_thumbnail_data(&self, chart_type: ChartType) -> (u128, u128, u128, u128) {
if chart_type.eq(&ChartType::Bytes) {
(
- self.tot_in_bytes,
- self.tot_out_bytes,
- self.all_bytes - self.tot_out_bytes - self.tot_in_bytes,
+ self.tot_data_info.incoming_bytes(),
+ self.tot_data_info.outgoing_bytes(),
+ self.all_bytes
+ - self.tot_data_info.outgoing_bytes()
+ - self.tot_data_info.incoming_bytes(),
// assume that the dropped packets have the same size as the average packet
u128::from(self.dropped_packets) * self.all_bytes / self.all_packets,
)
} else {
(
- self.tot_in_packets,
- self.tot_out_packets,
- self.all_packets - self.tot_out_packets - self.tot_in_packets,
+ self.tot_data_info.incoming_packets(),
+ self.tot_data_info.outgoing_packets(),
+ self.all_packets
+ - self.tot_data_info.outgoing_packets()
+ - self.tot_data_info.incoming_packets(),
u128::from(self.dropped_packets),
)
}
}
-}
-/// Struct containing traffic statistics and data related to the last time interval.
-#[derive(Debug, Clone, Default)]
-pub struct InfoTrafficMessage {
- /// Total amount of filtered bytes received.
- pub tot_in_bytes: u128,
- /// Total amount of filtered bytes sent.
- pub tot_out_bytes: u128,
- /// Total amount of filtered packets received.
- pub tot_in_packets: u128,
- /// Total amount of filtered packets sent.
- pub tot_out_packets: u128,
- /// Total packets including those not filtered
- pub all_packets: u128,
- /// Total bytes including those not filtered
- pub all_bytes: u128,
- /// Number of dropped packets
- pub dropped_packets: u32,
- /// Timestamp of the latest parsed packet
- pub last_packet_timestamp: Timestamp,
- /// Map of the filtered traffic
- pub map: HashMap,
- /// Map of the upper layer services with their data info
- pub services: HashMap,
- /// Map of the hosts with their data info
- pub hosts: HashMap,
- /// Collection of potentially favorite hosts that exchanged data in the last interval
- pub potential_favorites: HashSet,
-}
-
-impl InfoTrafficMessage {
- pub fn add_packet(&mut self, bytes: u128, traffic_direction: TrafficDirection) {
- if traffic_direction == TrafficDirection::Outgoing {
- //increment number of sent packets and bytes
- self.tot_out_packets += 1;
- self.tot_out_bytes += bytes;
- } else {
- //increment number of received packets and bytes
- self.tot_in_packets += 1;
- self.tot_in_bytes += bytes;
- }
- }
-
- pub fn take_but_leave_timestamp(&mut self) -> Self {
+ pub fn take_but_leave_something(&mut self) -> Self {
let info_traffic = Self {
last_packet_timestamp: self.last_packet_timestamp,
+ dropped_packets: self.dropped_packets,
..Self::default()
};
std::mem::replace(self, info_traffic)
diff --git a/src/networking/types/ip_collection.rs b/src/networking/types/ip_collection.rs
index dc91351a..20b469ac 100644
--- a/src/networking/types/ip_collection.rs
+++ b/src/networking/types/ip_collection.rs
@@ -30,16 +30,13 @@ pub(crate) fn new(str: &str) -> Option {
if object.contains(Self::RANGE_SEPARATOR) {
// IP range
let mut subparts = object.split(Self::RANGE_SEPARATOR);
+ if subparts.clone().count() != 2 {
+ return None;
+ }
let (lower_str, upper_str) =
(subparts.next().unwrap_or(""), subparts.next().unwrap_or(""));
- let lower_ip_res = IpAddr::from_str(lower_str);
- let upper_ip_res = IpAddr::from_str(upper_str);
- let Ok(lower_ip) = lower_ip_res else {
- return None;
- };
- let Ok(upper_ip) = upper_ip_res else {
- return None;
- };
+ let lower_ip = IpAddr::from_str(lower_str).ok()?;
+ let upper_ip = IpAddr::from_str(upper_str).ok()?;
let range = RangeInclusive::new(lower_ip, upper_ip);
if range.is_empty() || lower_ip.is_ipv4() != upper_ip.is_ipv4() {
return None;
@@ -47,11 +44,8 @@ pub(crate) fn new(str: &str) -> Option {
ranges.push(range);
} else {
// individual IP
- if let Ok(ip) = IpAddr::from_str(object) {
- ips.push(ip);
- } else {
- return None;
- }
+ let ip = IpAddr::from_str(object).ok()?;
+ ips.push(ip);
}
}
@@ -249,6 +243,10 @@ fn test_new_collections_invalid() {
assert_eq!(AddressCollection::new("aa::ff-aa::ee"), None);
assert_eq!(AddressCollection::new("1.1.1.1-1.1.0.1"), None);
+
+ assert_eq!(AddressCollection::new("1.1.1.1-2.2.2.2-3.3.3.3"), None);
+
+ assert_eq!(AddressCollection::new("1.1.1.1-2.2.2.2-"), None);
}
#[test]
diff --git a/src/networking/types/port_collection.rs b/src/networking/types/port_collection.rs
index 92fbe5f1..393c35a4 100644
--- a/src/networking/types/port_collection.rs
+++ b/src/networking/types/port_collection.rs
@@ -28,16 +28,13 @@ pub(crate) fn new(str: &str) -> Option {
if object.contains(Self::RANGE_SEPARATOR) {
// port range
let mut subparts = object.split(Self::RANGE_SEPARATOR);
+ if subparts.clone().count() != 2 {
+ return None;
+ }
let (lower_str, upper_str) =
(subparts.next().unwrap_or(""), subparts.next().unwrap_or(""));
- let lower_port_res = u16::from_str(lower_str);
- let upper_port_res = u16::from_str(upper_str);
- let Ok(lower_port) = lower_port_res else {
- return None;
- };
- let Ok(upper_port) = upper_port_res else {
- return None;
- };
+ let lower_port = u16::from_str(lower_str).ok()?;
+ let upper_port = u16::from_str(upper_str).ok()?;
let range = RangeInclusive::new(lower_port, upper_port);
if range.is_empty() {
return None;
@@ -45,11 +42,8 @@ pub(crate) fn new(str: &str) -> Option {
ranges.push(range);
} else {
// individual port
- if let Ok(port) = u16::from_str(object) {
- ports.push(port);
- } else {
- return None;
- }
+ let port = u16::from_str(object).ok()?;
+ ports.push(port);
}
}
@@ -158,6 +152,10 @@ fn test_new_port_collections_invalid() {
assert_eq!(PortCollection::new("999-1"), None);
assert_eq!(PortCollection::new("1:999"), None);
+
+ assert_eq!(PortCollection::new("1-2-3"), None);
+
+ assert_eq!(PortCollection::new("1-2-"), None);
}
#[test]
diff --git a/src/networking/types/traffic_type.rs b/src/networking/types/traffic_type.rs
index 70dadc35..7fa53b5b 100644
--- a/src/networking/types/traffic_type.rs
+++ b/src/networking/types/traffic_type.rs
@@ -1,5 +1,5 @@
/// Enum representing the possible traffic type (unicast, multicast or broadcast).
-#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub enum TrafficType {
/// Unicast traffic
Unicast,
diff --git a/src/notifications/notify_and_log.rs b/src/notifications/notify_and_log.rs
index d08cae34..93acddd9 100644
--- a/src/notifications/notify_and_log.rs
+++ b/src/notifications/notify_and_log.rs
@@ -1,97 +1,92 @@
use crate::InfoTraffic;
+use crate::chart::types::chart_type::ChartType;
use crate::networking::types::capture_context::CaptureSource;
+use crate::networking::types::data_info::DataInfo;
use crate::networking::types::data_info_host::DataInfoHost;
+use crate::networking::types::host::Host;
+use crate::networking::types::service::Service;
use crate::notifications::types::logged_notification::{
- BytesThresholdExceeded, FavoriteTransmitted, LoggedNotification, PacketsThresholdExceeded,
+ DataThresholdExceeded, FavoriteTransmitted, LoggedNotification,
};
use crate::notifications::types::notifications::Notifications;
use crate::notifications::types::sound::{Sound, play};
+use crate::report::types::sort_type::SortType;
use crate::utils::formatted_strings::get_formatted_timestamp;
-use std::collections::VecDeque;
+use std::cmp::min;
+use std::collections::{HashSet, VecDeque};
/// Checks if one or more notifications have to be emitted and logs them.
///
/// It returns the number of new notifications emitted
pub fn notify_and_log(
- logged_notifications: &mut VecDeque,
+ logged_notifications: &mut (VecDeque, usize),
notifications: Notifications,
- info_traffic: &InfoTraffic,
+ info_traffic_msg: &InfoTraffic,
+ favorites: &HashSet,
cs: &CaptureSource,
) -> usize {
let mut sound_to_play = Sound::None;
- let mut emitted_notifications = 0;
- let timestamp = info_traffic.last_packet_timestamp;
- // packets threshold
- if let Some(threshold) = notifications.packets_notification.threshold {
- let sent_packets_entry = info_traffic.tot_out_packets - info_traffic.tot_out_packets_prev;
- let received_packets_entry = info_traffic.tot_in_packets - info_traffic.tot_in_packets_prev;
- if received_packets_entry + sent_packets_entry > u128::from(threshold) {
- // log this notification
- emitted_notifications += 1;
- if logged_notifications.len() >= 30 {
- logged_notifications.pop_back();
- }
- logged_notifications.push_front(LoggedNotification::PacketsThresholdExceeded(
- PacketsThresholdExceeded {
- threshold: notifications.packets_notification.previous_threshold,
- incoming: received_packets_entry.try_into().unwrap_or_default(),
- outgoing: sent_packets_entry.try_into().unwrap_or_default(),
- timestamp: get_formatted_timestamp(timestamp),
- },
- ));
- if sound_to_play.eq(&Sound::None) {
- sound_to_play = notifications.packets_notification.sound;
- }
- }
- }
- // bytes threshold
- if let Some(threshold) = notifications.bytes_notification.threshold {
- let sent_bytes_entry = info_traffic.tot_out_bytes - info_traffic.tot_out_bytes_prev;
- let received_bytes_entry = info_traffic.tot_in_bytes - info_traffic.tot_in_bytes_prev;
- if received_bytes_entry + sent_bytes_entry > u128::from(threshold) {
+ let emitted_notifications_prev = logged_notifications.1;
+ let timestamp = info_traffic_msg.last_packet_timestamp;
+ let data_info = info_traffic_msg.tot_data_info;
+ // data threshold
+ if let Some(threshold) = notifications.data_notification.threshold {
+ let chart_type = notifications.data_notification.chart_type;
+ if data_info.tot_data(chart_type) > u128::from(threshold) {
//log this notification
- emitted_notifications += 1;
- if logged_notifications.len() >= 30 {
- logged_notifications.pop_back();
+ logged_notifications.1 += 1;
+ if logged_notifications.0.len() >= 30 {
+ logged_notifications.0.pop_back();
}
- logged_notifications.push_front(LoggedNotification::BytesThresholdExceeded(
- BytesThresholdExceeded {
- threshold: notifications.bytes_notification.previous_threshold,
- incoming: received_bytes_entry.try_into().unwrap_or_default(),
- outgoing: sent_bytes_entry.try_into().unwrap_or_default(),
- timestamp: get_formatted_timestamp(timestamp),
- },
- ));
+ logged_notifications
+ .0
+ .push_front(LoggedNotification::DataThresholdExceeded(
+ DataThresholdExceeded {
+ id: logged_notifications.1,
+ chart_type,
+ threshold: notifications.data_notification.previous_threshold,
+ data_info,
+ timestamp: get_formatted_timestamp(timestamp),
+ is_expanded: false,
+ hosts: hosts_list(info_traffic_msg, chart_type),
+ services: services_list(info_traffic_msg, chart_type),
+ },
+ ));
if sound_to_play.eq(&Sound::None) {
- sound_to_play = notifications.bytes_notification.sound;
+ sound_to_play = notifications.data_notification.sound;
}
}
}
// from favorites
- if notifications.favorite_notification.notify_on_favorite
- && !info_traffic.favorites_last_interval.is_empty()
- {
- for host in info_traffic.favorites_last_interval.clone() {
- //log this notification
- emitted_notifications += 1;
- if logged_notifications.len() >= 30 {
- logged_notifications.pop_back();
- }
+ if notifications.favorite_notification.notify_on_favorite {
+ let favorites_last_interval: HashSet<(Host, DataInfoHost)> = info_traffic_msg
+ .hosts
+ .iter()
+ .filter(|(h, _)| favorites.contains(h))
+ .map(|(h, data)| (h.clone(), *data))
+ .collect();
+ if !favorites_last_interval.is_empty() {
+ for (host, data_info_host) in favorites_last_interval {
+ //log this notification
+ logged_notifications.1 += 1;
+ if logged_notifications.0.len() >= 30 {
+ logged_notifications.0.pop_back();
+ }
- let data_info_host = *info_traffic
- .hosts
- .get(&host)
- .unwrap_or(&DataInfoHost::default());
- logged_notifications.push_front(LoggedNotification::FavoriteTransmitted(
- FavoriteTransmitted {
- host,
- data_info_host,
- timestamp: get_formatted_timestamp(timestamp),
- },
- ));
- }
- if sound_to_play.eq(&Sound::None) {
- sound_to_play = notifications.favorite_notification.sound;
+ logged_notifications
+ .0
+ .push_front(LoggedNotification::FavoriteTransmitted(
+ FavoriteTransmitted {
+ id: logged_notifications.1,
+ host,
+ data_info_host,
+ timestamp: get_formatted_timestamp(timestamp),
+ },
+ ));
+ }
+ if sound_to_play.eq(&Sound::None) {
+ sound_to_play = notifications.favorite_notification.sound;
+ }
}
}
@@ -100,5 +95,44 @@ pub fn notify_and_log(
play(sound_to_play, notifications.volume);
}
- emitted_notifications
+ logged_notifications.1 - emitted_notifications_prev
+}
+
+fn hosts_list(info_traffic_msg: &InfoTraffic, chart_type: ChartType) -> Vec<(Host, DataInfoHost)> {
+ let mut hosts: Vec<(Host, DataInfoHost)> = info_traffic_msg
+ .hosts
+ .iter()
+ .map(|(h, data)| (h.clone(), *data))
+ .collect();
+ hosts.sort_by(|(_, a), (_, b)| {
+ a.data_info
+ .compare(&b.data_info, SortType::Descending, chart_type)
+ });
+ let n_entry = min(hosts.len(), 4);
+ hosts
+ .get(..n_entry)
+ .unwrap_or_default()
+ .to_owned()
+ .into_iter()
+ .collect()
+}
+
+fn services_list(
+ info_traffic_msg: &InfoTraffic,
+ chart_type: ChartType,
+) -> Vec<(Service, DataInfo)> {
+ let mut services: Vec<(Service, DataInfo)> = info_traffic_msg
+ .services
+ .iter()
+ .filter(|(service, _)| service != &&Service::NotApplicable)
+ .map(|(s, data)| (*s, *data))
+ .collect();
+ services.sort_by(|(_, a), (_, b)| a.compare(b, SortType::Descending, chart_type));
+ let n_entry = min(services.len(), 4);
+ services
+ .get(..n_entry)
+ .unwrap_or_default()
+ .to_owned()
+ .into_iter()
+ .collect()
}
diff --git a/src/notifications/types/logged_notification.rs b/src/notifications/types/logged_notification.rs
index d3c90dcf..6a75d2cd 100644
--- a/src/notifications/types/logged_notification.rs
+++ b/src/notifications/types/logged_notification.rs
@@ -1,34 +1,55 @@
+use crate::chart::types::chart_type::ChartType;
+use crate::networking::types::data_info::DataInfo;
use crate::networking::types::data_info_host::DataInfoHost;
use crate::networking::types::host::Host;
+use crate::networking::types::service::Service;
/// Enum representing the possible notification events.
pub enum LoggedNotification {
- /// Packets threshold exceeded
- PacketsThresholdExceeded(PacketsThresholdExceeded),
- /// Byte threshold exceeded
- BytesThresholdExceeded(BytesThresholdExceeded),
+ /// Data threshold exceeded
+ DataThresholdExceeded(DataThresholdExceeded),
/// Favorite connection exchanged data
FavoriteTransmitted(FavoriteTransmitted),
}
-#[derive(Clone)]
-pub struct PacketsThresholdExceeded {
- pub(crate) threshold: u32,
- pub(crate) incoming: u32,
- pub(crate) outgoing: u32,
- pub(crate) timestamp: String,
+impl LoggedNotification {
+ pub fn id(&self) -> usize {
+ match self {
+ LoggedNotification::DataThresholdExceeded(d) => d.id,
+ LoggedNotification::FavoriteTransmitted(f) => f.id,
+ }
+ }
+
+ pub fn data_info(&self) -> DataInfo {
+ match self {
+ LoggedNotification::DataThresholdExceeded(d) => d.data_info,
+ LoggedNotification::FavoriteTransmitted(f) => f.data_info_host.data_info,
+ }
+ }
+
+ pub fn expand(&mut self, expand: bool) {
+ match self {
+ LoggedNotification::DataThresholdExceeded(d) => d.is_expanded = expand,
+ LoggedNotification::FavoriteTransmitted(_) => {}
+ }
+ }
}
#[derive(Clone)]
-pub struct BytesThresholdExceeded {
+pub struct DataThresholdExceeded {
+ pub(crate) id: usize,
+ pub(crate) chart_type: ChartType,
pub(crate) threshold: u64,
- pub(crate) incoming: u32,
- pub(crate) outgoing: u32,
+ pub(crate) data_info: DataInfo,
pub(crate) timestamp: String,
+ pub(crate) is_expanded: bool,
+ pub(crate) hosts: Vec<(Host, DataInfoHost)>,
+ pub(crate) services: Vec<(Service, DataInfo)>,
}
#[derive(Clone)]
pub struct FavoriteTransmitted {
+ pub(crate) id: usize,
pub(crate) host: Host,
pub(crate) data_info_host: DataInfoHost,
pub(crate) timestamp: String,
diff --git a/src/notifications/types/notifications.rs b/src/notifications/types/notifications.rs
index 07ab80ec..8935ad07 100644
--- a/src/notifications/types/notifications.rs
+++ b/src/notifications/types/notifications.rs
@@ -1,14 +1,14 @@
use serde::{Deserialize, Serialize};
use crate::ByteMultiple;
+use crate::chart::types::chart_type::ChartType;
use crate::notifications::types::sound::Sound;
/// Used to contain the notifications configuration set by the user
#[derive(Clone, Serialize, Deserialize, Copy, PartialEq, Debug)]
pub struct Notifications {
pub volume: u8,
- pub packets_notification: PacketsNotification,
- pub bytes_notification: BytesNotification,
+ pub data_notification: DataNotification,
pub favorite_notification: FavoriteNotification,
}
@@ -16,8 +16,7 @@ impl Default for Notifications {
fn default() -> Self {
Notifications {
volume: 60,
- packets_notification: PacketsNotification::default(),
- bytes_notification: BytesNotification::default(),
+ data_notification: DataNotification::default(),
favorite_notification: FavoriteNotification::default(),
}
}
@@ -26,54 +25,16 @@ fn default() -> Self {
/// Enum representing the possible notifications.
#[derive(Debug, Clone, Copy)]
pub enum Notification {
- /// Packets notification
- Packets(PacketsNotification),
- /// Bytes notification
- Bytes(BytesNotification),
+ /// Data notification
+ Data(DataNotification),
/// Favorites notification
Favorite(FavoriteNotification),
}
#[derive(Clone, Eq, PartialEq, Serialize, Deserialize, Debug, Copy)]
-pub struct PacketsNotification {
- /// Threshold of received + sent packets; if exceeded a notification is emitted
- pub threshold: Option,
- /// The sound to emit
- pub sound: Sound,
- /// The last used Some value for the threshold field
- pub previous_threshold: u32,
-}
-
-impl Default for PacketsNotification {
- fn default() -> Self {
- PacketsNotification {
- threshold: None,
- sound: Sound::Gulp,
- previous_threshold: 750,
- }
- }
-}
-
-impl PacketsNotification {
- /// Arbitrary string constructor. Will fallback values to existing notification if set, or default otherwise
- pub fn from(value: &str, existing: Option) -> Self {
- let default = existing.unwrap_or_default();
-
- let new_threshold = if value.is_empty() {
- 0
- } else {
- value.parse().unwrap_or(default.previous_threshold)
- };
- Self {
- threshold: Some(new_threshold),
- previous_threshold: new_threshold,
- ..default
- }
- }
-}
-
-#[derive(Clone, Eq, PartialEq, Serialize, Deserialize, Debug, Copy)]
-pub struct BytesNotification {
+pub struct DataNotification {
+ /// Data representation
+ pub chart_type: ChartType,
/// Threshold of received + sent bytes; if exceeded a notification is emitted
pub threshold: Option,
/// B, KB, MB or GB
@@ -84,9 +45,10 @@ pub struct BytesNotification {
pub previous_threshold: u64,
}
-impl Default for BytesNotification {
+impl Default for DataNotification {
fn default() -> Self {
- BytesNotification {
+ DataNotification {
+ chart_type: ChartType::Bytes,
threshold: None,
byte_multiple: ByteMultiple::KB,
sound: Sound::Pop,
@@ -95,7 +57,7 @@ fn default() -> Self {
}
}
-impl BytesNotification {
+impl DataNotification {
/// Arbitrary string constructor. Will fallback values to existing notification if set, or default otherwise
pub fn from(value: &str, existing: Option) -> Self {
let default = existing.unwrap_or_default();
@@ -181,42 +143,42 @@ mod tests {
#[rstest]
#[case("123",
- BytesNotification{
- previous_threshold: 123, threshold: Some(123), byte_multiple: ByteMultiple::B, ..BytesNotification::default() }
+ DataNotification{
+ previous_threshold: 123, threshold: Some(123), byte_multiple: ByteMultiple::B, ..DataNotification::default() }
)]
#[case("500k",
- BytesNotification{
- previous_threshold: 500_000, threshold: Some(500_000),byte_multiple: ByteMultiple::KB, ..BytesNotification::default() }
+ DataNotification{
+ previous_threshold: 500_000, threshold: Some(500_000),byte_multiple: ByteMultiple::KB, ..DataNotification::default() }
)]
#[case("420m",
- BytesNotification{
- previous_threshold: 420_000_000, threshold: Some(420_000_000),byte_multiple: ByteMultiple::MB, ..BytesNotification::default() }
+ DataNotification{
+ previous_threshold: 420_000_000, threshold: Some(420_000_000),byte_multiple: ByteMultiple::MB, ..DataNotification::default() }
)]
#[case("744ь",
- BytesNotification{
- previous_threshold: 744, threshold: Some(744),byte_multiple: ByteMultiple::B, ..BytesNotification::default() }
+ DataNotification{
+ previous_threshold: 744, threshold: Some(744),byte_multiple: ByteMultiple::B, ..DataNotification::default() }
)]
#[case("888g",
- BytesNotification{
- previous_threshold: 888_000_000_000, threshold: Some(888_000_000_000),byte_multiple: ByteMultiple::GB, ..BytesNotification::default() }
+ DataNotification{
+ previous_threshold: 888_000_000_000, threshold: Some(888_000_000_000),byte_multiple: ByteMultiple::GB, ..DataNotification::default() }
)]
fn test_can_instantiate_bytes_notification_from_string(
#[case] input: &str,
- #[case] expected: BytesNotification,
+ #[case] expected: DataNotification,
) {
- assert_eq!(expected, BytesNotification::from(input, None));
+ assert_eq!(expected, DataNotification::from(input, None));
}
#[rstest]
#[case("foob@r")]
#[case("2O6")]
fn test_will_reuse_previous_value_if_cannot_parse(#[case] input: &str) {
- let existing_notification = BytesNotification {
+ let existing_notification = DataNotification {
previous_threshold: 420_000_000_000,
byte_multiple: ByteMultiple::GB,
..Default::default()
};
- let expected = BytesNotification {
+ let expected = DataNotification {
previous_threshold: 420_000_000_000,
threshold: Some(420_000_000_000),
byte_multiple: ByteMultiple::GB,
@@ -224,7 +186,7 @@ fn test_will_reuse_previous_value_if_cannot_parse(#[case] input: &str) {
};
assert_eq!(
expected,
- BytesNotification::from(input, Some(existing_notification))
+ DataNotification::from(input, Some(existing_notification))
);
}
@@ -259,26 +221,4 @@ fn test_can_instantiate_favourite_notification() {
}
);
}
-
- #[rstest]
- #[case("123", PacketsNotification{
- previous_threshold: 123,
- threshold: Some(123),
- ..PacketsNotification::default() })]
- #[case("8888", PacketsNotification{
- previous_threshold: 8888,
- threshold: Some(8888),
- ..PacketsNotification::default() })]
- #[case("420 m", PacketsNotification{
- threshold: Some(750),
- ..PacketsNotification::default() })]
- #[case("foob@r", PacketsNotification{
- threshold: Some(750),
- ..PacketsNotification::default() })]
- fn test_can_instantiate_packet_notification_from_string(
- #[case] input: &str,
- #[case] expected: PacketsNotification,
- ) {
- assert_eq!(expected, PacketsNotification::from(input, None));
- }
}
diff --git a/src/notifications/types/sound.rs b/src/notifications/types/sound.rs
index c98c9bc0..aa371302 100644
--- a/src/notifications/types/sound.rs
+++ b/src/notifications/types/sound.rs
@@ -1,9 +1,9 @@
use std::fmt;
use std::thread;
+use iced::Font;
use iced::widget::Text;
-use iced::{Alignment, Font, Length};
-use rodio::{Decoder, OutputStream, Sink};
+use rodio::{Decoder, OutputStreamBuilder, Sink};
use serde::{Deserialize, Serialize};
use crate::gui::styles::style_constants::FONT_SIZE_FOOTER;
@@ -51,9 +51,6 @@ pub fn get_text<'a>(self, font: Font) -> iced::widget::Text<'a, StyleType> {
Sound::None => Icon::Forbidden.to_text(),
}
.size(FONT_SIZE_FOOTER)
- .width(Length::Fill)
- .align_x(Alignment::Center)
- .align_y(Alignment::Center)
}
}
@@ -66,13 +63,13 @@ pub fn play(sound: Sound, volume: u8) {
.name("thread_play_sound".to_string())
.spawn(move || {
// Get an output stream handle to the default physical sound device
- let Ok((_stream, stream_handle)) = OutputStream::try_default().log_err(location!())
+ let Ok(mut stream_handle) =
+ OutputStreamBuilder::open_default_stream().log_err(location!())
else {
return;
};
- let Ok(sink) = Sink::try_new(&stream_handle).log_err(location!()) else {
- return;
- };
+ stream_handle.log_on_drop(false);
+ let sink = Sink::connect_new(stream_handle.mixer());
//load data
let data = std::io::Cursor::new(mp3_sound);
// Decode that sound file into a source
diff --git a/src/translations/translations.rs b/src/translations/translations.rs
index 5b6c884b..010bef21 100644
--- a/src/translations/translations.rs
+++ b/src/translations/translations.rs
@@ -29,6 +29,7 @@ pub fn choose_adapters_translation<'a>(language: Language) -> Text<'a, StyleType
Language::UZ => "Tekshirish uchun tarmoq adapterini tanlang",
Language::VI => "Hãy chọn network adapter để quan sát",
Language::ID => "Pilih Adapter Jaringan yang ingin dicek",
+ Language::NL => "Selecteer netwerkadapter om te inspecteren",
})
}
@@ -55,6 +56,7 @@ pub fn choose_adapters_translation<'a>(language: Language) -> Text<'a, StyleType
// Language::JA => "アプリケーション プロトコル",
// Language::UZ => "Ilova protokoli",
// Language::ID => "Protokol Aplikasi",
+// Language::NL => "Toepassingsprotocol",
// }
// }
@@ -82,12 +84,13 @@ pub fn select_filters_translation<'a>(language: Language) -> Text<'a, StyleType>
Language::UZ => "Tarmoq trafigiga qo'llaniladigan filtrlarni tanlang",
Language::VI => "Hãy chọn bộ lọc cho lưu lượng mạng",
Language::ID => "Pilih filter yang ingin dipasang dilalulintas jaringan",
+ Language::NL => "Selecteer filters om toe te passen op netwerkverkeer",
})
}
pub fn start_translation(language: Language) -> &'static str {
match language {
- Language::EN | Language::DE | Language::RO | Language::KO => "Start!",
+ Language::EN | Language::DE | Language::RO | Language::KO | Language::NL => "Start!",
Language::IT => "Avvia!",
Language::FR => "Commencer!",
Language::ES => "¡Empieza!",
@@ -114,7 +117,7 @@ pub fn address_translation(language: Language) -> &'static str {
Language::IT => "Indirizzo",
Language::FR | Language::DE => "Adresse",
Language::ES => "Dirección",
- Language::PL | Language::TR => "Adres",
+ Language::PL | Language::TR | Language::NL => "Adres",
Language::UK => "Адреса",
Language::ZH => "网络地址",
Language::ZH_TW => "網路位址",
@@ -140,7 +143,7 @@ pub fn addresses_translation(language: Language) -> &'static str {
Language::FR => "Adresses",
Language::ES => "Direcciones",
Language::PL => "Adresy",
- Language::DE => "Adressen",
+ Language::DE | Language::NL => "Adressen",
Language::UK => "Адреси",
Language::ZH => "网络地址",
Language::ZH_TW => "網路位址",
@@ -184,6 +187,7 @@ pub fn ip_version_translation(language: Language) -> &'static str {
Language::UZ => "IP versiyasi",
Language::VI => "Phiên bản IP",
Language::ID => "Versi IP",
+ Language::NL => "IP versie",
}
}
@@ -209,12 +213,13 @@ pub fn ip_version_translation(language: Language) -> &'static str {
// Language::JA => "トランスポート プロトコル",
// Language::UZ => "Transport protokoli",
// Language::ID => "Protokol berjalan",
+// Language::NL => "Transportprotocol",
// }
// }
pub fn protocol_translation(language: Language) -> &'static str {
match language {
- Language::EN | Language::RO => "Protocol",
+ Language::EN | Language::RO | Language::NL => "Protocol",
Language::IT => "Protocollo",
Language::FR => "Protocole",
Language::ES | Language::PT => "Protocolo",
@@ -259,6 +264,7 @@ pub fn traffic_rate_translation<'a>(language: Language) -> Text<'a, StyleType> {
Language::UZ => "Trafik tezligi",
Language::VI => "Lưu lượng truy cập",
Language::ID => "Tingkat lalulintas",
+ Language::NL => "Verkeerssnelheid",
})
}
@@ -283,6 +289,7 @@ pub fn traffic_rate_translation<'a>(language: Language) -> Text<'a, StyleType> {
// Language::SE => "Relevanta anslutningar:",
// Language::UZ => "Tegishli ulanishlar:",
// Language::ID => "Koneksi yang berkaitan",
+// Language::NL => "Relevante verbindingen:",
// })
// }
@@ -309,6 +316,7 @@ pub fn settings_translation(language: Language) -> &'static str {
Language::UZ => "Sozlamalar",
Language::VI => "Cài đặt",
Language::ID => "Pengaturan",
+ Language::NL => "Instellingen",
}
}
@@ -319,7 +327,7 @@ pub fn yes_translation<'a>(language: Language) -> Text<'a, StyleType> {
Language::FR => "Oui",
Language::ES => "Sí",
Language::PL => "Tak",
- Language::DE | Language::SV => "Ja",
+ Language::DE | Language::SV | Language::NL => "Ja",
Language::UK => "Так",
Language::ZH | Language::ZH_TW => "是",
Language::RO => "Da",
@@ -361,6 +369,7 @@ pub fn ask_quit_translation<'a>(language: Language) -> Text<'a, StyleType> {
Language::UZ => "Tahlildan chiqishga ishonchingiz komilmi?",
Language::VI => "Bạn có chắc là muốn thoát phiên phân tích này?",
Language::ID => "Apa kamu yakin untuk berhenti analisa?",
+ Language::NL => "Weet je zeker dat je deze analyse wilt afsluiten?",
})
}
@@ -388,6 +397,7 @@ pub fn quit_analysis_translation(language: Language) -> &'static str {
Language::UZ => "Tahlildan chiqish",
Language::VI => "Thoát phiên phân tích",
Language::ID => "Berhenti analisa",
+ Language::NL => "Analyse afsluiten",
}
}
@@ -415,6 +425,7 @@ pub fn ask_clear_all_translation<'a>(language: Language) -> Text<'a, StyleType>
Language::UZ => "Haqiqatan ham bildirishnomalarni tozalamoqchimisiz?",
Language::VI => "Bạn có chắc là muốn xóa các thông báo?",
Language::ID => "Apa kamu yakin untuk membersihkan notifikasi?",
+ Language::NL => "Weet je zeker dat je alle meldingen wilt wissen?",
})
}
@@ -441,6 +452,7 @@ pub fn clear_all_translation(language: Language) -> &'static str {
Language::UZ => "Barchasini tozalash",
Language::VI => "Xóa tất cả",
Language::ID => "Bersihkan semua",
+ Language::NL => "Alles wissen",
}
}
@@ -468,6 +480,7 @@ pub fn hide_translation(language: Language) -> &'static str {
Language::UZ => "Yashirish",
Language::VI => "Ẩn",
Language::ID => "Sembunyikan",
+ Language::NL => "Verbergen",
}
}
@@ -494,6 +507,7 @@ pub fn network_adapter_translation(language: Language) -> &'static str {
Language::JA => "ネットワーク アダプター",
Language::UZ => "Tarmoq adapteri",
Language::ID => "Adapter jaringan",
+ Language::NL => "Netwerkadapter",
}
}
@@ -609,6 +623,11 @@ pub fn no_addresses_translation<'a>(language: Language, adapter: &str) -> Text<'
{network_adapter_translation}: {adapter}\n\n\
Jika kamu yakin kamu terhubung ke internet, coba untuk memilih adapter lainnya."
),
+ Language::NL => format!(
+ "Er kan geen verkeer worden waargenomen omdat de geselecteerde adapter geen actieve adressen heeft...\n\n\
+ {network_adapter_translation}: {adapter}\n\n\
+ Als je zeker weet dat je verbonden bent met het internet, probeer dan een andere adapter te kiezen."
+ ),
})
}
@@ -724,6 +743,11 @@ pub fn waiting_translation<'a>(language: Language, adapter: &str) -> Text<'a, St
{network_adapter_translation}: {adapter}\n\n\
Apa kamu yakin kamu terhubung ke internet, dan memilih adapter yang benar?"
),
+ Language::NL => format!(
+ "Er is nog geen verkeer waargenomen. Wachten op netwerkpakketten...\n\n\
+ {network_adapter_translation}: {adapter}\n\n\
+ Weet je zeker dat je verbonden bent met het internet en de juiste adapter hebt geselecteerd?"
+ ),
})
}
@@ -838,6 +862,11 @@ pub fn some_observed_translation<'a>(language: Language, observed: u128) -> Text
Paket yg difilter: 0\n\n\
Beberapa paket dilacak, tetapi tidak ada yg terlihat berdasarkan filter yang kamu pilih..."
),
+ Language::NL => format!(
+ "Totaal aantal onderschepte pakketten: {observed}\n\n\
+ Gefilterde pakketten: 0\n\n\
+ Er zijn enkele pakketten onderschept, maar nog geen enkele is geselecteerd volgens de filters die je hebt opgegeven..."
+ ),
})
}
@@ -865,6 +894,7 @@ pub fn some_observed_translation<'a>(language: Language, observed: u128) -> Text
// Language::UZ => "Filtrlangan paketlar",
// Language::VI => "Các gói tin đã được lọc",
// Language::ID => "Paket data tersaring",
+// Language::NL => "Gefilterde pakketten",
// }
// }
@@ -891,6 +921,7 @@ pub fn some_observed_translation<'a>(language: Language, observed: u128) -> Text
// Language::UZ => "Filtrlangan baytlar",
// Language::VI => "Các bytes đã được lọc",
// Language::ID => "Bytes tersaring",
+// Language::NL => "Gefilterde bytes",
// }
// }
@@ -934,6 +965,7 @@ pub fn some_observed_translation<'a>(language: Language, observed: u128) -> Text
// Language::UZ => format!("(Jami: {percentage} )"),
// Language::VI => format!("({percentage} trên tổng cộng)"),
// Language::ID => format!("({percentage} dari total)"),
+// Language::NL => format!("({percentage} van het totaal)"),
// }
// }
@@ -958,6 +990,7 @@ pub fn some_observed_translation<'a>(language: Language, observed: u128) -> Text
// Language::SE => "Filtrerade paket per applikationsprotokoll:",
// Language::UZ => "Har bir dastur protokoli uchun filtrlangan paketlar:",
// Language::ID => "Paket data tersaring dari setiap protokol aplikasi:",
+// Language::NL => "Gefilterde pakketten per applicatieprotocol:",
// })
// }
@@ -1000,6 +1033,8 @@ pub fn some_observed_translation<'a>(language: Language, observed: u128) -> Text
// Ulanishni sevimlilar ro'yhatiga qo'shish uchun ulanish yaqinidagi yulduzcha belgisini bosing."
// Language::ID => "Tidak ada yang ditampilkan untuk saatini.\n\
// untuk menambah koneksi ke favorit, klik dilogo bintang dekat dengan koneksi.",
+// Language::NL => "Niets om te laten zien op dit moment.\n\
+// Om een verbinding toe te voegen aan je favorieten, klik op het ster symbool naast de verbinding.",
// })
// }
@@ -1093,6 +1128,10 @@ pub fn error_translation(language: Language, error: &str) -> Text {
"Terjadi kesalahan! \n\n\
{error}"
),
+ Language::NL => format!(
+ "Er is een fout opgetreden! \n\n\
+ {error}"
+ ),
})
}
@@ -1118,6 +1157,7 @@ pub fn error_translation(language: Language, error: &str) -> Text {
// Language::JA => "両方",
// Language::UZ => "ikkalasi ham",
// Language::ID => "keduanya",
+// Language::NL => "beide",
// }
// }
@@ -1136,6 +1176,7 @@ pub fn error_translation(language: Language, error: &str) -> Text {
// Language::UZ => "Barcha protokollar",
// Language::UK => "Усі протоколи",
// Language::ID => "Semua protokol",
+// Language::NL => "Alle protocollen",
// }
// }
@@ -1161,6 +1202,7 @@ pub fn error_translation(language: Language, error: &str) -> Text {
// Language::JA => "すべて",
// Language::UZ => "Barchasi",
// Language::ID => "Semua",
+// Language::NL => "Alle",
// }
// }
@@ -1186,6 +1228,7 @@ pub fn packets_translation(language: Language) -> &'static str {
Language::JA => "パケット",
Language::UZ => "paketlar",
Language::VI => "các gói tin",
+ Language::NL => "pakketten",
}
}
@@ -1213,6 +1256,7 @@ pub fn packets_translation(language: Language) -> &'static str {
// Language::UZ => "paket soniyasiga",
// Language::VI => "gói tin trên giây",
// Language::ID => "paket per detik",
+// Language::NL => "pakketten per seconde",
// }
// }
@@ -1226,6 +1270,7 @@ pub fn bytes_translation(language: Language) -> &'static str {
| Language::SV
| Language::VI
| Language::ID
+ | Language::NL
| Language::DE => "bytes",
Language::FR => "octets",
Language::PL => "bajty",
@@ -1265,6 +1310,7 @@ pub fn bytes_translation(language: Language) -> &'static str {
// Language::UZ => "bayt soniyasiga",
// Language::VI => "byte trên giây",
// Language::ID => "bytes per detik",
+// Language::NL => "bytes per seconde",
// }
// }
@@ -1291,6 +1337,7 @@ pub fn bytes_translation(language: Language) -> &'static str {
// Language::JA => "新しい順",
// Language::UZ => "eng so'nggi",
// Language::ID => "paling terakhir",
+// Language::NL => "meest recent",
// }
// }
@@ -1317,6 +1364,7 @@ pub fn bytes_translation(language: Language) -> &'static str {
// Language::JA => "パケット数の多い順",
// Language::UZ => "eng ko'p paketlar",
// Language::ID => "paket terbanyak",
+// Language::NL => "meeste pakketten",
// }
// }
@@ -1342,6 +1390,7 @@ pub fn bytes_translation(language: Language) -> &'static str {
// Language::FI => "eniten tavuja",
// Language::JA => "バイト量の多い順",
// Language::UZ => "eng ko'p bayt",
+// Language::NL => "meeste bytes",
// }
// }
@@ -1365,6 +1414,7 @@ pub fn bytes_translation(language: Language) -> &'static str {
// Language::SE => "favoriter",
// Language::UZ => "sevimlilar",
// Language::ID => "favorit",
+// Language::NL => "favorieten",
// }
// }
@@ -1392,6 +1442,7 @@ pub fn notifications_title_translation<'a>(language: Language) -> Text<'a, Style
Language::UZ => "Bildirishnomalaringizni sozlang",
Language::VI => "Tùy chỉnh thông báo của bạn",
Language::ID => "Sesuaikan notifikasi Anda",
+ Language::NL => "Pas je meldingen aan",
})
}
@@ -1419,6 +1470,7 @@ pub fn appearance_title_translation<'a>(language: Language) -> Text<'a, StyleTyp
Language::UZ => "Sevimli mavzuingizni tanlang",
Language::VI => "Chọn chủ đề bạn muốn",
Language::ID => "Pilih tema favorit Kamu",
+ Language::NL => "Kies je favoriete thema",
})
}
@@ -1446,6 +1498,7 @@ pub fn active_filters_translation(language: Language) -> &'static str {
Language::UZ => "Faol filtrlar",
Language::VI => "Bộ lọc đang hoạt động",
Language::ID => "Filter aktif",
+ Language::NL => "Actieve filters",
}
}
@@ -1473,6 +1526,7 @@ pub fn none_translation(language: Language) -> &'static str {
Language::UZ => "hech biri",
Language::VI => "không có",
Language::ID => "Tidak ada",
+ Language::NL => "geen",
}
}
@@ -1500,6 +1554,7 @@ pub fn none_translation(language: Language) -> &'static str {
// Language::UZ => "Sniffnet-ning asl qora mavzusi",
// Language::VI => "Chủ đề tối của Sniffnet",
// Language::ID => "Tema gelap bawaan Sniffnet",
+// Language::NL => "Sniffnet's originele donkere thema",
// }
// }
@@ -1526,6 +1581,7 @@ pub fn none_translation(language: Language) -> &'static str {
// Language::UZ => "Sniffnet-ning asl oq mavzusi",
// Language::VI => "Chủ đề sáng của Sniffnet",
// Language::ID => "Tema terang bawaan Sniffnet",
+// Language::NL => "Sniffnet's originele lichte thema",
// }
// }
@@ -1553,6 +1609,7 @@ pub fn none_translation(language: Language) -> &'static str {
// Language::UZ => "Tarmoq trafigiga qo'shilish uchun",
// Language::VI => "Đắm chìm vào lưu lượng mạng",
// Language::ID => "Untuk mendalami lalu lintas jaringan",
+// Language::NL => "Om in het netwerkverkeer te duiken",
// }
// }
@@ -1580,6 +1637,7 @@ pub fn none_translation(language: Language) -> &'static str {
// Language::UZ => "Xayolparastlar uchun chiroyli mavzu",
// Language::VI => "Chủ đề mộng mơ cho những kẻ mơ mộng",
// Language::ID => "Tema yang indah dibuat untuk para pemimpi",
+// Language::NL => "Liefelijk thema gemaakt voor dromers",
// }
// }
@@ -1606,6 +1664,7 @@ pub fn incoming_translation(language: Language) -> &'static str {
Language::UZ => "Kiruvchi",
Language::VI => "Đang tới",
Language::ID => "Masuk",
+ Language::NL => "Inkomend",
}
}
@@ -1632,6 +1691,7 @@ pub fn outgoing_translation(language: Language) -> &'static str {
Language::UZ => "Chiquvchi",
Language::VI => "Đang hướng ra ngoài",
Language::ID => "Keluar",
+ Language::NL => "Uitgaand",
}
}
@@ -1656,6 +1716,7 @@ pub fn notifications_translation(language: Language) -> &'static str {
Language::UZ => "Bildirishnomalar",
Language::VI => "Thông báo",
Language::ID => "Pemberitahuan",
+ Language::NL => "Notificaties",
}
}
@@ -1678,6 +1739,7 @@ pub fn style_translation(language: Language) -> &'static str {
Language::UZ => "Uslub",
Language::VI => "Chủ đề",
Language::ID => "Gaya",
+ Language::NL => "Stijl",
}
}
@@ -1705,6 +1767,7 @@ pub fn language_translation(language: Language) -> &'static str {
Language::UZ => "Til",
Language::VI => "Ngôn ngữ",
Language::ID => "Bahasa",
+ Language::NL => "Taal",
}
}
@@ -1732,6 +1795,7 @@ pub fn overview_translation(language: Language) -> &'static str {
Language::UZ => "Ko'rib chiqish",
Language::VI => "Tổng quan",
Language::ID => "Ringkasan",
+ Language::NL => "Overzicht",
}
}
@@ -1759,6 +1823,7 @@ pub fn overview_translation(language: Language) -> &'static str {
// Language::UZ => "Paket chegarasi oshib ketganda xabar bering",
// Language::VI => "Báo cho tôi biết khi vượt quá ngưỡng gói tin",
// Language::ID => "Beritahu saya ketika ambang batas paket terlampaui",
+// Language::NL => "Geef me een melding wanneer een pakketdrempel is overschreden",
// }
// }
@@ -1786,6 +1851,7 @@ pub fn overview_translation(language: Language) -> &'static str {
// Language::UZ => "Bayt chegarasi oshib ketganda menga xabar bering",
// Language::VI => "Báo cho tôi biết khi vượt quá ngưỡng bytes",
// Language::ID => "Beritahu saya ketika ambang batas bytes terlampaui",
+// Language::NL => "Geef me een melding wanneer een byte-drempel is overschreden",
// }
// }
@@ -1810,6 +1876,7 @@ pub fn per_second_translation(language: Language) -> &'static str {
Language::UZ => "(soniyasiga)",
Language::VI => "(trên giây)",
Language::ID => "(per detik)",
+ Language::NL => "(per seconde)",
}
}
@@ -1837,6 +1904,7 @@ pub fn per_second_translation(language: Language) -> &'static str {
// Language::UZ => "'K', 'M' va 'G' ni ham belgilashingiz mumkin",
// Language::VI => "bạn cũng có thể chọn 'K', 'M' and 'G'",
// Language::ID => "Anda juga dapat menentukan 'K', 'M' dan 'G'",
+// Language::NL => "je kunt ook 'K', 'M' en 'G' specificeren",
// }
// }
@@ -1866,6 +1934,7 @@ pub fn per_second_translation(language: Language) -> &'static str {
// Language::UZ => "Sevimlilar ro'yhatidan yangi ma'lumotlar almashganda xabar bering",
// Language::VI => "Báo cho tôi biết khi dữ liệu mới được trao đổi từ mục yêu thích của tôi",
// Language::ID => "Beritahu saya ketika data baru dipertukarkan dari favorit saya",
+// Language::NL => "Geef me een melding wanneer nieuwe gegevens worden uitgewisseld van mijn favorieten",
// }
// }
@@ -1893,12 +1962,13 @@ pub fn threshold_translation(language: Language) -> &'static str {
Language::UZ => "Eshik",
Language::VI => "Ngưỡng",
Language::ID => "Ambang batas",
+ Language::NL => "Grens",
}
}
pub fn volume_translation(language: Language) -> &'static str {
match language {
- Language::EN | Language::IT | Language::FR | Language::PT => "Volume",
+ Language::EN | Language::IT | Language::FR | Language::PT | Language::NL => "Volume",
Language::ES => "Volumen",
Language::PL => "Głośność",
Language::DE => "Lautstärke",
@@ -1941,6 +2011,7 @@ pub fn sound_translation(language: Language) -> &'static str {
Language::UZ => "Ovoz",
Language::VI => "Âm thanh",
Language::ID => "Suara",
+ Language::NL => "Geluid",
}
}
@@ -1968,35 +2039,37 @@ pub fn bytes_exceeded_translation(language: Language) -> &'static str {
Language::UZ => "Bayt chegarasidan oshib ketdi",
Language::VI => "Bytes đã vượt ngưỡng",
Language::ID => "Ambang batas byte terlampaui",
+ Language::NL => "Byte-drempel overschreden",
}
}
-pub fn bytes_exceeded_value_translation(language: Language, value: &str) -> String {
- match language {
- Language::EN => format!("{value} have been exchanged"),
- Language::IT => format!("{value} sono stati scambiati"),
- Language::FR => format!("{value} ont été échangé"),
- Language::ES => format!("{value} han sido intercambiado/s"),
- Language::PL => format!("Wymieniono {value}"),
- Language::DE => format!("{value} wurden ausgetauscht"),
- Language::UK => format!("{value} було обміняно"),
- Language::ZH => format!("已交换字节 {value}"),
- Language::ZH_TW => format!("已交換 {value} 位元組"),
- Language::RO => format!("au fost transferați {value}"),
- Language::KO => format!("바이트 {value} 가 교환되었습니다"),
- Language::TR => format!("{value} aktarıldı"),
- Language::RU => format!("{value} обмена информацией"),
- Language::PT => format!("Foram trocados {value}"),
- Language::EL => format!("{value} έχουν ανταλλαγεί"),
- // Language::FA => format!("{value} بایت مبادله شده است"),
- Language::SV => format!("{value} har utbytts"),
- Language::FI => format!("{value} on vaihdettu"),
- Language::JA => format!("{value} の送受信が発生しました"),
- Language::UZ => format!("{value} ma'lumot almashinuvi"),
- Language::VI => format!("{value} đã được trao đổi"),
- Language::ID => format!("{value} telah dipertukarkan"),
- }
-}
+// pub fn bytes_exceeded_value_translation(language: Language, value: &str) -> String {
+// match language {
+// Language::EN => format!("{value} have been exchanged"),
+// Language::IT => format!("{value} sono stati scambiati"),
+// Language::FR => format!("{value} ont été échangé"),
+// Language::ES => format!("{value} han sido intercambiado/s"),
+// Language::PL => format!("Wymieniono {value}"),
+// Language::DE => format!("{value} wurden ausgetauscht"),
+// Language::UK => format!("{value} було обміняно"),
+// Language::ZH => format!("已交换字节 {value}"),
+// Language::ZH_TW => format!("已交換 {value} 位元組"),
+// Language::RO => format!("au fost transferați {value}"),
+// Language::KO => format!("바이트 {value} 가 교환되었습니다"),
+// Language::TR => format!("{value} aktarıldı"),
+// Language::RU => format!("{value} обмена информацией"),
+// Language::PT => format!("Foram trocados {value}"),
+// Language::EL => format!("{value} έχουν ανταλλαγεί"),
+// // Language::FA => format!("{value} بایت مبادله شده است"),
+// Language::SV => format!("{value} har utbytts"),
+// Language::FI => format!("{value} on vaihdettu"),
+// Language::JA => format!("{value} の送受信が発生しました"),
+// Language::UZ => format!("{value} ma'lumot almashinuvi"),
+// Language::VI => format!("{value} đã được trao đổi"),
+// Language::ID => format!("{value} telah dipertukarkan"),
+// Language::NL => format!("{value} zijn uitgewisseld"),
+// }
+// }
pub fn packets_exceeded_translation(language: Language) -> &'static str {
match language {
@@ -2022,59 +2095,61 @@ pub fn packets_exceeded_translation(language: Language) -> &'static str {
Language::UZ => "Paket chegarasidan oshib ketdi",
Language::VI => "Gói tin đã vượt ngưỡng",
Language::ID => "Ambang batas paket terlampaui",
+ Language::NL => "Pakketdrempel overschreden",
}
}
-pub fn packets_exceeded_value_translation(language: Language, value: u32) -> String {
- match language {
- Language::EN => match value {
- 1 => "1 packet has been exchanged".to_owned(),
- npackets => format!("{npackets} packets have been exchanged"),
- },
- Language::IT => match value {
- 1 => "1 pacchetto è stato scambiato".to_owned(),
- npackets => format!("{npackets} pacchetti sono stati scambiati"),
- },
- Language::FR => match value {
- 1 => "1 paquet a été échangé".to_owned(),
- npackets => format!("{npackets} paquets ont été échangés"),
- },
- Language::ES => format!("{value} paquete/s han sido intercambiado/s"),
- Language::PL => format!("Wymieniono {value} pakietów"),
- Language::DE => match value {
- 1 => "1 Paket wurde ausgetauscht".to_owned(),
- npackets => format!("{npackets} Pakete wurden ausgetauscht"),
- },
- Language::UK => format!("Обміняно {value} пакетів"),
- Language::ZH => format!("已交换数据包 {value}"),
- Language::ZH_TW => format!("已交換 {value} 個封包"),
- Language::RO => format!("au fost transferate {value} pachete"),
- Language::KO => format!("패킷 {value} 가 교환되었습니다"),
- Language::TR => format!("{value} paket aktarıldı"),
- Language::RU => format!("{value} пакет(ов) обмена информацией"),
- Language::PT => match value {
- 1 => "Foi trocado 1 pacote".to_owned(),
- npackets => format!("Foram trocados {npackets} pacotes"),
- },
- Language::EL => match value {
- 1 => "1 πακέτο έχει ανταλλαγεί".to_owned(),
- npackets => format!("{npackets} πακέτα έχουν ανταλλαγεί"),
- },
- // Language::FA => format!("{value} بسته مبادله شده است"),
- Language::SV => match value {
- 1 => "1 paket har utbytts".to_owned(),
- npackets => format!("{npackets} paket har utbytts"),
- },
- Language::FI => match value {
- 1 => "1 paketti vaihdettu".to_owned(),
- npackets => format!("{npackets} pakettia vaihdettu"),
- },
- Language::JA => format!("{value} パケットの送受信が発生しました"),
- Language::UZ => format!("{value} paket uzatildi"),
- Language::VI => format!("{value} gói tin đã được trao đổi"),
- Language::ID => format!("{value} paket telah dipertukarkan"),
- }
-}
+// pub fn packets_exceeded_value_translation(language: Language, value: u32) -> String {
+// match language {
+// Language::EN => match value {
+// 1 => "1 packet has been exchanged".to_owned(),
+// npackets => format!("{npackets} packets have been exchanged"),
+// },
+// Language::IT => match value {
+// 1 => "1 pacchetto è stato scambiato".to_owned(),
+// npackets => format!("{npackets} pacchetti sono stati scambiati"),
+// },
+// Language::FR => match value {
+// 1 => "1 paquet a été échangé".to_owned(),
+// npackets => format!("{npackets} paquets ont été échangés"),
+// },
+// Language::ES => format!("{value} paquete/s han sido intercambiado/s"),
+// Language::PL => format!("Wymieniono {value} pakietów"),
+// Language::DE => match value {
+// 1 => "1 Paket wurde ausgetauscht".to_owned(),
+// npackets => format!("{npackets} Pakete wurden ausgetauscht"),
+// },
+// Language::UK => format!("Обміняно {value} пакетів"),
+// Language::ZH => format!("已交换数据包 {value}"),
+// Language::ZH_TW => format!("已交換 {value} 個封包"),
+// Language::RO => format!("au fost transferate {value} pachete"),
+// Language::KO => format!("패킷 {value} 가 교환되었습니다"),
+// Language::TR => format!("{value} paket aktarıldı"),
+// Language::RU => format!("{value} пакет(ов) обмена информацией"),
+// Language::PT => match value {
+// 1 => "Foi trocado 1 pacote".to_owned(),
+// npackets => format!("Foram trocados {npackets} pacotes"),
+// },
+// Language::EL => match value {
+// 1 => "1 πακέτο έχει ανταλλαγεί".to_owned(),
+// npackets => format!("{npackets} πακέτα έχουν ανταλλαγεί"),
+// },
+// // Language::FA => format!("{value} بسته مبادله شده است"),
+// Language::SV => match value {
+// 1 => "1 paket har utbytts".to_owned(),
+// npackets => format!("{npackets} paket har utbytts"),
+// },
+// Language::FI => match value {
+// 1 => "1 paketti vaihdettu".to_owned(),
+// npackets => format!("{npackets} pakettia vaihdettu"),
+// },
+// Language::JA => format!("{value} パケットの送受信が発生しました"),
+// Language::UZ => format!("{value} paket uzatildi"),
+// Language::VI => format!("{value} gói tin đã được trao đổi"),
+// Language::ID => format!("{value} paket telah dipertukarkan"),
+// Language::NL => format!("{value} pakketten zijn uitgewisseld"),
+// }
+// }
pub fn favorite_transmitted_translation(language: Language) -> &'static str {
match language {
@@ -2100,6 +2175,7 @@ pub fn favorite_transmitted_translation(language: Language) -> &'static str {
Language::UZ => "Sevimli ulanishlar ro'yhatida yangi ma'lumotlar almashinuvi",
Language::VI => "Mục ưa thích vừa có trao đổi",
Language::ID => "Data baru dipertukarkan dari favorit",
+ Language::NL => "Nieuwe gegevens uitgewisseld van favorieten",
}
}
@@ -2214,6 +2290,11 @@ pub fn no_notifications_set_translation<'a>(language: Language) -> Text<'a, Styl
Setelah mengaktifkannya, halaman ini akan menampilkan log notifikasi Anda\n\n\
Anda dapat mengaktifkan notifikasi dari pengaturan:"
}
+ Language::NL => {
+ "Je hebt nog geen meldingen ingeschakeld!\n\n\
+ Nadat je ze hebt ingeschakeld, zal deze pagina een logboek van je meldingen weergeven\n\n\
+ Je kunt meldingen inschakelen vanuit de instellingen:"
+ }
})
}
@@ -2307,6 +2388,10 @@ pub fn no_notifications_received_translation<'a>(language: Language) -> Text<'a,
"Tidak ada yang bisa dilihat saat ini...\n\n\
Saat Anda menerima pemberitahuan, akan ditampilkan di sini"
}
+ Language::NL => {
+ "Er is momenteel niets te zien...\n\n\
+ Wanneer je een melding ontvangt, wordt deze hier weergegeven"
+ }
})
}
@@ -2334,5 +2419,6 @@ pub fn only_last_30_translation(language: Language) -> &'static str {
Language::UZ => "Faqat oxirgi 30 ta bildirishnoma ko'rsatiladi",
Language::VI => "Chỉ có 30 thông báo gần nhất được hiển thị",
Language::ID => "Hanya 30 notifikasi terakhir yang ditampilkan",
+ Language::NL => "Alleen de laatste 30 meldingen worden weergegeven",
}
}
diff --git a/src/translations/translations_2.rs b/src/translations/translations_2.rs
index 97dc6417..66c3a981 100644
--- a/src/translations/translations_2.rs
+++ b/src/translations/translations_2.rs
@@ -26,6 +26,7 @@ pub fn new_version_available_translation(language: Language) -> &'static str {
Language::PT => "Uma nova versão está disponível!",
Language::VI => "Phiên bản mới đã sẵn sàng!",
Language::ID => "Versi baru tersedia!",
+ Language::NL => "Een nieuwere versie is beschikbaar!",
}
}
@@ -52,6 +53,7 @@ pub fn inspect_translation(language: Language) -> &'static str {
Language::PT => "Inspecionar",
Language::VI => "Quan sát",
Language::ID => "Memeriksa",
+ Language::NL => "Inspecteren",
_ => "Inspect",
}
}
@@ -79,6 +81,7 @@ pub fn connection_details_translation(language: Language) -> &'static str {
Language::PT => "Detalhes da conexão",
Language::VI => "Thông tin kết nối",
Language::ID => "Rincian koneksi",
+ Language::NL => "Verbindingsdetails",
_ => "Connection details",
}
}
@@ -106,6 +109,7 @@ pub fn dropped_translation(language: Language) -> &'static str {
Language::UZ => "Yig'ilgan",
Language::VI => "Mất",
Language::ID => "Dihapus",
+ Language::NL => "Verloren",
_ => "Dropped",
}
}
@@ -133,6 +137,7 @@ pub fn data_representation_translation(language: Language) -> &'static str {
Language::PT => "Representação dos dados",
Language::VI => "Miêu tả dữ liệu",
Language::ID => "Penyajian ulang data",
+ Language::NL => "Gegevensweergave",
_ => "Data representation",
}
}
@@ -160,6 +165,7 @@ pub fn host_translation(language: Language) -> &'static str {
Language::PT => "Host da rede",
Language::VI => "Máy chủ",
Language::ID => "Jaringan asal",
+ Language::NL => "Netwerk host",
_ => "Network host",
}
}
@@ -187,6 +193,7 @@ pub fn only_top_30_items_translation(language: Language) -> &'static str {
Language::PT => "Apenas os 30 melhores unid são expostos aqui",
Language::VI => "Chỉ có 30 mục gần nhất được hiển thị ở đây",
Language::ID => "Hanya 30 teratas yang ditampilkan disini",
+ Language::NL => "Alleen de bovenste 30 items worden hier weergegeven",
_ => "Only the top 30 items are displayed here",
}
}
@@ -212,6 +219,7 @@ pub fn only_top_30_items_translation(language: Language) -> &'static str {
// Language::JA => "ソート",
// Language::UZ => "Saralash turi",
// Language::ID => "Urut berdasarkan",
+// Language::NL => "Sorteren op",
// _ => "Sort by",
// }
// }
@@ -239,6 +247,7 @@ pub fn local_translation(language: Language) -> &'static str {
Language::PT => "Rede local",
Language::VI => "Mạng nội bộ",
Language::ID => "Jaringan lokal",
+ Language::NL => "Lokaal netwerk",
_ => "Local network",
}
}
@@ -266,6 +275,7 @@ pub fn unknown_translation(language: Language) -> &'static str {
Language::PT => "Localização desconhecida",
Language::VI => "Không rõ địa điểm",
Language::ID => "Lokasi tidak diketahui",
+ Language::NL => "Onbekende locatie",
_ => "Unknown location",
}
}
@@ -293,6 +303,7 @@ pub fn your_network_adapter_translation(language: Language) -> &'static str {
Language::PT => "Seu adaptador de rede",
Language::VI => "Network adapter của bạn",
Language::ID => "Adaptor jaringan kamu",
+ Language::NL => "Uw netwerkadapter",
_ => "Your network adapter",
}
}
@@ -320,6 +331,7 @@ pub fn socket_address_translation(language: Language) -> &'static str {
Language::PT => "Endereço da socket",
Language::VI => "Địa chỉ socket",
Language::ID => "Alamat sambungan",
+ Language::NL => "Socket adres",
_ => "Socket address",
}
}
@@ -347,6 +359,7 @@ pub fn mac_address_translation(language: Language) -> &'static str {
Language::PT => "Endereço MAC",
Language::VI => "Địa chỉ MAC",
Language::ID => "Alamat MAC",
+ Language::NL => "MAC-adres",
_ => "MAC address",
}
}
@@ -374,6 +387,7 @@ pub fn source_translation(language: Language) -> &'static str {
Language::PT => "Fonte",
Language::VI => "Nguồn",
Language::ID => "Asal",
+ Language::NL => "Bron",
_ => "Source",
}
}
@@ -399,6 +413,7 @@ pub fn destination_translation(language: Language) -> &'static str {
Language::UZ => "Qabul qiluvchi",
Language::VI => "Đích",
Language::ID => "Tujuan",
+ Language::NL => "Bestemming",
_ => "Destination",
}
}
@@ -425,6 +440,7 @@ pub fn fqdn_translation(language: Language) -> &'static str {
Language::PT => "Nome de domínio completo",
Language::VI => "Tên miền đầy đủ",
Language::ID => "Nama domain yang memenuhi syarat",
+ Language::NL => "Volledig gekwalificeerde domeinnaam",
_ => "Fully qualified domain name",
}
}
@@ -452,6 +468,7 @@ pub fn administrative_entity_translation(language: Language) -> &'static str {
Language::PT => "Entidade administrativa",
Language::VI => "Tên Autonomous System",
Language::ID => "Nama System Otomatis",
+ Language::NL => "Naam van het autonome systeem",
_ => "Autonomous System name",
}
}
@@ -479,6 +496,7 @@ pub fn transmitted_data_translation(language: Language) -> &'static str {
Language::PT => "Dados transmitidos",
Language::VI => "Dữ liệu được truyền",
Language::ID => "Data terkirim",
+ Language::NL => "Verzonden gegevens",
_ => "Transmitted data",
}
}
@@ -505,6 +523,7 @@ pub fn country_translation(language: Language) -> &'static str {
Language::UZ => "Davlat",
Language::VI => "Quốc gia",
Language::ID => "Negara",
+ Language::NL => "Land",
_ => "Country",
}
}
@@ -532,6 +551,7 @@ pub fn domain_name_translation(language: Language) -> &'static str {
Language::PT => "Nome do domínio",
Language::VI => "Tên miền",
Language::ID => "Nama Domain",
+ Language::NL => "Domeinnaam",
_ => "Domain name",
}
}
@@ -559,6 +579,7 @@ pub fn only_show_favorites_translation(language: Language) -> &'static str {
Language::PT => "Apenas mostrar os favoritos",
Language::VI => "Chỉ hiển thị mục ưa thích",
Language::ID => "Hanya tunjukkan favorit",
+ Language::NL => "Toon alleen favorieten",
_ => "Only show favorites",
}
}
@@ -585,6 +606,7 @@ pub fn only_show_favorites_translation(language: Language) -> &'static str {
// Language::UZ => "Qidiruv filtrlari",
// Language::PT => "Filtros de busca",
// Language::ID => "Filter Pencarian",
+// Language::NL => "Zoekfilters",
// _ => "Search filters",
// }
// }
@@ -612,6 +634,7 @@ pub fn no_search_results_translation(language: Language) -> &'static str {
Language::PT => "Nenhum resultado disponível de acordo com os filtros selecionados",
Language::VI => "Không có kết quả nào theo các bộ lọc được chỉ định",
Language::ID => "Tidak ada hasil berdasarkan filter pencarian spesifik",
+ Language::NL => "Geen resultaten beschikbaar volgens de opgegeven zoekfilters",
_ => "No result available according to the specified search filters",
}
}
@@ -644,6 +667,9 @@ pub fn showing_results_translation(
Language::PT => format!("Mostrando {start}-{end} de {total} resultados totais"),
Language::VI => format!("Đang hiển thị {start}-{end} của {total} tổng số kết quả"),
Language::ID => format!("Menampilkan {start}-{end} dari {total} semua hasil"),
+ Language::NL => {
+ format!("{start}-{end} van de {total} totale resultaten worden weergegeven")
+ }
_ => format!("Showing {start}-{end} of {total} total results"),
}
}
@@ -672,6 +698,7 @@ pub fn color_gradients_translation(language: Language) -> &'static str {
Language::PT => "Aplicar gradientes de cor",
Language::VI => "Áp dụng color gradients",
Language::ID => "Aplikasikan gradasi warna",
+ Language::NL => "Kleurverlopen toepassen",
_ => "Apply color gradients",
}
}
diff --git a/src/translations/translations_3.rs b/src/translations/translations_3.rs
index 53d2e51f..3cfe33c4 100644
--- a/src/translations/translations_3.rs
+++ b/src/translations/translations_3.rs
@@ -27,6 +27,7 @@ pub fn general_translation(language: Language) -> &'static str {
Language::PT => "Geral",
Language::UK => "Загальні",
Language::ID => "Umum",
+ Language::NL => "Algemeen",
_ => "General",
}
}
@@ -40,6 +41,7 @@ pub fn zoom_translation(language: Language) -> &'static str {
| Language::DE
| Language::RO
| Language::PT
+ | Language::NL
| Language::SV => "Zoom",
// Language::FA => "بزرگنمایی",
Language::PL => "Powiększenie",
@@ -79,6 +81,7 @@ pub fn mmdb_files_translation(language: Language) -> &'static str {
Language::PT => "Arquivos da base de dados",
Language::UK => "Файли бази даних",
Language::ID => "Berkas database",
+ Language::NL => "Database bestanden",
_ => "Database files",
}
}
@@ -105,6 +108,7 @@ pub fn params_not_editable_translation(language: Language) -> &'static str {
Language::PT => "Os seguintes parâmetros não podem ser modificados durante a análise",
Language::UK => "Наступні параметри не можна змінювати під час аналізу трафіку",
Language::ID => "Parameter berikut tidak bisa diubah saat dianalisa",
+ Language::NL => "De volgende parameters kunnen niet worden aangepast tijdens de analyse",
_ => "The following parameters can't be modified during the analysis",
}
}
@@ -130,6 +134,7 @@ pub fn custom_style_translation(language: Language) -> &'static str {
Language::TR => "Kişisel görünüm",
Language::UK => "Власний стиль",
Language::ID => "Ubah Model",
+ Language::NL => "Aangepaste stijl",
_ => "Custom style",
}
}
@@ -154,6 +159,7 @@ pub fn copy_translation(language: Language) -> &'static str {
Language::PT => "Copiar",
Language::UK => "Копіювати",
Language::ID => "Salin",
+ Language::NL => "Kopiëren",
_ => "Copy",
}
}
@@ -179,6 +185,7 @@ pub fn port_translation(language: Language) -> &'static str {
Language::KO => "포트",
Language::UK => "Порт",
Language::ID => "Port",
+ Language::NL => "Poort",
_ => "Port",
}
}
@@ -204,6 +211,7 @@ pub fn invalid_filters_translation(language: Language) -> &'static str {
Language::TR => "Geçersiz filtreler",
Language::UK => "Неправильний формат фільтрів",
Language::ID => "Filter salah",
+ Language::NL => "Ongeldige filters",
_ => "Invalid filters",
}
}
@@ -229,13 +237,14 @@ pub fn messages_translation(language: Language) -> &'static str {
Language::PT => "Mensagens",
Language::UK => "Повідомлення",
Language::ID => "Pesan",
+ Language::NL => "Berichten",
_ => "Messages",
}
}
pub fn link_type_translation(language: Language) -> &'static str {
match language {
- Language::EN => "Link type",
+ Language::EN | Language::NL => "Link type",
// Language::FA => "نوع پیوند",
Language::ES => "Tipo de conexión",
Language::IT => "Tipo di collegamento",
@@ -312,6 +321,9 @@ pub fn unsupported_link_type_translation<'a>(
Language::ID => {
"Tipe koneksi yang terhubung dengan adaptor ini belum didukung oleh Sniffnet"
}
+ Language::NL => {
+ "Het linktype dat is gekoppeld aan deze adapter wordt nog niet ondersteund door Sniffnet..."
+ }
_ => "The link type associated with this adapter is not supported by Sniffnet yet...",
};
@@ -343,6 +355,7 @@ pub fn style_from_file_translation(language: Language) -> &'static str {
Language::PT => "Selecionar estilo a partir de um arquivo",
Language::UK => "Виберіть стиль з файлу",
Language::ID => "Pilih model / gaya dari berkas",
+ Language::NL => "Selecteer stijl vanuit een bestand",
_ => "Select style from a file",
}
}
@@ -369,6 +382,7 @@ pub fn database_from_file_translation(language: Language) -> &'static str {
Language::PT => "Selecione um arquivo de base de dados",
Language::UK => "Виберіть файл бази даних",
Language::ID => "Pilih berkas database",
+ Language::NL => "Selecteer database bestand",
_ => "Select database file",
}
}
@@ -395,6 +409,7 @@ pub fn filter_by_host_translation(language: Language) -> &'static str {
Language::PT => "Filtrar por host de rede",
Language::UK => "Фільтр за хостом мережі",
Language::ID => "Filter berdasarkan jaringan asal",
+ Language::NL => "Filteren op netwerk host",
_ => "Filter by network host",
}
}
@@ -418,6 +433,7 @@ pub fn service_translation(language: Language) -> &'static str {
Language::PT => "Serviço",
Language::UK => "Сервіс",
Language::ID => "Layanan",
+ Language::NL => "Dienst",
_ => "Service",
}
}
@@ -444,6 +460,7 @@ pub fn export_capture_translation(language: Language) -> &'static str {
Language::UK => "Експорт файлу захоплення",
Language::ID => "Ekspor data tangkapan",
Language::ES => "Exportar archivo de captura",
+ Language::NL => "Exporteer capture bestand",
_ => "Export capture file",
}
}
@@ -469,6 +486,7 @@ pub fn directory_translation(language: Language) -> &'static str {
Language::UK => "Тека",
Language::ID => "Direktori",
Language::ES => "Directorio",
+ Language::NL => "Map",
_ => "Directory",
}
}
@@ -495,6 +513,7 @@ pub fn select_directory_translation(language: Language) -> &'static str {
Language::UK => "Виберіть теку призначення",
Language::ID => "Pilih direktori tujuan",
Language::ES => "Selecciona el directorio de destino",
+ Language::NL => "Selecteer doelmap",
_ => "Select destination directory",
}
}
@@ -521,6 +540,7 @@ pub fn file_name_translation(language: Language) -> &'static str {
Language::UK => "Назва файлу",
Language::ID => "Nama berkas",
Language::ES => "Nombre del archivo",
+ Language::NL => "Bestandsnaam",
_ => "File name",
}
}
@@ -546,32 +566,34 @@ pub fn thumbnail_mode_translation(language: Language) -> &'static str {
Language::PT | Language::ES => "Modo miniatura",
Language::UK => "Режим мініатюри",
Language::ID => "Mode gambar kecil",
+ Language::NL => "Miniatuur modus",
_ => "Thumbnail mode",
}
}
-pub fn learn_more_translation(language: Language) -> &'static str {
- match language {
- Language::EN => "Do you want to learn more?",
- // Language::FA => "آیا می خواهید بیشتر یاد بگیرید؟",
- Language::IT => "Vuoi saperne di più?",
- Language::FR => "Voulez-vous en savoir davantage?",
- Language::DE => "Mehr erfahren",
- Language::PL => "Chcesz dowiedzieć się więcej?",
- Language::RU => "Хотите узнать больше?",
- Language::RO => "Vrei să înveți mai multe?",
- Language::JA => "もっと知りたいですか?",
- Language::UZ => "Ko'proq bilishni hohlaysizmi?",
- Language::SV => "Vill du veta mer?",
- Language::VI => "Bạn có muốn tìm hiểu thêm?",
- Language::ZH => "想知道更多吗?",
- Language::ZH_TW => "想了解更多嗎?",
- Language::KO => "더 자세히 알고 싶으십니까?",
- Language::TR => "Daha fazlasını öğrenmek ister misin?",
- Language::PT => "Quer aprender mais?",
- Language::UK => "Бажаєте дізнатись більше?",
- Language::ID => "Apakah kamu mau belajar lebih lanjut?",
- Language::ES => "¿Quieres aprender más?",
- _ => "Do you want to learn more?",
- }
-}
+// pub fn learn_more_translation(language: Language) -> &'static str {
+// match language {
+// Language::EN => "Do you want to learn more?",
+// // Language::FA => "آیا می خواهید بیشتر یاد بگیرید؟",
+// Language::IT => "Vuoi saperne di più?",
+// Language::FR => "Voulez-vous en savoir davantage?",
+// Language::DE => "Mehr erfahren",
+// Language::PL => "Chcesz dowiedzieć się więcej?",
+// Language::RU => "Хотите узнать больше?",
+// Language::RO => "Vrei să înveți mai multe?",
+// Language::JA => "もっと知りたいですか?",
+// Language::UZ => "Ko'proq bilishni hohlaysizmi?",
+// Language::SV => "Vill du veta mer?",
+// Language::VI => "Bạn có muốn tìm hiểu thêm?",
+// Language::ZH => "想知道更多吗?",
+// Language::ZH_TW => "想了解更多嗎?",
+// Language::KO => "더 자세히 알고 싶으십니까?",
+// Language::TR => "Daha fazlasını öğrenmek ister misin?",
+// Language::PT => "Quer aprender mais?",
+// Language::UK => "Бажаєте дізнатись більше?",
+// Language::ID => "Apakah kamu mau belajar lebih lanjut?",
+// Language::ES => "¿Quieres aprender más?",
+// Language::NL => "Wil je meer leren?",
+// _ => "Do you want to learn more?",
+// }
+// }
diff --git a/src/translations/translations_4.rs b/src/translations/translations_4.rs
index 797fc6f1..895ae240 100644
--- a/src/translations/translations_4.rs
+++ b/src/translations/translations_4.rs
@@ -12,18 +12,20 @@ pub fn reserved_address_translation(language: Language, info: &str) -> String {
Language::PT => format!("Endereço reservado ({info})"),
Language::UK => format!("Зарезервована адреса ({info})"),
Language::ZH_TW => format!("保留的網路位址 ({info})"),
+ Language::NL => format!("Gereserveerd adres ({info})"),
_ => format!("Reserved address ({info})"),
}
}
-// pub fn share_feedback_translation(language: Language) -> &'static str {
-// match language {
-// Language::EN => "Share your feedback",
-// Language::IT => "Condividi il tuo feedback",
-// Language::ZH_TW => "分享您的意見回饋",
-// _ => "Share your feedback",
-// }
-// }
+pub fn share_feedback_translation(language: Language) -> &'static str {
+ match language {
+ Language::EN => "Share your feedback",
+ Language::IT => "Condividi il tuo feedback",
+ Language::ZH_TW => "分享您的意見回饋",
+ Language::NL => "Deel uw feedback",
+ _ => "Share your feedback",
+ }
+}
// refers to bytes or packets excluded because of the filters
pub fn excluded_translation(language: Language) -> &'static str {
@@ -31,6 +33,7 @@ pub fn excluded_translation(language: Language) -> &'static str {
Language::EN => "Excluded",
Language::IT => "Esclusi",
Language::ZH_TW => "已排除",
+ Language::NL => "Uitgesloten",
_ => "Excluded",
}
}
@@ -39,6 +42,7 @@ pub fn import_capture_translation(language: Language) -> &'static str {
match language {
Language::EN => "Import capture file",
Language::IT => "Importa file di cattura",
+ Language::NL => "Importeer capture bestand",
_ => "Import capture file",
}
}
@@ -47,6 +51,7 @@ pub fn select_capture_translation(language: Language) -> &'static str {
match language {
Language::EN => "Select capture file",
Language::IT => "Seleziona file di cattura",
+ Language::NL => "Selecteer capture bestand",
_ => "Select capture file",
}
}
@@ -64,6 +69,11 @@ pub fn reading_from_pcap_translation<'a>(language: Language, file: &str) -> Text
{file_name_translation}: {file}\n\n\
Sei sicuro che il file che hai selezionato non sia vuoto?"
),
+ Language::NL => format!(
+ "Pakketten lezen uit bestand...\n\n\
+ {file_name_translation}: {file}\n\n\
+ Weet je zeker dat het geselecteerde bestand niet leeg is?"
+ ),
_ => format!(
"Reading packets from file...\n\n\
{file_name_translation}: {file}\n\n\
@@ -71,3 +81,51 @@ pub fn reading_from_pcap_translation<'a>(language: Language, file: &str) -> Text
),
})
}
+
+pub fn data_exceeded_translation(language: Language) -> &'static str {
+ match language {
+ Language::EN => "Data threshold exceeded",
+ Language::IT => "Soglia di dati superata",
+ Language::NL => "Gegevenslimiet overschreden",
+ _ => "Data threshold exceeded",
+ }
+}
+
+#[allow(dead_code)]
+pub fn bits_exceeded_translation(language: Language) -> &'static str {
+ match language {
+ Language::EN => "Bits threshold exceeded",
+ Language::IT => "Soglia di bit superata",
+ Language::NL => "Bits limiet overschreden",
+ _ => "Bits threshold exceeded",
+ }
+}
+
+#[allow(dead_code)]
+pub fn bits_translation(language: Language) -> &'static str {
+ match language {
+ Language::EN | Language::IT => "Bits",
+ Language::NL => "Bits",
+ _ => "Bits",
+ }
+}
+
+#[allow(dead_code)]
+pub fn pause_translation(language: Language) -> &'static str {
+ match language {
+ Language::EN => "Pause",
+ Language::IT => "Pausa",
+ Language::NL => "Pauzeren",
+ _ => "Pause",
+ }
+}
+
+#[allow(dead_code)]
+pub fn resume_translation(language: Language) -> &'static str {
+ match language {
+ Language::EN => "Resume",
+ Language::IT => "Riprendi",
+ Language::NL => "Hervatten",
+ _ => "Resume",
+ }
+}
diff --git a/src/translations/types/language.rs b/src/translations/types/language.rs
index 5212fce1..2126c2af 100644
--- a/src/translations/types/language.rs
+++ b/src/translations/types/language.rs
@@ -6,8 +6,8 @@
use crate::StyleType;
use crate::countries::flags_pictures::{
- CN, DE, ES, FI, FLAGS_WIDTH_BIG, FR, GB, GR, ID, IT, JP, KR, PL, PT, RO, RU, SE, TR, TW, UA,
- UZ, VN,
+ CN, DE, ES, FI, FLAGS_WIDTH_BIG, FR, GB, GR, ID, IT, JP, KR, NL, PL, PT, RO, RU, SE, TR, TW,
+ UA, UZ, VN,
};
/// This enum defines the available languages.
@@ -59,10 +59,12 @@ pub enum Language {
VI,
/// Indonesian
ID,
+ /// Dutch
+ NL,
}
impl Language {
- pub const ALL: [Language; 21] = [
+ pub const ALL: [Language; 22] = [
Language::EN,
Language::DE,
Language::EL,
@@ -73,6 +75,7 @@ impl Language {
Language::IT,
Language::JA,
Language::KO,
+ Language::NL,
Language::PL,
Language::PT,
Language::RO,
@@ -110,33 +113,13 @@ pub fn get_flag<'a>(self) -> Svg<'a, StyleType> {
Language::UZ => UZ,
Language::VI => VN,
Language::ID => ID,
+ Language::NL => NL,
})))
.width(FLAGS_WIDTH_BIG)
}
pub fn is_up_to_date(self) -> bool {
- matches!(
- self,
- Language::FR
- | Language::EN
- | Language::IT
- | Language::DE
- | Language::PL
- | Language::RU
- | Language::RO
- | Language::JA
- | Language::UZ
- | Language::SV
- | Language::VI
- | Language::ZH
- | Language::ZH_TW
- | Language::KO
- | Language::TR
- | Language::PT
- | Language::UK
- | Language::ID
- | Language::ES
- )
+ matches!(self, Language::EN | Language::IT | Language::NL)
}
}
@@ -165,6 +148,7 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
Language::UZ => "O'zbekcha",
Language::VI => "Tiếng Việt",
Language::ID => "Bahasa Indonesia",
+ Language::NL => "Nederlands",
};
write!(f, "{self:?} - {lang_str}")
}
diff --git a/src/utils/types/icon.rs b/src/utils/types/icon.rs
index bacf36a9..87f16a39 100644
--- a/src/utils/types/icon.rs
+++ b/src/utils/types/icon.rs
@@ -14,9 +14,12 @@ pub enum Icon {
Book,
BytesThreshold,
Clock,
+ // Collapse,
Copy,
Generals,
Error,
+ // Expand,
+ Feedback,
File,
Forbidden,
Funnel,
@@ -61,10 +64,11 @@ pub fn codepoint(&self) -> char {
Icon::AudioHigh => 'Z',
Icon::AudioMute => 'Y',
Icon::Bin => 'h',
- Icon::BytesThreshold => 'f',
+ Icon::BytesThreshold => '[',
Icon::Clock => '9',
Icon::Generals => 'Q',
Icon::Error => 'U',
+ Icon::Feedback => '=',
Icon::File => '8',
Icon::Forbidden => 'x',
Icon::Funnel => 'V',
@@ -79,7 +83,7 @@ pub fn codepoint(&self) -> char {
Icon::Moon => 'G',
Icon::Notification => '7',
Icon::Overview => 'd',
- Icon::PacketsThreshold => 'e',
+ Icon::PacketsThreshold => '\\',
// Icon::Restore => 'k',
Icon::Rocket => 'S',
Icon::Settings => 'a',
@@ -99,6 +103,8 @@ pub fn codepoint(&self) -> char {
Icon::Roadmap => '?',
Icon::News => '>',
Icon::Update => '<',
+ // Icon::Expand => 'p',
+ // Icon::Collapse => 'q',
}
}
diff --git a/src/utils/types/web_page.rs b/src/utils/types/web_page.rs
index 2c161bd7..3795bc01 100644
--- a/src/utils/types/web_page.rs
+++ b/src/utils/types/web_page.rs
@@ -13,6 +13,8 @@ pub enum WebPage {
WebsiteSponsor,
/// Sniffnet Roadmap
Roadmap,
+ /// Sniffnet issues on GitHub
+ Issues,
/// Sniffnet issue #60 on GitHub
IssueLanguages,
/// Sniffnet Wiki
@@ -30,6 +32,7 @@ pub fn get_url(&self) -> &str {
WebPage::WebsiteDownload => "https://www.sniffnet.net/download",
WebPage::WebsiteNews => "https://www.sniffnet.net/news",
WebPage::Roadmap => "https://whimsical.com/sniffnet-roadmap-Damodrdfx22V9jGnpHSCGo",
+ WebPage::Issues => "https://github.com/GyulyVGC/sniffnet/issues",
WebPage::IssueLanguages => "https://github.com/GyulyVGC/sniffnet/issues/60",
WebPage::Wiki => "https://github.com/GyulyVGC/sniffnet/wiki",
WebPage::MyGitHub => "https://github.com/GyulyVGC",