diff --git a/.all-contributorsrc b/.all-contributorsrc index 249cc6a4..2c5ac105 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -658,6 +658,51 @@ "contributions": [ "financial" ] + }, + { + "login": "aguacero7", + "name": "Aguacero 🌧️", + "avatar_url": "https://avatars.githubusercontent.com/u/72492241?v=4", + "profile": "https://github.com/aguacero7", + "contributions": [ + "translation" + ] + }, + { + "login": "rons4", + "name": "Ron", + "avatar_url": "https://avatars.githubusercontent.com/u/43244104?v=4", + "profile": "https://www.signl4.com", + "contributions": [ + "ideas" + ] + }, + { + "login": "ADS-Fund", + "name": "ADS Fund", + "avatar_url": "https://avatars.githubusercontent.com/u/202042116?v=4", + "profile": "https://ads.fund", + "contributions": [ + "financial" + ] + }, + { + "login": "louis-ym4", + "name": "louis-ym4", + "avatar_url": "https://avatars.githubusercontent.com/u/200361621?v=4", + "profile": "https://github.com/louis-ym4", + "contributions": [ + "design" + ] + }, + { + "login": "vanharen07", + "name": "Ylva", + "avatar_url": "https://avatars.githubusercontent.com/u/91621548?v=4", + "profile": "https://github.com/vanharen07", + "contributions": [ + "translation" + ] } ], "contributorsPerLine": 7, diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 3dd419f2..fd513caa 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -1,9 +1,9 @@ name: Docker on: - push: - tags: - - 'v[0-9]+.[0-9]+.[0-9]+' + # push: + # tags: + # - 'v[0-9]+.[0-9]+.[0-9]+' workflow_dispatch: jobs: @@ -14,12 +14,12 @@ jobs: - name: Checkout uses: actions/checkout@v4 - # - name: Extract version from Cargo.toml - # id: cargo-version - # run: | - # VERSION=v$(grep -m1 "^version" Cargo.toml | cut -d'"' -f2) - # echo "VERSION=$VERSION" >> $GITHUB_ENV - # echo "version=$VERSION" >> $GITHUB_OUTPUT + - name: Extract version from Cargo.toml + id: cargo-version + run: | + VERSION=v$(grep -m1 "^version" Cargo.toml | cut -d'"' -f2) + echo "VERSION=$VERSION" >> $GITHUB_ENV + echo "version=$VERSION" >> $GITHUB_OUTPUT - name: Set up QEMU uses: docker/setup-qemu-action@v3 @@ -42,4 +42,5 @@ jobs: push: true tags: | ghcr.io/gyulyvgc/sniffnet:latest - ghcr.io/gyulyvgc/sniffnet:${{ github.ref_name }} + ghcr.io/gyulyvgc/sniffnet:${{ env.VERSION }} +# ghcr.io/gyulyvgc/sniffnet:${{ github.ref_name }} diff --git a/CHANGELOG.md b/CHANGELOG.md index ca32c718..538225ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,8 +3,12 @@ # Changelog All Sniffnet releases with the relative changes are documented in this file. ## [UNRELEASED] +- Added Dutch translation 🇳🇱 ([#854](https://github.com/GyulyVGC/sniffnet/pull/854)) + +## [1.4.0] - 2025-06-27 - Import PCAP files ([#795](https://github.com/GyulyVGC/sniffnet/pull/795) — fixes [#283](https://github.com/GyulyVGC/sniffnet/issues/283)) - Donut chart reporting overall traffic statistics ([#756](https://github.com/GyulyVGC/sniffnet/pull/756) — fixes [#687](https://github.com/GyulyVGC/sniffnet/issues/687)) +- Notifications: more details and other improvements ([#830](https://github.com/GyulyVGC/sniffnet/pull/830) — fixes [#637](https://github.com/GyulyVGC/sniffnet/issues/637)) - Added support for ARP protocol ([#759](https://github.com/GyulyVGC/sniffnet/pull/759) — fixes [#680](https://github.com/GyulyVGC/sniffnet/issues/680)) - Identify and tag unassigned/reserved "bogon" IP addresses ([#678](https://github.com/GyulyVGC/sniffnet/pull/678) — fixes [#209](https://github.com/GyulyVGC/sniffnet/issues/209)) - Show data agglomerates in _Inspect_ page table ([#684](https://github.com/GyulyVGC/sniffnet/pull/684) — fixes [#601](https://github.com/GyulyVGC/sniffnet/issues/601)) @@ -15,6 +19,7 @@ ## [UNRELEASED] - Portuguese ([#690](https://github.com/GyulyVGC/sniffnet/pull/690)) - Ukrainian ([#692](https://github.com/GyulyVGC/sniffnet/pull/692)) - Spanish ([#805](https://github.com/GyulyVGC/sniffnet/pull/805)) +- Do not apply new notification thresholds while user is typing them ([#777](https://github.com/GyulyVGC/sniffnet/pull/777) — fixes [#658](https://github.com/GyulyVGC/sniffnet/issues/658)) - Show more information when domain name is short ([#720](https://github.com/GyulyVGC/sniffnet/pull/720) — fixes [#696](https://github.com/GyulyVGC/sniffnet/issues/696)) - Added new themes _A11y (Night)_ and _A11y (Day)_ based on palettes optimized for Accessibility ([#785](https://github.com/GyulyVGC/sniffnet/pull/785) — fixes [#786](https://github.com/GyulyVGC/sniffnet/issues/786)) - Avoid directory traversal when selecting file name for PCAP exports ([#776](https://github.com/GyulyVGC/sniffnet/pull/776) — fixes [#767](https://github.com/GyulyVGC/sniffnet/issues/767)) @@ -22,7 +27,6 @@ ## [UNRELEASED] - Update footer buttons and links ([#755](https://github.com/GyulyVGC/sniffnet/pull/755) — fixes [#553](https://github.com/GyulyVGC/sniffnet/issues/553)) - Handle errors to reduce the number of possible crash occurrences ([#784](https://github.com/GyulyVGC/sniffnet/pull/784)) - Use asynchronous channels to update app state from backend ([#806](https://github.com/GyulyVGC/sniffnet/pull/806)) -- Do not apply new notification thresholds while user is typing them ([#777](https://github.com/GyulyVGC/sniffnet/pull/777) — fixes [#658](https://github.com/GyulyVGC/sniffnet/issues/658)) - Fix _crates.io_ package for Windows ([#718](https://github.com/GyulyVGC/sniffnet/pull/718) — fixes [#681](https://github.com/GyulyVGC/sniffnet/issues/681)) - Fix crash when inserting characters longer than one byte in the text input for byte threshold notification setting ([#747](https://github.com/GyulyVGC/sniffnet/pull/747) — fixes [#744](https://github.com/GyulyVGC/sniffnet/issues/744)) - Remove pre-uninstall script on Linux (fixes [#644](https://github.com/GyulyVGC/sniffnet/issues/644)) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index b099b1e2..6dc7e385 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -6,96 +6,101 @@ + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + - - - + + + + + + + - - + + - - diff --git a/Cargo.lock b/Cargo.lock index c290780e..53b514cf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 4 [[package]] name = "ab_glyph" -version = "0.2.29" +version = "0.2.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec3672c180e71eeaaac3a541fbbc5f5ad4def8b747c595ad30d674e43049f7b0" +checksum = "e074464580a518d16a7126262fffaaa47af89d4099d4cb403f8ed938ba12ee7d" dependencies = [ "ab_glyph_rasterizer", "owned_ttf_parser", @@ -14,9 +14,9 @@ dependencies = [ [[package]] name = "ab_glyph_rasterizer" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" +checksum = "b2187590a23ab1e3df8681afdf0987c48504d80291f002fcdb651f0ef5e25169" [[package]] name = "addr2line" @@ -29,9 +29,9 @@ dependencies = [ [[package]] name = "adler2" -version = "2.0.0" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "ahash" @@ -114,7 +114,7 @@ dependencies = [ "jni-sys", "libc", "log", - "ndk 0.9.0", + "ndk", "ndk-context", "ndk-sys 0.6.0+11769913", "num_enum", @@ -239,12 +239,15 @@ dependencies = [ "enumflags2", "futures-channel", "futures-util", - "rand 0.9.1", + "rand 0.9.2", "raw-window-handle", "serde", "serde_repr", "url", - "zbus 5.7.1", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "zbus 5.9.0", ] [[package]] @@ -261,9 +264,9 @@ dependencies = [ [[package]] name = "async-channel" -version = "2.3.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" dependencies = [ "concurrent-queue", "event-listener-strategy", @@ -287,9 +290,9 @@ dependencies = [ [[package]] name = "async-fs" -version = "2.1.2" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebcd09b382f40fcd159c2d695175b2ae620ffa5f3bd6f664131efff4e8b9e04a" +checksum = "09f7e37c0ed80b2a977691c47dae8625cfb21e205827106c64f7c588766b2e50" dependencies = [ "async-lock", "blocking", @@ -298,9 +301,9 @@ dependencies = [ [[package]] name = "async-io" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1237c0ae75a0f3765f58910ff9cdd0a12eeb39ab2f4c7de23262f337f0aacbb3" +checksum = "19634d6336019ef220f09fd31168ce5c184b295cbf80345437cc36094ef223ca" dependencies = [ "async-lock", "cfg-if", @@ -309,10 +312,9 @@ dependencies = [ "futures-lite", "parking", "polling", - "rustix 1.0.7", + "rustix 1.0.8", "slab", - "tracing", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -339,9 +341,9 @@ dependencies = [ [[package]] name = "async-process" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cde3f4e40e6021d7acffc90095cbd6dc54cb593903d1de5832f435eb274b85dc" +checksum = "65daa13722ad51e6ab1a1b9c01299142bc75135b337923cfa10e79bbbd669f00" dependencies = [ "async-channel", "async-io", @@ -352,8 +354,7 @@ dependencies = [ "cfg-if", "event-listener", "futures-lite", - "rustix 1.0.7", - "tracing", + "rustix 1.0.8", ] [[package]] @@ -364,14 +365,14 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] name = "async-signal" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7605a4e50d4b06df3898d5a70bf5fde51ed9059b0434b73105193bc27acce0d" +checksum = "f567af260ef69e1d52c2b560ce0ea230763e6fbb9214a85d768760a920e3e3c1" dependencies = [ "async-io", "async-lock", @@ -379,10 +380,10 @@ dependencies = [ "cfg-if", "futures-core", "futures-io", - "rustix 1.0.7", + "rustix 1.0.8", "signal-hook-registry", "slab", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -399,7 +400,7 @@ checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -410,9 +411,9 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "backtrace" @@ -435,24 +436,6 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" -[[package]] -name = "bindgen" -version = "0.70.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" -dependencies = [ - "bitflags 2.9.1", - "cexpr", - "clang-sys", - "itertools 0.13.0", - "proc-macro2", - "quote", - "regex", - "rustc-hash 1.1.0", - "shlex", - "syn 2.0.101", -] - [[package]] name = "bit-set" version = "0.5.3" @@ -521,9 +504,9 @@ dependencies = [ [[package]] name = "blocking" -version = "1.6.1" +version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" +checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" dependencies = [ "async-channel", "async-task", @@ -534,9 +517,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.18.1" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db76d6187cd04dff33004d8e6c9cc4e05cd330500379d2394209271b4aeee" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "by_address" @@ -546,22 +529,22 @@ checksum = "64fa3c856b712db6612c019f14756e64e4bcea13337a6b33b696333a9eaa2d06" [[package]] name = "bytemuck" -version = "1.23.0" +version = "1.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9134a6ef01ce4b366b50689c94f82c14bc72bc5d0386829828a2e2752ef7958c" +checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.9.3" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ecc273b49b3205b83d648f0690daa588925572cc5063745bfe547fe7ec8e1a1" +checksum = "441473f2b4b0459a68628c744bc61d23e730fb00128b841d30fa4bb3972257e4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -604,9 +587,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.26" +version = "1.2.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "956a5e21988b87f372569b66183b78babf23ebc2e744b733e4350a752c4dafac" +checksum = "deec109607ca693028562ed836a5f1c4b8bd77755c4e132fc5ce11b0b6211ae7" dependencies = [ "jobserver", "libc", @@ -619,20 +602,11 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" -[[package]] -name = "cexpr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" -dependencies = [ - "nom", -] - [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" [[package]] name = "cfg_aliases" @@ -658,22 +632,11 @@ dependencies = [ "windows-link", ] -[[package]] -name = "clang-sys" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" -dependencies = [ - "glob", - "libc", - "libloading 0.8.8", -] - [[package]] name = "clap" -version = "4.5.39" +version = "4.5.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd60e63e9be68e5fb56422e397cf9baddded06dae1d2e523401542383bc72a9f" +checksum = "be92d32e80243a54711e5d7ce823c35c41c9d929dc4ab58e1276f625841aadf9" dependencies = [ "clap_builder", "clap_derive", @@ -681,9 +644,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.39" +version = "4.5.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89cc6392a1f72bbeb820d71f32108f61fdaf18bc526e1d23954168a67759ef51" +checksum = "707eab41e9622f9139419d573eca0900137718000c517d47da73045f54331c3d" dependencies = [ "anstream", "anstyle", @@ -693,27 +656,27 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.32" +version = "4.5.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" +checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] name = "clap_lex" -version = "0.7.4" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" [[package]] name = "clipboard-win" -version = "5.4.0" +version = "5.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15efe7a882b08f34e38556b14f2fb3daa98769d06c7f0c1b076dfd0d983bc892" +checksum = "bde03770d3df201d4fb868f2c9c59e66a3e4e2bd06692a0fe701e7103c7e84d4" dependencies = [ "error-code", ] @@ -908,22 +871,16 @@ dependencies = [ [[package]] name = "coreaudio-rs" -version = "0.11.3" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "321077172d79c662f64f5071a03120748d5bb652f5231570141be24cfcd2bace" +checksum = "1aae284fbaf7d27aa0e292f7677dfbe26503b0d555026f702940805a630eac17" dependencies = [ "bitflags 1.3.2", - "core-foundation-sys", - "coreaudio-sys", -] - -[[package]] -name = "coreaudio-sys" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ce857aa0b77d77287acc1ac3e37a05a8c95a2af3647d23b15f263bdaeb7562b" -dependencies = [ - "bindgen", + "libc", + "objc2-audio-toolbox", + "objc2-core-audio", + "objc2-core-audio-types", + "objc2-core-foundation", ] [[package]] @@ -951,21 +908,24 @@ dependencies = [ [[package]] name = "cpal" -version = "0.15.3" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "873dab07c8f743075e57f524c583985fbaf745602acbe916a01539364369a779" +checksum = "cbd307f43cc2a697e2d1f8bc7a1d824b5269e052209e28883e5bc04d095aaa3f" dependencies = [ "alsa", - "core-foundation-sys", "coreaudio-rs", "dasp_sample", "jni", "js-sys", "libc", "mach2", - "ndk 0.8.0", + "ndk", "ndk-context", - "oboe", + "num-derive", + "num-traits", + "objc2-audio-toolbox", + "objc2-core-audio", + "objc2-core-audio-types", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -983,9 +943,9 @@ dependencies = [ [[package]] name = "crc32fast" -version = "1.4.2" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" dependencies = [ "cfg-if", ] @@ -1017,9 +977,9 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crunchy" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "crypto-common" @@ -1152,7 +1112,7 @@ dependencies = [ "libc", "option-ext", "redox_users 0.5.0", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -1161,18 +1121,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" -[[package]] -name = "dispatch2" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a0d569e003ff27784e0e14e4a594048698e0c0f0b66cabcb51511be55a7caa0" -dependencies = [ - "bitflags 2.9.1", - "block2 0.6.1", - "libc", - "objc2 0.6.1", -] - [[package]] name = "dispatch2" version = "0.3.0" @@ -1180,6 +1128,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" dependencies = [ "bitflags 2.9.1", + "block2 0.6.1", + "libc", "objc2 0.6.1", ] @@ -1191,7 +1141,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -1301,9 +1251,9 @@ checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" [[package]] name = "enumflags2" -version = "0.7.11" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba2f4b465f5318854c6f8dd686ede6c0a9dc67d4b1ac241cf0eb51521a309147" +checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef" dependencies = [ "enumflags2_derive", "serde", @@ -1311,13 +1261,13 @@ dependencies = [ [[package]] name = "enumflags2_derive" -version = "0.7.11" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc4caf64a58d7a6d65ab00639b046ff54399a39f5f2554728895ace4b297cd79" +checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -1339,12 +1289,12 @@ dependencies = [ [[package]] name = "errno" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18" +checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -1467,9 +1417,9 @@ checksum = "94c970b525906eb37d3940083aa65b95e481fc1857d467d13374e1d925cfc163" [[package]] name = "flate2" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" +checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" dependencies = [ "crc32fast", "miniz_oxide", @@ -1566,7 +1516,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -1660,7 +1610,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -1738,7 +1688,7 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi 0.11.1+wasi-snapshot-preview1", "wasm-bindgen", ] @@ -1758,9 +1708,9 @@ dependencies = [ [[package]] name = "gif" -version = "0.13.1" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2" +checksum = "4ae047235e33e2829703574b54fdec96bfbad892062d97fed2f76022287de61b" dependencies = [ "color_quant", "weezl", @@ -1880,9 +1830,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9421a676d1b147b16b82c9225157dc629087ef8ec4d5e2960f9437a90dac0a5" +checksum = "17da50a276f1e01e0ba6c029e47b7100754904ee8a278f886546e98575380785" dependencies = [ "atomic-waker", "bytes", @@ -1928,9 +1878,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.3" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" [[package]] name = "hassle-rs" @@ -1961,9 +1911,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermit-abi" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f154ce46856750ed433c8649605bf7ed2de3bc35fd9d2a9f30cddd873c80cb08" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" [[package]] name = "hex" @@ -2072,9 +2022,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.14" +version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc2fdfdbff08affe55bb779f33b053aa1fe5dd5b54c257343c17edfa55711bdb" +checksum = "7f66d5bd4c6f02bf0542fad85d626775bab9258cf795a4256dcaf3161114d1df" dependencies = [ "base64", "bytes", @@ -2440,12 +2390,12 @@ checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284" [[package]] name = "indexmap" -version = "2.9.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" dependencies = [ "equivalent", - "hashbrown 0.15.3", + "hashbrown 0.15.4", ] [[package]] @@ -2457,6 +2407,17 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "io-uring" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b86e202f00093dcba4275d4636b93ef9dd75d025ae560d2521b45ea28ab49013" +dependencies = [ + "bitflags 2.9.1", + "cfg-if", + "libc", +] + [[package]] name = "ipnet" version = "2.11.0" @@ -2494,15 +2455,6 @@ dependencies = [ "either", ] -[[package]] -name = "itertools" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" -dependencies = [ - "either", -] - [[package]] name = "itoa" version = "1.0.15" @@ -2543,9 +2495,9 @@ dependencies = [ [[package]] name = "jpeg-decoder" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" +checksum = "00810f1d8b74be64b13dbf3db89ac67740615d6c891f0e7b6179326533011a07" dependencies = [ "rayon", ] @@ -2620,9 +2572,9 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" [[package]] name = "libc" -version = "0.2.172" +version = "0.2.174" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" [[package]] name = "libloading" @@ -2641,7 +2593,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" dependencies = [ "cfg-if", - "windows-targets 0.53.0", + "windows-targets 0.53.2", ] [[package]] @@ -2652,13 +2604,13 @@ checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" [[package]] name = "libredox" -version = "0.1.3" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +checksum = "4488594b9328dee448adb906d8b126d9b7deb7cf5c22161ee591610bb1be83c0" dependencies = [ "bitflags 2.9.1", "libc", - "redox_syscall 0.5.12", + "redox_syscall 0.5.14", ] [[package]] @@ -2767,9 +2719,9 @@ dependencies = [ [[package]] name = "mach2" -version = "0.4.2" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709" +checksum = "d640282b302c0bb0a2a8e0233ead9035e3bed871f0b7e81fe4a1ec829765db44" dependencies = [ "libc", ] @@ -2798,15 +2750,15 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "memmap2" -version = "0.9.5" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" +checksum = "483758ad303d734cec05e5c12b41d7e93e6a6390c5e9dae6bdeb7c1259012d28" dependencies = [ "libc", ] @@ -2841,17 +2793,11 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - [[package]] name = "miniz_oxide" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", "simd-adler32", @@ -2864,7 +2810,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi 0.11.1+wasi-snapshot-preview1", "windows-sys 0.59.0", ] @@ -2911,20 +2857,6 @@ dependencies = [ "tempfile", ] -[[package]] -name = "ndk" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2076a31b7010b17a38c01907c45b945e8f11495ee4dd588309718901b1f7a5b7" -dependencies = [ - "bitflags 2.9.1", - "jni-sys", - "log", - "ndk-sys 0.5.0+25.2.9519653", - "num_enum", - "thiserror 1.0.69", -] - [[package]] name = "ndk" version = "0.9.0" @@ -2991,13 +2923,13 @@ dependencies = [ ] [[package]] -name = "nom" -version = "7.1.3" +name = "num-bigint" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ - "memchr", - "minimal-lexical", + "num-integer", + "num-traits", ] [[package]] @@ -3008,7 +2940,27 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", ] [[package]] @@ -3033,23 +2985,24 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" +checksum = "a973b4e44ce6cad84ce69d797acf9a044532e4184c4f267913d1b546a0727b7a" dependencies = [ "num_enum_derive", + "rustversion", ] [[package]] name = "num_enum_derive" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" +checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -3115,6 +3068,21 @@ dependencies = [ "objc2-foundation 0.3.1", ] +[[package]] +name = "objc2-audio-toolbox" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10cbe18d879e20a4aea544f8befe38bcf52255eb63d3f23eca2842f3319e4c07" +dependencies = [ + "bitflags 2.9.1", + "libc", + "objc2 0.6.1", + "objc2-core-audio", + "objc2-core-audio-types", + "objc2-core-foundation", + "objc2-foundation 0.3.1", +] + [[package]] name = "objc2-cloud-kit" version = "0.2.2" @@ -3139,6 +3107,28 @@ dependencies = [ "objc2-foundation 0.2.2", ] +[[package]] +name = "objc2-core-audio" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca44961e888e19313b808f23497073e3f6b3c22bb485056674c8b49f3b025c82" +dependencies = [ + "dispatch2", + "objc2 0.6.1", + "objc2-core-audio-types", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-core-audio-types" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0f1cc99bb07ad2ddb6527ddf83db6a15271bb036b3eb94b801cd44fdc666ee1" +dependencies = [ + "bitflags 2.9.1", + "objc2 0.6.1", +] + [[package]] name = "objc2-core-data" version = "0.2.2" @@ -3158,7 +3148,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" dependencies = [ "bitflags 2.9.1", - "dispatch2 0.3.0", + "dispatch2", "objc2 0.6.1", ] @@ -3326,29 +3316,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "oboe" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8b61bebd49e5d43f5f8cc7ee2891c16e0f41ec7954d36bcb6c14c5e0de867fb" -dependencies = [ - "jni", - "ndk 0.8.0", - "ndk-context", - "num-derive", - "num-traits", - "oboe-sys", -] - -[[package]] -name = "oboe-sys" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8bb09a4a2b1d668170cfe0a7d5bc103f8999fb316c98099b6a9939c9f2e79d" -dependencies = [ - "cc", -] - [[package]] name = "once_cell" version = "1.21.3" @@ -3384,7 +3351,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -3461,7 +3428,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -3482,7 +3449,7 @@ dependencies = [ "approx", "fast-srgb8", "palette_derive", - "phf", + "phf 0.11.3", ] [[package]] @@ -3494,7 +3461,7 @@ dependencies = [ "by_address", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -3546,7 +3513,7 @@ checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.12", + "redox_syscall 0.5.14", "smallvec", "windows-targets 0.52.6", ] @@ -3559,9 +3526,9 @@ checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "pcap" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "499125886165f62fbc0c095ead9189b253f48eb1c5fcab49f81a270f2f220652" +checksum = "83cdabc34a80d9ec3563694cc31423fba6bb9bab4f31a9a5d5b85f29bd6d660a" dependencies = [ "bitflags 1.3.2", "errno 0.2.8", @@ -3585,17 +3552,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" dependencies = [ "phf_macros", - "phf_shared", + "phf_shared 0.11.3", +] + +[[package]] +name = "phf" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "913273894cec178f401a31ec4b656318d95473527be05c0752cc41cdc32be8b7" +dependencies = [ + "phf_shared 0.12.1", + "serde", ] [[package]] name = "phf_codegen" -version = "0.11.3" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aef8048c789fa5e851558d709946d6d79a8ff88c0440c587967f8e94bfb1216a" +checksum = "efbdcb6f01d193b17f0b9c3360fa7e0e620991b193ff08702f78b3ce365d7e61" dependencies = [ - "phf_generator", - "phf_shared", + "phf_generator 0.12.1", + "phf_shared 0.12.1", ] [[package]] @@ -3604,21 +3581,31 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ - "phf_shared", + "phf_shared 0.11.3", "rand 0.8.5", ] +[[package]] +name = "phf_generator" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cbb1126afed61dd6368748dae63b1ee7dc480191c6262a3b4ff1e29d86a6c5b" +dependencies = [ + "fastrand", + "phf_shared 0.12.1", +] + [[package]] name = "phf_macros" version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" dependencies = [ - "phf_generator", - "phf_shared", + "phf_generator 0.11.3", + "phf_shared 0.11.3", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -3630,6 +3617,15 @@ dependencies = [ "siphasher", ] +[[package]] +name = "phf_shared" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06005508882fb681fd97892ecff4b7fd0fee13ef1aa569f8695dae7ab9099981" +dependencies = [ + "siphasher", +] + [[package]] name = "pico-args" version = "0.5.0" @@ -3653,7 +3649,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -3731,17 +3727,16 @@ dependencies = [ [[package]] name = "polling" -version = "3.8.0" +version = "3.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b53a684391ad002dd6a596ceb6c74fd004fdce75f4be2e3f615068abbea5fd50" +checksum = "8ee9b2fa7a4517d2c91ff5bc6c297a427a96749d15f98fcdbb22c05571a4d4b7" dependencies = [ "cfg-if", "concurrent-queue", "hermit-abi", "pin-project-lite", - "rustix 1.0.7", - "tracing", - "windows-sys 0.59.0", + "rustix 1.0.8", + "windows-sys 0.60.2", ] [[package]] @@ -3800,16 +3795,16 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", "version_check", "yansi", ] [[package]] name = "profiling" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afbdc74edc00b6f6a218ca6a5364d6226a259d4b8ea1af4a0ea063f27e179f4d" +checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773" [[package]] name = "qoi" @@ -3858,7 +3853,7 @@ dependencies = [ "bytes", "getrandom 0.3.3", "lru-slab", - "rand 0.9.1", + "rand 0.9.2", "ring", "rustc-hash 2.1.1", "rustls", @@ -3872,9 +3867,9 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.12" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee4e529991f949c5e25755532370b8af5d114acae52326361d68d47af64aa842" +checksum = "fcebb1209ee276352ef14ff8732e24cc2b02bbac986cd74a4c81bcb2f9881970" dependencies = [ "cfg_aliases 0.2.1", "libc", @@ -3895,9 +3890,9 @@ dependencies = [ [[package]] name = "r-efi" -version = "5.2.0" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] name = "rand" @@ -3912,9 +3907,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.3", @@ -4026,9 +4021,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.12" +version = "0.5.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" +checksum = "de3a5d9f0aba1dbcec1cc47f0ff94a4b778fe55bca98a6dfa92e4e094e57b1c4" dependencies = [ "bitflags 2.9.1", ] @@ -4098,9 +4093,9 @@ checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" [[package]] name = "reqwest" -version = "0.12.19" +version = "0.12.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2f8e5513d63f2e5b386eb5106dc67eaf3f84e95258e210489136b8b92ad6119" +checksum = "cbc931937e6ca3a06e3b6c0aa7841849b160a90351d6ab467a8b9b9959767531" dependencies = [ "base64", "bytes", @@ -4114,12 +4109,10 @@ dependencies = [ "hyper-rustls", "hyper-tls", "hyper-util", - "ipnet", "js-sys", "log", "mime", "native-tls", - "once_cell", "percent-encoding", "pin-project-lite", "quinn", @@ -4160,13 +4153,13 @@ dependencies = [ [[package]] name = "rfd" -version = "0.15.3" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80c844748fdc82aae252ee4594a89b6e7ebef1063de7951545564cbc4e57075d" +checksum = "ef2bee61e6cffa4635c72d7d81a84294e28f0930db0ddcb0f66d10244674ebed" dependencies = [ "ashpd", "block2 0.6.1", - "dispatch2 0.2.0", + "dispatch2", "js-sys", "log", "objc2 0.6.1", @@ -4184,9 +4177,9 @@ dependencies = [ [[package]] name = "rgb" -version = "0.8.50" +version = "0.8.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a" +checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce" dependencies = [ "bytemuck", ] @@ -4207,11 +4200,13 @@ dependencies = [ [[package]] name = "rodio" -version = "0.20.1" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7ceb6607dd738c99bc8cb28eff249b7cd5c8ec88b9db96c0608c1480d140fb1" +checksum = "e40ecf59e742e03336be6a3d53755e789fd05a059fa22dfa0ed624722319e183" dependencies = [ "cpal", + "dasp_sample", + "num-rational", "symphonia", ] @@ -4247,7 +4242,7 @@ dependencies = [ "regex", "relative-path", "rustc_version", - "syn 2.0.101", + "syn 2.0.104", "unicode-ident", ] @@ -4263,9 +4258,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" +checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" [[package]] name = "rustc-hash" @@ -4295,7 +4290,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ "bitflags 2.9.1", - "errno 0.3.12", + "errno 0.3.13", "libc", "linux-raw-sys 0.4.15", "windows-sys 0.59.0", @@ -4303,22 +4298,22 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" dependencies = [ "bitflags 2.9.1", - "errno 0.3.12", + "errno 0.3.13", "libc", "linux-raw-sys 0.9.4", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "rustls" -version = "0.23.27" +version = "0.23.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "730944ca083c1c233a75c09f199e973ca499344a2b7ba9e755c457e86fb4a321" +checksum = "2491382039b29b9b11ff08b76ff6c97cf287671dbb74f0be44bda389fffe9bd1" dependencies = [ "once_cell", "ring", @@ -4340,9 +4335,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.3" +version = "0.103.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435" +checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" dependencies = [ "ring", "rustls-pki-types", @@ -4359,7 +4354,7 @@ dependencies = [ "bitflags 1.3.2", "doc-comment", "finl_unicode", - "itertools 0.10.5", + "itertools", "lazy_static", "rustc-hash 1.1.0", "unicode-normalization", @@ -4448,9 +4443,9 @@ dependencies = [ [[package]] name = "sdd" -version = "3.0.8" +version = "3.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "584e070911c7017da6cb2eb0788d09f43d789029b5877d3e5ecc8acf86ceee21" +checksum = "490dcfcbfef26be6800d11870ff2df8774fa6e86d047e3e8c8a76b25655e41ca" [[package]] name = "security-framework" @@ -4504,14 +4499,14 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] name = "serde_json" -version = "1.0.140" +version = "1.0.141" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +checksum = "30b9eff21ebe718216c6ec64e1d9ac57087aad11efc64e32002bce4a0d4c03d3" dependencies = [ "itoa", "memchr", @@ -4527,7 +4522,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -4539,6 +4534,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40734c41988f7306bb04f0ecf60ec0f3f1caa34290e4e8ea471dcd3346483b83" +dependencies = [ + "serde", +] + [[package]] name = "serde_test" version = "1.0.177" @@ -4580,7 +4584,7 @@ checksum = "5d69265a08751de7844521fd15003ae0a888e035773ba05695c5c759a6f89eef" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -4642,12 +4646,9 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -dependencies = [ - "autocfg", -] +checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" [[package]] name = "slotmap" @@ -4711,7 +4712,7 @@ dependencies = [ [[package]] name = "sniffnet" -version = "1.3.2" +version = "1.4.0" dependencies = [ "async-channel", "chrono", @@ -4724,9 +4725,9 @@ dependencies = [ "iced", "maxminddb", "pcap", - "phf", + "phf 0.12.1", "phf_codegen", - "phf_shared", + "phf_shared 0.12.1", "plotters", "plotters-iced", "reqwest", @@ -4740,7 +4741,7 @@ dependencies = [ "serial_test", "splines", "tokio", - "toml 0.8.23", + "toml 0.9.2", "winres", ] @@ -4774,7 +4775,7 @@ dependencies = [ "objc2-foundation 0.2.2", "objc2-quartz-core", "raw-window-handle", - "redox_syscall 0.5.12", + "redox_syscall 0.5.14", "rustix 0.38.44", "tiny-xlib", "wasm-bindgen", @@ -4923,9 +4924,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.101" +version = "2.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" dependencies = [ "proc-macro2", "quote", @@ -4949,7 +4950,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -4991,7 +4992,7 @@ dependencies = [ "fastrand", "getrandom 0.3.3", "once_cell", - "rustix 1.0.7", + "rustix 1.0.8", "windows-sys 0.59.0", ] @@ -5030,7 +5031,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -5041,7 +5042,7 @@ checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -5121,15 +5122,17 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.45.1" +version = "1.46.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" +checksum = "0cc3a2344dafbe23a245241fe8b09735b521110d30fcefbbd5feb1797ca35d17" dependencies = [ "backtrace", "bytes", + "io-uring", "libc", "mio", "pin-project-lite", + "slab", "socket2", "tokio-macros", "windows-sys 0.52.0", @@ -5143,7 +5146,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -5195,11 +5198,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" dependencies = [ "serde", - "serde_spanned", - "toml_datetime", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", "toml_edit", ] +[[package]] +name = "toml" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed0aee96c12fa71097902e0bb061a5e1ebd766a6636bb605ba401c45c1650eac" +dependencies = [ + "indexmap", + "serde", + "serde_spanned 1.0.0", + "toml_datetime 0.7.0", + "toml_parser", + "toml_writer", + "winnow", +] + [[package]] name = "toml_datetime" version = "0.6.11" @@ -5209,6 +5227,15 @@ dependencies = [ "serde", ] +[[package]] +name = "toml_datetime" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bade1c3e902f58d73d3f294cd7f20391c1cb2fbcb643b73566bc773971df91e3" +dependencies = [ + "serde", +] + [[package]] name = "toml_edit" version = "0.22.27" @@ -5217,18 +5244,33 @@ checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ "indexmap", "serde", - "serde_spanned", - "toml_datetime", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", "toml_write", "winnow", ] +[[package]] +name = "toml_parser" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97200572db069e74c512a14117b296ba0a80a30123fbbb5aa1f4a348f639ca30" +dependencies = [ + "winnow", +] + [[package]] name = "toml_write" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" +[[package]] +name = "toml_writer" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc842091f2def52017664b53082ecbbeb5c7731092bad69d2c63050401dfd64" + [[package]] name = "tower" version = "0.5.2" @@ -5287,13 +5329,13 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.29" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1ffbcf9c6f6b99d386e7444eb608ba646ae452a36b39737deb9663b610f662" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -5517,9 +5559,9 @@ dependencies = [ [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasi" @@ -5552,7 +5594,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", "wasm-bindgen-shared", ] @@ -5587,7 +5629,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -5747,9 +5789,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "1.0.0" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2853738d1cc4f2da3a225c18ec6c3721abb31961096e9dbf5ab35fa88b19cfdb" +checksum = "7e8983c3ab33d6fb807cfcdad2491c4ea8cbc8ed839181c7dfd9c67c83e261b2" dependencies = [ "rustls-pki-types", ] @@ -5978,7 +6020,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -5989,20 +6031,20 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] name = "windows-link" -version = "0.1.1" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" [[package]] name = "windows-registry" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3bab093bdd303a1240bb99b8aba8ea8a69ee19d34c9e2ef9594e708a4878820" +checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e" dependencies = [ "windows-link", "windows-result 0.3.4", @@ -6085,6 +6127,15 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.2", +] + [[package]] name = "windows-targets" version = "0.42.2" @@ -6133,9 +6184,9 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.0" +version = "0.53.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" +checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef" dependencies = [ "windows_aarch64_gnullvm 0.53.0", "windows_aarch64_msvc 0.53.0", @@ -6379,7 +6430,7 @@ dependencies = [ "js-sys", "libc", "memmap2", - "ndk 0.9.0", + "ndk", "objc2 0.5.2", "objc2-app-kit 0.2.2", "objc2-foundation 0.2.2", @@ -6411,9 +6462,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.10" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" +checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" dependencies = [ "memchr", ] @@ -6485,9 +6536,9 @@ checksum = "ec107c4503ea0b4a98ef47356329af139c0a4f7750e621cf2973cd3385ebcb3d" [[package]] name = "xcursor" -version = "0.3.8" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ef33da6b1660b4ddbfb3aef0ade110c8b8a781a3b6382fa5f2b5b040fd55f61" +checksum = "bec9e4a500ca8864c5b47b8b482a73d62e4237670e5b5f1d6b9e3cae50f28f2b" [[package]] name = "xdg-home" @@ -6520,9 +6571,9 @@ checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" [[package]] name = "xml-rs" -version = "0.8.26" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62ce76d9b56901b19a74f19431b0d8b3bc7ca4ad685a746dfd78ca8f4fc6bda" +checksum = "6fd8403733700263c6eb89f192880191f1b83e332f7a20371ddcf421c4a337c7" [[package]] name = "xmlwriter" @@ -6562,7 +6613,7 @@ checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", "synstructure", ] @@ -6606,9 +6657,9 @@ dependencies = [ [[package]] name = "zbus" -version = "5.7.1" +version = "5.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3a7c7cee313d044fca3f48fa782cb750c79e4ca76ba7bc7718cd4024cdf6f68" +checksum = "4bb4f9a464286d42851d18a605f7193b8febaf5b0919d71c6399b7b26e5b0aad" dependencies = [ "async-broadcast", "async-executor", @@ -6632,9 +6683,9 @@ dependencies = [ "uds_windows", "windows-sys 0.59.0", "winnow", - "zbus_macros 5.7.1", + "zbus_macros 5.9.0", "zbus_names 4.2.0", - "zvariant 5.5.3", + "zvariant 5.6.0", ] [[package]] @@ -6646,22 +6697,22 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", "zvariant_utils 2.1.0", ] [[package]] name = "zbus_macros" -version = "5.7.1" +version = "5.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a17e7e5eec1550f747e71a058df81a9a83813ba0f6a95f39c4e218bdc7ba366a" +checksum = "ef9859f68ee0c4ee2e8cde84737c78e3f4c54f946f2a38645d0d4c7a95327659" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", "zbus_names 4.2.0", - "zvariant 5.5.3", + "zvariant 5.6.0", "zvariant_utils 3.2.0", ] @@ -6685,7 +6736,7 @@ dependencies = [ "serde", "static_assertions", "winnow", - "zvariant 5.5.3", + "zvariant 5.6.0", ] [[package]] @@ -6696,22 +6747,22 @@ checksum = "dd15f8e0dbb966fd9245e7498c7e9e5055d9e5c8b676b95bd67091cd11a1e697" [[package]] name = "zerocopy" -version = "0.8.25" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" +checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.25" +version = "0.8.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" +checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -6731,7 +6782,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", "synstructure", ] @@ -6771,7 +6822,7 @@ checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -6798,16 +6849,16 @@ dependencies = [ [[package]] name = "zvariant" -version = "5.5.3" +version = "5.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d30786f75e393ee63a21de4f9074d4c038d52c5b1bb4471f955db249f9dffb1" +checksum = "d91b3680bb339216abd84714172b5138a4edac677e641ef17e1d8cb1b3ca6e6f" dependencies = [ "endi", "enumflags2", "serde", "url", "winnow", - "zvariant_derive 5.5.3", + "zvariant_derive 5.6.0", "zvariant_utils 3.2.0", ] @@ -6820,20 +6871,20 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", "zvariant_utils 2.1.0", ] [[package]] name = "zvariant_derive" -version = "5.5.3" +version = "5.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75fda702cd42d735ccd48117b1630432219c0e9616bf6cb0f8350844ee4d9580" +checksum = "3a8c68501be459a8dbfffbe5d792acdd23b4959940fc87785fb013b32edbc208" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", "zvariant_utils 3.2.0", ] @@ -6845,7 +6896,7 @@ checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340" dependencies = [ "proc-macro2", "quote", - "syn 2.0.101", + "syn 2.0.104", ] [[package]] @@ -6858,6 +6909,6 @@ dependencies = [ "quote", "serde", "static_assertions", - "syn 2.0.101", + "syn 2.0.104", "winnow", ] diff --git a/Cargo.toml b/Cargo.toml index 18e0ed5d..90afa45c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "sniffnet" -version = "1.3.2" +version = "1.4.0" authors = ["Giuliano Bellini "] 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 LinkedIn  Mastodon  Telegram  - Twitter / X + Twitter / X \ 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. - Roadmap + Roadmap 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",

ADS Fund
ADS Fund

💵
Abdullah
Abdullah

🤔 🖋
Adriano
Adriano

🤔
Aguacero 🌧️
Aguacero 🌧️

🌍
Ahmet Kaan GÜMÜŞ
Ahmet Kaan GÜMÜŞ

🌍
Alexandr Shashkin
Alexandr Shashkin

🐛
AmadeusGraves
AmadeusGraves

🌍
Angelos Bousis
Angelos Bousis

🌍
Antoine Colombier
Antoine Colombier

⚠️ 🌍
Angelos Bousis
Angelos Bousis

🌍
Antoine Colombier
Antoine Colombier

⚠️ 🌍
BugsQuanti
BugsQuanti

🌍
Charpy
Charpy

🌍
Christoph Wanja
Christoph Wanja

💵
Colin Delahunty
Colin Delahunty

⚠️
Cornelius Roemer
Cornelius Roemer

🤔
CosminPerRam
CosminPerRam

💻
Cristiano
Cristiano

💻 🤔
CosminPerRam
CosminPerRam

💻
Cristiano
Cristiano

💻 🤔
Cthulu201
Cthulu201

💵
Dinar Shagaliev
Dinar Shagaliev

🌍
Dominic Kim
Dominic Kim

🌍
Echo
Echo

💵
Embers-of-the-Fire
Embers-of-the-Fire

🌍
Francisco Salgueiro
Francisco Salgueiro

🌍
GNUser
GNUser

📖 📦
Francisco Salgueiro
Francisco Salgueiro

🌍
GNUser
GNUser

📖 📦
George Shuklin
George Shuklin

🌍
Giusy Digital
Giusy Digital

🐛
Hiroki Tagato
Hiroki Tagato

📦
Hubert
Hubert

🌍
Hüseyin Fahri Uzun
Hüseyin Fahri Uzun

🌍
IPinfo
IPinfo

💵
Ilmi2
Ilmi2

💵
IPinfo
IPinfo

💵
Ilmi2
Ilmi2

💵
Jan Walter
Jan Walter

💵
Jauder Ho
Jauder Ho

🚇
Joshua Megnauth
Joshua Megnauth

💻 🎨
Julian Schmid
Julian Schmid

💻 🤔
LiChenG-P
LiChenG-P

💻
Limdongju
Limdongju

🌍
Ludwig Stecher
Ludwig Stecher

🤔 💻
Limdongju
Limdongju

🌍
Ludwig Stecher
Ludwig Stecher

🤔 💻
Marc Gavilán
Marc Gavilán

🌍
Marco Cadetg
Marco Cadetg

📦
Matthias Braun
Matthias Braun

📖
Michel Hansma
Michel Hansma

🎨 ️️️️♿️
Morgan Hill
Morgan Hill

🛡️
Muhammadali Hakimov
Muhammadali Hakimov

🌍
Nubi
Nubi

🌍
Muhammadali Hakimov
Muhammadali Hakimov

🌍
Nubi
Nubi

🌍
Oleksii Filonenko
Oleksii Filonenko

🌍
Orhun Parmaksız
Orhun Parmaksız

📖 📦 💵
Peter Dave Hello
Peter Dave Hello

🌍
Phil Clifford
Phil Clifford

📦
Quetzal-coalt
Quetzal-coalt

🌍
Safaraliev Maxim
Safaraliev Maxim

🌍
Shawn Yeager
Shawn Yeager

💵
Ron
Ron

🤔
Safaraliev Maxim
Safaraliev Maxim

🌍
Shawn Yeager
Shawn Yeager

💵
The Artifex
The Artifex

🌍 📦
Trịnh Duy Hưng
Trịnh Duy Hưng

🌍
TyseEX
TyseEX

🐛
Victor Nilsson
Victor Nilsson

🌍 💻
Wang Zishi
Wang Zishi

🌍
Yevhen
Yevhen

🌍
ZEROF
ZEROF

💵
Wang Zishi
Wang Zishi

🌍
Yevhen
Yevhen

🌍
Ylva
Ylva

🌍
ZEROF
ZEROF

💵
ZeroDot1
ZeroDot1

🎨 ️️️️♿️
clr
clr

📖 🌍
ervinpopescu
ervinpopescu

🌍
glitsj16
glitsj16

📦
guilherme-demarchi
guilherme-demarchi

🌍
hirotake111
hirotake111

🌍
louis-ym4
louis-ym4

🎨
luca3s
luca3s

🌍
pia
pia

🌍
pin
pin

📦
shu-kitamura
shu-kitamura

💻
starccy
starccy

💻
tiansheng li
tiansheng li

💵
vtiinanen
vtiinanen

🌍
yossarian
yossarian

🌍
陈寒彤
陈寒彤

🌍