diff --git a/.all-contributorsrc b/.all-contributorsrc index 7db83d3d..adc2fb71 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -601,7 +601,8 @@ "avatar_url": "https://avatars.githubusercontent.com/u/171437458?v=4", "profile": "https://github.com/shu-kitamura", "contributions": [ - "code" + "code", + "translation" ] }, { @@ -721,6 +722,60 @@ "contributions": [ "platform" ] + }, + { + "login": "islameehassan", + "name": "islameehassan", + "avatar_url": "https://avatars.githubusercontent.com/u/98806155?v=4", + "profile": "https://github.com/islameehassan", + "contributions": [ + "code" + ] + }, + { + "login": "lionrayonnant", + "name": "Lion Rayonnant", + "avatar_url": "https://avatars.githubusercontent.com/u/106342136?v=4", + "profile": "https://github.com/lionrayonnant", + "contributions": [ + "translation" + ] + }, + { + "login": "18601673727", + "name": "18601673727", + "avatar_url": "https://avatars.githubusercontent.com/u/3302620?v=4", + "profile": "https://github.com/18601673727", + "contributions": [ + "translation" + ] + }, + { + "login": "AlleM43", + "name": "AlleM43", + "avatar_url": "https://avatars.githubusercontent.com/u/16104173?v=4", + "profile": "https://github.com/AlleM43", + "contributions": [ + "platform" + ] + }, + { + "login": "aris1009", + "name": "Aris Konstantoulas", + "avatar_url": "https://avatars.githubusercontent.com/u/25184469?v=4", + "profile": "https://github.com/aris1009", + "contributions": [ + "translation" + ] + }, + { + "login": "mmseng", + "name": "Matt Seng", + "avatar_url": "https://avatars.githubusercontent.com/u/8218085?v=4", + "profile": "https://github.com/mmseng", + "contributions": [ + "financial" + ] } ], "contributorsPerLine": 7, diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index fd513caa..a7693ffa 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -1,9 +1,6 @@ name: Docker on: - # push: - # tags: - # - 'v[0-9]+.[0-9]+.[0-9]+' workflow_dispatch: jobs: @@ -12,7 +9,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Extract version from Cargo.toml id: cargo-version @@ -21,6 +18,13 @@ jobs: echo "VERSION=$VERSION" >> $GITHUB_ENV echo "version=$VERSION" >> $GITHUB_OUTPUT + - name: Check there is no existing image with the same version + run: | + if docker manifest inspect ghcr.io/gyulyvgc/sniffnet:${{ env.VERSION }}; then + echo "Image with version ${{ env.VERSION }} already exists" + exit 1 + fi + - name: Set up QEMU uses: docker/setup-qemu-action@v3 @@ -43,4 +47,3 @@ jobs: tags: | ghcr.io/gyulyvgc/sniffnet:latest ghcr.io/gyulyvgc/sniffnet:${{ env.VERSION }} -# ghcr.io/gyulyvgc/sniffnet:${{ github.ref_name }} diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml index f89dbbe4..3fa67ab0 100644 --- a/.github/workflows/package.yml +++ b/.github/workflows/package.yml @@ -38,7 +38,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Install Linux dependencies if: matrix.os == 'ubuntu' @@ -126,7 +126,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Install dependencies run: apt-get update -y && apt-get install -y curl build-essential @@ -139,7 +139,7 @@ jobs: - name: Install packaging tools run: cargo install cargo-deb - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v5 with: name: build-ubuntu-${{ matrix.target }} path: target/ @@ -158,6 +158,63 @@ jobs: path: artifacts/ if-no-files-found: error + appimage: + runs-on: ubuntu-latest + container: + image: debian:latest + options: --privileged + needs: deb + strategy: + fail-fast: true + matrix: + include: + - arch: amd64 + appimgarch: x86_64 + - arch: arm64 + appimgarch: aarch64 + - arch: i386 + appimgarch: i686 + - arch: armhf + appimgarch: armhf + + steps: + - name: Checkout repository + uses: actions/checkout@v5 + + - name: Install dependencies + run: apt-get update -y && apt-get install -y git build-essential graphicsmagick-imagemagick-compat wget file desktop-file-utils libfuse2 + + - name: Download Debian package + uses: actions/download-artifact@v5 + with: + name: deb-${{ matrix.arch }} + path: /target/ + + - name: Download pkg2appimage + shell: bash + run: git clone https://github.com/AppImageCommunity/pkg2appimage.git + + - name: Replace placeholders + shell: bash + run: | + sed -i -e 's/REPLACE_TAG/Sniffnet_LinuxDEB_${{ matrix.arch }}.deb/g' resources/packaging/linux/AppImage/sniffnet.yml + sed -i -e 's/binary-${arch}/binary-${{ matrix.arch }}/g' pkg2appimage/functions.sh + + - name: Repackage for AppImage + shell: bash + run: | + cd pkg2appimage + ARCH=${{ matrix.appimgarch }} FUNCTIONS_SH=$(pwd)/functions.sh ./pkg2appimage ../resources/packaging/linux/AppImage/sniffnet.yml + mkdir /out + mv out/*.AppImage /out/Sniffnet_LinuxAppImage_${{ matrix.arch }}.AppImage + + - name: Upload package artifacts + uses: actions/upload-artifact@v4 + with: + name: appimage-${{ matrix.arch }} + path: /out/ + if-no-files-found: error + rpm: runs-on: ubuntu-latest container: @@ -174,7 +231,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Install dependencies run: dnf update -y && dnf install -y @development-tools patchelf @@ -187,7 +244,7 @@ jobs: - name: Install packaging tools run: cargo install cargo-generate-rpm - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v5 with: name: build-ubuntu-${{ matrix.target }} path: target/ @@ -221,7 +278,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Install Rust uses: dtolnay/rust-toolchain@stable @@ -231,7 +288,7 @@ jobs: cargo install toml-cli brew install create-dmg - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v5 with: name: build-macos-${{ matrix.target }} path: target/ @@ -285,7 +342,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Install dependencies shell: powershell @@ -306,7 +363,7 @@ jobs: - name: Install packaging tools run: cargo install cargo-wix - - uses: actions/download-artifact@v4 + - uses: actions/download-artifact@v5 with: name: build-windows-${{ matrix.target }} path: target/ @@ -318,9 +375,29 @@ jobs: cargo wix --no-build --nocapture --target ${{ matrix.target }} Move-Item -Path target\wix\sniffnet*.msi -Destination .\artifacts\Sniffnet_Windows_${{ matrix.arch }}.msi - - name: Upload package artifacts + - name: Upload unsigned package artifacts + id: upload-unsigned-artifact uses: actions/upload-artifact@v4 with: name: msi-${{ matrix.arch }} path: artifacts/ if-no-files-found: error + + - name: Sign package artifacts + uses: signpath/github-action-submit-signing-request@v1.3 + with: + api-token: '${{ secrets.SIGNPATH_API_TOKEN }}' + organization-id: '3b533e02-73c3-4908-a018-d09a34498a6a' + project-slug: 'sniffnet' + signing-policy-slug: 'release-signing' + github-artifact-id: '${{ steps.upload-unsigned-artifact.outputs.artifact-id }}' + wait-for-completion: true + output-artifact-directory: './artifacts' + + - name: Upload signed package artifacts (overwrite unsigned) + uses: actions/upload-artifact@v4 + with: + name: msi-${{ matrix.arch }} + path: artifacts/ + if-no-files-found: error + overwrite: true diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 85f91c52..82704275 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -32,7 +32,7 @@ jobs: - os: windows steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: dtolnay/rust-toolchain@stable with: components: rustfmt, clippy diff --git a/.gitignore b/.gitignore index fe36f504..bc65973c 100644 --- a/.gitignore +++ b/.gitignore @@ -57,3 +57,6 @@ $RECYCLE.BIN/ ### Custom... ### lcov.info *.pcap +node_modules +package.json +yarn.lock diff --git a/CHANGELOG.md b/CHANGELOG.md index a7604354..84e205b0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,10 +3,21 @@ # Changelog All Sniffnet releases with the relative changes are documented in this file. ## [UNRELEASED] +- Enhanced traffic filtering capabilities: Berkeley Packet Filter ([#937](https://github.com/GyulyVGC/sniffnet/pull/937) — fixes [#810](https://github.com/GyulyVGC/sniffnet/issues/810)) +- Added support for `Linux SLL` link type, enabling to monitor the `any` interface on Linux ([#945](https://github.com/GyulyVGC/sniffnet/pull/945)) +- Added _bits_ data representation ([#936](https://github.com/GyulyVGC/sniffnet/pull/936) — fixes [#506](https://github.com/GyulyVGC/sniffnet/issues/506)) +- An AppImage of Sniffnet is now available ([#859](https://github.com/GyulyVGC/sniffnet/pull/859) — fixes [#900](https://github.com/GyulyVGC/sniffnet/issues/900)) - Added Dutch translation 🇳🇱 ([#854](https://github.com/GyulyVGC/sniffnet/pull/854)) +- Improved configurations persistence across different runs of the app ([#938](https://github.com/GyulyVGC/sniffnet/pull/938) — fixes [#507](https://github.com/GyulyVGC/sniffnet/issues/507)) +- The Windows Installer is now signed with a code signing certificate provided by the [SignPath Foundation](https://signpath.org/) ([#897](https://github.com/GyulyVGC/sniffnet/pull/897) — fixes [#894](https://github.com/GyulyVGC/sniffnet/issues/894)) - Updated some of the existing translations to v1.4: - German ([#833](https://github.com/GyulyVGC/sniffnet/pull/833)) - Uzbek ([#834](https://github.com/GyulyVGC/sniffnet/pull/834)) + - Simplified Chinese ([#838](https://github.com/GyulyVGC/sniffnet/pull/838)) + - Japanese ([#849](https://github.com/GyulyVGC/sniffnet/pull/849)) + - French ([#864](https://github.com/GyulyVGC/sniffnet/pull/864)) + - Greek ([#879](https://github.com/GyulyVGC/sniffnet/pull/879)) +- Fix support for IPinfo's databases (the most recent version renamed the `country` field to `country_code`) ## [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)) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 9aeb7a50..0f0831f6 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -6,105 +6,111 @@ + - + + + + + - - + + - - + + - - + + - - + - - - - + + + + + + + - - + + - - + + - - + + + + - - + - - diff --git a/Cargo.lock b/Cargo.lock index 28ee19c3..c68b0b6c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,9 +14,9 @@ dependencies = [ [[package]] name = "ab_glyph_rasterizer" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2187590a23ab1e3df8681afdf0987c48504d80291f002fcdb651f0ef5e25169" +checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618" [[package]] name = "addr2line" @@ -80,12 +80,12 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "alsa" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed7572b7ba83a31e20d1b48970ee402d2e3e0537dcfe0a3ff4d6eb7508617d43" +checksum = "bdc00893e7a970727e9304671b2c88577b4cfe53dc64019fdfdf9683573a09c4" dependencies = [ "alsa-sys", - "bitflags 2.9.1", + "bitflags 2.9.3", "cfg-if", "libc", ] @@ -107,7 +107,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046" dependencies = [ "android-properties", - "bitflags 2.9.1", + "bitflags 2.9.3", "cc", "cesu8", "jni", @@ -144,9 +144,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.19" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" +checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192" dependencies = [ "anstyle", "anstyle-parse", @@ -174,22 +174,22 @@ dependencies = [ [[package]] name = "anstyle-query" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" +checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.9" +version = "3.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" +checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -247,7 +247,7 @@ dependencies = [ "wayland-backend", "wayland-client", "wayland-protocols", - "zbus 5.9.0", + "zbus 5.10.0", ] [[package]] @@ -276,9 +276,9 @@ dependencies = [ [[package]] name = "async-executor" -version = "1.13.2" +version = "1.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb812ffb58524bdd10860d7d974e2f01cc0950c2438a74ee5ec2e2280c6c4ffa" +checksum = "497c00e0fd83a72a79a39fcbd8e3e2f055d6f6c7e025f3b3d91f4f8e76527fb8" dependencies = [ "async-task", "concurrent-queue", @@ -319,9 +319,9 @@ dependencies = [ [[package]] name = "async-lock" -version = "3.4.0" +version = "3.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" +checksum = "5fd03604047cee9b6ce9de9f70c6cd540a0520c813cbd49bae61f33ab80ed1dc" dependencies = [ "event-listener", "event-listener-strategy", @@ -365,7 +365,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -394,13 +394,13 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] name = "async-trait" -version = "0.1.88" +version = "0.1.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -453,9 +453,9 @@ checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" [[package]] name = "bit_field" -version = "0.10.2" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" +checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6" [[package]] name = "bitflags" @@ -465,9 +465,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.1" +version = "2.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +checksum = "34efbcccd345379ca2868b2b2c9d3782e9cc58ba87bc7d79d5b53d9c9ae6f25d" [[package]] name = "block" @@ -499,7 +499,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "340d2f0bdb2a43c1d3cd40513185b2bd7def0aa1052f956455114bc98f82dcf2" dependencies = [ - "objc2 0.6.1", + "objc2 0.6.2", ] [[package]] @@ -529,22 +529,22 @@ checksum = "64fa3c856b712db6612c019f14756e64e4bcea13337a6b33b696333a9eaa2d06" [[package]] name = "bytemuck" -version = "1.23.1" +version = "1.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422" +checksum = "3995eaeebcdf32f91f980d360f78732ddc061097ab4e39991ae7a6ace9194677" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.10.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "441473f2b4b0459a68628c744bc61d23e730fb00128b841d30fa4bb3972257e4" +checksum = "4f154e572231cb6ba2bd1176980827e3d5dc04cc183a75dea38109fbdd672d29" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -565,7 +565,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "log", "polling", "rustix 0.38.44", @@ -587,9 +587,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.30" +version = "1.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "deec109607ca693028562ed836a5f1c4b8bd77755c4e132fc5ce11b0b6211ae7" +checksum = "42bc4aea80032b7bf409b0bc7ccad88853858911b7713a8062fdc0623867bedc" dependencies = [ "jobserver", "libc", @@ -604,9 +604,9 @@ checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" [[package]] name = "cfg-if" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" +checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" [[package]] name = "cfg_aliases" @@ -634,9 +634,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.41" +version = "4.5.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be92d32e80243a54711e5d7ce823c35c41c9d929dc4ab58e1276f625841aadf9" +checksum = "2c5e4fcf9c21d2e544ca1ee9d8552de13019a42aa7dbf32747fa7aaf1df76e57" dependencies = [ "clap_builder", "clap_derive", @@ -644,9 +644,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.41" +version = "4.5.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707eab41e9622f9139419d573eca0900137718000c517d47da73045f54331c3d" +checksum = "fecb53a0e6fcfb055f686001bc2e2592fa527efaf38dbe81a6a9563562e57d41" dependencies = [ "anstream", "anstyle", @@ -656,14 +656,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.41" +version = "4.5.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491" +checksum = "14cb31bb0a7d536caef2639baa7fad459e15c3144efefa6dbd1c84562c4739f6" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -791,7 +791,7 @@ checksum = "f29222b549d4e3ded127989d523da9e928918d0d0d7f7c1690b439d0d538bae9" dependencies = [ "directories", "serde", - "thiserror 2.0.12", + "thiserror 2.0.16", "toml 0.8.23", ] @@ -840,7 +840,7 @@ version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "core-foundation 0.10.1", "core-graphics-types 0.2.0", "foreign-types 0.5.0", @@ -864,7 +864,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "core-foundation 0.10.1", "libc", ] @@ -889,7 +889,7 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59fd57d82eb4bfe7ffa9b1cec0c05e2fd378155b47f255a67983cb4afe0e80c2" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "fontdb 0.16.2", "log", "rangemap", @@ -1019,7 +1019,7 @@ version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e3d747f100290a1ca24b752186f61f6637e1deffe3bf6320de6fcb29510a307" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "libloading 0.8.8", "winapi", ] @@ -1048,9 +1048,9 @@ checksum = "0c87e182de0887fd5361989c677c4e8f5000cd9491d6d563161a8f3a5519fc7f" [[package]] name = "data-url" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a" +checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376" [[package]] name = "dconf_rs" @@ -1111,7 +1111,7 @@ checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" dependencies = [ "libc", "option-ext", - "redox_users 0.5.0", + "redox_users 0.5.2", "windows-sys 0.60.2", ] @@ -1127,10 +1127,10 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "block2 0.6.1", "libc", - "objc2 0.6.1", + "objc2 0.6.2", ] [[package]] @@ -1141,7 +1141,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -1161,14 +1161,14 @@ checksum = "0688c2a7f92e427f44895cd63841bff7b29f8d7a1648b9e7e07a4a365b2e1257" [[package]] name = "dns-lookup" -version = "2.0.4" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5766087c2235fec47fafa4cfecc81e494ee679d0fd4a59887ea0919bfb0e4fc" +checksum = "853d5bcf0b73bd5e6d945b976288621825c7166e9f06c5a035ae1aaf42d1b64f" dependencies = [ "cfg-if", "libc", "socket2", - "windows-sys 0.48.0", + "windows-sys 0.60.2", ] [[package]] @@ -1195,7 +1195,7 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98888c4bbd601524c11a7ed63f814b8825f420514f78e96f752c437ae9cbb5d1" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "bytemuck", "drm-ffi", "drm-fourcc", @@ -1267,7 +1267,7 @@ checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -1325,9 +1325,9 @@ dependencies = [ [[package]] name = "etherparse" -version = "0.18.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff83a5facf1a7cbfef93cfb48d6d4fb6a1f42d8ac2341a96b3255acb4d4f860" +checksum = "b119b9796ff800751a220394b8b3613f26dd30c48f254f6837e64c464872d1c7" dependencies = [ "arrayvec", ] @@ -1343,9 +1343,9 @@ dependencies = [ [[package]] name = "event-listener" -version = "5.4.0" +version = "5.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" dependencies = [ "concurrent-queue", "parking", @@ -1516,7 +1516,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -1533,9 +1533,9 @@ checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" [[package]] name = "form_urlencoded" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" dependencies = [ "percent-encoding", ] @@ -1591,9 +1591,9 @@ checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-lite" -version = "2.6.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532" +checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" dependencies = [ "fastrand", "futures-core", @@ -1610,7 +1610,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -1741,9 +1741,9 @@ checksum = "151665d9be52f9bb40fc7966565d39666f2d1e69233571b71b87791c7e0528b3" [[package]] name = "glob" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" [[package]] name = "glow" @@ -1772,7 +1772,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "gpu-alloc-types", ] @@ -1782,7 +1782,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", ] [[package]] @@ -1804,7 +1804,7 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc11df1ace8e7e564511f53af41f3e42ddc95b56fd07b3f4445d2a6048bc682c" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "gpu-descriptor-types", "hashbrown 0.14.5", ] @@ -1815,7 +1815,7 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6bf0b36e6f090b7e1d8a4b49c0cb81c1f8376f72198c65dd3ad9ff3556b8b78c" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", ] [[package]] @@ -1830,9 +1830,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17da50a276f1e01e0ba6c029e47b7100754904ee8a278f886546e98575380785" +checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" dependencies = [ "atomic-waker", "bytes", @@ -1878,9 +1878,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.4" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" [[package]] name = "hassle-rs" @@ -1888,7 +1888,7 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af2a7e73e1f34c48da31fb668a907f250794837e08faa144fd24f0b8b741e890" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "com", "libc", "libloading 0.8.8", @@ -1969,19 +1969,21 @@ checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "hyper" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" +checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" dependencies = [ + "atomic-waker", "bytes", "futures-channel", - "futures-util", + "futures-core", "h2", "http", "http-body", "httparse", "itoa", "pin-project-lite", + "pin-utils", "smallvec", "tokio", "want", @@ -2022,9 +2024,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f66d5bd4c6f02bf0542fad85d626775bab9258cf795a4256dcaf3161114d1df" +checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e" dependencies = [ "base64", "bytes", @@ -2091,7 +2093,7 @@ version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0013a238275494641bf8f1732a23a808196540dc67b22ff97099c044ae4c8a1c" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "bytes", "dark-light", "glam", @@ -2139,7 +2141,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba25a18cfa6d5cc160aca7e1b34f73ccdff21680fa8702168c09739767b6c66f" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "bytemuck", "cosmic-text", "half", @@ -2205,7 +2207,7 @@ version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "15708887133671d2bcc6c1d01d1f176f43a64d6cdc3b2bf893396c3ee498295f" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "bytemuck", "futures", "glam", @@ -2345,9 +2347,9 @@ dependencies = [ [[package]] name = "idna" -version = "1.0.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" dependencies = [ "idna_adapter", "smallvec", @@ -2390,12 +2392,12 @@ checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284" [[package]] name = "indexmap" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" +checksum = "f2481980430f9f78649238835720ddccc57e52df14ffce1c6f37391d61b563e9" dependencies = [ "equivalent", - "hashbrown 0.15.4", + "hashbrown 0.15.5", ] [[package]] @@ -2409,11 +2411,11 @@ dependencies = [ [[package]] name = "io-uring" -version = "0.7.8" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b86e202f00093dcba4275d4636b93ef9dd75d025ae560d2521b45ea28ab49013" +checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "cfg-if", "libc", ] @@ -2485,9 +2487,9 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "jobserver" -version = "0.1.33" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" dependencies = [ "getrandom 0.3.3", "libc", @@ -2550,11 +2552,12 @@ dependencies = [ [[package]] name = "kurbo" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1077d333efea6170d9ccb96d3c3026f300ca0773da4938cc4c811daa6df68b0c" +checksum = "c62026ae44756f8a599ba21140f350303d4f08dcdcc71b5ad9c9bb8128c13c62" dependencies = [ "arrayvec", + "euclid", "smallvec", ] @@ -2572,9 +2575,9 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" [[package]] name = "libc" -version = "0.2.174" +version = "0.2.175" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" +checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" [[package]] name = "libloading" @@ -2593,7 +2596,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" dependencies = [ "cfg-if", - "windows-targets 0.53.2", + "windows-targets 0.53.3", ] [[package]] @@ -2604,13 +2607,13 @@ checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" [[package]] name = "libredox" -version = "0.1.6" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4488594b9328dee448adb906d8b126d9b7deb7cf5c22161ee591610bb1be83c0" +checksum = "391290121bad3d37fbddad76d8f5d1c1c314cfc646d143d7e07a3086ddff0ce3" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "libc", - "redox_syscall 0.5.14", + "redox_syscall 0.5.17", ] [[package]] @@ -2745,7 +2748,7 @@ dependencies = [ "log", "memchr", "serde", - "thiserror 2.0.12", + "thiserror 2.0.16", ] [[package]] @@ -2756,9 +2759,9 @@ checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "memmap2" -version = "0.9.7" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "483758ad303d734cec05e5c12b41d7e93e6a6390c5e9dae6bdeb7c1259012d28" +checksum = "843a98750cd611cc2965a8213b53b43e715f13c37a9e096c6408e69990961db7" dependencies = [ "libc", ] @@ -2778,7 +2781,7 @@ version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c43f73953f8cbe511f021b58f18c3ce1c3d1ae13fe953293e13345bf83217f25" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "block", "core-graphics-types 0.1.3", "foreign-types 0.5.0", @@ -2816,9 +2819,9 @@ dependencies = [ [[package]] name = "mutate_once" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16cf681a23b4d0a43fc35024c176437f9dcd818db34e0f42ab456a0ee5ad497b" +checksum = "13d2233c9842d08cfe13f9eac96e207ca6a2ea10b80259ebe8ad0268be27d2af" [[package]] name = "naga" @@ -2827,7 +2830,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50e3524642f53d9af419ab5e8dd29d3ba155708267667c2f3f06c88c9e130843" dependencies = [ "bit-set", - "bitflags 2.9.1", + "bitflags 2.9.3", "codespan-reporting", "hexf-parse", "indexmap", @@ -2863,7 +2866,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "jni-sys", "log", "ndk-sys 0.6.0+11769913", @@ -2902,7 +2905,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "cfg-if", "cfg_aliases 0.2.1", "libc", @@ -2915,7 +2918,7 @@ version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "cfg-if", "cfg_aliases 0.2.1", "libc", @@ -2940,7 +2943,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -3002,7 +3005,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -3033,9 +3036,9 @@ dependencies = [ [[package]] name = "objc2" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88c6597e14493ab2e44ce58f2fdecf095a51f12ca57bec060a11c57332520551" +checksum = "561f357ba7f3a2a61563a186a163d0a3a5247e1089524a3981d49adb775078bc" dependencies = [ "objc2-encode", ] @@ -3046,7 +3049,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "block2 0.5.1", "libc", "objc2 0.5.2", @@ -3062,9 +3065,9 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6f29f568bec459b0ddff777cec4fe3fd8666d82d5a40ebd0ff7e66134f89bcc" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "block2 0.6.1", - "objc2 0.6.1", + "objc2 0.6.2", "objc2-foundation 0.3.1", ] @@ -3074,9 +3077,9 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10cbe18d879e20a4aea544f8befe38bcf52255eb63d3f23eca2842f3319e4c07" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "libc", - "objc2 0.6.1", + "objc2 0.6.2", "objc2-core-audio", "objc2-core-audio-types", "objc2-core-foundation", @@ -3089,7 +3092,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "block2 0.5.1", "objc2 0.5.2", "objc2-core-location", @@ -3114,7 +3117,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca44961e888e19313b808f23497073e3f6b3c22bb485056674c8b49f3b025c82" dependencies = [ "dispatch2", - "objc2 0.6.1", + "objc2 0.6.2", "objc2-core-audio-types", "objc2-core-foundation", ] @@ -3125,8 +3128,8 @@ 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", + "bitflags 2.9.3", + "objc2 0.6.2", ] [[package]] @@ -3135,7 +3138,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -3147,9 +3150,9 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "dispatch2", - "objc2 0.6.1", + "objc2 0.6.2", ] [[package]] @@ -3188,7 +3191,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "block2 0.5.1", "dispatch", "libc", @@ -3201,8 +3204,8 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "900831247d2fe1a09a683278e5384cfb8c80c79fe6b166f9d14bfdde0ea1b03c" dependencies = [ - "bitflags 2.9.1", - "objc2 0.6.1", + "bitflags 2.9.3", + "objc2 0.6.2", "objc2-core-foundation", ] @@ -3224,7 +3227,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -3236,7 +3239,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -3259,7 +3262,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "block2 0.5.1", "objc2 0.5.2", "objc2-cloud-kit", @@ -3291,7 +3294,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "block2 0.5.1", "objc2 0.5.2", "objc2-core-location", @@ -3334,7 +3337,7 @@ version = "0.10.73" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "cfg-if", "foreign-types 0.3.2", "libc", @@ -3351,7 +3354,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -3428,14 +3431,14 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] name = "owned_ttf_parser" -version = "0.25.0" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec719bbf3b2a81c109a4e20b1f129b5566b7dce654bc3872f6a05abf82b2c4" +checksum = "36820e9051aca1014ddc75770aab4d68bc1e9e632f0f5627c4086bc216fb583b" dependencies = [ "ttf-parser 0.25.1", ] @@ -3461,7 +3464,7 @@ dependencies = [ "by_address", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -3513,7 +3516,7 @@ checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.14", + "redox_syscall 0.5.17", "smallvec", "windows-targets 0.52.6", ] @@ -3541,9 +3544,9 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.3.1" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "phf" @@ -3557,22 +3560,22 @@ dependencies = [ [[package]] name = "phf" -version = "0.12.1" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "913273894cec178f401a31ec4b656318d95473527be05c0752cc41cdc32be8b7" +checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" dependencies = [ - "phf_shared 0.12.1", + "phf_shared 0.13.1", "serde", ] [[package]] name = "phf_codegen" -version = "0.12.1" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efbdcb6f01d193b17f0b9c3360fa7e0e620991b193ff08702f78b3ce365d7e61" +checksum = "49aa7f9d80421bca176ca8dbfebe668cc7a2684708594ec9f3c0db0805d5d6e1" dependencies = [ - "phf_generator 0.12.1", - "phf_shared 0.12.1", + "phf_generator 0.13.1", + "phf_shared 0.13.1", ] [[package]] @@ -3587,12 +3590,12 @@ dependencies = [ [[package]] name = "phf_generator" -version = "0.12.1" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cbb1126afed61dd6368748dae63b1ee7dc480191c6262a3b4ff1e29d86a6c5b" +checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" dependencies = [ "fastrand", - "phf_shared 0.12.1", + "phf_shared 0.13.1", ] [[package]] @@ -3605,7 +3608,7 @@ dependencies = [ "phf_shared 0.11.3", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -3619,9 +3622,9 @@ dependencies = [ [[package]] name = "phf_shared" -version = "0.12.1" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06005508882fb681fd97892ecff4b7fd0fee13ef1aa569f8695dae7ab9099981" +checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" dependencies = [ "siphasher", ] @@ -3649,7 +3652,7 @@ checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -3727,9 +3730,9 @@ dependencies = [ [[package]] name = "polling" -version = "3.9.0" +version = "3.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ee9b2fa7a4517d2c91ff5bc6c297a427a96749d15f98fcdbb22c05571a4d4b7" +checksum = "b5bd19146350fe804f7cb2669c851c03d69da628803dab0d98018142aaa5d829" dependencies = [ "cfg-if", "concurrent-queue", @@ -3780,9 +3783,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.95" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ "unicode-ident", ] @@ -3795,7 +3798,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "version_check", "yansi", ] @@ -3826,9 +3829,9 @@ dependencies = [ [[package]] name = "quinn" -version = "0.11.8" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "626214629cda6781b6dc1d316ba307189c85ba657213ce642d9c77670f8202c8" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" dependencies = [ "bytes", "cfg_aliases 0.2.1", @@ -3838,7 +3841,7 @@ dependencies = [ "rustc-hash 2.1.1", "rustls", "socket2", - "thiserror 2.0.12", + "thiserror 2.0.16", "tokio", "tracing", "web-time", @@ -3846,9 +3849,9 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.12" +version = "0.11.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49df843a9161c85bb8aae55f101bc0bac8bcafd637a620d9122fd7e0b2f7422e" +checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" dependencies = [ "bytes", "getrandom 0.3.3", @@ -3859,7 +3862,7 @@ dependencies = [ "rustls", "rustls-pki-types", "slab", - "thiserror 2.0.12", + "thiserror 2.0.16", "tinyvec", "tracing", "web-time", @@ -3867,16 +3870,16 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.13" +version = "0.5.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcebb1209ee276352ef14ff8732e24cc2b02bbac986cd74a4c81bcb2f9881970" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" dependencies = [ "cfg_aliases 0.2.1", "libc", "once_cell", "socket2", "tracing", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -3961,9 +3964,9 @@ checksum = "c3d6831663a5098ea164f89cff59c6284e95f4e3c76ce9848d4529f5ccca9bde" [[package]] name = "rangemap" -version = "1.5.1" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60fcc7d6849342eff22c4350c8b9a989ee8ceabc4b481253e8946b9fe83d684" +checksum = "f93e7e49bb0bf967717f7bd674458b3d6b0c5f48ec7e3038166026a69fc22223" [[package]] name = "raw-window-handle" @@ -3973,9 +3976,9 @@ checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" [[package]] name = "rayon" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" dependencies = [ "either", "rayon-core", @@ -3983,9 +3986,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.12.1" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" dependencies = [ "crossbeam-deque", "crossbeam-utils", @@ -4021,11 +4024,11 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.14" +version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3a5d9f0aba1dbcec1cc47f0ff94a4b778fe55bca98a6dfa92e4e094e57b1c4" +checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", ] [[package]] @@ -4041,20 +4044,20 @@ dependencies = [ [[package]] name = "redox_users" -version = "0.5.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ "getrandom 0.2.16", "libredox", - "thiserror 2.0.12", + "thiserror 2.0.16", ] [[package]] name = "regex" -version = "1.11.1" +version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912" dependencies = [ "aho-corasick", "memchr", @@ -4064,9 +4067,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6" dependencies = [ "aho-corasick", "memchr", @@ -4075,9 +4078,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" [[package]] name = "relative-path" @@ -4093,9 +4096,9 @@ checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" [[package]] name = "reqwest" -version = "0.12.22" +version = "0.12.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbc931937e6ca3a06e3b6c0aa7841849b160a90351d6ab467a8b9b9959767531" +checksum = "d429f34c8092b2d42c7c93cec323bb4adeb7c67698f70839adec842ec10c7ceb" dependencies = [ "base64", "bytes", @@ -4162,7 +4165,7 @@ dependencies = [ "dispatch2", "js-sys", "log", - "objc2 0.6.1", + "objc2 0.6.2", "objc2-app-kit 0.3.1", "objc2-core-foundation", "objc2-foundation 0.3.1", @@ -4218,21 +4221,20 @@ checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" [[package]] name = "rstest" -version = "0.25.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fc39292f8613e913f7df8fa892b8944ceb47c247b78e1b1ae2f09e019be789d" +checksum = "f5a3193c063baaa2a95a33f03035c8a72b83d97a54916055ba22d35ed3839d49" dependencies = [ "futures-timer", "futures-util", "rstest_macros", - "rustc_version", ] [[package]] name = "rstest_macros" -version = "0.25.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f168d99749d307be9de54d23fd226628d99768225ef08f6ffb52e0182a27746" +checksum = "9c845311f0ff7951c5506121a9ad75aec44d083c31583b2ea5a30bcb0b0abba0" dependencies = [ "cfg-if", "glob", @@ -4242,7 +4244,7 @@ dependencies = [ "regex", "relative-path", "rustc_version", - "syn 2.0.104", + "syn 2.0.106", "unicode-ident", ] @@ -4258,9 +4260,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.25" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" [[package]] name = "rustc-hash" @@ -4289,7 +4291,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "errno 0.3.13", "libc", "linux-raw-sys 0.4.15", @@ -4302,7 +4304,7 @@ version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "errno 0.3.13", "libc", "linux-raw-sys 0.9.4", @@ -4311,9 +4313,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.29" +version = "0.23.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2491382039b29b9b11ff08b76ff6c97cf287671dbb74f0be44bda389fffe9bd1" +checksum = "c0ebcbd2f03de0fc1122ad9bb24b127a5a6cd51d72604a3f3c50ac459762b6cc" dependencies = [ "once_cell", "ring", @@ -4346,9 +4348,9 @@ dependencies = [ [[package]] name = "rustrict" -version = "0.7.35" +version = "0.7.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dfe349049fa49baa564f8483d40e7561ff19ccaa308ab4f844bb59d2c5d8d34" +checksum = "d93719b9aa6a53f9beae62fe36f34ed88226be314aea6829031ed0f878ca493d" dependencies = [ "arrayvec", "bitflags 1.3.2", @@ -4362,9 +4364,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.21" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "rustybuzz" @@ -4372,7 +4374,7 @@ version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfb9cf8877777222e4a3bc7eb247e398b56baba500c38c1c46842431adc8b55c" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "bytemuck", "libm", "smallvec", @@ -4400,9 +4402,9 @@ dependencies = [ [[package]] name = "scc" -version = "2.3.4" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22b2d775fb28f245817589471dd49c5edf64237f4a19d10ce9a92ff4651a27f4" +checksum = "46e6f046b7fef48e2660c57ed794263155d713de679057f2d0c169bfc6e756cc" dependencies = [ "sdd", ] @@ -4453,7 +4455,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "core-foundation 0.9.4", "core-foundation-sys", "libc", @@ -4499,14 +4501,14 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] name = "serde_json" -version = "1.0.141" +version = "1.0.143" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30b9eff21ebe718216c6ec64e1d9ac57087aad11efc64e32002bce4a0d4c03d3" +checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a" dependencies = [ "itoa", "memchr", @@ -4522,7 +4524,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -4584,7 +4586,7 @@ checksum = "5d69265a08751de7844521fd15003ae0a888e035773ba05695c5c759a6f89eef" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -4606,9 +4608,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.5" +version = "1.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" +checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" dependencies = [ "libc", ] @@ -4646,9 +4648,9 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" [[package]] name = "slotmap" @@ -4671,7 +4673,7 @@ version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "calloop", "calloop-wayland-source", "cursor-icon", @@ -4725,9 +4727,9 @@ dependencies = [ "iced", "maxminddb", "pcap", - "phf 0.12.1", + "phf 0.13.1", "phf_codegen", - "phf_shared 0.12.1", + "phf_shared 0.13.1", "plotters", "plotters-iced", "reqwest", @@ -4740,18 +4742,18 @@ dependencies = [ "serial_test", "splines", "tokio", - "toml 0.9.2", + "toml 0.9.5", "winres", ] [[package]] name = "socket2" -version = "0.5.10" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -4774,7 +4776,7 @@ dependencies = [ "objc2-foundation 0.2.2", "objc2-quartz-core", "raw-window-handle", - "redox_syscall 0.5.14", + "redox_syscall 0.5.17", "rustix 0.38.44", "tiny-xlib", "wasm-bindgen", @@ -4792,7 +4794,7 @@ version = "0.3.0+sdk-1.3.268.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", ] [[package]] @@ -4846,7 +4848,7 @@ version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68c7541fff44b35860c1a7a47a7cadf3e4a304c457b58f9870d9706ece028afc" dependencies = [ - "kurbo 0.11.2", + "kurbo 0.11.3", "siphasher", ] @@ -4923,9 +4925,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.104" +version = "2.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" dependencies = [ "proc-macro2", "quote", @@ -4949,7 +4951,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -4967,7 +4969,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "core-foundation 0.9.4", "system-configuration-sys", ] @@ -4984,15 +4986,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.20.0" +version = "3.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" +checksum = "15b61f8f20e3a6f7e0649d825294eaf317edce30f82cf6026e7e4cb9222a7d1e" dependencies = [ "fastrand", "getrandom 0.3.3", "once_cell", "rustix 1.0.8", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -5015,11 +5017,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.12" +version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" dependencies = [ - "thiserror-impl 2.0.12", + "thiserror-impl 2.0.16", ] [[package]] @@ -5030,18 +5032,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] name = "thiserror-impl" -version = "2.0.12" +version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -5106,9 +5108,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" dependencies = [ "tinyvec_macros", ] @@ -5121,9 +5123,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.46.1" +version = "1.47.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc3a2344dafbe23a245241fe8b09735b521110d30fcefbbd5feb1797ca35d17" +checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" dependencies = [ "backtrace", "bytes", @@ -5134,7 +5136,7 @@ dependencies = [ "slab", "socket2", "tokio-macros", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -5145,7 +5147,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -5170,9 +5172,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.15" +version = "0.7.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" +checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" dependencies = [ "bytes", "futures-core", @@ -5204,9 +5206,9 @@ dependencies = [ [[package]] name = "toml" -version = "0.9.2" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed0aee96c12fa71097902e0bb061a5e1ebd766a6636bb605ba401c45c1650eac" +checksum = "75129e1dc5000bfbaa9fee9d1b21f974f9fbad9daec557a521ee6e080825f6e8" dependencies = [ "indexmap", "serde", @@ -5251,9 +5253,9 @@ dependencies = [ [[package]] name = "toml_parser" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97200572db069e74c512a14117b296ba0a80a30123fbbb5aa1f4a348f639ca30" +checksum = "b551886f449aa90d4fe2bdaa9f4a2577ad2dde302c61ecf262d80b116db95c10" dependencies = [ "winnow", ] @@ -5291,7 +5293,7 @@ version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "bytes", "futures-util", "http", @@ -5334,7 +5336,7 @@ checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -5470,9 +5472,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.4" +version = "2.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" dependencies = [ "form_urlencoded", "idna", @@ -5497,7 +5499,7 @@ dependencies = [ "flate2", "fontdb 0.18.0", "imagesize", - "kurbo 0.11.2", + "kurbo 0.11.3", "log", "pico-args", "roxmltree", @@ -5593,7 +5595,7 @@ dependencies = [ "log", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "wasm-bindgen-shared", ] @@ -5628,7 +5630,7 @@ checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -5659,13 +5661,13 @@ dependencies = [ [[package]] name = "wayland-backend" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe770181423e5fc79d3e2a7f4410b7799d5aab1de4372853de3c6aa13ca24121" +checksum = "673a33c33048a5ade91a6b139580fa174e19fb0d23f396dca9fa15f2e1e49b35" dependencies = [ "cc", "downcast-rs", - "rustix 0.38.44", + "rustix 1.0.8", "scoped-tls", "smallvec", "wayland-sys", @@ -5673,12 +5675,12 @@ dependencies = [ [[package]] name = "wayland-client" -version = "0.31.10" +version = "0.31.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978fa7c67b0847dbd6a9f350ca2569174974cd4082737054dbb7fbb79d7d9a61" +checksum = "c66a47e840dc20793f2264eb4b3e4ecb4b75d91c0dd4af04b456128e0bdd449d" dependencies = [ - "bitflags 2.9.1", - "rustix 0.38.44", + "bitflags 2.9.3", + "rustix 1.0.8", "wayland-backend", "wayland-scanner", ] @@ -5689,29 +5691,29 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "cursor-icon", "wayland-backend", ] [[package]] name = "wayland-cursor" -version = "0.31.10" +version = "0.31.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a65317158dec28d00416cb16705934070aef4f8393353d41126c54264ae0f182" +checksum = "447ccc440a881271b19e9989f75726d60faa09b95b0200a9b7eb5cc47c3eeb29" dependencies = [ - "rustix 0.38.44", + "rustix 1.0.8", "wayland-client", "xcursor", ] [[package]] name = "wayland-protocols" -version = "0.32.8" +version = "0.32.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "779075454e1e9a521794fed15886323ea0feda3f8b0fc1390f5398141310422a" +checksum = "efa790ed75fbfd71283bd2521a1cfdc022aabcc28bdcff00851f9e4ae88d9901" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "wayland-backend", "wayland-client", "wayland-scanner", @@ -5719,11 +5721,11 @@ dependencies = [ [[package]] name = "wayland-protocols-plasma" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fd38cdad69b56ace413c6bcc1fbf5acc5e2ef4af9d5f8f1f9570c0c83eae175" +checksum = "a07a14257c077ab3279987c4f8bb987851bf57081b93710381daea94f2c2c032" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "wayland-backend", "wayland-client", "wayland-protocols", @@ -5732,11 +5734,11 @@ dependencies = [ [[package]] name = "wayland-protocols-wlr" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cb6cdc73399c0e06504c437fe3cf886f25568dd5454473d565085b36d6a8bbf" +checksum = "efd94963ed43cf9938a090ca4f7da58eb55325ec8200c3848963e98dc25b78ec" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "wayland-backend", "wayland-client", "wayland-protocols", @@ -5745,9 +5747,9 @@ dependencies = [ [[package]] name = "wayland-scanner" -version = "0.31.6" +version = "0.31.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "896fdafd5d28145fce7958917d69f2fd44469b1d4e861cb5961bcbeebc6d1484" +checksum = "54cb1e9dc49da91950bdfd8b848c49330536d9d1fb03d4bfec8cae50caa50ae3" dependencies = [ "proc-macro2", "quick-xml", @@ -5756,9 +5758,9 @@ dependencies = [ [[package]] name = "wayland-sys" -version = "0.31.6" +version = "0.31.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbcebb399c77d5aa9fa5db874806ee7b4eba4e73650948e8f93963f128896615" +checksum = "34949b42822155826b41db8e5d0c1be3a2bd296c747577a43a3e6daefc296142" dependencies = [ "dlib", "log", @@ -5834,7 +5836,7 @@ checksum = "28b94525fc99ba9e5c9a9e24764f2bc29bad0911a7446c12f446a8277369bf3a" dependencies = [ "arrayvec", "bit-vec", - "bitflags 2.9.1", + "bitflags 2.9.3", "cfg_aliases 0.1.1", "codespan-reporting", "indexmap", @@ -5862,7 +5864,7 @@ dependencies = [ "arrayvec", "ash", "bit-set", - "bitflags 2.9.1", + "bitflags 2.9.3", "block", "cfg_aliases 0.1.1", "core-graphics-types 0.1.3", @@ -5903,7 +5905,7 @@ version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b671ff9fb03f78b46ff176494ee1ebe7d603393f42664be55b64dc8d53969805" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "js-sys", "web-sys", ] @@ -5932,11 +5934,11 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +checksum = "0978bf7171b3d90bac376700cb56d606feb40f251a475a5d6634613564460b22" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -6019,7 +6021,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -6030,7 +6032,7 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -6099,15 +6101,6 @@ dependencies = [ "windows-targets 0.42.2", ] -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - [[package]] name = "windows-sys" version = "0.52.0" @@ -6132,7 +6125,7 @@ version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets 0.53.2", + "windows-targets 0.53.3", ] [[package]] @@ -6183,10 +6176,11 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.2" +version = "0.53.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" dependencies = [ + "windows-link", "windows_aarch64_gnullvm 0.53.0", "windows_aarch64_msvc 0.53.0", "windows_i686_gnu 0.53.0", @@ -6409,14 +6403,14 @@ checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" [[package]] name = "winit" -version = "0.30.11" +version = "0.30.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4409c10174df8779dc29a4788cac85ed84024ccbc1743b776b21a520ee1aaf4" +checksum = "c66d4b9ed69c4009f6321f762d6e61ad8a2389cd431b97cb1e146812e9e6c732" dependencies = [ "ahash 0.8.12", "android-activity", "atomic-waker", - "bitflags 2.9.1", + "bitflags 2.9.3", "block2 0.5.1", "bytemuck", "calloop", @@ -6461,9 +6455,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.12" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" +checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" dependencies = [ "memchr", ] @@ -6492,7 +6486,7 @@ version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", ] [[package]] @@ -6555,7 +6549,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "dlib", "log", "once_cell", @@ -6612,7 +6606,7 @@ checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "synstructure", ] @@ -6656,9 +6650,9 @@ dependencies = [ [[package]] name = "zbus" -version = "5.9.0" +version = "5.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bb4f9a464286d42851d18a605f7193b8febaf5b0919d71c6399b7b26e5b0aad" +checksum = "67a073be99ace1adc48af593701c8015cd9817df372e14a1a6b0ee8f8bf043be" dependencies = [ "async-broadcast", "async-executor", @@ -6680,11 +6674,11 @@ dependencies = [ "serde_repr", "tracing", "uds_windows", - "windows-sys 0.59.0", + "windows-sys 0.60.2", "winnow", - "zbus_macros 5.9.0", + "zbus_macros 5.10.0", "zbus_names 4.2.0", - "zvariant 5.6.0", + "zvariant 5.7.0", ] [[package]] @@ -6696,23 +6690,23 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "zvariant_utils 2.1.0", ] [[package]] name = "zbus_macros" -version = "5.9.0" +version = "5.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef9859f68ee0c4ee2e8cde84737c78e3f4c54f946f2a38645d0d4c7a95327659" +checksum = "0e80cd713a45a49859dcb648053f63265f4f2851b6420d47a958e5697c68b131" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "zbus_names 4.2.0", - "zvariant 5.6.0", - "zvariant_utils 3.2.0", + "zvariant 5.7.0", + "zvariant_utils 3.2.1", ] [[package]] @@ -6735,7 +6729,7 @@ dependencies = [ "serde", "static_assertions", "winnow", - "zvariant 5.6.0", + "zvariant 5.7.0", ] [[package]] @@ -6761,7 +6755,7 @@ checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -6781,7 +6775,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "synstructure", ] @@ -6804,9 +6798,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.2" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" +checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" dependencies = [ "yoke", "zerofrom", @@ -6821,7 +6815,7 @@ checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -6848,17 +6842,17 @@ dependencies = [ [[package]] name = "zvariant" -version = "5.6.0" +version = "5.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d91b3680bb339216abd84714172b5138a4edac677e641ef17e1d8cb1b3ca6e6f" +checksum = "999dd3be73c52b1fccd109a4a81e4fcd20fab1d3599c8121b38d04e1419498db" dependencies = [ "endi", "enumflags2", "serde", "url", "winnow", - "zvariant_derive 5.6.0", - "zvariant_utils 3.2.0", + "zvariant_derive 5.7.0", + "zvariant_utils 3.2.1", ] [[package]] @@ -6870,21 +6864,21 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "zvariant_utils 2.1.0", ] [[package]] name = "zvariant_derive" -version = "5.6.0" +version = "5.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a8c68501be459a8dbfffbe5d792acdd23b4959940fc87785fb013b32edbc208" +checksum = "6643fd0b26a46d226bd90d3f07c1b5321fe9bb7f04673cb37ac6d6883885b68e" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.104", - "zvariant_utils 3.2.0", + "syn 2.0.106", + "zvariant_utils 3.2.1", ] [[package]] @@ -6895,19 +6889,18 @@ checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] name = "zvariant_utils" -version = "3.2.0" +version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16edfee43e5d7b553b77872d99bc36afdda75c223ca7ad5e3fbecd82ca5fc34" +checksum = "c6949d142f89f6916deca2232cf26a8afacf2b9fdc35ce766105e104478be599" dependencies = [ "proc-macro2", "quote", "serde", - "static_assertions", - "syn 2.0.104", + "syn 2.0.106", "winnow", ] diff --git a/Cargo.toml b/Cargo.toml index ba545074..98a243e5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,7 +38,7 @@ strip = true [dependencies] pcap = "2.3.0" -etherparse = "0.18.0" +etherparse = "0.19.0" chrono = { version = "0.4.41", default-features = false, features = ["clock"] } plotters = { version = "0.3.7", default-features = false, features = ["area_series", "line_series"] } iced = { version = "0.13.1", features = ["tokio", "svg", "advanced", "lazy", "image"] } @@ -47,39 +47,39 @@ maxminddb = "0.26.0" confy = "1.0.0" serde = { version = "1.0.219", default-features = false, features = ["derive"] } rodio = { version = "0.21.1", default-features = false, features = ["mp3", "playback"] } -dns-lookup = "2.0.4" -toml = "0.9.2" +dns-lookup = "3.0.0" +toml = "0.9.5" ctrlc = { version = "3.4.7", features = ["termination"] } rfd = "0.15.4" -phf = "0.12.1" -phf_shared = "0.12.1" +phf = "0.13.1" +phf_shared = "0.13.1" splines = "5.0.0" -clap = { version = "4.5.41", features = ["derive"] } -tokio = { version = "1.46.1", features = ["macros"] } +clap = { version = "4.5.46", features = ["derive"] } +tokio = { version = "1.47.1", features = ["macros"] } async-channel = "2.5.0" [target.'cfg(windows)'.dependencies] gag = "1.0.0" [target.'cfg(not(target_arch = "powerpc64"))'.dependencies] -reqwest = { version = "0.12.22", default-features = false, features = ["json", "rustls-tls"] } +reqwest = { version = "0.12.23", default-features = false, features = ["json", "rustls-tls"] } [target.'cfg(target_arch = "powerpc64")'.dependencies] -reqwest = { version = "0.12.22", features = ["json"] } +reqwest = { version = "0.12.23", features = ["json"] } #─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── [dev-dependencies] serde_test = "1.0.177" -rstest = "0.25.0" +rstest = "0.26.1" serial_test = { version = "3.2.0", default-features = false } #─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── [build-dependencies] -phf_codegen = "0.12.1" -phf_shared = "0.12.1" -rustrict = { version = "0.7.35", default-features = false, features = ["censor"] } +phf_codegen = "0.13.1" +phf_shared = "0.13.1" +rustrict = { version = "0.7.36", default-features = false, features = ["censor"] } [target."cfg(windows)".build-dependencies] winres = "0.1.12" diff --git a/README.md b/README.md index 524e1d0f..b3e95376 100644 --- a/README.md +++ b/README.md @@ -59,9 +59,33 @@ ## _Support Sniffnet's development_ 💖 ## Download -| Windows | macOS | Linux (.deb) | Linux (.rpm) | -|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:| -|         [64‑bit](https://github.com/GyulyVGC/sniffnet/releases/latest/download/Sniffnet_Windows_64-bit.msi) \| [32‑bit](https://github.com/GyulyVGC/sniffnet/releases/latest/download/Sniffnet_Windows_32-bit.msi)         | [Intel](https://github.com/GyulyVGC/sniffnet/releases/latest/download/Sniffnet_macOS_Intel.dmg) \| [Apple silicon](https://github.com/GyulyVGC/sniffnet/releases/latest/download/Sniffnet_macOS_AppleSilicon.dmg) | [amd64](https://github.com/GyulyVGC/sniffnet/releases/latest/download/Sniffnet_LinuxDEB_amd64.deb) \| [arm64](https://github.com/GyulyVGC/sniffnet/releases/latest/download/Sniffnet_LinuxDEB_arm64.deb) \| [i386](https://github.com/GyulyVGC/sniffnet/releases/latest/download/Sniffnet_LinuxDEB_i386.deb) \| [armhf](https://github.com/GyulyVGC/sniffnet/releases/latest/download/Sniffnet_LinuxDEB_armhf.deb) |         [x86_64](https://github.com/GyulyVGC/sniffnet/releases/latest/download/Sniffnet_LinuxRPM_x86_64.rpm) \| [aarch64](https://github.com/GyulyVGC/sniffnet/releases/latest/download/Sniffnet_LinuxRPM_aarch64.rpm)         | +
18601673727
18601673727

🌍
ADS Fund
ADS Fund

💵
Abdullah
Abdullah

🤔 🖋
Adriano
Adriano

🤔
Aguacero 🌧️
Aguacero 🌧️

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

🌍
Alexandr Shashkin
Alexandr Shashkin

🐛
AmadeusGraves
AmadeusGraves

🌍
AlleM43
AlleM43

📦
AmadeusGraves
AmadeusGraves

🌍
Angelos Bousis
Angelos Bousis

🌍
Antoine Colombier
Antoine Colombier

⚠️ 🌍
Aris Konstantoulas
Aris Konstantoulas

🌍
BugsQuanti
BugsQuanti

🌍
Charpy
Charpy

🌍
Christoph Wanja
Christoph Wanja

💵
Colin Delahunty
Colin Delahunty

⚠️
Cornelius Roemer
Cornelius Roemer

🤔
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

📖 📦
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

💵
Jan Walter
Jan Walter

💵
Jauder Ho
Jauder Ho

🚇
Joshua Megnauth
Joshua Megnauth

💻 🎨
Julian Schmid
Julian Schmid

💻 🤔
LiChenG-P
LiChenG-P

💻
Liam OBrien
Liam OBrien

🤔
Limdongju
Limdongju

🌍
Lion Rayonnant
Lion Rayonnant

🌍
Ludwig Stecher
Ludwig Stecher

🤔 💻
Marc Gavilán
Marc Gavilán

🌍
Marco Cadetg
Marco Cadetg

📦
Matthias Braun
Matthias Braun

📖
Michel Hansma
Michel Hansma

🎨 ️️️️♿️
Marc Gavilán
Marc Gavilán

🌍
Marco Cadetg
Marco Cadetg

📦
Matt Seng
Matt Seng

💵
Matthias Braun
Matthias Braun

📖
Michel Hansma
Michel Hansma

🎨 ️️️️♿️
Morgan Hill
Morgan Hill

🛡️
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

🌍
Ron
Ron

🤔
Safaraliev Maxim
Safaraliev Maxim

🌍
Shawn Yeager
Shawn Yeager

💵
SignPath GmbH
SignPath GmbH

📦
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

🌍
Ylva
Ylva

🌍
ZEROF
ZEROF

💵
ZeroDot1
ZeroDot1

🎨 ️️️️♿️
clr
clr

📖 🌍
ervinpopescu
ervinpopescu

🌍
glitsj16
glitsj16

📦
guilherme-demarchi
guilherme-demarchi

🌍
hirotake111
hirotake111

🌍
islameehassan
islameehassan

💻
louis-ym4
louis-ym4

🎨
luca3s
luca3s

🌍
pia
pia

🌍
pia
pia

🌍
pin
pin

📦
shu-kitamura
shu-kitamura

💻
shu-kitamura
shu-kitamura

💻 🌍
starccy
starccy

💻
tiansheng li
tiansheng li

💵
vtiinanen
vtiinanen

🌍
yossarian
yossarian

🌍
陈寒彤
陈寒彤

🌍
+ + + + + + + + + + + + +
+ Windows + + 64-bit | 32-bit +
+ macOS + + Intel | Apple silicon +
+ Linux + + DEB: amd64 | arm64 | i386 | armhf
+ RPM: x86_64 | aarch64 +
Links in the table above will download the latest version of Sniffnet directly from [GitHub releases](https://github.com/GyulyVGC/sniffnet/releases).
Not what you're looking for? Check out [alternative installation methods](https://github.com/GyulyVGC/sniffnet/wiki/Alternative-installation-methods). @@ -136,6 +160,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) +- Free code signing for Windows Installer is provided by [SignPath.io](https://about.signpath.io/), certificate by [SignPath Foundation](https://signpath.org/) - [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! diff --git a/giscus.json b/giscus.json new file mode 100644 index 00000000..80f1a31a --- /dev/null +++ b/giscus.json @@ -0,0 +1,5 @@ +{ + "origins": ["https://sniffnet.net"], + "originsRegex": ["http://localhost:[0-9]+"], + "defaultCommentOrder": "oldest" +} \ No newline at end of file diff --git a/resources/fonts/full/subset_characters.txt b/resources/fonts/full/subset_characters.txt index 33c02891..64a7260b 100644 --- a/resources/fonts/full/subset_characters.txt +++ b/resources/fonts/full/subset_characters.txt @@ -150,6 +150,7 @@ z Ț ț ʼ +Ά Έ Ή Ό @@ -158,15 +159,20 @@ z Γ Δ Ε +Θ Κ +Λ Μ Ν Ξ +Ο Π Ρ Σ Τ +Υ Φ +Χ ά έ ή @@ -333,10 +339,8 @@ z ừ ử ữ -• … → -⏎ ─ │ ╭ @@ -352,6 +356,7 @@ z 」 あ い +え お か が @@ -454,6 +459,7 @@ z 义 也 了 +予 互 些 交 @@ -486,6 +492,7 @@ z 値 值 偏 +停 傳 僅 元 @@ -498,6 +505,7 @@ z 关 其 内 +再 出 击 分 @@ -525,6 +533,7 @@ z 原 参 參 +反 发 取 受 @@ -564,6 +573,7 @@ z 士 変 复 +外 多 夢 夹 @@ -608,6 +618,7 @@ z 從 志 总 +恢 息 您 情 @@ -695,9 +706,11 @@ z 檔 檢 欢 +止 正 此 每 +比 気 沒 没 @@ -711,6 +724,7 @@ z 深 淺 清 +済 渐 測 源 @@ -721,6 +735,7 @@ z 為 無 版 +特 率 現 生 @@ -754,6 +769,7 @@ z 篩 类 紀 +約 索 累 細 @@ -785,6 +801,7 @@ z 處 號 表 +被 裡 製 複 @@ -805,6 +822,7 @@ z 認 語 誤 +読 調 請 计 @@ -815,6 +833,7 @@ z 语 误 请 +读 資 贴 超 @@ -823,6 +842,7 @@ z 転 輸 输 +込 达 过 近 @@ -873,8 +893,10 @@ z 顯 页 项 +预 题 饋 +馈 體 黑 가 diff --git a/resources/fonts/subset/sarasa-mono-sc-bold.subset.ttf b/resources/fonts/subset/sarasa-mono-sc-bold.subset.ttf index 0e28e1ab..be965920 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 f33f1e57..71dde468 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/packaging/linux/AppImage/sniffnet.yml b/resources/packaging/linux/AppImage/sniffnet.yml new file mode 100644 index 00000000..da04ab96 --- /dev/null +++ b/resources/packaging/linux/AppImage/sniffnet.yml @@ -0,0 +1,9 @@ +app: Sniffnet +ingredients: + dist: bookworm + packages: + - libpcap0.8 + sources: + - deb https://deb.debian.org/debian stable main + debs: + - /target/REPLACE_TAG diff --git a/resources/repository/badges/linux.svg b/resources/repository/badges/linux.svg new file mode 100644 index 00000000..51830469 --- /dev/null +++ b/resources/repository/badges/linux.svg @@ -0,0 +1,13 @@ + + Linux + + + + + + Linux + + \ No newline at end of file diff --git a/resources/repository/badges/linux_deb.svg b/resources/repository/badges/linux_deb.svg deleted file mode 100644 index ee254170..00000000 --- a/resources/repository/badges/linux_deb.svg +++ /dev/null @@ -1 +0,0 @@ -LINUX (.DEB)LINUX (.DEB) \ No newline at end of file diff --git a/resources/repository/badges/linux_rpm.svg b/resources/repository/badges/linux_rpm.svg deleted file mode 100644 index b5688b2a..00000000 --- a/resources/repository/badges/linux_rpm.svg +++ /dev/null @@ -1 +0,0 @@ -LINUX (.RPM)LINUX (.RPM) \ No newline at end of file diff --git a/resources/repository/badges/macos.svg b/resources/repository/badges/macos.svg index 865f992c..f046a790 100644 --- a/resources/repository/badges/macos.svg +++ b/resources/repository/badges/macos.svg @@ -1 +1,13 @@ -MACOSMACOS \ No newline at end of file + + macOS + + + + + + macOS + + \ No newline at end of file diff --git a/resources/repository/badges/windows.svg b/resources/repository/badges/windows.svg index e877fa7c..40c1eacd 100644 --- a/resources/repository/badges/windows.svg +++ b/resources/repository/badges/windows.svg @@ -1 +1,13 @@ -WINDOWSWINDOWS \ No newline at end of file + + Windows + + + + + + Windows + + \ No newline at end of file diff --git a/resources/test/ipinfo_asn_sample.mmdb b/resources/test/ipinfo_asn_sample.mmdb index 6e86ae38..b1e36399 100644 Binary files a/resources/test/ipinfo_asn_sample.mmdb and b/resources/test/ipinfo_asn_sample.mmdb differ diff --git a/resources/test/ipinfo_country_asn_sample.mmdb b/resources/test/ipinfo_country_asn_sample.mmdb deleted file mode 100644 index b68cd28d..00000000 Binary files a/resources/test/ipinfo_country_asn_sample.mmdb and /dev/null differ diff --git a/resources/test/ipinfo_country_sample.mmdb b/resources/test/ipinfo_country_sample.mmdb deleted file mode 100644 index bbb4987c..00000000 Binary files a/resources/test/ipinfo_country_sample.mmdb and /dev/null differ diff --git a/resources/test/ipinfo_lite_sample.mmdb b/resources/test/ipinfo_lite_sample.mmdb new file mode 100644 index 00000000..da15dab2 Binary files /dev/null and b/resources/test/ipinfo_lite_sample.mmdb differ diff --git a/resources/test/ipinfo_location_sample.mmdb b/resources/test/ipinfo_location_sample.mmdb new file mode 100644 index 00000000..aee65ad5 Binary files /dev/null and b/resources/test/ipinfo_location_sample.mmdb differ diff --git a/src/chart/manage_chart_data.rs b/src/chart/manage_chart_data.rs index 27558ec3..f194672e 100644 --- a/src/chart/manage_chart_data.rs +++ b/src/chart/manage_chart_data.rs @@ -1,7 +1,7 @@ -use splines::{Interpolation, Key, Spline}; - use crate::TrafficChart; +use crate::networking::types::data_representation::DataRepr; use crate::networking::types::info_traffic::InfoTraffic; +use splines::{Interpolation, Key, Spline}; impl TrafficChart { pub fn update_charts_data(&mut self, info_traffic_msg: &InfoTraffic, no_more_packets: bool) { @@ -16,13 +16,21 @@ pub fn update_charts_data(&mut self, info_traffic_msg: &InfoTraffic, no_more_pac self.ticks += 1; #[allow(clippy::cast_precision_loss)] - let out_bytes_entry = -(info_traffic_msg.tot_data_info.outgoing_bytes() as f32); + let out_bytes_entry = -(info_traffic_msg + .tot_data_info + .outgoing_data(DataRepr::Bytes) as f32); #[allow(clippy::cast_precision_loss)] - let in_bytes_entry = info_traffic_msg.tot_data_info.incoming_bytes() as f32; + let in_bytes_entry = info_traffic_msg + .tot_data_info + .incoming_data(DataRepr::Bytes) as f32; #[allow(clippy::cast_precision_loss)] - let out_packets_entry = -(info_traffic_msg.tot_data_info.outgoing_packets() as f32); + let out_packets_entry = -(info_traffic_msg + .tot_data_info + .outgoing_data(DataRepr::Packets) as f32); #[allow(clippy::cast_precision_loss)] - let in_packets_entry = info_traffic_msg.tot_data_info.incoming_packets() as f32; + let in_packets_entry = info_traffic_msg + .tot_data_info + .incoming_data(DataRepr::Packets) as f32; let out_bytes_point = (tot_seconds, out_bytes_entry); let in_bytes_point = (tot_seconds, in_bytes_entry); @@ -145,10 +153,10 @@ fn reduce_all_time_data(all_time: &mut Vec<(f32, f32)>) { while all_time.len() > 150 { let mut new_vec = Vec::new(); all_time.iter().enumerate().for_each(|(i, (x, y))| { - if i % 2 == 0 { - if let Some(next) = all_time.get(i + 1) { - new_vec.push((*x, (y + next.1) / 2.0)); - } + if i % 2 == 0 + && let Some(next) = all_time.get(i + 1) + { + new_vec.push((*x, (y + next.1) / 2.0)); } }); *all_time = new_vec; @@ -213,8 +221,9 @@ mod tests { use crate::chart::manage_chart_data::{ChartSeries, get_max, get_min}; use crate::networking::types::data_info::DataInfo; + use crate::networking::types::data_representation::DataRepr; use crate::utils::types::timestamp::Timestamp; - use crate::{ChartType, InfoTraffic, Language, StyleType, TrafficChart}; + use crate::{InfoTraffic, Language, StyleType, TrafficChart}; fn spline_from_vec(vec: Vec<(i32, i32)>) -> Spline { Spline::from_vec( @@ -310,7 +319,7 @@ fn test_chart_data_updates() { min_packets: -1000.0, max_packets: 21000.0, language: Language::default(), - chart_type: ChartType::Packets, + data_repr: DataRepr::Packets, style: StyleType::default(), thumbnail: false, is_live_capture: true, @@ -318,8 +327,6 @@ fn test_chart_data_updates() { no_more_packets: false, }; let mut info_traffic = InfoTraffic { - all_bytes: 0, - all_packets: 0, tot_data_info, dropped_packets: 0, ..Default::default() diff --git a/src/chart/types/chart_type.rs b/src/chart/types/chart_type.rs deleted file mode 100644 index 0e339435..00000000 --- a/src/chart/types/chart_type.rs +++ /dev/null @@ -1,21 +0,0 @@ -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, Serialize, Deserialize)] -pub enum ChartType { - Packets, - Bytes, -} - -impl ChartType { - pub(crate) const ALL: [ChartType; 2] = [ChartType::Bytes, ChartType::Packets]; - - pub fn get_label(&self, language: Language) -> &str { - match self { - ChartType::Packets => packets_translation(language), - ChartType::Bytes => bytes_translation(language), - } - } -} diff --git a/src/chart/types/donut_chart.rs b/src/chart/types/donut_chart.rs index b2f392b7..14566466 100644 --- a/src/chart/types/donut_chart.rs +++ b/src/chart/types/donut_chart.rs @@ -1,7 +1,6 @@ -use crate::chart::types::chart_type::ChartType; use crate::gui::styles::donut::Catalog; use crate::gui::styles::style_constants::{FONT_SIZE_FOOTER, FONT_SIZE_SUBTITLE}; -use crate::networking::types::byte_multiple::ByteMultiple; +use crate::networking::types::data_representation::DataRepr; use iced::alignment::{Horizontal, Vertical}; use iced::widget::canvas::path::Arc; use iced::widget::canvas::{Frame, Text}; @@ -10,10 +9,9 @@ use std::f32::consts; pub struct DonutChart { - chart_type: ChartType, + data_repr: DataRepr, incoming: u128, outgoing: u128, - filtered_out: u128, dropped: u128, font: Font, thumbnail: bool, @@ -21,19 +19,17 @@ pub struct DonutChart { impl DonutChart { fn new( - chart_type: ChartType, + data_repr: DataRepr, incoming: u128, outgoing: u128, - filtered_out: u128, dropped: u128, font: Font, thumbnail: bool, ) -> Self { Self { - chart_type, + data_repr, incoming, outgoing, - filtered_out, dropped, font, thumbnail, @@ -41,24 +37,19 @@ fn new( } fn total(&self) -> u128 { - self.incoming + self.outgoing + self.filtered_out + self.dropped + self.incoming + self.outgoing + self.dropped } fn title(&self) -> String { let total = self.total(); - if self.chart_type.eq(&ChartType::Bytes) { - ByteMultiple::formatted_string(total) - } else { - total.to_string() - } + self.data_repr.formatted_string(total) } - fn angles(&self) -> [(Radians, Radians); 4] { + fn angles(&self) -> [(Radians, Radians); 3] { #[allow(clippy::cast_precision_loss)] let mut values = [ self.incoming as f32, self.outgoing as f32, - self.filtered_out as f32, self.dropped as f32, ]; let total: f32 = values.iter().sum(); @@ -105,12 +96,7 @@ fn draw( let radius = (frame.width().min(frame.height()) / 2.0) * 0.9; let style = ::style(theme, &::default()); - let colors = [ - style.incoming, - style.outgoing, - style.filtered_out, - style.dropped, - ]; + let colors = [style.incoming, style.outgoing, style.dropped]; for ((start_angle, end_angle), color) in self.angles().into_iter().zip(colors) { let path = canvas::Path::new(|builder| { @@ -150,10 +136,9 @@ fn draw( } pub fn donut_chart( - chart_type: ChartType, + data_repr: DataRepr, incoming: u128, outgoing: u128, - filtered_out: u128, dropped: u128, font: Font, thumbnail: bool, @@ -164,13 +149,7 @@ pub fn donut_chart( Length::Fixed(110.0) }; iced::widget::canvas(DonutChart::new( - chart_type, - incoming, - outgoing, - filtered_out, - dropped, - font, - thumbnail, + data_repr, incoming, outgoing, dropped, font, thumbnail, )) .width(size) .height(size) diff --git a/src/chart/types/mod.rs b/src/chart/types/mod.rs index ca3f7e1b..c84d0fef 100644 --- a/src/chart/types/mod.rs +++ b/src/chart/types/mod.rs @@ -1,3 +1,2 @@ -pub mod chart_type; pub mod donut_chart; pub mod traffic_chart; diff --git a/src/chart/types/traffic_chart.rs b/src/chart/types/traffic_chart.rs index 835ff54c..ccb7efad 100644 --- a/src/chart/types/traffic_chart.rs +++ b/src/chart/types/traffic_chart.rs @@ -15,24 +15,25 @@ use crate::gui::styles::style_constants::CHARTS_LINE_BORDER; use crate::gui::styles::types::palette::to_rgb_color; use crate::gui::types::message::Message; +use crate::networking::types::data_representation::DataRepr; use crate::networking::types::traffic_direction::TrafficDirection; use crate::translations::translations::{incoming_translation, outgoing_translation}; use crate::utils::error_logger::{ErrorLogger, Location}; use crate::utils::formatted_strings::{get_formatted_num_seconds, get_formatted_timestamp}; use crate::utils::types::timestamp::Timestamp; -use crate::{ByteMultiple, ChartType, Language, StyleType, location}; +use crate::{Language, StyleType, location}; /// Struct defining the chart to be displayed in gui run page pub struct TrafficChart { /// Current time interval number pub ticks: u32, - /// Sent bytes filtered and their time occurrence + /// Sent bytes and their time occurrence pub out_bytes: ChartSeries, - /// Received bytes filtered and their time occurrence + /// Received bytes and their time occurrence pub in_bytes: ChartSeries, - /// Sent packets filtered and their time occurrence + /// Sent packets and their time occurrence pub out_packets: ChartSeries, - /// Received packets filtered and their time occurrence + /// Received packets and their time occurrence pub in_packets: ChartSeries, /// Minimum number of bytes per time interval (computed on last 30 intervals) pub min_bytes: f32, @@ -45,7 +46,7 @@ pub struct TrafficChart { /// Language used for the chart legend pub language: Language, /// Packets or bytes - pub chart_type: ChartType, + pub data_repr: DataRepr, /// Style of the chart pub style: StyleType, /// Whether the chart is for the thumbnail page @@ -71,7 +72,7 @@ pub fn new(style: StyleType, language: Language) -> Self { min_packets: 0.0, max_packets: 0.0, language, - chart_type: ChartType::Bytes, + data_repr: DataRepr::Bytes, style, thumbnail: false, is_live_capture: true, @@ -80,7 +81,7 @@ pub fn new(style: StyleType, language: Language) -> Self { } } - pub fn view(&self) -> Element { + pub fn view(&self) -> Element<'_, Message, StyleType> { let x_labels = if self.is_live_capture || self.thumbnail { None } else { @@ -115,8 +116,8 @@ pub fn view(&self) -> Element { .into() } - pub fn change_kind(&mut self, kind: ChartType) { - self.chart_type = kind; + pub fn change_kind(&mut self, kind: DataRepr) { + self.data_repr = kind; } pub fn change_language(&mut self, language: Language) { @@ -169,9 +170,10 @@ fn x_axis_range(&self) -> Range { } fn y_axis_range(&self) -> Range { - let (min, max) = match self.chart_type { - ChartType::Packets => (self.min_packets, self.max_packets), - ChartType::Bytes => (self.min_bytes, self.max_bytes), + let (min, max) = match self.data_repr { + DataRepr::Packets => (self.min_packets, self.max_packets), + DataRepr::Bytes => (self.min_bytes, self.max_bytes), + DataRepr::Bits => (self.min_bytes * 8.0, self.max_bytes * 8.0), }; let fs = max - min; let gap = fs * 0.05; @@ -186,12 +188,12 @@ fn font<'a>(&self, size: f64) -> TextStyle<'a> { } fn spline_to_plot(&self, direction: TrafficDirection) -> &Spline { - match self.chart_type { - ChartType::Packets => match direction { + match self.data_repr { + DataRepr::Packets => match direction { TrafficDirection::Incoming => &self.in_packets.spline, TrafficDirection::Outgoing => &self.out_packets.spline, }, - ChartType::Bytes => match direction { + DataRepr::Bytes | DataRepr::Bits => match direction { TrafficDirection::Incoming => &self.in_bytes.spline, TrafficDirection::Outgoing => &self.out_bytes.spline, }, @@ -219,11 +221,16 @@ fn area_series( let color = self.series_color(direction); let alpha = self.style.get_extension().alpha_chart_badge; let spline = self.spline_to_plot(direction); + let multiplier = if self.data_repr == DataRepr::Bits { + 8.0 + } else { + 1.0 + }; let data = match spline.keys() { // if we have only one tick, we need to add a second point to draw the area - [k] => vec![(0.0, k.value), (0.1, k.value)], - _ => sample_spline(spline), + [k] => vec![(0.0, k.value * multiplier), (0.1, k.value * multiplier)], + _ => sample_spline(spline, multiplier), }; AreaSeries::new(data, 0.0, color.mix(alpha.into())) @@ -283,12 +290,10 @@ fn build_chart( .max_light_lines(0) .label_style(self.font(12.5)) .y_labels(min(5, y_labels)) - .y_label_formatter(if self.chart_type.eq(&ChartType::Packets) { - &|packets| packets.abs().to_string() - } else { + .y_label_formatter( #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] - &|bytes| ByteMultiple::formatted_string(bytes.abs() as u128) - }) + &|amount| self.data_repr.formatted_string(amount.abs() as u128), + ) .x_labels(min(6, x_labels)) .x_label_formatter( #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] @@ -331,7 +336,7 @@ fn build_chart( } } -fn sample_spline(spline: &Spline) -> Vec<(f32, f32)> { +fn sample_spline(spline: &Spline, multiplier: f32) -> Vec<(f32, f32)> { let pts = spline.len() * 10; // 10 samples per key let mut ret_val = Vec::new(); let len = spline.len(); @@ -348,7 +353,7 @@ fn sample_spline(spline: &Spline) -> Vec<(f32, f32)> { 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(); + let p = spline.clamped_sample(x).unwrap_or_default() * multiplier; ret_val.push((x, p)); } ret_val @@ -402,7 +407,7 @@ fn test_spline_samples() { let eps = 0.001; let pts = spline.len() * 10; - let samples = sample_spline(&spline); + let samples = sample_spline(&spline, 1.0); assert_eq!(samples.len(), pts); let delta = samples[1].0 - samples[0].0; diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 0256167c..138a88cc 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -1,12 +1,11 @@ +use crate::SNIFFNET_LOWERCASE; +use crate::gui::types::conf::{CONF, Conf}; +use crate::gui::types::message::Message; +use crate::networking::types::capture_context::CaptureSourcePicklist; +use crate::utils::formatted_strings::APP_VERSION; use clap::Parser; use iced::{Task, window}; -use crate::CONFIGS; -use crate::Configs; -use crate::SNIFFNET_LOWERCASE; -use crate::gui::types::message::Message; -use crate::utils::formatted_strings::APP_VERSION; - #[derive(Parser, Debug)] #[command( name = SNIFFNET_LOWERCASE, @@ -16,7 +15,7 @@ )] struct Args { /// Start sniffing packets from the supplied network adapter - #[arg(short, long, value_name = "NAME", default_missing_value = CONFIGS.device.device_name.as_str(), num_args = 0..=1)] + #[arg(short, long, value_name = "NAME", default_missing_value = CONF.device.device_name.as_str(), num_args = 0..=1)] adapter: Option, #[cfg(all(windows, not(debug_assertions)))] /// Show the logs (stdout and stderr) of the most recent application run @@ -50,7 +49,7 @@ pub fn handle_cli_args() -> Task { } if args.restore_default { - if Configs::default().store().is_ok() { + if Conf::default().store().is_ok() { println!("Restored default settings"); } std::process::exit(0); @@ -60,7 +59,12 @@ pub fn handle_cli_args() -> Task { .map(Message::StartApp) .chain(Task::done(Message::Periodic)); if let Some(adapter) = args.adapter { + // TODO: check if this works once #653 is fixed + // currently the link type and device name aren't displayed properly when starting from CLI boot_task_chain = boot_task_chain + .chain(Task::done(Message::SetCaptureSource( + CaptureSourcePicklist::Device, + ))) .chain(Task::done(Message::DeviceSelection(adapter))) .chain(Task::done(Message::Start)); } @@ -72,21 +76,28 @@ pub fn handle_cli_args() -> Task { mod tests { use serial_test::serial; - use crate::configs::types::config_window::{PositionTuple, SizeTuple}; + use crate::gui::pages::types::running_page::RunningPage; + use crate::gui::pages::types::settings_page::SettingsPage; use crate::gui::styles::types::custom_palette::ExtraStyles; use crate::gui::styles::types::gradient_type::GradientType; + use crate::gui::types::conf::Conf; + use crate::gui::types::config_window::{PositionTuple, SizeTuple}; + use crate::gui::types::export_pcap::ExportPcap; + use crate::gui::types::filters::Filters; + use crate::gui::types::settings::Settings; + use crate::networking::types::capture_context::CaptureSourcePicklist; + use crate::networking::types::config_device::ConfigDevice; use crate::notifications::types::notifications::Notifications; - use crate::{ConfigDevice, ConfigSettings, ConfigWindow, Language, Sniffer, StyleType}; - - use super::*; + use crate::report::types::sort_type::SortType; + use crate::{ConfigWindow, Language, Sniffer, StyleType}; #[test] #[serial] fn test_restore_default_configs() { // initial configs stored are the default ones - assert_eq!(Configs::load(), Configs::default()); - let modified_configs = Configs { - settings: ConfigSettings { + assert_eq!(Conf::load(), Conf::default()); + let modified_conf = Conf { + settings: Settings { color_gradient: GradientType::Wild, language: Language::ZH, scale_factor: 0.65, @@ -111,19 +122,35 @@ fn test_restore_default_configs() { size: SizeTuple(452.0, 870.0), thumbnail_position: PositionTuple(20.0, 20.0), }, + capture_source_picklist: CaptureSourcePicklist::File, + report_sort_type: SortType::Ascending, + host_sort_type: SortType::Descending, + service_sort_type: SortType::Neutral, + filters: Filters { + bpf: "tcp".to_string(), + expanded: true, + }, + import_pcap_path: "whole_day.pcapng".to_string(), + export_pcap: ExportPcap { + enabled: true, + file_name: "sniffnet.pcap".to_string(), + directory: "home".to_string(), + }, + last_opened_setting: SettingsPage::General, + last_opened_page: RunningPage::Inspect, }; // we want to be sure that modified config is different from defaults - assert_ne!(Configs::default(), modified_configs); + assert_ne!(Conf::default(), modified_conf); //store modified configs - modified_configs.clone().store().unwrap(); + modified_conf.clone().store().unwrap(); // assert they've been stored - assert_eq!(Configs::load(), modified_configs); + assert_eq!(Conf::load(), modified_conf); // restore defaults - Configs::default().store().unwrap(); + Conf::default().store().unwrap(); // assert that defaults are stored - assert_eq!(Configs::load(), Configs::default()); + assert_eq!(Conf::load(), Conf::default()); // only needed because it will delete config files via its Drop implementation - Sniffer::new(Configs::default()); + Sniffer::new(Conf::default()); } } diff --git a/src/configs/mod.rs b/src/configs/mod.rs deleted file mode 100644 index cd408564..00000000 --- a/src/configs/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod types; diff --git a/src/configs/types/config_device.rs b/src/configs/types/config_device.rs deleted file mode 100644 index f33226d1..00000000 --- a/src/configs/types/config_device.rs +++ /dev/null @@ -1,86 +0,0 @@ -//! Module defining the `ConfigDevice` struct, which allows to save and reload -//! the application default configuration. - -use crate::networking::types::my_device::MyDevice; -#[cfg(not(test))] -use crate::utils::error_logger::{ErrorLogger, Location}; -#[cfg(not(test))] -use crate::{SNIFFNET_LOWERCASE, location}; -use pcap::{Device, DeviceFlags}; -use serde::{Deserialize, Serialize}; - -#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)] -pub struct ConfigDevice { - pub device_name: String, -} - -impl Default for ConfigDevice { - fn default() -> Self { - Self { - device_name: Device::lookup() - .unwrap_or(None) - .unwrap_or_else(|| Device { - name: String::new(), - desc: None, - addresses: vec![], - flags: DeviceFlags::empty(), - }) - .name, - } - } -} - -impl ConfigDevice { - const FILE_NAME: &'static str = "device"; - - #[cfg(not(test))] - pub fn load() -> Self { - if let Ok(device) = confy::load::(SNIFFNET_LOWERCASE, Self::FILE_NAME) { - device - } else { - let _ = confy::store(SNIFFNET_LOWERCASE, Self::FILE_NAME, ConfigDevice::default()) - .log_err(location!()); - ConfigDevice::default() - } - } - - #[cfg(not(test))] - pub fn store(self) -> Result<(), confy::ConfyError> { - confy::store(SNIFFNET_LOWERCASE, Self::FILE_NAME, self).log_err(location!()) - } - - pub fn to_my_device(&self) -> MyDevice { - for device in Device::list().unwrap_or_default() { - if device.name.eq(&self.device_name) { - return MyDevice::from_pcap_device(device); - } - } - let standard_device = Device::lookup().unwrap_or(None).unwrap_or_else(|| Device { - name: String::new(), - desc: None, - addresses: vec![], - flags: DeviceFlags::empty(), - }); - MyDevice::from_pcap_device(standard_device) - } -} - -#[cfg(test)] -mod tests { - use crate::ConfigDevice; - - impl ConfigDevice { - pub fn test_path() -> String { - format!("{}/{}.toml", env!("CARGO_MANIFEST_DIR"), Self::FILE_NAME) - } - - pub fn load() -> Self { - confy::load_path::(ConfigDevice::test_path()) - .unwrap_or_else(|_| ConfigDevice::default()) - } - - pub fn store(self) -> Result<(), confy::ConfyError> { - confy::store_path(ConfigDevice::test_path(), self) - } - } -} diff --git a/src/configs/types/config_settings.rs b/src/configs/types/config_settings.rs deleted file mode 100644 index 284b674b..00000000 --- a/src/configs/types/config_settings.rs +++ /dev/null @@ -1,84 +0,0 @@ -//! Module defining the `ConfigSettings` struct, which allows to save and reload -//! the application default configuration. - -use serde::{Deserialize, Serialize}; - -use crate::gui::styles::types::gradient_type::GradientType; -use crate::notifications::types::notifications::Notifications; -#[cfg(not(test))] -use crate::utils::error_logger::{ErrorLogger, Location}; -use crate::{Language, StyleType}; -#[cfg(not(test))] -use crate::{SNIFFNET_LOWERCASE, location}; - -#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)] -pub struct ConfigSettings { - pub color_gradient: GradientType, - pub language: Language, - pub scale_factor: f64, - pub mmdb_country: String, - pub mmdb_asn: String, - pub style_path: String, - pub notifications: Notifications, - // StyleType should be last in order to deserialize as a table properly - pub style: StyleType, -} - -impl ConfigSettings { - const FILE_NAME: &'static str = "settings"; - - #[cfg(not(test))] - pub fn load() -> Self { - if let Ok(settings) = confy::load::(SNIFFNET_LOWERCASE, Self::FILE_NAME) { - settings - } else { - let _ = confy::store( - SNIFFNET_LOWERCASE, - Self::FILE_NAME, - ConfigSettings::default(), - ) - .log_err(location!()); - ConfigSettings::default() - } - } - - #[cfg(not(test))] - pub fn store(self) -> Result<(), confy::ConfyError> { - confy::store(SNIFFNET_LOWERCASE, Self::FILE_NAME, self).log_err(location!()) - } -} - -impl Default for ConfigSettings { - fn default() -> Self { - ConfigSettings { - color_gradient: GradientType::default(), - language: Language::default(), - scale_factor: 1.0, - mmdb_country: String::new(), - mmdb_asn: String::new(), - style_path: String::new(), - notifications: Notifications::default(), - style: StyleType::default(), - } - } -} - -#[cfg(test)] -mod tests { - use crate::ConfigSettings; - - impl ConfigSettings { - pub fn test_path() -> String { - format!("{}/{}.toml", env!("CARGO_MANIFEST_DIR"), Self::FILE_NAME) - } - - pub fn load() -> Self { - confy::load_path::(ConfigSettings::test_path()) - .unwrap_or_else(|_| ConfigSettings::default()) - } - - pub fn store(self) -> Result<(), confy::ConfyError> { - confy::store_path(ConfigSettings::test_path(), self) - } - } -} diff --git a/src/configs/types/configs.rs b/src/configs/types/configs.rs deleted file mode 100644 index db66fd88..00000000 --- a/src/configs/types/configs.rs +++ /dev/null @@ -1,30 +0,0 @@ -use crate::{ConfigDevice, ConfigSettings, ConfigWindow}; -use confy::ConfyError; - -pub static CONFIGS: std::sync::LazyLock = std::sync::LazyLock::new(Configs::load); - -#[derive(Default, Clone, PartialEq, Debug)] -pub struct Configs { - pub settings: ConfigSettings, - pub device: ConfigDevice, - pub window: ConfigWindow, -} - -impl Configs { - /// This should only be used directly to load fresh configs; - /// use `CONFIGS` instead to access the initial instance - pub fn load() -> Self { - Configs { - settings: ConfigSettings::load(), - device: ConfigDevice::load(), - window: ConfigWindow::load(), - } - } - - pub fn store(self) -> Result<(), ConfyError> { - self.settings.store()?; - self.device.store()?; - self.window.store()?; - Ok(()) - } -} diff --git a/src/configs/types/mod.rs b/src/configs/types/mod.rs deleted file mode 100644 index 76d46b33..00000000 --- a/src/configs/types/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod config_device; -pub mod config_settings; -pub mod config_window; -pub mod configs; diff --git a/src/gui/components/header.rs b/src/gui/components/header.rs index f90cca1e..5f0b980a 100644 --- a/src/gui/components/header.rs +++ b/src/gui/components/header.rs @@ -5,28 +5,27 @@ use iced::widget::{Container, Row, Space, Text, Tooltip, button, horizontal_space}; use iced::{Alignment, Font, Length}; -use crate::configs::types::config_settings::ConfigSettings; use crate::gui::components::tab::notifications_badge; -use crate::gui::pages::types::running_page::RunningPage; use crate::gui::pages::types::settings_page::SettingsPage; use crate::gui::sniffer::Sniffer; use crate::gui::styles::button::ButtonType; use crate::gui::styles::container::ContainerType; use crate::gui::styles::types::gradient_type::GradientType; use crate::gui::types::message::Message; +use crate::gui::types::settings::Settings; use crate::translations::translations::{quit_analysis_translation, settings_translation}; use crate::translations::translations_3::thumbnail_mode_translation; use crate::utils::types::icon::Icon; use crate::{Language, SNIFFNET_TITLECASE, StyleType}; -pub fn header(sniffer: &Sniffer) -> Container { +pub fn header(sniffer: &Sniffer) -> Container<'_, Message, StyleType> { let thumbnail = sniffer.thumbnail; - let ConfigSettings { + let Settings { style, language, color_gradient, .. - } = sniffer.configs.settings; + } = sniffer.conf.settings; let font = style.get_extension().font; if thumbnail { @@ -41,8 +40,8 @@ pub fn header(sniffer: &Sniffer) -> Container { ); } - let last_opened_setting = sniffer.last_opened_setting; - let is_running = sniffer.running_page.ne(&RunningPage::Init); + let last_opened_setting = sniffer.conf.last_opened_setting; + let is_running = sniffer.running_page.is_some(); let logo = Icon::Sniffnet .to_text() diff --git a/src/gui/components/tab.rs b/src/gui/components/tab.rs index a70bca4a..cbf5e033 100644 --- a/src/gui/components/tab.rs +++ b/src/gui/components/tab.rs @@ -99,12 +99,12 @@ fn new_page_tab<'a>( .align_y(alignment::Alignment::Center), ); - if let Some(num) = unread { - if num > 0 { - content = content - .push(Space::with_width(7)) - .push(notifications_badge(font_headers, num)); - } + if let Some(num) = unread + && num > 0 + { + content = content + .push(Space::with_width(7)) + .push(notifications_badge(font_headers, num)); } content = content.push(horizontal_space()); diff --git a/src/gui/pages/connection_details_page.rs b/src/gui/pages/connection_details_page.rs index 3e479ee3..f26a53d4 100644 --- a/src/gui/pages/connection_details_page.rs +++ b/src/gui/pages/connection_details_page.rs @@ -8,6 +8,7 @@ use crate::gui::styles::text::TextType; use crate::gui::styles::types::gradient_type::GradientType; use crate::gui::types::message::Message; +use crate::gui::types::settings::Settings; use crate::gui::types::timing_events::TimingEvents; use crate::networking::manage_packets::{ get_address_to_lookup, get_traffic_type, is_local_connection, is_my_address, @@ -15,6 +16,7 @@ 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::data_representation::DataRepr; use crate::networking::types::host::Host; use crate::networking::types::icmp_type::IcmpType; use crate::networking::types::info_address_port_pair::InfoAddressPortPair; @@ -33,7 +35,7 @@ }; use crate::utils::formatted_strings::{get_formatted_timestamp, get_socket_address}; use crate::utils::types::icon::Icon; -use crate::{ByteMultiple, ConfigSettings, Language, Protocol, Sniffer, StyleType}; +use crate::{Language, Protocol, Sniffer, StyleType}; use iced::alignment::Vertical; use iced::widget::scrollable::Direction; use iced::widget::tooltip::Position; @@ -44,17 +46,18 @@ pub fn connection_details_page( sniffer: &Sniffer, key: AddressPortPair, -) -> Container { +) -> Container<'_, Message, StyleType> { Container::new(page_content(sniffer, &key)) } fn page_content<'a>(sniffer: &Sniffer, key: &AddressPortPair) -> Container<'a, Message, StyleType> { - let ConfigSettings { + let Settings { style, language, color_gradient, .. - } = sniffer.configs.settings; + } = sniffer.conf.settings; + let data_repr = sniffer.traffic_chart.data_repr; let font = style.get_extension().font; let font_headers = style.get_extension().font_headers; @@ -130,7 +133,7 @@ fn page_content<'a>(sniffer: &Sniffer, key: &AddressPortPair) -> Container<'a, M dest_col = dest_col.push(host_info_col); } - let col_info = col_info(key, &val, font, language); + let col_info = col_info(key, &val, data_repr, font, language); let content = assemble_widgets(col_info, source_col, dest_col); @@ -172,6 +175,7 @@ fn page_header<'a>( fn col_info<'a>( key: &AddressPortPair, val: &InfoAddressPortPair, + data_repr: DataRepr, font: Font, language: Language, ) -> Column<'a, Message, StyleType> { @@ -221,12 +225,13 @@ fn col_info<'a>( incoming_translation(language).to_lowercase() } ), - &format!( - "{}\n {} {}", - ByteMultiple::formatted_string(val.transmitted_bytes), - val.transmitted_packets, - packets_translation(language) - ), + &(data_repr.formatted_string(val.transmitted_data(data_repr)) + + if data_repr == DataRepr::Packets { + format!(" {}", packets_translation(language)) + } else { + String::new() + } + .as_ref()), font, )); @@ -295,9 +300,9 @@ fn get_local_tooltip<'a>( address_to_lookup: &IpAddr, key: &AddressPortPair, ) -> Tooltip<'a, Message, StyleType> { - let ConfigSettings { + let Settings { style, language, .. - } = sniffer.configs.settings; + } = sniffer.conf.settings; let local_address = if address_to_lookup.eq(&key.address1) { &key.address2 diff --git a/src/gui/pages/initial_page.rs b/src/gui/pages/initial_page.rs index eb39e06d..5294355d 100644 --- a/src/gui/pages/initial_page.rs +++ b/src/gui/pages/initial_page.rs @@ -2,18 +2,8 @@ //! //! It contains elements to select network adapter and traffic filters. -use std::collections::HashSet; use std::fmt::Write; -use iced::Length::FillPortion; -use iced::widget::scrollable::Direction; -use iced::widget::tooltip::Position; -use iced::widget::{ - Button, Checkbox, Column, Container, Row, Rule, Scrollable, Space, Text, TextInput, Tooltip, - button, center, -}; -use iced::{Alignment, Font, Length, Padding, alignment}; - use crate::gui::components::button::button_open_file; use crate::gui::sniffer::Sniffer; use crate::gui::styles::button::ButtonType; @@ -21,287 +11,171 @@ use crate::gui::styles::scrollbar::ScrollbarType; use crate::gui::styles::style_constants::{FONT_SIZE_SUBTITLE, FONT_SIZE_TITLE}; use crate::gui::styles::text::TextType; -use crate::gui::styles::text_input::TextInputType; use crate::gui::styles::types::gradient_type::GradientType; use crate::gui::types::export_pcap::ExportPcap; +use crate::gui::types::filters::Filters; use crate::gui::types::message::Message; -use crate::networking::types::capture_context::CaptureSource; -use crate::networking::types::filters::Filters; -use crate::networking::types::ip_collection::AddressCollection; -use crate::networking::types::port_collection::PortCollection; +use crate::gui::types::settings::Settings; +use crate::networking::types::capture_context::{CaptureSource, CaptureSourcePicklist}; use crate::translations::translations::{ - address_translation, addresses_translation, choose_adapters_translation, - ip_version_translation, protocol_translation, select_filters_translation, start_translation, + address_translation, addresses_translation, network_adapter_translation, start_translation, }; use crate::translations::translations_3::{ - directory_translation, export_capture_translation, file_name_translation, port_translation, + directory_translation, export_capture_translation, file_name_translation, }; -use crate::translations::translations_4::import_capture_translation; -use crate::utils::formatted_strings::{get_invalid_filters_string, get_path_termination_string}; +use crate::translations::translations_4::capture_file_translation; +use crate::translations::translations_5::{filter_traffic_translation, traffic_source_translation}; +use crate::utils::formatted_strings::get_path_termination_string; use crate::utils::types::file_info::FileInfo; use crate::utils::types::icon::Icon; -use crate::{ConfigSettings, IpVersion, Language, Protocol, StyleType}; +use crate::{Language, StyleType}; +use iced::Length::FillPortion; +use iced::widget::scrollable::Direction; +use iced::widget::{ + Button, Checkbox, Column, Container, PickList, Row, Scrollable, Space, Text, TextInput, button, + center, vertical_space, +}; +use iced::{Alignment, Font, Length, Padding, alignment}; /// Computes the body of gui initial page -pub fn initial_page(sniffer: &Sniffer) -> Container { - let ConfigSettings { +pub fn initial_page(sniffer: &Sniffer) -> Container<'_, Message, StyleType> { + let Settings { style, language, color_gradient, .. - } = sniffer.configs.settings; + } = sniffer.conf.settings; let font = style.get_extension().font; + let font_headers = style.get_extension().font_headers; - let col_adapter = get_col_adapter(sniffer, font); - let col_import_pcap = get_col_import_pcap( - language, - font, - &sniffer.capture_source, - &sniffer.import_pcap_path, - ); - let col_capture_source = Column::new().push(col_adapter).push(col_import_pcap); + let col_data_source = get_col_data_source(sniffer, font, language); - let ip_active = &sniffer.filters.ip_versions; - let col_ip_buttons = col_ip_buttons(ip_active, font, language); - - let protocol_active = &sniffer.filters.protocols; - let col_protocol_buttons = col_protocol_buttons(protocol_active, font, language); - - let address_active = &sniffer.filters.address_str; - let col_address_filter = col_address_input(address_active, font, language); - - let port_active = &sniffer.filters.port_str; - let col_port_filter = col_port_input(port_active, font, language); - - let filters_pane = Column::new() - .width(FillPortion(6)) - .padding(10) - .spacing(15) - .push( - select_filters_translation(language) - .font(font) - .class(TextType::Title) - .size(FONT_SIZE_TITLE), - ) - .push( - Row::new() - .spacing(20) - .push(col_ip_buttons) - .push(col_protocol_buttons), - ) - .push( - Row::new() - .spacing(20) - .push(col_address_filter) - .push(col_port_filter), - ) - .push(Rule::horizontal(40)) - .push(get_export_pcap_group( - &sniffer.capture_source, - &sniffer.export_pcap, + let col_checkboxes = Column::new() + .spacing(10) + .push(get_filters_group(&sniffer.conf.filters, font, language)) + .push_maybe(get_export_pcap_group_maybe( + sniffer.conf.capture_source_picklist, + &sniffer.conf.export_pcap, language, font, + )); + + let is_capture_source_consistent = sniffer.is_capture_source_consistent(); + let right_col = Column::new() + .width(FillPortion(1)) + .padding(10) + .push(Space::with_height(76)) + .push(col_checkboxes) + .push(vertical_space()) + .push(button_start( + font_headers, + language, + color_gradient, + is_capture_source_consistent, )) - .push( - Container::new(button_start( - font, - language, - color_gradient, - &sniffer.filters, - )) - .width(Length::Fill) - .height(Length::Fill) - .align_y(Alignment::Start) - .align_x(Alignment::Center), - ); + .push(vertical_space()); let body = Column::new().push(Space::with_height(5)).push( Row::new() - .push(col_capture_source) - .push(Space::with_width(30)) - .push(filters_pane), + .push(col_data_source) + .push(Space::with_width(15)) + .push(right_col), ); Container::new(body).height(Length::Fill) } -fn col_ip_buttons( - active_ip_filters: &HashSet, - font: Font, - language: Language, -) -> Column { - let mut buttons_row = Row::new().spacing(5).padding(Padding::ZERO.left(5)); - for option in IpVersion::ALL { - let is_active = active_ip_filters.contains(&option); - let check_symbol = if is_active { "✔" } else { "✘" }; - buttons_row = buttons_row.push( - Button::new( - Text::new(format!("{option} {check_symbol}")) - .align_x(Alignment::Center) - .align_y(Alignment::Center) - .font(font), - ) - .width(80) - .height(35) - .class(if is_active { - ButtonType::BorderedRoundSelected - } else { - ButtonType::BorderedRound - }) - .on_press(Message::IpVersionSelection(option, !is_active)), - ); - } - - Column::new() - .width(Length::Fill) - .spacing(7) - .push( - Text::new(ip_version_translation(language)) - .font(font) - .class(TextType::Subtitle) - .size(FONT_SIZE_SUBTITLE), - ) - .push(buttons_row) -} - -fn col_protocol_buttons( - active_protocol_filters: &HashSet, - font: Font, - language: Language, -) -> Column { - let mut buttons_row = Row::new().spacing(5).padding(Padding::ZERO.left(5)); - for option in Protocol::ALL { - let is_active = active_protocol_filters.contains(&option); - let check_symbol = if is_active { "✔" } else { "✘" }; - buttons_row = buttons_row.push( - Button::new( - Text::new(format!("{option} {check_symbol}")) - .align_x(Alignment::Center) - .align_y(Alignment::Center) - .font(font), - ) - .width(80) - .height(35) - .class(if is_active { - ButtonType::BorderedRoundSelected - } else { - ButtonType::BorderedRound - }) - .on_press(Message::ProtocolSelection(option, !is_active)), - ); - } - - Column::new() - .width(Length::Fill) - .spacing(7) - .push( - Text::new(protocol_translation(language)) - .font(font) - .class(TextType::Subtitle) - .size(FONT_SIZE_SUBTITLE), - ) - .push(buttons_row) -} - -fn col_address_input(value: &str, font: Font, language: Language) -> Column { - let is_error = if value.is_empty() { - false - } else { - AddressCollection::new(value).is_none() - }; - let input_row = Row::new().padding(Padding::ZERO.left(5)).push( - TextInput::new(AddressCollection::PLACEHOLDER_STR, value) - .padding([3, 5]) - .on_input(Message::AddressFilter) - .font(font) - .width(310) - .class(if is_error { - TextInputType::Error - } else { - TextInputType::Standard - }), - ); - - Column::new() - .width(Length::Fill) - .spacing(7) - .push( - Text::new(address_translation(language)) - .font(font) - .class(TextType::Subtitle) - .size(FONT_SIZE_SUBTITLE), - ) - .push(input_row) -} - -fn col_port_input(value: &str, font: Font, language: Language) -> Column { - let is_error = if value.is_empty() { - false - } else { - PortCollection::new(value).is_none() - }; - let input_row = Row::new().padding(Padding::ZERO.left(5)).push( - TextInput::new(PortCollection::PLACEHOLDER_STR, value) - .padding([3, 5]) - .on_input(Message::PortFilter) - .font(font) - .width(180) - .class(if is_error { - TextInputType::Error - } else { - TextInputType::Standard - }), - ); - - Column::new() - .width(Length::Fill) - .spacing(7) - .push( - Text::new(port_translation(language)) - .font(font) - .class(TextType::Subtitle) - .size(FONT_SIZE_SUBTITLE), - ) - .push(input_row) -} - -fn button_start( - font: Font, +fn button_start<'a>( + font_headers: Font, language: Language, color_gradient: GradientType, - filters: &Filters, -) -> Tooltip { - let mut content = button( - Icon::Rocket - .to_text() - .size(25) + is_capture_source_consistent: bool, +) -> Button<'a, Message, StyleType> { + button( + Text::new(start_translation(language)) + .font(font_headers) + .size(FONT_SIZE_TITLE) + .width(Length::Fill) .align_x(alignment::Alignment::Center) .align_y(alignment::Alignment::Center), ) - .padding(10) - .height(80) - .width(160) - .class(ButtonType::Gradient(color_gradient)); - - let mut tooltip = start_translation(language).to_string(); - //tooltip.push_str(" [⏎]"); - let mut position = Position::Top; - - if filters.are_valid() { - content = content.on_press(Message::Start); + .padding(20) + .width(Length::Fill) + .class(ButtonType::Gradient(color_gradient)) + .on_press_maybe(if is_capture_source_consistent { + Some(Message::Start) } else { - tooltip = get_invalid_filters_string(filters, language); - position = Position::FollowCursor; - } - - Tooltip::new(content, Text::new(tooltip).font(font), position) - .gap(5) - .class(ContainerType::Tooltip) + None + }) } -fn get_col_adapter(sniffer: &Sniffer, font: Font) -> Column { - let ConfigSettings { language, .. } = sniffer.configs.settings; +fn get_col_data_source( + sniffer: &Sniffer, + font: Font, + language: Language, +) -> Column<'_, Message, StyleType> { + let current_option = if sniffer.conf.capture_source_picklist == CaptureSourcePicklist::Device { + network_adapter_translation(language) + } else { + capture_file_translation(language) + }; + let picklist = PickList::new( + [ + network_adapter_translation(language), + capture_file_translation(language), + ], + Some(current_option), + move |option| { + if option == network_adapter_translation(language) { + Message::SetCaptureSource(CaptureSourcePicklist::Device) + } else { + Message::SetCaptureSource(CaptureSourcePicklist::File) + } + }, + ) + .padding([2, 7]) + .font(font); + let mut col = Column::new() + .align_x(Alignment::Center) + .padding(Padding::new(10.0).top(30)) + .spacing(30) + .height(Length::Fill) + .width(FillPortion(1)) + .push( + Row::new() + .spacing(10) + .push( + Text::new(traffic_source_translation(language)) + .font(font) + .class(TextType::Title) + .size(FONT_SIZE_TITLE), + ) + .push(picklist), + ); + + match &sniffer.conf.capture_source_picklist { + CaptureSourcePicklist::Device => { + col = col.push(get_col_adapter(sniffer, font, language)); + } + CaptureSourcePicklist::File => { + col = col.push(get_col_import_pcap( + language, + font, + &sniffer.capture_source, + &sniffer.conf.import_pcap_path, + )); + } + } + + col +} + +fn get_col_adapter( + sniffer: &Sniffer, + font: Font, + language: Language, +) -> Column<'_, Message, StyleType> { let mut dev_str_list = vec![]; for my_dev in &sniffer.my_devices { let mut title = String::new(); @@ -342,16 +216,8 @@ fn get_col_adapter(sniffer: &Sniffer, font: Font) -> Column } Column::new() - .padding(10) .spacing(5) .height(Length::Fill) - .width(FillPortion(4)) - .push( - choose_adapters_translation(language) - .font(font) - .class(TextType::Title) - .size(FONT_SIZE_TITLE), - ) .push(if dev_str_list.is_empty() { Into::>::into(center( Icon::get_hourglass(sniffer.dots_pulse.0.len()).size(60), @@ -359,7 +225,7 @@ fn get_col_adapter(sniffer: &Sniffer, font: Font) -> Column } else { Scrollable::with_direction( dev_str_list.into_iter().fold( - Column::new().padding(13).spacing(5), + Column::new().padding(Padding::ZERO.right(13)).spacing(5), |scroll_adapters, (name, title, subtitle, addrs)| { let addrs_text = if addrs.is_empty() { None @@ -424,7 +290,7 @@ fn get_col_import_pcap<'a>( let content = Column::new() .width(Length::Fill) - .align_x(Alignment::Center) + .align_x(alignment::Alignment::Center) .spacing(5) .push(button_row); @@ -439,29 +305,59 @@ fn get_col_import_pcap<'a>( }) .on_press(Message::SetPcapImport(path.to_string())), ) - .padding(13); + .padding(Padding::ZERO.right(13)); - Column::new() - .padding(10) - .spacing(5) - .width(FillPortion(4)) - .push( - Text::new(import_capture_translation(language)) - .font(font) - .class(TextType::Title) - .size(FONT_SIZE_TITLE), - ) - .push(button) + Column::new().spacing(5).push(button) } -fn get_export_pcap_group<'a>( - cs: &CaptureSource, +fn get_filters_group<'a>( + filters: &Filters, + font: Font, + language: Language, +) -> Container<'a, Message, StyleType> { + let expanded = filters.expanded(); + let bpf = filters.bpf(); + + let caption = filter_traffic_translation(language); + let checkbox = Checkbox::new(caption, expanded) + .on_toggle(move |_| Message::ToggleFilters) + .size(18) + .font(font); + + let mut ret_val = Column::new().spacing(10).push(checkbox); + + if expanded { + let input = TextInput::new("", bpf) + .on_input(Message::BpfFilter) + .padding([2, 5]) + .font(font); + let inner_col = Column::new() + .spacing(10) + .padding(Padding::ZERO.left(26)) + .push( + Row::new() + .align_y(Alignment::Center) + .spacing(5) + .push(Text::new("BPF:").font(font)) + .push(input), + ); + ret_val = ret_val.push(inner_col); + } + + Container::new(ret_val) + .padding(15) + .width(Length::Fill) + .class(ContainerType::BorderedRound) +} + +fn get_export_pcap_group_maybe<'a>( + cs_pick: CaptureSourcePicklist, export_pcap: &ExportPcap, language: Language, font: Font, -) -> Container<'a, Message, StyleType> { - if matches!(cs, CaptureSource::File(_)) { - return Container::new(Space::with_height(Length::Fill)); +) -> Option> { + if cs_pick == CaptureSourcePicklist::File { + return None; } let enabled = export_pcap.enabled(); @@ -479,7 +375,7 @@ fn get_export_pcap_group<'a>( if enabled { let inner_col = Column::new() .spacing(10) - .padding(Padding::ZERO.left(45)) + .padding(Padding::ZERO.left(26)) .push( Row::new() .align_y(Alignment::Center) @@ -489,8 +385,7 @@ fn get_export_pcap_group<'a>( TextInput::new(ExportPcap::DEFAULT_FILE_NAME, file_name) .on_input(Message::OutputPcapFile) .padding([2, 5]) - .font(font) - .width(200), + .font(font), ), ) .push( @@ -511,12 +406,10 @@ fn get_export_pcap_group<'a>( ret_val = ret_val.push(inner_col); } - Container::new( + Some( Container::new(ret_val) - .padding(10) + .padding(15) .width(Length::Fill) .class(ContainerType::BorderedRound), ) - .height(Length::Fill) - .align_y(Alignment::Start) } diff --git a/src/gui/pages/inspect_page.rs b/src/gui/pages/inspect_page.rs index 0274f63a..c2cdc0f5 100644 --- a/src/gui/pages/inspect_page.rs +++ b/src/gui/pages/inspect_page.rs @@ -11,7 +11,6 @@ }; use iced::{Alignment, Font, Length, Padding, Pixels, alignment}; -use crate::chart::types::chart_type::ChartType; 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}; @@ -22,28 +21,30 @@ use crate::gui::styles::text::TextType; use crate::gui::styles::text_input::TextInputType; use crate::gui::types::message::Message; +use crate::gui::types::settings::Settings; use crate::networking::types::address_port_pair::AddressPortPair; -use crate::networking::types::byte_multiple::ByteMultiple; use crate::networking::types::data_info::DataInfo; +use crate::networking::types::data_representation::DataRepr; use crate::networking::types::host_data_states::HostStates; use crate::networking::types::info_address_port_pair::InfoAddressPortPair; use crate::networking::types::traffic_direction::TrafficDirection; use crate::report::get_report_entries::get_searched_entries; use crate::report::types::report_col::ReportCol; use crate::report::types::search_parameters::{FilterInputType, SearchParameters}; +use crate::report::types::sort_type::SortType; use crate::translations::translations_2::{ administrative_entity_translation, country_translation, domain_name_translation, no_search_results_translation, only_show_favorites_translation, showing_results_translation, }; use crate::translations::translations_3::filter_by_host_translation; use crate::utils::types::icon::Icon; -use crate::{ConfigSettings, Language, ReportSortType, RunningPage, Sniffer, StyleType}; +use crate::{Language, RunningPage, Sniffer, StyleType}; /// Computes the body of gui inspect page -pub fn inspect_page(sniffer: &Sniffer) -> Container { - let ConfigSettings { +pub fn inspect_page(sniffer: &Sniffer) -> Container<'_, Message, StyleType> { + let Settings { style, language, .. - } = sniffer.configs.settings; + } = sniffer.conf.settings; let font = style.get_extension().font; let font_headers = style.get_extension().font_headers; @@ -75,7 +76,8 @@ pub fn inspect_page(sniffer: &Sniffer) -> Container { language, &sniffer.search, font, - sniffer.report_sort_type, + sniffer.conf.report_sort_type, + sniffer.traffic_chart.data_repr, )) .push(Space::with_height(4)) .push(Rule::horizontal(5)) @@ -97,7 +99,7 @@ pub fn inspect_page(sniffer: &Sniffer) -> Container { .align_y(Alignment::Center) .align_x(Alignment::Center) .padding(Padding::new(7.0).top(10).bottom(3)) - .width(1042) + .width(947) .class(ContainerType::BorderedRound), ); @@ -105,9 +107,10 @@ pub fn inspect_page(sniffer: &Sniffer) -> Container { } fn report<'a>(sniffer: &Sniffer) -> Column<'a, Message, StyleType> { - let ConfigSettings { + let Settings { style, language, .. - } = sniffer.configs.settings; + } = sniffer.conf.settings; + let data_repr = sniffer.traffic_chart.data_repr; let font = style.get_extension().font; let (search_results, results_number, agglomerate) = get_searched_entries(sniffer); @@ -122,12 +125,17 @@ fn report<'a>(sniffer: &Sniffer) -> Column<'a, Message, StyleType> { let end_entry_num = start_entry_num + search_results.len() - 1; for report_entry in search_results { scroll_report = scroll_report.push( - button(row_report_entry(&report_entry.0, &report_entry.1, font)) - .padding(2) - .on_press(Message::ShowModal(MyModal::ConnectionDetails( - report_entry.0, - ))) - .class(ButtonType::Neutral), + button(row_report_entry( + &report_entry.0, + &report_entry.1, + data_repr, + font, + )) + .padding(2) + .on_press(Message::ShowModal(MyModal::ConnectionDetails( + report_entry.0, + ))) + .class(ButtonType::Neutral), ); } if results_number > 0 { @@ -144,7 +152,7 @@ fn report<'a>(sniffer: &Sniffer) -> Column<'a, Message, StyleType> { .push(get_agglomerates_row( font, agglomerate, - sniffer.traffic_chart.chart_type, + sniffer.traffic_chart.data_repr, )) .push(Rule::horizontal(5)) .push(get_change_page_row( @@ -177,12 +185,13 @@ fn report_header_row( language: Language, search_params: &SearchParameters, font: Font, - sort_type: ReportSortType, -) -> Row { + sort_type: SortType, + data_repr: DataRepr, +) -> Row<'_, Message, StyleType> { let mut ret_val = Row::new().padding([0, 2]).align_y(Alignment::Center); for report_col in ReportCol::ALL { let (title_display, title_small_display, tooltip_val) = - title_report_col_display(&report_col, language); + title_report_col_display(&report_col, data_repr, language); let title_row = Row::new() .align_y(Alignment::End) .push(Text::new(title_display).font(font)) @@ -208,7 +217,9 @@ fn report_header_row( .width(report_col.get_width()) .height(56) .push(title_tooltip); - if report_col != ReportCol::Packets && report_col != ReportCol::Bytes { + if report_col == ReportCol::Data { + col_header = col_header.push(sort_arrows(sort_type)); + } else { col_header = col_header.push( Container::new(filter_input( report_col.get_filter_input_type(), @@ -218,8 +229,6 @@ fn report_header_row( .height(Length::Fill) .align_y(Alignment::Center), ); - } else { - col_header = col_header.push(sort_arrows(sort_type, &report_col)); } ret_val = ret_val.push(col_header); } @@ -228,10 +237,11 @@ fn report_header_row( fn title_report_col_display( report_col: &ReportCol, + data_repr: DataRepr, language: Language, ) -> (String, String, String) { let max_chars = report_col.get_max_chars(Some(language)); - let title = report_col.get_title(language); + let title = report_col.get_title(language, data_repr); let title_direction_info = report_col.get_title_direction_info(language); let chars_title = title.chars().collect::>(); let chars_title_direction_info = title_direction_info.chars().collect::>(); @@ -261,21 +271,16 @@ fn title_report_col_display( } } -fn sort_arrows<'a>( - active_sort_type: ReportSortType, - report_col: &ReportCol, -) -> Container<'a, Message, StyleType> { +fn sort_arrows<'a>(active_sort_type: SortType) -> Container<'a, Message, StyleType> { Container::new( button( active_sort_type - .icon(report_col) + .icon() .align_x(Alignment::Center) .align_y(Alignment::Center), ) - .class(active_sort_type.button_type(report_col)) - .on_press(Message::ReportSortSelection( - active_sort_type.next_sort(report_col), - )), + .class(active_sort_type.button_type()) + .on_press(Message::ReportSortSelection(active_sort_type.next_sort())), ) .align_y(Alignment::Center) .height(Length::Fill) @@ -284,6 +289,7 @@ fn sort_arrows<'a>( fn row_report_entry<'a>( key: &AddressPortPair, val: &InfoAddressPortPair, + data_repr: DataRepr, font: Font, ) -> Row<'a, Message, StyleType> { let text_type = if val.traffic_direction == TrafficDirection::Outgoing { @@ -296,7 +302,7 @@ fn row_report_entry<'a>( for report_col in ReportCol::ALL { let max_chars = report_col.get_max_chars(None); - let col_value = report_col.get_value(key, val); + let col_value = report_col.get_value(key, val, data_repr); ret_val = ret_val.push( Container::new( Text::new(if col_value.len() <= max_chars { @@ -476,7 +482,7 @@ fn filter_combobox( combo_box_state: &combo_box::State, search_params: SearchParameters, font: Font, -) -> Container { +) -> Container<'_, Message, StyleType> { let filter_value = filter_input_type.current_value(&search_params).to_string(); let is_filter_active = !filter_value.is_empty(); @@ -550,31 +556,22 @@ fn get_button_change_page<'a>(increment: bool) -> Button<'a, Message, StyleType> fn get_agglomerates_row<'a>( font: Font, tot: DataInfo, - chart_type: ChartType, + data_repr: DataRepr, ) -> Row<'a, Message, StyleType> { - let tot_packets = tot.tot_packets(); - let tot_bytes = tot.tot_bytes(); - - let (in_length, out_length) = get_bars_length(chart_type, &tot, &tot); + let (in_length, out_length) = get_bars_length(data_repr, &tot, &tot); let bars = get_bars(in_length, out_length).width(ReportCol::FILTER_COLUMNS_WIDTH); - let bytes_col = Column::new() + let data_col = Column::new() .align_x(Alignment::Center) - .width(ReportCol::Bytes.get_width()) - .push(Text::new(ByteMultiple::formatted_string(tot_bytes)).font(font)); - - let packets_col = Column::new() - .align_x(Alignment::Center) - .width(ReportCol::Packets.get_width()) - .push(Text::new(tot_packets.to_string()).font(font)); + .width(ReportCol::Data.get_width()) + .push(Text::new(data_repr.formatted_string(tot.tot_data(data_repr))).font(font)); Row::new() .padding([0, 2]) .height(40) .align_y(Alignment::Center) .push(bars) - .push(bytes_col) - .push(packets_col) + .push(data_col) } fn get_change_page_row<'a>( @@ -633,6 +630,7 @@ fn button_clear_filter<'a>( #[cfg(test)] mod tests { use crate::gui::pages::inspect_page::title_report_col_display; + use crate::networking::types::data_representation::DataRepr; use crate::report::types::report_col::ReportCol; use crate::translations::types::language::Language; @@ -641,78 +639,81 @@ fn test_table_titles_display_and_tooltip_values_for_each_language() { // check glyph len when adding new language... assert_eq!(Language::ALL.len(), 22); for report_col in ReportCol::ALL { - for language in Language::ALL { - let (title, title_small, tooltip_val) = - title_report_col_display(&report_col, language); - let title_chars = title.chars().collect::>(); - let title_small_chars = title_small.chars().collect::>(); - let max_chars = report_col.get_max_chars(Some(language)); - if tooltip_val.is_empty() { - // all is entirely displayed - assert!(title_chars.len() + title_small_chars.len() <= max_chars); - assert_eq!(title, report_col.get_title(language)); - assert_eq!(title_small, report_col.get_title_direction_info(language)); - } else { - // tooltip is the full concatenation - assert_eq!( - tooltip_val, - [ - report_col.get_title(language), - report_col.get_title_direction_info(language) - ] - .concat() - ); - if report_col.get_title_direction_info(language).len() == 0 { - // displayed values have max len -1 (they include "…" that counts for 2 units) - assert_eq!(title_chars.len() + title_small_chars.len(), max_chars - 1); + for data_repr in DataRepr::ALL { + for language in Language::ALL { + let (title, title_small, tooltip_val) = + title_report_col_display(&report_col, data_repr, language); + let title_chars = title.chars().collect::>(); + let title_small_chars = title_small.chars().collect::>(); + let max_chars = report_col.get_max_chars(Some(language)); + if tooltip_val.is_empty() { + // all is entirely displayed + assert!(title_chars.len() + title_small_chars.len() <= max_chars); + assert_eq!(title, report_col.get_title(language, data_repr)); + assert_eq!(title_small, report_col.get_title_direction_info(language)); } else { - match title_chars.len() { - x if x == max_chars - 4 || x == max_chars - 3 => { - assert_eq!(title_small_chars.len(), 1) - } - _ => assert_eq!( - title_chars.len() + title_small_chars.len(), - max_chars - 1 - ), - } - } - if title != report_col.get_title(language) { - // first title part is not full, so second one is suspensions - assert_eq!(title_small, "…"); - // check len wrt max - assert!(title_chars.len() >= max_chars - 4); - // first title part is max - 2 chars of full self + // tooltip is the full concatenation assert_eq!( - title, - report_col - .get_title(language) - .chars() - .collect::>()[..max_chars - 2] - .iter() - .collect::() + tooltip_val, + [ + report_col.get_title(language, data_repr), + report_col.get_title_direction_info(language) + ] + .concat() ); - } else { - // first part is untouched - // second title part is max - title.len - 2 chars of full self, plus suspensions - let mut second_part = [ - &report_col - .get_title_direction_info(language) - .chars() - .collect::>()[..max_chars - 2 - title_chars.len()] - .iter() - .collect::(), - "…", - ] - .concat(); - if second_part == String::from(" (…") || second_part == String::from(" …") - { - second_part = String::from("…"); + if report_col.get_title_direction_info(language).len() == 0 { + // displayed values have max len -1 (they include "…" that counts for 2 units) + assert_eq!(title_chars.len() + title_small_chars.len(), max_chars - 1); + } else { + match title_chars.len() { + x if x == max_chars - 4 || x == max_chars - 3 => { + assert_eq!(title_small_chars.len(), 1) + } + _ => assert_eq!( + title_chars.len() + title_small_chars.len(), + max_chars - 1 + ), + } + } + if title != report_col.get_title(language, data_repr) { + // first title part is not full, so second one is suspensions + assert_eq!(title_small, "…"); + // check len wrt max + assert!(title_chars.len() >= max_chars - 4); + // first title part is max - 2 chars of full self + assert_eq!( + title, + report_col + .get_title(language, data_repr) + .chars() + .collect::>()[..max_chars - 2] + .iter() + .collect::() + ); + } else { + // first part is untouched + // second title part is max - title.len - 2 chars of full self, plus suspensions + let mut second_part = [ + &report_col + .get_title_direction_info(language) + .chars() + .collect::>()[..max_chars - 2 - title_chars.len()] + .iter() + .collect::(), + "…", + ] + .concat(); + if second_part == String::from(" (…") + || second_part == String::from(" …") + { + second_part = String::from("…"); + } + assert_eq!(title_small, second_part); + // second part never terminates with "(…" + assert!(!title_small.ends_with("(…")); + // second part never terminates with " …" + assert!(!title_small.ends_with(" …")); } - assert_eq!(title_small, second_part); - // second part never terminates with "(…" - assert!(!title_small.ends_with("(…")); - // second part never terminates with " …" - assert!(!title_small.ends_with(" …")); } } } diff --git a/src/gui/pages/notifications_page.rs b/src/gui/pages/notifications_page.rs index 0249de8d..3c92f6d7 100644 --- a/src/gui/pages/notifications_page.rs +++ b/src/gui/pages/notifications_page.rs @@ -1,4 +1,3 @@ -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; @@ -11,8 +10,10 @@ use crate::gui::styles::style_constants::FONT_SIZE_FOOTER; use crate::gui::styles::text::TextType; use crate::gui::types::message::Message; +use crate::gui::types::settings::Settings; use crate::networking::types::data_info::DataInfo; use crate::networking::types::data_info_host::DataInfoHost; +use crate::networking::types::data_representation::DataRepr; use crate::networking::types::host::Host; use crate::networking::types::service::Service; use crate::networking::types::traffic_type::TrafficType; @@ -21,13 +22,12 @@ }; use crate::report::types::sort_type::SortType; use crate::translations::translations::{ - 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, + clear_all_translation, favorite_transmitted_translation, no_notifications_received_translation, + no_notifications_set_translation, only_last_30_translation, per_second_translation, threshold_translation, }; use crate::utils::types::icon::Icon; -use crate::{ByteMultiple, ConfigSettings, Language, RunningPage, Sniffer, StyleType}; +use crate::{Language, RunningPage, Sniffer, StyleType}; use iced::Length::FillPortion; use iced::widget::scrollable::Direction; use iced::widget::text::LineHeight; @@ -38,13 +38,13 @@ use std::cmp::max; /// Computes the body of gui notifications page -pub fn notifications_page(sniffer: &Sniffer) -> Container { - let ConfigSettings { +pub fn notifications_page(sniffer: &Sniffer) -> Container<'_, Message, StyleType> { + let Settings { style, language, notifications, .. - } = sniffer.configs.settings; + } = sniffer.conf.settings; let font = style.get_extension().font; let font_headers = style.get_extension().font_headers; @@ -128,7 +128,7 @@ fn body_no_notifications_received( font: Font, language: Language, dots: &str, -) -> Column { +) -> Column<'_, Message, StyleType> { Column::new() .padding(5) .spacing(5) @@ -150,13 +150,9 @@ fn data_notification_log<'a>( 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 { + let data_repr = logged_notification.data_repr; + let data_string = data_repr.formatted_string(logged_notification.threshold.into()); + let icon = if data_repr == DataRepr::Packets { Icon::PacketsThreshold } else { Icon::BytesThreshold @@ -184,13 +180,9 @@ fn data_notification_log<'a>( .push(Text::new(logged_notification.timestamp.clone()).font(font)), ) .push( - Text::new(if chart_type == ChartType::Bytes { - bytes_exceeded_translation(language) - } else { - packets_exceeded_translation(language) - }) - .class(TextType::Title) - .font(font), + Text::new(data_repr.data_exceeded_translation(language).to_string()) + .class(TextType::Title) + .font(font), ) .push( Text::new(threshold_str) @@ -222,14 +214,14 @@ fn data_notification_log<'a>( fn favorite_notification_log<'a>( logged_notification: &FavoriteTransmitted, first_entry_data_info: DataInfo, - chart_type: ChartType, + data_repr: DataRepr, language: Language, font: Font, ) -> Container<'a, Message, StyleType> { let host_bar = host_bar( &logged_notification.host, &logged_notification.data_info_host, - chart_type, + data_repr, first_entry_data_info, font, language, @@ -291,10 +283,10 @@ fn get_button_clear_all<'a>(font: Font, language: Language) -> Tooltip<'a, Messa } fn logged_notifications<'a>(sniffer: &Sniffer) -> Column<'a, Message, StyleType> { - let ConfigSettings { + let Settings { style, language, .. - } = sniffer.configs.settings; - let chart_type = sniffer.traffic_chart.chart_type; + } = sniffer.conf.settings; + let data_repr = sniffer.traffic_chart.data_repr; let font = style.get_extension().font; let mut ret_val = Column::new() .padding(Padding::ZERO.right(15)) @@ -306,7 +298,7 @@ fn logged_notifications<'a>(sniffer: &Sniffer) -> Column<'a, Message, StyleType> .0 .iter() .map(LoggedNotification::data_info) - .max_by(|d1, d2| d1.compare(d2, SortType::Ascending, chart_type)) + .max_by(|d1, d2| d1.compare(d2, SortType::Ascending, data_repr)) .unwrap_or_default(); for logged_notification in &sniffer.logged_notifications.0 { @@ -323,7 +315,7 @@ fn logged_notifications<'a>(sniffer: &Sniffer) -> Column<'a, Message, StyleType> favorite_notification_log( favorite_transmitted, first_entry_data_info, - chart_type, + data_repr, language, font, ) @@ -339,10 +331,10 @@ fn threshold_bar<'a>( language: Language, font: Font, ) -> Row<'a, Message, StyleType> { - let chart_type = logged_notification.chart_type; + let data_repr = logged_notification.data_repr; 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); + get_bars_length(data_repr, &first_entry_data_info, &data_info); Row::new() .align_y(Alignment::Center) @@ -358,16 +350,9 @@ fn threshold_bar<'a>( .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(Row::new().push(horizontal_space()).push( + Text::new(data_repr.formatted_string(data_info.tot_data(data_repr))).font(font), + )) .push(get_bars(incoming_bar_len, outgoing_bar_len)), ) } @@ -424,7 +409,7 @@ fn data_notification_extra<'a>( let host_bar = host_bar( host, data_info_host, - logged_notification.chart_type, + logged_notification.data_repr, first_data_info_host, font, language, @@ -442,7 +427,7 @@ fn data_notification_extra<'a>( let service_bar = service_bar( service, data_info, - logged_notification.chart_type, + logged_notification.data_repr, first_data_info_service, font, ); diff --git a/src/gui/pages/overview_page.rs b/src/gui/pages/overview_page.rs index 9f8341e2..41ed59df 100644 --- a/src/gui/pages/overview_page.rs +++ b/src/gui/pages/overview_page.rs @@ -1,7 +1,7 @@ //! Module defining the run page of the application. //! //! It contains elements to display traffic statistics: chart, detailed connections data -//! and overall statistics about the filtered traffic. +//! and overall statistics about the traffic. use crate::chart::types::donut_chart::donut_chart; use crate::countries::country_utils::get_flag_tooltip; @@ -12,14 +12,16 @@ use crate::gui::styles::container::ContainerType; use crate::gui::styles::rule::RuleType; use crate::gui::styles::scrollbar::ScrollbarType; -use crate::gui::styles::style_constants::FONT_SIZE_TITLE; +use crate::gui::styles::style_constants::{FONT_SIZE_FOOTER, FONT_SIZE_TITLE}; use crate::gui::styles::text::TextType; use crate::gui::styles::types::palette_extension::PaletteExtension; +use crate::gui::types::filters::Filters; use crate::gui::types::message::Message; +use crate::gui::types::settings::Settings; 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::data_representation::DataRepr; use crate::networking::types::host::Host; use crate::networking::types::service::Service; use crate::report::get_report_entries::{get_host_entries, get_service_entries}; @@ -27,18 +29,16 @@ use crate::report::types::sort_type::SortType; use crate::translations::translations::{ active_filters_translation, error_translation, incoming_translation, no_addresses_translation, - none_translation, outgoing_translation, some_observed_translation, traffic_rate_translation, - waiting_translation, + none_translation, outgoing_translation, traffic_rate_translation, waiting_translation, }; use crate::translations::translations_2::{ data_representation_translation, dropped_translation, host_translation, only_top_30_items_translation, }; use crate::translations::translations_3::{service_translation, unsupported_link_type_translation}; -use crate::translations::translations_4::{excluded_translation, reading_from_pcap_translation}; -use crate::utils::formatted_strings::get_active_filters_string; +use crate::translations::translations_4::reading_from_pcap_translation; use crate::utils::types::icon::Icon; -use crate::{ByteMultiple, ChartType, ConfigSettings, Language, RunningPage, StyleType}; +use crate::{Language, RunningPage, StyleType}; use iced::Length::{Fill, FillPortion}; use iced::alignment::{Horizontal, Vertical}; use iced::widget::scrollable::Direction; @@ -48,76 +48,79 @@ Button, Column, Container, Row, Rule, Scrollable, Space, Text, Tooltip, button, horizontal_space, vertical_space, }; -use iced::{Alignment, Font, Length, Padding}; +use iced::{Alignment, Element, Font, Length, Padding}; use std::fmt::Write; /// Computes the body of gui overview page -pub fn overview_page(sniffer: &Sniffer) -> Container { - let ConfigSettings { +pub fn overview_page(sniffer: &Sniffer) -> Container<'_, Message, StyleType> { + let Settings { style, language, .. - } = sniffer.configs.settings; + } = sniffer.conf.settings; let font = style.get_extension().font; let font_headers = style.get_extension().font_headers; let mut body = Column::new(); let mut tab_and_body = Column::new().height(Length::Fill); - let dots = &sniffer.dots_pulse.0; + // some packets are there! + let tabs = get_pages_tabs( + RunningPage::Overview, + font, + font_headers, + language, + sniffer.unread_notifications, + ); + tab_and_body = tab_and_body.push(tabs); - if let Some(error) = sniffer.pcap_error.as_ref() { - // pcap threw an ERROR! - body = body_pcap_error(error, dots, language, font); - } else { - // NO pcap error detected - let observed = sniffer.info_traffic.all_packets; - let filtered = sniffer.info_traffic.tot_data_info.tot_packets(); + let container_chart = container_chart(sniffer, font); - match (observed, filtered) { - (0, 0) => { - //no packets observed at all - body = body_no_packets(&sniffer.capture_source, font, language, dots); - } - (observed, 0) => { - //no packets have been filtered but some have been observed - body = body_no_observed(&sniffer.filters, observed, font, language, dots); - } - (_observed, _filtered) => { - //observed > filtered > 0 || observed = filtered > 0 - let tabs = get_pages_tabs( - RunningPage::Overview, - font, - font_headers, - language, - sniffer.unread_notifications, - ); - tab_and_body = tab_and_body.push(tabs); + let container_info = col_info(sniffer); - let container_chart = container_chart(sniffer, font); + let container_report = row_report(sniffer); - let container_info = col_info(sniffer); - - let container_report = row_report(sniffer); - - body = body - .width(Length::Fill) - .padding(10) - .spacing(10) - .align_x(Alignment::Center) - .push( - Row::new() - .height(280) - .spacing(10) - .push(container_info) - .push(container_chart), - ) - .push(container_report); - } - } - } + body = body + .width(Length::Fill) + .padding(10) + .spacing(10) + .align_x(Alignment::Center) + .push( + Row::new() + .height(280) + .spacing(10) + .push(container_info) + .push(container_chart), + ) + .push(container_report); Container::new(Column::new().push(tab_and_body.push(body))).height(Length::Fill) } +pub fn waiting_page(sniffer: &Sniffer) -> Option> { + let Settings { + style, language, .. + } = sniffer.conf.settings; + let font = style.get_extension().font; + + let dots = &sniffer.dots_pulse.0; + + let tot_packets = sniffer + .info_traffic + .tot_data_info + .tot_data(DataRepr::Packets); + + let body = if let Some(error) = sniffer.pcap_error.as_ref() { + // pcap threw an ERROR! + body_pcap_error(error, dots, language, font) + } else if tot_packets == 0 { + // no packets observed at all + body_no_packets(&sniffer.capture_source, font, language, dots) + } else { + return None; + }; + + Some(Container::new(Column::new().push(body)).height(Length::Fill)) +} + fn body_no_packets<'a>( cs: &CaptureSource, font: Font, @@ -170,31 +173,6 @@ fn body_no_packets<'a>( .push(Space::with_height(FillPortion(2))) } -fn body_no_observed<'a>( - filters: &Filters, - observed: u128, - font: Font, - language: Language, - dots: &str, -) -> Column<'a, Message, StyleType> { - let tot_packets_text = some_observed_translation(language, observed) - .align_x(Alignment::Center) - .font(font); - - Column::new() - .width(Length::Fill) - .padding(10) - .spacing(10) - .align_x(Alignment::Center) - .push(vertical_space()) - .push(Icon::Funnel.to_text().size(60)) - .push(get_active_filters_col(filters, language, font)) - .push(Rule::horizontal(20)) - .push(tot_packets_text) - .push(Text::new(dots.to_owned()).font(font).size(50)) - .push(Space::with_height(FillPortion(2))) -} - fn body_pcap_error<'a>( pcap_error: &'a str, dots: &'a str, @@ -241,20 +219,24 @@ fn row_report<'a>(sniffer: &Sniffer) -> Row<'a, Message, StyleType> { } fn col_host<'a>(sniffer: &Sniffer) -> Column<'a, Message, StyleType> { - let ConfigSettings { + let Settings { style, language, .. - } = sniffer.configs.settings; + } = sniffer.conf.settings; let font = style.get_extension().font; - let chart_type = sniffer.traffic_chart.chart_type; + let data_repr = sniffer.traffic_chart.data_repr; 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 entries = get_host_entries( + &sniffer.info_traffic, + data_repr, + sniffer.conf.host_sort_type, + ); let first_entry_data_info = entries .iter() .map(|(_, d)| d.data_info) - .max_by(|d1, d2| d1.compare(d2, SortType::Ascending, chart_type)) + .max_by(|d1, d2| d1.compare(d2, SortType::Ascending, data_repr)) .unwrap_or_default(); for (host, data_info_host) in &entries { @@ -263,7 +245,7 @@ fn col_host<'a>(sniffer: &Sniffer) -> Column<'a, Message, StyleType> { let host_bar = host_bar( host, data_info_host, - chart_type, + data_repr, first_entry_data_info, font, language, @@ -304,7 +286,7 @@ fn col_host<'a>(sniffer: &Sniffer) -> Column<'a, Message, StyleType> { ) .push(horizontal_space()) .push(sort_arrows( - sniffer.host_sort_type, + sniffer.conf.host_sort_type, Message::HostSortSelection, )), ) @@ -318,24 +300,28 @@ fn col_host<'a>(sniffer: &Sniffer) -> Column<'a, Message, StyleType> { } fn col_service<'a>(sniffer: &Sniffer) -> Column<'a, Message, StyleType> { - let ConfigSettings { + let Settings { style, language, .. - } = sniffer.configs.settings; + } = sniffer.conf.settings; let font = style.get_extension().font; - let chart_type = sniffer.traffic_chart.chart_type; + let data_repr = sniffer.traffic_chart.data_repr; 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 entries = get_service_entries( + &sniffer.info_traffic, + data_repr, + sniffer.conf.service_sort_type, + ); let first_entry_data_info = entries .iter() .map(|&(_, d)| d) - .max_by(|d1, d2| d1.compare(d2, SortType::Ascending, chart_type)) + .max_by(|d1, d2| d1.compare(d2, SortType::Ascending, data_repr)) .unwrap_or_default(); for (service, data_info) in &entries { - let content = service_bar(service, data_info, chart_type, first_entry_data_info, font); + let content = service_bar(service, data_info, data_repr, first_entry_data_info, font); scroll_service = scroll_service.push( button(content) @@ -368,7 +354,7 @@ fn col_service<'a>(sniffer: &Sniffer) -> Column<'a, Message, StyleType> { ) .push(horizontal_space()) .push(sort_arrows( - sniffer.service_sort_type, + sniffer.conf.service_sort_type, Message::ServiceSortSelection, )), ) @@ -384,16 +370,13 @@ fn col_service<'a>(sniffer: &Sniffer) -> Column<'a, Message, StyleType> { pub fn host_bar<'a>( host: &Host, data_info_host: &DataInfoHost, - chart_type: ChartType, + data_repr: DataRepr, 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, - ); + let (incoming_bar_len, outgoing_bar_len) = + get_bars_length(data_repr, &first_entry_data_info, &data_info_host.data_info); Row::new() .height(FLAGS_HEIGHT_BIG) @@ -422,11 +405,10 @@ pub fn host_bar<'a>( ) .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()) - }) + Text::new( + data_repr + .formatted_string(data_info_host.data_info.tot_data(data_repr)), + ) .font(font), ), ) @@ -437,12 +419,12 @@ pub fn host_bar<'a>( pub fn service_bar<'a>( service: &Service, data_info: &DataInfo, - chart_type: ChartType, + data_repr: DataRepr, 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); + get_bars_length(data_repr, &first_entry_data_info, data_info); Row::new() .height(FLAGS_HEIGHT_BIG) @@ -456,28 +438,29 @@ pub fn service_bar<'a>( .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), + Text::new(data_repr.formatted_string(data_info.tot_data(data_repr))) + .font(font), ), ) .push(get_bars(incoming_bar_len, outgoing_bar_len)), ) } -fn col_info<'a>(sniffer: &Sniffer) -> Container<'a, Message, StyleType> { - let ConfigSettings { +fn col_info(sniffer: &Sniffer) -> Container<'_, Message, StyleType> { + let Settings { style, language, .. - } = sniffer.configs.settings; + } = sniffer.conf.settings; let PaletteExtension { font, .. } = style.get_extension(); - let col_device = col_device(language, font, &sniffer.capture_source); + let col_device = col_device( + language, + font, + &sniffer.capture_source, + &sniffer.conf.filters, + ); let col_data_representation = - col_data_representation(language, font, sniffer.traffic_chart.chart_type); + col_data_representation(language, font, sniffer.traffic_chart.data_repr); let donut_row = donut_row(language, font, sniffer); @@ -507,8 +490,8 @@ fn col_info<'a>(sniffer: &Sniffer) -> Container<'a, Message, StyleType> { .class(ContainerType::BorderedRound) } -fn container_chart(sniffer: &Sniffer, font: Font) -> Container { - let ConfigSettings { language, .. } = sniffer.configs.settings; +fn container_chart(sniffer: &Sniffer, font: Font) -> Container<'_, Message, StyleType> { + let Settings { language, .. } = sniffer.conf.settings; let traffic_chart = &sniffer.traffic_chart; Container::new( @@ -534,6 +517,7 @@ fn col_device<'a>( language: Language, font: Font, cs: &CaptureSource, + filters: &'a Filters, ) -> Column<'a, Message, StyleType> { let link_type = cs.get_link_type(); #[cfg(not(target_os = "windows"))] @@ -541,21 +525,55 @@ fn col_device<'a>( #[cfg(target_os = "windows")] let cs_info = cs.get_desc().unwrap_or(cs.get_name()); + let filters_desc: Element = if filters.is_some_filter_active() { + Row::new() + .spacing(10) + .push(Text::new("BPF").font(font)) + .push(get_info_tooltip(filters.bpf().to_string(), font)) + .into() + } else { + Text::new(none_translation(language)).font(font).into() + }; + Column::new() .height(Length::Fill) .spacing(10) - .push(TextType::highlighted_subtitle_with_desc( - cs.title(language), - &cs_info, - font, - )) - .push(link_type.link_type_col(language, font)) + .push( + Column::new() + .push( + Text::new(format!("{}:", cs.title(language))) + .class(TextType::Subtitle) + .font(font), + ) + .push( + Row::new() + .spacing(10) + .push(Text::new(format!(" {}", &cs_info)).font(font)) + .push(get_info_tooltip( + link_type.full_print_on_one_line(language), + font, + )), + ), + ) + .push( + Column::new() + .push( + Text::new(format!("{}:", active_filters_translation(language))) + .class(TextType::Subtitle) + .font(font), + ) + .push( + Row::new() + .push(Text::new(" ".to_string()).font(font)) + .push(filters_desc), + ), + ) } fn col_data_representation<'a>( language: Language, font: Font, - chart_type: ChartType, + data_repr: DataRepr, ) -> Column<'a, Message, StyleType> { let mut ret_val = Column::new().spacing(5).push( Text::new(format!("{}:", data_representation_translation(language))) @@ -563,77 +581,61 @@ fn col_data_representation<'a>( .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()) - .width(Length::Fill) - .align_x(Alignment::Center) - .align_y(Alignment::Center) - .font(font), - ) - .width(Length::Fill) - .height(33) - .class(if is_active { - ButtonType::BorderedRoundSelected - } else { - ButtonType::BorderedRound - }) - .on_press(Message::ChartSelection(option)), - ); - } + let [bits, bytes, packets] = DataRepr::ALL.map(|option| { + let is_active = data_repr.eq(&option); + Button::new( + Text::new(option.get_label(language).to_owned()) + .width(Length::Fill) + .align_x(Alignment::Center) + .align_y(Alignment::Center) + .font(font), + ) + .width(Length::Fill) + .height(33) + .class(if is_active { + ButtonType::BorderedRoundSelected + } else { + ButtonType::BorderedRound + }) + .on_press(Message::DataReprSelection(option)) + }); + + ret_val = ret_val + .push(Row::new().spacing(5).push(bits).push(bytes)) + .push(packets); + ret_val } -fn donut_row<'a>( +fn donut_row( language: Language, font: Font, sniffer: &Sniffer, -) -> Container<'a, Message, StyleType> { - let chart_type = sniffer.traffic_chart.chart_type; - let filters = &sniffer.filters; +) -> Container<'_, Message, StyleType> { + let data_repr = sniffer.traffic_chart.data_repr; - let (in_data, out_data, filtered_out, dropped) = - sniffer.info_traffic.get_thumbnail_data(chart_type); - - let legend_entry_filtered = if filters.none_active() { - None - } else { - Some(donut_legend_entry( - filtered_out, - chart_type, - RuleType::FilteredOut, - filters, - font, - language, - )) - }; + let (in_data, out_data, dropped) = sniffer.info_traffic.get_thumbnail_data(data_repr); let legend_col = Column::new() .spacing(5) .push(donut_legend_entry( in_data, - chart_type, + data_repr, RuleType::Incoming, - filters, font, language, )) .push(donut_legend_entry( out_data, - chart_type, + data_repr, RuleType::Outgoing, - filters, font, language, )) - .push_maybe(legend_entry_filtered) .push(donut_legend_entry( dropped, - chart_type, + data_repr, RuleType::Dropped, - filters, font, language, )); @@ -642,10 +644,9 @@ fn donut_row<'a>( .align_y(Vertical::Center) .spacing(20) .push(donut_chart( - chart_type, + data_repr, in_data, out_data, - filtered_out, dropped, font, sniffer.thumbnail, @@ -661,32 +662,20 @@ fn donut_row<'a>( fn donut_legend_entry<'a>( value: u128, - chart_type: ChartType, + data_repr: DataRepr, rule_type: RuleType, - filters: &Filters, font: Font, language: Language, ) -> Row<'a, Message, StyleType> { - let value_text = if chart_type.eq(&ChartType::Bytes) { - ByteMultiple::formatted_string(value) - } else { - value.to_string() - }; + let value_text = data_repr.formatted_string(value); let label = match rule_type { RuleType::Incoming => incoming_translation(language), RuleType::Outgoing => outgoing_translation(language), - RuleType::FilteredOut => excluded_translation(language), RuleType::Dropped => dropped_translation(language), _ => "", }; - let tooltip = if matches!(rule_type, RuleType::FilteredOut) { - Some(get_active_filters_tooltip(filters, language, font)) - } else { - None - }; - Row::new() .spacing(10) .align_y(Alignment::Center) @@ -696,28 +685,18 @@ fn donut_legend_entry<'a>( .push(Rule::horizontal(1).class(rule_type)), ) .push(Text::new(format!("{label}: {value_text}")).font(font)) - .push_maybe(tooltip) } const MIN_BARS_LENGTH: f32 = 4.0; pub fn get_bars_length( - chart_type: ChartType, + data_repr: DataRepr, first_entry: &DataInfo, data_info: &DataInfo, ) -> (u16, u16) { - let (in_val, out_val, first_entry_tot_val) = match chart_type { - ChartType::Packets => ( - data_info.incoming_packets(), - data_info.outgoing_packets(), - first_entry.tot_packets(), - ), - ChartType::Bytes => ( - data_info.incoming_bytes(), - data_info.outgoing_bytes(), - first_entry.tot_bytes(), - ), - }; + let in_val = data_info.incoming_data(data_repr); + let out_val = data_info.outgoing_data(data_repr); + let first_entry_tot_val = first_entry.tot_data(data_repr); let tot_val = in_val + out_val; if tot_val == 0 { @@ -808,46 +787,12 @@ fn get_star_button<'a>(is_favorite: bool, host: Host) -> Button<'a, Message, Sty .on_press(Message::AddOrRemoveFavorite(host, !is_favorite)) } -fn get_active_filters_col<'a>( - filters: &Filters, - language: Language, - font: Font, -) -> Column<'a, Message, StyleType> { - let mut ret_val = Column::new().push( - Text::new(active_filters_translation(language)) - .font(font) - .class(TextType::Subtitle), - ); - - if filters.none_active() { - ret_val = ret_val.push(Text::new(format!(" {}", none_translation(language))).font(font)); - } else { - let filters_string = get_active_filters_string(filters, language); - ret_val = ret_val.push(Row::new().push(Text::new(filters_string).font(font))); - } - ret_val -} - -fn get_active_filters_tooltip<'a>( - filters: &Filters, - language: Language, - font: Font, -) -> Tooltip<'a, Message, StyleType> { - let filters_string = get_active_filters_string(filters, language); - - let mut ret_val = Column::new().push( - Text::new(active_filters_translation(language)) - .font(font) - .class(TextType::Subtitle), - ); - - ret_val = ret_val.push(Row::new().push(Text::new(filters_string).font(font))); - +fn get_info_tooltip<'a>(info_str: String, font: Font) -> Tooltip<'a, Message, StyleType> { Tooltip::new( Container::new( Text::new("i") + .size(FONT_SIZE_FOOTER) .font(font) - .size(15) .line_height(LineHeight::Relative(1.0)), ) .align_x(Alignment::Center) @@ -855,7 +800,7 @@ fn get_active_filters_tooltip<'a>( .height(20) .width(20) .class(ContainerType::BadgeInfo), - ret_val, + Text::new(info_str).font(font), Position::FollowCursor, ) .class(ContainerType::Tooltip) @@ -881,20 +826,24 @@ fn sort_arrows<'a>( #[cfg(test)] mod tests { - use crate::chart::types::chart_type::ChartType; use crate::gui::pages::overview_page::{MIN_BARS_LENGTH, get_bars_length}; use crate::networking::types::data_info::DataInfo; + use crate::networking::types::data_representation::DataRepr; #[test] 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(ChartType::Packets, &first_entry, &data_info), + get_bars_length(DataRepr::Packets, &first_entry, &data_info), (25, 55) ); assert_eq!( - get_bars_length(ChartType::Bytes, &first_entry, &data_info), + get_bars_length(DataRepr::Bytes, &first_entry, &data_info), + (83, 15) + ); + assert_eq!( + get_bars_length(DataRepr::Bits, &first_entry, &data_info), (83, 15) ); } @@ -904,21 +853,21 @@ 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(ChartType::Packets, &first_entry, &data_info), + get_bars_length(DataRepr::Packets, &first_entry, &data_info), (MIN_BARS_LENGTH as u16 / 2, MIN_BARS_LENGTH as u16 / 2) ); assert_eq!( - get_bars_length(ChartType::Bytes, &first_entry, &data_info), + get_bars_length(DataRepr::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(ChartType::Packets, &first_entry, &data_info), + get_bars_length(DataRepr::Packets, &first_entry, &data_info), (0, MIN_BARS_LENGTH as u16) ); assert_eq!( - get_bars_length(ChartType::Bytes, &first_entry, &data_info), + get_bars_length(DataRepr::Bytes, &first_entry, &data_info), (0, MIN_BARS_LENGTH as u16) ); } @@ -929,31 +878,31 @@ 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(ChartType::Packets, &first_entry, &data_info), + get_bars_length(DataRepr::Packets, &first_entry, &data_info), (MIN_BARS_LENGTH as u16 / 2, MIN_BARS_LENGTH as u16 / 2) ); assert_eq!( - get_bars_length(ChartType::Bytes, &first_entry, &data_info), + get_bars_length(DataRepr::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(ChartType::Packets, &first_entry, &data_info), + get_bars_length(DataRepr::Packets, &first_entry, &data_info), (0, MIN_BARS_LENGTH as u16) ); assert_eq!( - get_bars_length(ChartType::Bytes, &first_entry, &data_info), + get_bars_length(DataRepr::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(ChartType::Packets, &first_entry, &data_info), + get_bars_length(DataRepr::Packets, &first_entry, &data_info), (MIN_BARS_LENGTH as u16, 0) ); assert_eq!( - get_bars_length(ChartType::Bytes, &first_entry, &data_info), + get_bars_length(DataRepr::Bytes, &first_entry, &data_info), (MIN_BARS_LENGTH as u16, 0) ); } @@ -964,93 +913,93 @@ fn test_get_bars_length_complex() { let mut data_info = DataInfo::new_for_tests(0, 9, 0, 10); assert_eq!( - get_bars_length(ChartType::Packets, &first_entry, &data_info), + get_bars_length(DataRepr::Packets, &first_entry, &data_info), (0, 16) ); assert_eq!( - get_bars_length(ChartType::Bytes, &first_entry, &data_info), + get_bars_length(DataRepr::Bytes, &first_entry, &data_info), (0, 71) ); data_info = DataInfo::new_for_tests(9, 0, 13, 0); assert_eq!( - get_bars_length(ChartType::Packets, &first_entry, &data_info), + get_bars_length(DataRepr::Packets, &first_entry, &data_info), (16, 0) ); assert_eq!( - get_bars_length(ChartType::Bytes, &first_entry, &data_info), + get_bars_length(DataRepr::Bytes, &first_entry, &data_info), (93, 0) ); data_info = DataInfo::new_for_tests(4, 5, 6, 7); assert_eq!( - get_bars_length(ChartType::Packets, &first_entry, &data_info), + get_bars_length(DataRepr::Packets, &first_entry, &data_info), (7, 9) ); assert_eq!( - get_bars_length(ChartType::Bytes, &first_entry, &data_info), + get_bars_length(DataRepr::Bytes, &first_entry, &data_info), (43, 50) ); data_info = DataInfo::new_for_tests(5, 4, 7, 6); assert_eq!( - get_bars_length(ChartType::Packets, &first_entry, &data_info), + get_bars_length(DataRepr::Packets, &first_entry, &data_info), (9, 7) ); assert_eq!( - get_bars_length(ChartType::Bytes, &first_entry, &data_info), + get_bars_length(DataRepr::Bytes, &first_entry, &data_info), (50, 43) ); data_info = DataInfo::new_for_tests(1, 8, 1, 12); assert_eq!( - get_bars_length(ChartType::Packets, &first_entry, &data_info), + get_bars_length(DataRepr::Packets, &first_entry, &data_info), (MIN_BARS_LENGTH as u16 / 2, 14) ); assert_eq!( - get_bars_length(ChartType::Bytes, &first_entry, &data_info), + get_bars_length(DataRepr::Bytes, &first_entry, &data_info), (7, 86) ); data_info = DataInfo::new_for_tests(8, 1, 12, 1); assert_eq!( - get_bars_length(ChartType::Packets, &first_entry, &data_info), + get_bars_length(DataRepr::Packets, &first_entry, &data_info), (14, MIN_BARS_LENGTH as u16 / 2) ); assert_eq!( - get_bars_length(ChartType::Bytes, &first_entry, &data_info), + get_bars_length(DataRepr::Bytes, &first_entry, &data_info), (86, 7) ); data_info = DataInfo::new_for_tests(6, 1, 10, 1); assert_eq!( - get_bars_length(ChartType::Packets, &first_entry, &data_info), + get_bars_length(DataRepr::Packets, &first_entry, &data_info), (11, MIN_BARS_LENGTH as u16 / 2) ); assert_eq!( - get_bars_length(ChartType::Bytes, &first_entry, &data_info), + get_bars_length(DataRepr::Bytes, &first_entry, &data_info), (71, 7) ); data_info = DataInfo::new_for_tests(1, 6, 1, 9); assert_eq!( - get_bars_length(ChartType::Packets, &first_entry, &data_info), + get_bars_length(DataRepr::Packets, &first_entry, &data_info), (MIN_BARS_LENGTH as u16 / 2, 11,) ); assert_eq!( - get_bars_length(ChartType::Bytes, &first_entry, &data_info), + get_bars_length(DataRepr::Bytes, &first_entry, &data_info), (7, 64) ); data_info = DataInfo::new_for_tests(1, 6, 5, 5); assert_eq!( - get_bars_length(ChartType::Bytes, &first_entry, &data_info), + get_bars_length(DataRepr::Bytes, &first_entry, &data_info), (36, 36) ); data_info = DataInfo::new_for_tests(0, 0, 0, 0); assert_eq!( - get_bars_length(ChartType::Packets, &first_entry, &data_info), + get_bars_length(DataRepr::Packets, &first_entry, &data_info), (0, 0) ); assert_eq!( - get_bars_length(ChartType::Bytes, &first_entry, &data_info), + get_bars_length(DataRepr::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 f4b2aafc..902b9ad0 100644 --- a/src/gui/pages/settings_general_page.rs +++ b/src/gui/pages/settings_general_page.rs @@ -14,6 +14,7 @@ use crate::gui::styles::style_constants::FONT_SIZE_SUBTITLE; use crate::gui::styles::text::TextType; use crate::gui::types::message::Message; +use crate::gui::types::settings::Settings; use crate::mmdb::types::mmdb_reader::{MmdbReader, MmdbReaders}; use crate::translations::translations::language_translation; use crate::translations::translations_2::country_translation; @@ -25,15 +26,15 @@ use crate::utils::types::file_info::FileInfo; use crate::utils::types::icon::Icon; use crate::utils::types::web_page::WebPage; -use crate::{ConfigSettings, Language, RunningPage, Sniffer, StyleType}; +use crate::{Language, Sniffer, StyleType}; -pub fn settings_general_page(sniffer: &Sniffer) -> Container { - let ConfigSettings { +pub fn settings_general_page(sniffer: &Sniffer) -> Container<'_, Message, StyleType> { + let Settings { style, language, color_gradient, .. - } = sniffer.configs.settings; + } = sniffer.conf.settings; let font = style.get_extension().font; let font_headers = style.get_extension().font_headers; @@ -56,16 +57,16 @@ pub fn settings_general_page(sniffer: &Sniffer) -> Container .class(ContainerType::Modal) } -fn column_all_general_setting(sniffer: &Sniffer, font: Font) -> Column { - let ConfigSettings { +fn column_all_general_setting(sniffer: &Sniffer, font: Font) -> Column<'_, Message, StyleType> { + let Settings { language, scale_factor, mmdb_country, mmdb_asn, .. - } = sniffer.configs.settings.clone(); + } = sniffer.conf.settings.clone(); - let is_editable = sniffer.running_page.eq(&RunningPage::Init); + let is_editable = sniffer.running_page.is_none(); let mut column = Column::new() .align_x(Alignment::Center) diff --git a/src/gui/pages/settings_notifications_page.rs b/src/gui/pages/settings_notifications_page.rs index 6c10c1c8..c1df9e9a 100644 --- a/src/gui/pages/settings_notifications_page.rs +++ b/src/gui/pages/settings_notifications_page.rs @@ -3,7 +3,6 @@ use iced::widget::{Checkbox, Column, Container, Row, Scrollable, Space, Text, TextInput}; 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,6 +13,8 @@ use crate::gui::styles::text::TextType; use crate::gui::styles::types::gradient_type::GradientType; use crate::gui::types::message::Message; +use crate::gui::types::settings::Settings; +use crate::networking::types::data_representation::DataRepr; use crate::notifications::types::notifications::{ DataNotification, FavoriteNotification, Notification, }; @@ -25,16 +26,16 @@ 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}; +use crate::{Language, Sniffer, StyleType}; pub fn settings_notifications_page<'a>(sniffer: &Sniffer) -> Container<'a, Message, StyleType> { - let ConfigSettings { + let Settings { style, language, color_gradient, mut notifications, .. - } = sniffer.configs.settings; + } = sniffer.conf.settings; let font = style.get_extension().font; let font_headers = style.get_extension().font_headers; @@ -71,11 +72,14 @@ pub fn settings_notifications_page<'a>(sniffer: &Sniffer) -> Container<'a, Messa .push(Space::with_height(5)); let volume_notification_col = Column::new() + .padding(5) + .spacing(10) .align_x(Alignment::Center) .width(Length::Fill) .push(volume_slider(language, font, notifications.volume)) .push(Scrollable::with_direction( Column::new() + .spacing(10) .align_x(Alignment::Center) .width(Length::Fill) .push(get_data_notify( @@ -103,7 +107,7 @@ fn get_data_notify<'a>( data_notification: DataNotification, language: Language, font: Font, -) -> Column<'a, Message, StyleType> { +) -> Container<'a, Message, StyleType> { let checkbox = Checkbox::new( data_exceeded_translation(language), data_notification.threshold.is_some(), @@ -133,18 +137,16 @@ fn get_data_notify<'a>( let mut ret_val = Column::new().spacing(15).push(checkbox); if data_notification.threshold.is_none() { - Column::new().padding(5).push( - Container::new(ret_val) - .padding(10) - .width(700) - .class(ContainerType::BorderedRound), - ) + Container::new(ret_val) + .padding(15) + .width(700) + .class(ContainerType::BorderedRound) } else { let data_representation_row = row_data_representation( data_notification, language, font, - data_notification.chart_type, + data_notification.data_repr, ); let input_row = input_group_bytes(data_notification, font, language); let sound_row = sound_buttons(Notification::Data(data_notification), font, language); @@ -152,12 +154,11 @@ fn get_data_notify<'a>( .push(sound_row) .push(data_representation_row) .push(input_row); - Column::new().padding(5).push( - Container::new(ret_val) - .padding(10) - .width(700) - .class(ContainerType::BorderedRound), - ) + + Container::new(ret_val) + .padding(15) + .width(700) + .class(ContainerType::BorderedRound) } } @@ -165,7 +166,7 @@ fn get_favorite_notify<'a>( favorite_notification: FavoriteNotification, language: Language, font: Font, -) -> Column<'a, Message, StyleType> { +) -> Container<'a, Message, StyleType> { let checkbox = Checkbox::new( favorite_transmitted_translation(language), favorite_notification.notify_on_favorite, @@ -192,19 +193,15 @@ fn get_favorite_notify<'a>( language, ); ret_val = ret_val.push(sound_row); - Column::new().padding(5).push( - Container::new(ret_val) - .padding(10) - .width(700) - .class(ContainerType::BorderedRound), - ) + Container::new(ret_val) + .padding(15) + .width(700) + .class(ContainerType::BorderedRound) } else { - Column::new().padding(5).push( - Container::new(ret_val) - .padding(10) - .width(700) - .class(ContainerType::BorderedRound), - ) + Container::new(ret_val) + .padding(15) + .width(700) + .class(ContainerType::BorderedRound) } } @@ -220,7 +217,7 @@ fn input_group_bytes<'a>( let input_row = Row::new() .spacing(5) .align_y(Alignment::Center) - .push(Space::with_width(45)) + .padding(Padding::ZERO.left(26)) .push(Text::new(format!("{}:", threshold_translation(language))).font(font)) .push( TextInput::new( @@ -305,7 +302,7 @@ fn sound_buttons<'a>( .width(Length::Shrink) .align_y(Alignment::Center) .spacing(5) - .push(Space::with_width(45)) + .padding(Padding::ZERO.left(26)) .push(Text::new(format!("{}:", sound_translation(language))).font(font)); for option in Sound::ALL { @@ -372,17 +369,17 @@ fn row_data_representation<'a>( data_notification: DataNotification, language: Language, font: Font, - chart_type: ChartType, + data_repr: DataRepr, ) -> Row<'a, Message, StyleType> { let mut ret_val = Row::new() .width(Length::Shrink) .align_y(Alignment::Center) .spacing(5) - .push(Space::with_width(45)) + .padding(Padding::ZERO.left(26)) .push(Text::new(format!("{}:", data_representation_translation(language))).font(font)); - for option in ChartType::ALL { - let is_active = chart_type.eq(&option); + for option in DataRepr::ALL { + let is_active = data_repr.eq(&option); ret_val = ret_val.push( Button::new( Text::new(option.get_label(language).to_owned()) @@ -400,7 +397,7 @@ fn row_data_representation<'a>( }) .on_press(Message::UpdateNotificationSettings( Notification::Data(DataNotification { - chart_type: option, + data_repr: option, ..data_notification }), false, diff --git a/src/gui/pages/settings_style_page.rs b/src/gui/pages/settings_style_page.rs index f3a41a61..efbd68fe 100644 --- a/src/gui/pages/settings_style_page.rs +++ b/src/gui/pages/settings_style_page.rs @@ -19,22 +19,23 @@ use crate::gui::styles::types::palette::Palette; use crate::gui::styles::types::palette_extension::PaletteExtension; use crate::gui::types::message::Message; +use crate::gui::types::settings::Settings; use crate::translations::translations::appearance_title_translation; use crate::translations::translations_2::color_gradients_translation; use crate::translations::translations_3::custom_style_translation; use crate::utils::formatted_strings::get_path_termination_string; use crate::utils::types::file_info::FileInfo; use crate::utils::types::icon::Icon; -use crate::{ConfigSettings, Language, Sniffer, StyleType}; +use crate::{Language, Sniffer, StyleType}; -pub fn settings_style_page(sniffer: &Sniffer) -> Container { - let ConfigSettings { +pub fn settings_style_page(sniffer: &Sniffer) -> Container<'_, Message, StyleType> { + let Settings { style, language, color_gradient, style_path, .. - } = sniffer.configs.settings.clone(); + } = sniffer.conf.settings.clone(); let PaletteExtension { font, font_headers, .. } = style.get_extension(); diff --git a/src/gui/pages/thumbnail_page.rs b/src/gui/pages/thumbnail_page.rs index 86a3ae61..17914b48 100644 --- a/src/gui/pages/thumbnail_page.rs +++ b/src/gui/pages/thumbnail_page.rs @@ -4,14 +4,14 @@ use iced::widget::{Column, Container, Row, Rule, Space, Text, vertical_space}; use iced::{Alignment, Font, Length}; -use crate::chart::types::chart_type::ChartType; use crate::chart::types::donut_chart::donut_chart; -use crate::configs::types::config_settings::ConfigSettings; use crate::countries::country_utils::get_flag_tooltip; use crate::gui::sniffer::Sniffer; use crate::gui::styles::style_constants::FONT_SIZE_FOOTER; use crate::gui::styles::types::style_type::StyleType; use crate::gui::types::message::Message; +use crate::gui::types::settings::Settings; +use crate::networking::types::data_representation::DataRepr; use crate::networking::types::host::{Host, ThumbnailHost}; use crate::networking::types::info_traffic::InfoTraffic; use crate::report::get_report_entries::{get_host_entries, get_service_entries}; @@ -23,13 +23,16 @@ const MAX_CHARS_SERVICE: usize = 13; /// Computes the body of the thumbnail view -pub fn thumbnail_page(sniffer: &Sniffer) -> Container { - let ConfigSettings { style, .. } = sniffer.configs.settings; +pub fn thumbnail_page(sniffer: &Sniffer) -> Container<'_, Message, StyleType> { + let Settings { style, .. } = sniffer.conf.settings; let font = style.get_extension().font; - let filtered = sniffer.info_traffic.tot_data_info.tot_packets(); + let tot_packets = sniffer + .info_traffic + .tot_data_info + .tot_data(DataRepr::Packets); - if filtered == 0 { + if tot_packets == 0 { return Container::new( Column::new() .push(vertical_space()) @@ -41,19 +44,18 @@ pub fn thumbnail_page(sniffer: &Sniffer) -> Container { } let info_traffic = &sniffer.info_traffic; - let chart_type = sniffer.traffic_chart.chart_type; + let data_repr = sniffer.traffic_chart.data_repr; - let (in_data, out_data, filtered_out, dropped) = info_traffic.get_thumbnail_data(chart_type); + let (in_data, out_data, dropped) = info_traffic.get_thumbnail_data(data_repr); let charts = Row::new() .padding(5) .height(Length::Fill) .align_y(Alignment::Center) .push(donut_chart( - chart_type, + data_repr, in_data, out_data, - filtered_out, dropped, font, sniffer.thumbnail, @@ -71,16 +73,16 @@ pub fn thumbnail_page(sniffer: &Sniffer) -> Container { .align_y(Alignment::Start) .push(host_col( info_traffic, - chart_type, + data_repr, font, - sniffer.host_sort_type, + sniffer.conf.host_sort_type, )) .push(Rule::vertical(10)) .push(service_col( info_traffic, - chart_type, + data_repr, font, - sniffer.service_sort_type, + sniffer.conf.service_sort_type, )); let content = Column::new() @@ -93,7 +95,7 @@ pub fn thumbnail_page(sniffer: &Sniffer) -> Container { fn host_col<'a>( info_traffic: &InfoTraffic, - chart_type: ChartType, + data_repr: DataRepr, font: Font, sort_type: SortType, ) -> Column<'a, Message, StyleType> { @@ -101,7 +103,7 @@ fn host_col<'a>( .padding([0, 5]) .spacing(3) .width(Length::FillPortion(2)); - let hosts = get_host_entries(info_traffic, chart_type, sort_type); + let hosts = get_host_entries(info_traffic, data_repr, sort_type); let mut thumbnail_hosts = Vec::new(); for (host, data_info_host) in &hosts { @@ -136,12 +138,12 @@ fn host_col<'a>( fn service_col<'a>( info_traffic: &InfoTraffic, - chart_type: ChartType, + data_repr: DataRepr, 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, sort_type); + let services = get_service_entries(info_traffic, data_repr, 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/pages/types/running_page.rs b/src/gui/pages/types/running_page.rs index a37817c8..f0693964 100644 --- a/src/gui/pages/types/running_page.rs +++ b/src/gui/pages/types/running_page.rs @@ -3,13 +3,13 @@ use crate::translations::translations_2::inspect_translation; use crate::utils::types::icon::Icon; use crate::{Language, StyleType}; +use serde::{Deserialize, Serialize}; -/// This enum defines the current GUI page. -#[derive(PartialEq, Eq, Clone, Copy, Debug)] +/// This enum defines the current running page. +#[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize, Default)] pub enum RunningPage { - /// Initial page. - Init, /// Overview page. + #[default] Overview, /// Inspect page. Inspect, @@ -29,7 +29,6 @@ pub fn get_tab_label(&self, language: Language) -> &str { RunningPage::Overview => overview_translation(language), RunningPage::Inspect => inspect_translation(language), RunningPage::Notifications => notifications_translation(language), - RunningPage::Init => "", } } @@ -38,7 +37,6 @@ pub fn next(self) -> Self { RunningPage::Overview => RunningPage::Inspect, RunningPage::Inspect => RunningPage::Notifications, RunningPage::Notifications => RunningPage::Overview, - RunningPage::Init => RunningPage::Init, } } @@ -47,7 +45,6 @@ pub fn previous(self) -> Self { RunningPage::Overview => RunningPage::Notifications, RunningPage::Inspect => RunningPage::Overview, RunningPage::Notifications => RunningPage::Inspect, - RunningPage::Init => RunningPage::Init, } } @@ -56,7 +53,6 @@ pub fn icon<'a>(self) -> iced::widget::Text<'a, StyleType> { RunningPage::Overview => Icon::Overview, RunningPage::Inspect => Icon::Inspect, RunningPage::Notifications => Icon::Notification, - RunningPage::Init => Icon::Sniffnet, } .to_text() } diff --git a/src/gui/pages/types/settings_page.rs b/src/gui/pages/types/settings_page.rs index 2dbaf9ca..d2457507 100644 --- a/src/gui/pages/types/settings_page.rs +++ b/src/gui/pages/types/settings_page.rs @@ -3,11 +3,13 @@ use crate::translations::translations_3::general_translation; use crate::utils::types::icon::Icon; use crate::{Language, StyleType}; +use serde::{Deserialize, Serialize}; /// This enum defines the current settings page. -#[derive(PartialEq, Eq, Clone, Copy, Debug)] +#[derive(PartialEq, Eq, Clone, Copy, Debug, Serialize, Deserialize, Default)] pub enum SettingsPage { /// Settings Notifications page. + #[default] Notifications, /// Settings Appearance page. Appearance, diff --git a/src/gui/sniffer.rs b/src/gui/sniffer.rs index 79f74228..e30aea97 100644 --- a/src/gui/sniffer.rs +++ b/src/gui/sniffer.rs @@ -1,5 +1,6 @@ //! Module defining the application structure: messages, updates, subscriptions. +use crate::gui::pages::overview_page::waiting_page; use async_channel::Receiver; use iced::Event::{Keyboard, Window}; use iced::keyboard::key::Named; @@ -17,9 +18,6 @@ use std::thread; use std::time::Duration; -use crate::configs::types::config_window::{ - ConfigWindow, PositionTuple, ScaleAndCheck, SizeTuple, ToPoint, ToSize, -}; use crate::gui::components::footer::footer; use crate::gui::components::header::header; use crate::gui::components::modal::{get_clear_all_overlay, get_exit_overlay, modal}; @@ -37,46 +35,48 @@ use crate::gui::pages::types::settings_page::SettingsPage; use crate::gui::styles::types::custom_palette::{CustomPalette, ExtraStyles}; use crate::gui::styles::types::palette::Palette; -use crate::gui::types::export_pcap::ExportPcap; +use crate::gui::types::conf::Conf; +use crate::gui::types::config_window::{ + ConfigWindow, PositionTuple, ScaleAndCheck, SizeTuple, ToPoint, ToSize, +}; use crate::gui::types::message::Message; +use crate::gui::types::settings::Settings; use crate::gui::types::timing_events::TimingEvents; use crate::mmdb::asn::ASN_MMDB; use crate::mmdb::country::COUNTRY_MMDB; use crate::mmdb::types::mmdb_reader::{MmdbReader, MmdbReaders}; 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::filters::Filters; +use crate::networking::types::capture_context::{ + CaptureContext, CaptureSource, CaptureSourcePicklist, MyPcapImport, +}; +use crate::networking::types::data_representation::DataRepr; use crate::networking::types::host::{Host, HostMessage}; use crate::networking::types::host_data_states::HostDataStates; 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::{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; use crate::report::types::search_parameters::SearchParameters; -use crate::report::types::sort_type::SortType; use crate::translations::types::language::Language; use crate::utils::check_updates::set_newer_release_status; 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, StyleType, TrafficChart, location}; +use crate::{StyleType, TrafficChart, location}; pub const FONT_FAMILY_NAME: &str = "Sarasa Mono SC for Sniffnet"; pub const ICON_FONT_FAMILY_NAME: &str = "Icons for Sniffnet"; -/// Struct on which the gui is based +/// Struct on which the GUI is based /// -/// It contains gui statuses and network traffic statistics +/// It carries statuses, network traffic statistics, and more pub struct Sniffer { - /// Application's configurations: settings, window properties, name of last device sniffed - pub configs: Configs, + /// Parameters that are persistent across runs + pub conf: Conf, /// Capture receiver clone (to close the channel after every run), with the current capture id (to ignore pending messages from previous captures) pub current_capture_rx: (usize, Option>), /// Capture data @@ -93,28 +93,18 @@ pub struct Sniffer { pub capture_source: CaptureSource, /// List of network devices pub my_devices: Vec, - /// Active filters on the observed traffic - pub filters: Filters, /// Signals if a pcap error occurred pub pcap_error: Option, /// Messages status pub dots_pulse: (String, u8), /// Chart displayed pub traffic_chart: TrafficChart, - /// Report sort type (inspect page) - pub report_sort_type: ReportSortType, - /// Host sort type (overview page) - pub host_sort_type: SortType, - /// Service sort type (overview page) - pub service_sort_type: SortType, /// Currently displayed modal; None if no modal is displayed pub modal: Option, /// Currently displayed settings page; None if settings is closed pub settings_page: Option, - /// Remembers the last opened setting page - pub last_opened_setting: SettingsPage, - /// Defines the current running page - pub running_page: RunningPage, + /// Defines the current running page; None if initial page + pub running_page: Option, /// Number of unread notifications pub unread_notifications: usize, /// Search parameters of inspect page @@ -125,49 +115,40 @@ pub struct Sniffer { pub mmdb_readers: MmdbReaders, /// Time-related events pub timing_events: TimingEvents, - /// Information about PCAP file export - pub export_pcap: ExportPcap, /// Whether thumbnail mode is currently active pub thumbnail: bool, /// Window id pub id: Option, /// Host data for filter dropdowns (comboboxes) pub host_data_states: HostDataStates, - /// Import path for PCAP file - pub import_pcap_path: String, } impl Sniffer { - pub fn new(configs: Configs) -> Self { - let ConfigSettings { + pub fn new(conf: Conf) -> Self { + let Settings { style, language, mmdb_country, mmdb_asn, .. - } = configs.settings.clone(); - let device = configs.device.to_my_device(); + } = conf.settings.clone(); + let capture_source = CaptureSource::from_conf(&conf); Self { - configs, + conf, current_capture_rx: (0, None), info_traffic: InfoTraffic::default(), addresses_resolved: HashMap::new(), favorite_hosts: HashSet::new(), logged_notifications: (VecDeque::new(), 0), newer_release_available: None, - capture_source: CaptureSource::Device(device), + capture_source, my_devices: Vec::new(), - filters: Filters::default(), pcap_error: None, dots_pulse: (".".to_string(), 0), traffic_chart: TrafficChart::new(style, language), - report_sort_type: ReportSortType::default(), - host_sort_type: SortType::default(), - service_sort_type: SortType::default(), modal: None, settings_page: None, - last_opened_setting: SettingsPage::Notifications, - running_page: RunningPage::Init, + running_page: None, unread_notifications: 0, search: SearchParameters::default(), page_number: 1, @@ -176,11 +157,9 @@ pub fn new(configs: Configs) -> Self { asn: Arc::new(MmdbReader::from(&mmdb_asn, ASN_MMDB)), }, timing_events: TimingEvents::default(), - export_pcap: ExportPcap::default(), thumbnail: false, id: None, host_data_states: HostDataStates::default(), - import_pcap_path: String::new(), } } @@ -278,51 +257,45 @@ pub fn update(&mut self, message: Message) -> Task { } } Message::DeviceSelection(name) => self.set_device(&name), - Message::IpVersionSelection(version, insert) => { - if insert { - self.filters.ip_versions.insert(version); + Message::SetCaptureSource(cs_pick) => { + self.conf.capture_source_picklist = cs_pick; + return if cs_pick == CaptureSourcePicklist::File { + Task::done(Message::SetPcapImport(self.conf.import_pcap_path.clone())) } else { - self.filters.ip_versions.remove(&version); - } + Task::done(Message::DeviceSelection( + self.conf.device.device_name.clone(), + )) + }; } - Message::ProtocolSelection(protocol, insert) => { - if insert { - self.filters.protocols.insert(protocol); - } else { - self.filters.protocols.remove(&protocol); - } + Message::ToggleFilters => { + self.conf.filters.toggle(); } - Message::AddressFilter(value) => { - if let Some(collection) = AddressCollection::new(&value) { - self.filters.address_collection = collection; - } - self.filters.address_str = value; + Message::BpfFilter(value) => { + self.conf.filters.set_bpf(value); } - Message::PortFilter(value) => { - if let Some(collection) = PortCollection::new(&value) { - self.filters.port_collection = collection; - } - self.filters.port_str = value; - } - Message::ChartSelection(unit) => self.traffic_chart.change_kind(unit), + Message::DataReprSelection(unit) => self.traffic_chart.change_kind(unit), Message::ReportSortSelection(sort) => { self.page_number = 1; - self.report_sort_type = sort; + self.conf.report_sort_type = sort; } Message::OpenWebPage(web_page) => Self::open_web(&web_page), - Message::Start => return self.start(), + Message::Start => { + if self.is_capture_source_consistent() { + return self.start(); + } + } Message::Reset => self.reset(), Message::Style(style) => { - self.configs.settings.style = style; + self.conf.settings.style = style; self.traffic_chart.change_style(style); } Message::LoadStyle(path) => { - self.configs.settings.style_path.clone_from(&path); + self.conf.settings.style_path.clone_from(&path); if let Ok(palette) = Palette::from_file(path) { let style = StyleType::Custom(ExtraStyles::CustomToml( CustomPalette::from_palette(palette), )); - self.configs.settings.style = style; + self.conf.settings.style = style; self.traffic_chart.change_style(style); } } @@ -336,22 +309,24 @@ pub fn update(&mut self, message: Message) -> Task { Message::OpenSettings(settings_page) => { if self.modal.is_none() { self.settings_page = Some(settings_page); + self.conf.last_opened_setting = settings_page; } } Message::OpenLastSettings => { if self.modal.is_none() && self.settings_page.is_none() { - self.settings_page = Some(self.last_opened_setting); + self.settings_page = Some(self.conf.last_opened_setting); } } Message::CloseSettings => self.close_settings(), Message::ChangeRunningPage(running_page) => { - self.running_page = running_page; + self.running_page = Some(running_page); + self.conf.last_opened_page = running_page; if running_page.eq(&RunningPage::Notifications) { self.unread_notifications = 0; } } Message::LanguageSelection(language) => { - self.configs.settings.language = language; + self.conf.settings.language = language; self.traffic_chart.change_language(language); } Message::UpdateNotificationSettings(notification, emit_sound) => { @@ -359,7 +334,7 @@ pub fn update(&mut self, message: Message) -> Task { } Message::ChangeVolume(volume) => { play(Sound::Pop, volume); - self.configs.settings.notifications.volume = volume; + self.conf.settings.notifications.volume = volume; } Message::ClearAllNotifications => { self.logged_notifications.0 = VecDeque::new(); @@ -385,7 +360,8 @@ pub fn update(&mut self, message: Message) -> Task { self.host_data_states.update_states(¶meters); self.page_number = 1; - self.running_page = RunningPage::Inspect; + self.running_page = Some(RunningPage::Inspect); + self.conf.last_opened_page = RunningPage::Inspect; self.search = parameters; } Message::UpdatePageNumber(increment) => { @@ -398,7 +374,9 @@ pub fn update(&mut self, message: Message) -> Task { } } Message::ArrowPressed(increment) => { - if self.running_page.eq(&RunningPage::Inspect) + if self + .running_page + .is_some_and(|p| p.eq(&RunningPage::Inspect)) && self.settings_page.is_none() && self.modal.is_none() { @@ -407,41 +385,40 @@ pub fn update(&mut self, message: Message) -> Task { } Message::WindowFocused => self.timing_events.focus_now(), Message::GradientsSelection(gradient_type) => { - self.configs.settings.color_gradient = gradient_type; + self.conf.settings.color_gradient = gradient_type; } Message::ChangeScaleFactor(slider_val) => { let scale_factor_str = format!("{:.1}", 3.0_f64.powf(slider_val)); - self.configs.settings.scale_factor = scale_factor_str.parse().unwrap_or(1.0); + self.conf.settings.scale_factor = scale_factor_str.parse().unwrap_or(1.0); } Message::WindowMoved(x, y) => { - let scale_factor = self.configs.settings.scale_factor; + let scale_factor = self.conf.settings.scale_factor; let scaled = PositionTuple(x, y).scale_and_check(scale_factor); if self.thumbnail { - self.configs.window.thumbnail_position = scaled; + self.conf.window.thumbnail_position = scaled; } else { - self.configs.window.position = scaled; + self.conf.window.position = scaled; } } Message::WindowResized(width, height) => { if !self.thumbnail { - let scale_factor = self.configs.settings.scale_factor; - self.configs.window.size = - SizeTuple(width, height).scale_and_check(scale_factor); + let scale_factor = self.conf.settings.scale_factor; + self.conf.window.size = SizeTuple(width, height).scale_and_check(scale_factor); } else if !self.timing_events.was_just_thumbnail_enter() { return Task::done(Message::ToggleThumbnail(true)); } } Message::CustomCountryDb(db) => { - self.configs.settings.mmdb_country.clone_from(&db); + self.conf.settings.mmdb_country.clone_from(&db); self.mmdb_readers.country = Arc::new(MmdbReader::from(&db, COUNTRY_MMDB)); } Message::CustomAsnDb(db) => { - self.configs.settings.mmdb_asn.clone_from(&db); + self.conf.settings.mmdb_asn.clone_from(&db); self.mmdb_readers.asn = Arc::new(MmdbReader::from(&db, ASN_MMDB)); } Message::QuitWrapper => return self.quit_wrapper(), Message::Quit => { - let _ = self.configs.clone().store(); + let _ = self.conf.clone().store(); return window::close(self.id.unwrap_or_else(Id::unique)); } Message::CopyIp(ip) => { @@ -450,24 +427,24 @@ pub fn update(&mut self, message: Message) -> Task { } Message::OpenFile(old_file, file_info, consumer_message) => { return Task::perform( - Self::open_file(old_file, file_info, self.configs.settings.language), + Self::open_file(old_file, file_info, self.conf.settings.language), consumer_message, ); } Message::HostSortSelection(sort_type) => { - self.host_sort_type = sort_type; + self.conf.host_sort_type = sort_type; } Message::ServiceSortSelection(sort_type) => { - self.service_sort_type = sort_type; + self.conf.service_sort_type = sort_type; } Message::ToggleExportPcap => { - self.export_pcap.toggle(); + self.conf.export_pcap.toggle(); } Message::OutputPcapDir(path) => { - self.export_pcap.set_directory(path); + self.conf.export_pcap.set_directory(path); } Message::OutputPcapFile(name) => { - self.export_pcap.set_file_name(&name); + self.conf.export_pcap.set_file_name(&name); } Message::ToggleThumbnail(triggered_by_resize) => { let window_id = self.id.unwrap_or_else(Id::unique); @@ -476,9 +453,9 @@ pub fn update(&mut self, message: Message) -> Task { self.traffic_chart.thumbnail = self.thumbnail; return if self.thumbnail { - let scale_factor = self.configs.settings.scale_factor; + let scale_factor = self.conf.settings.scale_factor; let size = ConfigWindow::thumbnail_size(scale_factor).to_size(); - let position = self.configs.window.thumbnail_position; + let position = self.conf.window.thumbnail_position; self.timing_events.thumbnail_enter_now(); Task::batch([ window::maximize(window_id, false), @@ -488,7 +465,10 @@ pub fn update(&mut self, message: Message) -> Task { window::change_level(window_id, Level::AlwaysOnTop), ]) } else { - if self.running_page.eq(&RunningPage::Notifications) { + if self + .running_page + .is_some_and(|p| p.eq(&RunningPage::Notifications)) + { self.unread_notifications = 0; } let mut commands = vec![ @@ -496,8 +476,8 @@ pub fn update(&mut self, message: Message) -> Task { window::change_level(window_id, Level::Normal), ]; if !triggered_by_resize { - let size = self.configs.window.size.to_size(); - let position = self.configs.window.position.to_point(); + let size = self.conf.window.size.to_size(); + let position = self.conf.window.position.to_point(); commands.push(window::move_to(window_id, position)); commands.push(window::resize(window_id, size)); } @@ -512,7 +492,7 @@ pub fn update(&mut self, message: Message) -> Task { } } Message::CtrlTPressed => { - if self.running_page.ne(&RunningPage::Init) + if self.running_page.is_some() && self.settings_page.is_none() && self.modal.is_none() && !self.timing_events.was_just_thumbnail_enter() @@ -521,16 +501,16 @@ pub fn update(&mut self, message: Message) -> Task { } } Message::ScaleFactorShortcut(increase) => { - let scale_factor = self.configs.settings.scale_factor; + let scale_factor = self.conf.settings.scale_factor; if !(scale_factor > 2.99 && increase || scale_factor < 0.31 && !increase) { let delta = if increase { 0.1 } else { -0.1 }; - self.configs.settings.scale_factor += delta; + self.conf.settings.scale_factor += delta; } } Message::SetNewerReleaseStatus(status) => self.newer_release_available = status, Message::SetPcapImport(path) => { if !path.is_empty() { - self.import_pcap_path.clone_from(&path); + self.conf.import_pcap_path.clone_from(&path); self.capture_source = CaptureSource::File(MyPcapImport::new(path)); } } @@ -565,13 +545,13 @@ pub fn update(&mut self, message: Message) -> Task { Task::none() } - pub fn view(&self) -> Element { - let ConfigSettings { + pub fn view(&self) -> Element<'_, Message, StyleType> { + let Settings { style, language, color_gradient, .. - } = self.configs.settings; + } = self.conf.settings; let font = style.get_extension().font; let font_headers = style.get_extension().font_headers; @@ -581,10 +561,18 @@ pub fn view(&self) -> Element { thumbnail_page(self) } else { match self.running_page { - RunningPage::Init => initial_page(self), - RunningPage::Overview => overview_page(self), - RunningPage::Inspect => inspect_page(self), - RunningPage::Notifications => notifications_page(self), + None => initial_page(self), + Some(running_page) => { + if let Some(waiting_page) = waiting_page(self) { + waiting_page + } else { + match running_page { + RunningPage::Overview => overview_page(self), + RunningPage::Inspect => inspect_page(self), + RunningPage::Notifications => notifications_page(self), + } + } + } } }; @@ -654,11 +642,11 @@ pub fn subscription(&self) -> Subscription { } pub fn theme(&self) -> StyleType { - self.configs.settings.style + self.conf.settings.style } pub fn scale_factor(&self) -> f64 { - self.configs.settings.scale_factor + self.conf.settings.scale_factor } /// Updates threshold if it hasn't been edited for a while @@ -666,17 +654,13 @@ fn update_threshold(&mut self) { // Ignore if just edited if let Some(temp_threshold) = self.timing_events.threshold_adjust_expired_take() { // Apply the temporary threshold to the actual config - self.configs - .settings - .notifications - .data_notification - .threshold = temp_threshold.threshold; - self.configs + self.conf.settings.notifications.data_notification.threshold = temp_threshold.threshold; + self.conf .settings .notifications .data_notification .byte_multiple = temp_threshold.byte_multiple; - self.configs + self.conf .settings .notifications .data_notification @@ -686,29 +670,25 @@ fn update_threshold(&mut self) { 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 { + if self.info_traffic.tot_data_info.tot_data(DataRepr::Packets) == 0 { return; } let emitted_notifications = notify_and_log( &mut self.logged_notifications, - self.configs.settings.notifications, + self.conf.settings.notifications, &msg, &self.favorite_hosts, &self.capture_source, ); - if self.thumbnail || self.running_page.ne(&RunningPage::Notifications) { + if self.thumbnail + || self + .running_page + .is_some_and(|p| p.ne(&RunningPage::Notifications)) + { self.unread_notifications += emitted_notifications; } 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(); - // update ConfigDevice stored if different from last sniffed device - let last_device_name_sniffed = self.configs.device.device_name.clone(); - if current_device_name.ne(&last_device_name_sniffed) { - self.configs.device.device_name = current_device_name; - } - } // update host dropdowns self.host_data_states.update_states(&self.search); } @@ -739,18 +719,19 @@ fn start(&mut self) -> Task { let current_device_name = &self.capture_source.get_name(); self.set_device(current_device_name); } - let pcap_path = self.export_pcap.full_path(); - let capture_context = CaptureContext::new(&self.capture_source, pcap_path.as_ref()); + let pcap_path = self.conf.export_pcap.full_path(); + let capture_context = + CaptureContext::new(&self.capture_source, pcap_path.as_ref(), &self.conf.filters); self.pcap_error = capture_context.error().map(ToString::to_string); - self.running_page = RunningPage::Overview; + self.running_page = Some(self.conf.last_opened_page); if capture_context.error().is_none() { // no pcap error let curr_cap_id = self.current_capture_rx.0; - let filters = self.filters.clone(); let mmdb_readers = self.mmdb_readers.clone(); self.capture_source .set_link_type(capture_context.my_link_type()); + self.capture_source.set_addresses(); let capture_source = self.capture_source.clone(); self.traffic_chart .change_capture_source(matches!(capture_source, CaptureSource::Device(_))); @@ -761,7 +742,6 @@ fn start(&mut self) -> Task { parse_packets( curr_cap_id, capture_source, - &filters, &mmdb_readers, capture_context, &tx, @@ -787,9 +767,9 @@ fn reset(&mut self) { if let Some(rx) = &self.current_capture_rx.1 { rx.close(); } - let ConfigSettings { + let Settings { style, language, .. - } = self.configs.settings; + } = self.conf.settings; // increment capture id to ignore pending messages from previous captures self.current_capture_rx = (self.current_capture_rx.0 + 1, None); self.info_traffic = InfoTraffic::default(); @@ -798,12 +778,9 @@ fn reset(&mut self) { self.logged_notifications = (VecDeque::new(), 0); self.pcap_error = None; self.traffic_chart = TrafficChart::new(style, language); - self.report_sort_type = ReportSortType::default(); - self.host_sort_type = SortType::default(); - self.service_sort_type = SortType::default(); self.modal = None; self.settings_page = None; - self.running_page = RunningPage::Init; + self.running_page = None; self.unread_notifications = 0; self.search = SearchParameters::default(); self.page_number = 1; @@ -814,6 +791,7 @@ fn reset(&mut self) { fn set_device(&mut self, name: &str) { for my_dev in &self.my_devices { if my_dev.get_name().eq(&name) { + self.conf.device.device_name = name.to_string(); self.capture_source = CaptureSource::Device(my_dev.clone()); break; } @@ -822,13 +800,8 @@ fn set_device(&mut self, name: &str) { fn fetch_devices(&mut self) { self.my_devices.clear(); + self.capture_source.set_addresses(); for dev in Device::list().log_err(location!()).unwrap_or_default() { - if matches!(&self.capture_source, CaptureSource::Device(_)) - && dev.name.eq(&self.capture_source.get_name()) - { - // refresh active addresses - self.capture_source.set_addresses(dev.addresses.clone()); - } let my_dev = MyDevice::from_pcap_device(dev); self.my_devices.push(my_dev); } @@ -854,10 +827,7 @@ fn add_or_remove_favorite(&mut self, host: &Host, add: bool) { } fn close_settings(&mut self) { - if let Some(page) = self.settings_page { - self.last_opened_setting = page; - self.settings_page = None; - } + self.settings_page = None; } /// Don't update adjustments to threshold immediately: @@ -865,10 +835,10 @@ fn close_settings(&mut self) { /// 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 notifications = self.conf.settings.notifications; let sound = match notification { Notification::Data(DataNotification { - chart_type, + data_repr, threshold, byte_multiple, sound, @@ -880,7 +850,7 @@ fn update_notifications_settings(&mut self, notification: Notification, emit_sou || temp_threshold.previous_threshold != previous_threshold { temp_threshold = DataNotification { - chart_type, + data_repr, threshold, byte_multiple, sound, @@ -889,37 +859,29 @@ fn update_notifications_settings(&mut self, notification: Notification, emit_sou self.timing_events.threshold_adjust_now(temp_threshold); } if threshold.is_some() != notifications.data_notification.threshold.is_some() { - self.configs - .settings - .notifications - .data_notification - .threshold = threshold; - self.configs + self.conf.settings.notifications.data_notification.threshold = threshold; + self.conf .settings .notifications .data_notification .byte_multiple = byte_multiple; - self.configs + self.conf .settings .notifications .data_notification .previous_threshold = previous_threshold; } - self.configs.settings.notifications.data_notification.sound = sound; - self.configs - .settings - .notifications - .data_notification - .chart_type = chart_type; + self.conf.settings.notifications.data_notification.sound = sound; + self.conf.settings.notifications.data_notification.data_repr = data_repr; sound } Notification::Favorite(favorite_notification) => { - self.configs.settings.notifications.favorite_notification = favorite_notification; + self.conf.settings.notifications.favorite_notification = favorite_notification; favorite_notification.sound } }; if emit_sound { - play(sound, self.configs.settings.notifications.volume); + play(sound, self.conf.settings.notifications.volume); } } @@ -928,7 +890,7 @@ 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; + let notifications = self.conf.settings.notifications; notifications.data_notification } } @@ -937,26 +899,29 @@ fn switch_page(&mut self, next: bool) { match (self.running_page, self.settings_page, self.modal.is_none()) { (_, Some(current_setting), true) => { // Settings opened - if next { - self.settings_page = Some(current_setting.next()); + let new_setting = if next { + current_setting.next() } else { - self.settings_page = Some(current_setting.previous()); - } + current_setting.previous() + }; + self.settings_page = Some(new_setting); + self.conf.last_opened_setting = new_setting; } - ( - RunningPage::Inspect | RunningPage::Notifications | RunningPage::Overview, - None, - true, - ) => { + (Some(current_page), None, true) => { // Running with no overlays - 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() + if self.info_traffic.tot_data_info.tot_data(DataRepr::Packets) > 0 { + // Running with no overlays and some packets + let new_page = if next { + current_page.next() } else { - self.running_page.previous() + current_page.previous() }; - if self.running_page.eq(&RunningPage::Notifications) { + self.running_page = Some(new_page); + self.conf.last_opened_page = new_page; + if self + .running_page + .is_some_and(|p| p.eq(&RunningPage::Notifications)) + { self.unread_notifications = 0; } } @@ -966,13 +931,8 @@ fn switch_page(&mut self, next: bool) { } fn shortcut_return(&mut self) -> Task { - if self.running_page.eq(&RunningPage::Init) - && self.settings_page.is_none() - && self.modal.is_none() - { - if self.filters.are_valid() { - return Task::done(Message::Start); - } + if self.running_page.is_none() && self.settings_page.is_none() && self.modal.is_none() { + return Task::done(Message::Start); } else if self.modal.eq(&Some(MyModal::Reset)) { return Task::done(Message::Reset); } else if self.modal.eq(&Some(MyModal::Quit)) { @@ -994,8 +954,9 @@ fn shortcut_esc(&mut self) -> Task { // also called when the backspace shortcut is pressed fn reset_button_pressed(&mut self) -> Task { - if self.running_page.ne(&RunningPage::Init) { - return if self.info_traffic.all_packets == 0 && self.settings_page.is_none() { + if self.running_page.is_some() { + let tot_packets = self.info_traffic.tot_data_info.tot_data(DataRepr::Packets); + return if tot_packets == 0 && self.settings_page.is_none() { Task::done(Message::Reset) } else { Task::done(Message::ShowModal(MyModal::Reset)) @@ -1005,7 +966,8 @@ fn reset_button_pressed(&mut self) -> Task { } fn quit_wrapper(&mut self) -> Task { - if self.running_page.eq(&RunningPage::Init) || self.info_traffic.all_packets == 0 { + let tot_packets = self.info_traffic.tot_data_info.tot_data(DataRepr::Packets); + if self.running_page.is_none() || tot_packets == 0 { Task::done(Message::Quit) } else if self.thumbnail { // TODO: uncomment once issue #653 is fixed @@ -1020,7 +982,9 @@ fn quit_wrapper(&mut self) -> Task { } fn shortcut_ctrl_d(&mut self) -> Task { - if self.running_page.eq(&RunningPage::Notifications) + if self + .running_page + .is_some_and(|p| p.eq(&RunningPage::Notifications)) && !self.logged_notifications.0.is_empty() { return Task::done(Message::ShowModal(MyModal::ClearAll)); @@ -1091,6 +1055,13 @@ fn register_sigint_handler() -> Task { Task::run(rx, |()| Message::Quit) } + + pub fn is_capture_source_consistent(&self) -> bool { + self.conf.capture_source_picklist == CaptureSourcePicklist::Device + && matches!(self.capture_source, CaptureSource::Device(_)) + || self.conf.capture_source_picklist == CaptureSourcePicklist::File + && matches!(self.capture_source, CaptureSource::File(_)) + } } #[cfg(test)] @@ -1104,15 +1075,22 @@ mod tests { use serial_test::{parallel, serial}; - use crate::configs::types::config_window::{PositionTuple, SizeTuple}; use crate::countries::types::country::Country; use crate::gui::components::types::my_modal::MyModal; use crate::gui::pages::types::settings_page::SettingsPage; use crate::gui::styles::types::custom_palette::ExtraStyles; use crate::gui::styles::types::gradient_type::GradientType; + use crate::gui::types::conf::Conf; + use crate::gui::types::config_window::{PositionTuple, SizeTuple}; + use crate::gui::types::export_pcap::ExportPcap; + use crate::gui::types::filters::Filters; use crate::gui::types::message::Message; + use crate::gui::types::settings::Settings; use crate::gui::types::timing_events::TimingEvents; + use crate::networking::types::capture_context::CaptureSourcePicklist; + use crate::networking::types::config_device::ConfigDevice; use crate::networking::types::data_info::DataInfo; + use crate::networking::types::data_representation::DataRepr; use crate::networking::types::host::Host; use crate::networking::types::traffic_direction::TrafficDirection; use crate::notifications::types::logged_notification::{ @@ -1122,216 +1100,120 @@ mod tests { DataNotification, FavoriteNotification, Notification, Notifications, }; use crate::notifications::types::sound::Sound; - use crate::report::types::report_col::ReportCol; use crate::report::types::sort_type::SortType; - use crate::{ - ByteMultiple, ChartType, ConfigDevice, ConfigSettings, ConfigWindow, Configs, IpVersion, - Language, Protocol, ReportSortType, RunningPage, Sniffer, StyleType, - }; + use crate::{ByteMultiple, ConfigWindow, Language, RunningPage, Sniffer, StyleType}; // helpful to clean up files generated from tests impl Drop for Sniffer { fn drop(&mut self) { - let settings_path_str = ConfigSettings::test_path(); - let settings_path = Path::new(&settings_path_str); - if settings_path.exists() { - remove_file(ConfigSettings::test_path()).unwrap(); - } - - let device_path_str = ConfigDevice::test_path(); - let device_path = Path::new(&device_path_str); - if device_path.exists() { - remove_file(ConfigDevice::test_path()).unwrap(); - } - - let window_path_str = ConfigWindow::test_path(); - let window_path = Path::new(&window_path_str); - if window_path.exists() { - remove_file(ConfigWindow::test_path()).unwrap(); + let conf_path_str = Conf::test_path(); + let conf_path = Path::new(&conf_path_str); + if conf_path.exists() { + remove_file(Conf::test_path()).unwrap(); } } } - #[test] - #[parallel] // needed to not collide with other tests generating configs files - fn test_correctly_update_ip_version() { - let mut sniffer = Sniffer::new(Configs::default()); - - assert_eq!(sniffer.filters.ip_versions, HashSet::from(IpVersion::ALL)); - sniffer.update(Message::IpVersionSelection(IpVersion::IPv6, true)); - assert_eq!(sniffer.filters.ip_versions, HashSet::from(IpVersion::ALL)); - sniffer.update(Message::IpVersionSelection(IpVersion::IPv4, false)); - assert_eq!( - sniffer.filters.ip_versions, - HashSet::from([IpVersion::IPv6]) - ); - sniffer.update(Message::IpVersionSelection(IpVersion::IPv6, false)); - assert_eq!(sniffer.filters.ip_versions, HashSet::new()); - } - - #[test] - #[parallel] // needed to not collide with other tests generating configs files - fn test_correctly_update_protocol() { - let mut sniffer = Sniffer::new(Configs::default()); - - assert_eq!(sniffer.filters.protocols, HashSet::from(Protocol::ALL)); - sniffer.update(Message::ProtocolSelection(Protocol::UDP, true)); - assert_eq!(sniffer.filters.protocols, HashSet::from(Protocol::ALL)); - sniffer.update(Message::ProtocolSelection(Protocol::UDP, false)); - assert_eq!( - sniffer.filters.protocols, - HashSet::from([Protocol::TCP, Protocol::ICMP, Protocol::ARP]) - ); - sniffer.update(Message::ProtocolSelection(Protocol::TCP, false)); - assert_eq!( - sniffer.filters.protocols, - HashSet::from([Protocol::ICMP, Protocol::ARP]) - ); - sniffer.update(Message::ProtocolSelection(Protocol::ICMP, false)); - assert_eq!(sniffer.filters.protocols, HashSet::from([Protocol::ARP])); - sniffer.update(Message::ProtocolSelection(Protocol::ARP, false)); - assert_eq!(sniffer.filters.protocols, HashSet::new()); - sniffer.update(Message::ProtocolSelection(Protocol::UDP, true)); - assert_eq!(sniffer.filters.protocols, HashSet::from([Protocol::UDP])); - } - #[test] #[parallel] // needed to not collide with other tests generating configs files fn test_correctly_update_chart_kind() { - let mut sniffer = Sniffer::new(Configs::default()); + let mut sniffer = Sniffer::new(Conf::default()); - assert_eq!(sniffer.traffic_chart.chart_type, ChartType::Bytes); - sniffer.update(Message::ChartSelection(ChartType::Packets)); - assert_eq!(sniffer.traffic_chart.chart_type, ChartType::Packets); - sniffer.update(Message::ChartSelection(ChartType::Packets)); - assert_eq!(sniffer.traffic_chart.chart_type, ChartType::Packets); - sniffer.update(Message::ChartSelection(ChartType::Bytes)); - assert_eq!(sniffer.traffic_chart.chart_type, ChartType::Bytes); + assert_eq!(sniffer.traffic_chart.data_repr, DataRepr::Bytes); + sniffer.update(Message::DataReprSelection(DataRepr::Packets)); + assert_eq!(sniffer.traffic_chart.data_repr, DataRepr::Packets); + sniffer.update(Message::DataReprSelection(DataRepr::Packets)); + assert_eq!(sniffer.traffic_chart.data_repr, DataRepr::Packets); + sniffer.update(Message::DataReprSelection(DataRepr::Bytes)); + assert_eq!(sniffer.traffic_chart.data_repr, DataRepr::Bytes); + sniffer.update(Message::DataReprSelection(DataRepr::Bits)); + assert_eq!(sniffer.traffic_chart.data_repr, DataRepr::Bits); } #[test] #[parallel] // needed to not collide with other tests generating configs files fn test_correctly_update_report_sort_kind() { - let mut sniffer = Sniffer::new(Configs::default()); + let mut sniffer = Sniffer::new(Conf::default()); - let sort = ReportSortType { - byte_sort: SortType::Neutral, - packet_sort: SortType::Neutral, - }; + let sort = SortType::Neutral; - assert_eq!(sniffer.report_sort_type, sort); + assert_eq!(sniffer.conf.report_sort_type, sort); + sniffer.update(Message::ReportSortSelection(sort.next_sort())); + assert_eq!(sniffer.conf.report_sort_type, SortType::Descending); + sniffer.update(Message::ReportSortSelection(sort.next_sort().next_sort())); + assert_eq!(sniffer.conf.report_sort_type, SortType::Ascending); sniffer.update(Message::ReportSortSelection( - sort.next_sort(&ReportCol::Bytes), + sort.next_sort().next_sort().next_sort(), )); - assert_eq!( - sniffer.report_sort_type, - ReportSortType { - byte_sort: SortType::Descending, - packet_sort: SortType::Neutral - } - ); - sniffer.update(Message::ReportSortSelection( - sort.next_sort(&ReportCol::Bytes) - .next_sort(&ReportCol::Bytes), - )); - assert_eq!( - sniffer.report_sort_type, - ReportSortType { - byte_sort: SortType::Ascending, - packet_sort: SortType::Neutral - } - ); - sniffer.update(Message::ReportSortSelection( - sort.next_sort(&ReportCol::Bytes) - .next_sort(&ReportCol::Packets), - )); - assert_eq!( - sniffer.report_sort_type, - ReportSortType { - byte_sort: SortType::Neutral, - packet_sort: SortType::Descending - } - ); - sniffer.update(Message::ReportSortSelection( - sort.next_sort(&ReportCol::Bytes) - .next_sort(&ReportCol::Bytes) - .next_sort(&ReportCol::Bytes), - )); - assert_eq!( - sniffer.report_sort_type, - ReportSortType { - byte_sort: SortType::Neutral, - packet_sort: SortType::Neutral - } - ); + assert_eq!(sniffer.conf.report_sort_type, SortType::Neutral); } #[test] #[parallel] // needed to not collide with other tests generating configs files fn test_correctly_update_host_sort_kind() { - let mut sniffer = Sniffer::new(Configs::default()); + let mut sniffer = Sniffer::new(Conf::default()); let mut sort = SortType::Neutral; - assert_eq!(sniffer.host_sort_type, sort); + assert_eq!(sniffer.conf.host_sort_type, sort); sort = sort.next_sort(); sniffer.update(Message::HostSortSelection(sort)); - assert_eq!(sniffer.host_sort_type, SortType::Descending); + assert_eq!(sniffer.conf.host_sort_type, SortType::Descending); sort = sort.next_sort(); sniffer.update(Message::HostSortSelection(sort)); - assert_eq!(sniffer.host_sort_type, SortType::Ascending); + assert_eq!(sniffer.conf.host_sort_type, SortType::Ascending); sort = sort.next_sort(); sniffer.update(Message::HostSortSelection(sort)); - assert_eq!(sniffer.host_sort_type, SortType::Neutral); + assert_eq!(sniffer.conf.host_sort_type, SortType::Neutral); } #[test] #[parallel] // needed to not collide with other tests generating configs files fn test_correctly_update_service_sort_kind() { - let mut sniffer = Sniffer::new(Configs::default()); + let mut sniffer = Sniffer::new(Conf::default()); let mut sort = SortType::Neutral; - assert_eq!(sniffer.service_sort_type, sort); + assert_eq!(sniffer.conf.service_sort_type, sort); sort = sort.next_sort(); sniffer.update(Message::ServiceSortSelection(sort)); - assert_eq!(sniffer.service_sort_type, SortType::Descending); + assert_eq!(sniffer.conf.service_sort_type, SortType::Descending); sort = sort.next_sort(); sniffer.update(Message::ServiceSortSelection(sort)); - assert_eq!(sniffer.service_sort_type, SortType::Ascending); + assert_eq!(sniffer.conf.service_sort_type, SortType::Ascending); sort = sort.next_sort(); sniffer.update(Message::ServiceSortSelection(sort)); - assert_eq!(sniffer.service_sort_type, SortType::Neutral); + assert_eq!(sniffer.conf.service_sort_type, SortType::Neutral); } #[test] #[parallel] // needed to not collide with other tests generating configs files fn test_correctly_update_style() { - let mut sniffer = Sniffer::new(Configs::default()); + let mut sniffer = Sniffer::new(Conf::default()); sniffer.update(Message::Style(StyleType::MonAmour)); - assert_eq!(sniffer.configs.settings.style, StyleType::MonAmour); + assert_eq!(sniffer.conf.settings.style, StyleType::MonAmour); sniffer.update(Message::Style(StyleType::Day)); - assert_eq!(sniffer.configs.settings.style, StyleType::Day); + assert_eq!(sniffer.conf.settings.style, StyleType::Day); sniffer.update(Message::Style(StyleType::Night)); - assert_eq!(sniffer.configs.settings.style, StyleType::Night); + assert_eq!(sniffer.conf.settings.style, StyleType::Night); sniffer.update(Message::Style(StyleType::DeepSea)); - assert_eq!(sniffer.configs.settings.style, StyleType::DeepSea); + assert_eq!(sniffer.conf.settings.style, StyleType::DeepSea); sniffer.update(Message::Style(StyleType::DeepSea)); - assert_eq!(sniffer.configs.settings.style, StyleType::DeepSea); + assert_eq!(sniffer.conf.settings.style, StyleType::DeepSea); } #[test] #[parallel] // needed to not collide with other tests generating configs files fn test_dots_pulse_update() { // every kind of message will the integer, but only Periodic will update the string - let mut sniffer = Sniffer::new(Configs::default()); + let mut sniffer = Sniffer::new(Conf::default()); assert_eq!(sniffer.dots_pulse, (".".to_string(), 0)); @@ -1357,7 +1239,7 @@ fn test_dots_pulse_update() { #[test] #[parallel] // needed to not collide with other tests generating configs files fn test_modify_favorite_connections() { - let mut sniffer = Sniffer::new(Configs::default()); + let mut sniffer = Sniffer::new(Conf::default()); // remove 1 sniffer.update(Message::AddOrRemoveFavorite( Host { @@ -1538,16 +1420,22 @@ fn test_modify_favorite_connections() { #[test] #[parallel] // needed to not collide with other tests generating configs files fn test_show_and_hide_modal_and_settings() { - let mut sniffer = Sniffer::new(Configs::default()); + let mut sniffer = Sniffer::new(Conf::default()); assert_eq!(sniffer.modal, None); assert_eq!(sniffer.settings_page, None); - assert_eq!(sniffer.last_opened_setting, SettingsPage::Notifications); + assert_eq!( + sniffer.conf.last_opened_setting, + SettingsPage::Notifications + ); // open settings sniffer.update(Message::OpenLastSettings); assert_eq!(sniffer.modal, None); assert_eq!(sniffer.settings_page, Some(SettingsPage::Notifications)); - assert_eq!(sniffer.last_opened_setting, SettingsPage::Notifications); + assert_eq!( + sniffer.conf.last_opened_setting, + SettingsPage::Notifications + ); // switch settings page sniffer.update(Message::OpenSettings(SettingsPage::Appearance)); assert_eq!(sniffer.modal, None); @@ -1559,17 +1447,17 @@ fn test_show_and_hide_modal_and_settings() { sniffer.update(Message::ShowModal(MyModal::Quit)); assert_eq!(sniffer.modal, None); assert_eq!(sniffer.settings_page, Some(SettingsPage::General)); - assert_eq!(sniffer.last_opened_setting, SettingsPage::Notifications); + assert_eq!(sniffer.conf.last_opened_setting, SettingsPage::General); // close settings sniffer.update(Message::CloseSettings); assert_eq!(sniffer.modal, None); assert_eq!(sniffer.settings_page, None); - assert_eq!(sniffer.last_opened_setting, SettingsPage::General); + assert_eq!(sniffer.conf.last_opened_setting, SettingsPage::General); // reopen settings sniffer.update(Message::OpenLastSettings); assert_eq!(sniffer.modal, None); assert_eq!(sniffer.settings_page, Some(SettingsPage::General)); - assert_eq!(sniffer.last_opened_setting, SettingsPage::General); + assert_eq!(sniffer.conf.last_opened_setting, SettingsPage::General); // switch settings page sniffer.update(Message::OpenSettings(SettingsPage::Appearance)); assert_eq!(sniffer.modal, None); @@ -1578,66 +1466,66 @@ fn test_show_and_hide_modal_and_settings() { sniffer.update(Message::CloseSettings); assert_eq!(sniffer.modal, None); assert_eq!(sniffer.settings_page, None); - assert_eq!(sniffer.last_opened_setting, SettingsPage::Appearance); + assert_eq!(sniffer.conf.last_opened_setting, SettingsPage::Appearance); // open clear all modal sniffer.update(Message::ShowModal(MyModal::ClearAll)); assert_eq!(sniffer.modal, Some(MyModal::ClearAll)); assert_eq!(sniffer.settings_page, None); - assert_eq!(sniffer.last_opened_setting, SettingsPage::Appearance); + assert_eq!(sniffer.conf.last_opened_setting, SettingsPage::Appearance); // try opening settings with clear all modal opened sniffer.update(Message::OpenLastSettings); assert_eq!(sniffer.modal, Some(MyModal::ClearAll)); assert_eq!(sniffer.settings_page, None); - assert_eq!(sniffer.last_opened_setting, SettingsPage::Appearance); + assert_eq!(sniffer.conf.last_opened_setting, SettingsPage::Appearance); // try opening quit modal with clear all modal opened sniffer.update(Message::ShowModal(MyModal::Quit)); assert_eq!(sniffer.modal, Some(MyModal::ClearAll)); assert_eq!(sniffer.settings_page, None); - assert_eq!(sniffer.last_opened_setting, SettingsPage::Appearance); + assert_eq!(sniffer.conf.last_opened_setting, SettingsPage::Appearance); // close clear all modal sniffer.update(Message::HideModal); assert_eq!(sniffer.modal, None); assert_eq!(sniffer.settings_page, None); - assert_eq!(sniffer.last_opened_setting, SettingsPage::Appearance); + assert_eq!(sniffer.conf.last_opened_setting, SettingsPage::Appearance); // open quit modal sniffer.update(Message::ShowModal(MyModal::Quit)); assert_eq!(sniffer.modal, Some(MyModal::Quit)); assert_eq!(sniffer.settings_page, None); - assert_eq!(sniffer.last_opened_setting, SettingsPage::Appearance); + assert_eq!(sniffer.conf.last_opened_setting, SettingsPage::Appearance); // try opening settings with clear all modal opened sniffer.update(Message::OpenLastSettings); assert_eq!(sniffer.modal, Some(MyModal::Quit)); assert_eq!(sniffer.settings_page, None); - assert_eq!(sniffer.last_opened_setting, SettingsPage::Appearance); + assert_eq!(sniffer.conf.last_opened_setting, SettingsPage::Appearance); // try opening clear all modal with quit modal opened sniffer.update(Message::ShowModal(MyModal::ClearAll)); assert_eq!(sniffer.modal, Some(MyModal::Quit)); assert_eq!(sniffer.settings_page, None); - assert_eq!(sniffer.last_opened_setting, SettingsPage::Appearance); + assert_eq!(sniffer.conf.last_opened_setting, SettingsPage::Appearance); // close quit modal sniffer.update(Message::HideModal); assert_eq!(sniffer.modal, None); assert_eq!(sniffer.settings_page, None); - assert_eq!(sniffer.last_opened_setting, SettingsPage::Appearance); + assert_eq!(sniffer.conf.last_opened_setting, SettingsPage::Appearance); } #[test] #[parallel] // needed to not collide with other tests generating configs files fn test_correctly_update_language() { - let mut sniffer = Sniffer::new(Configs::default()); + let mut sniffer = Sniffer::new(Conf::default()); - assert_eq!(sniffer.configs.settings.language, Language::EN); + assert_eq!(sniffer.conf.settings.language, Language::EN); assert_eq!(sniffer.traffic_chart.language, Language::EN); sniffer.update(Message::LanguageSelection(Language::IT)); - assert_eq!(sniffer.configs.settings.language, Language::IT); + assert_eq!(sniffer.conf.settings.language, Language::IT); assert_eq!(sniffer.traffic_chart.language, Language::IT); sniffer.update(Message::LanguageSelection(Language::IT)); - assert_eq!(sniffer.configs.settings.language, Language::IT); + assert_eq!(sniffer.conf.settings.language, Language::IT); assert_eq!(sniffer.traffic_chart.language, Language::IT); sniffer.update(Message::LanguageSelection(Language::ZH)); - assert_eq!(sniffer.configs.settings.language, Language::ZH); + assert_eq!(sniffer.conf.settings.language, Language::ZH); assert_eq!(sniffer.traffic_chart.language, Language::ZH); } @@ -1659,10 +1547,10 @@ fn expire_notifications_timeout(sniffer: &mut Sniffer) { // Simulate an update to apply the settings sniffer.update(Message::Periodic); } - let mut sniffer = Sniffer::new(Configs::default()); + let mut sniffer = Sniffer::new(Conf::default()); let bytes_notification_init = DataNotification { - chart_type: ChartType::Bytes, + data_repr: DataRepr::Bytes, threshold: None, byte_multiple: ByteMultiple::KB, sound: Sound::Pop, @@ -1670,7 +1558,7 @@ fn expire_notifications_timeout(sniffer: &mut Sniffer) { }; let bytes_notification_toggled_on = DataNotification { - chart_type: ChartType::Bytes, + data_repr: DataRepr::Bytes, threshold: Some(800_000), byte_multiple: ByteMultiple::GB, sound: Sound::Pop, @@ -1678,7 +1566,7 @@ fn expire_notifications_timeout(sniffer: &mut Sniffer) { }; let bytes_notification_adjusted_threshold_sound_off = DataNotification { - chart_type: ChartType::Bytes, + data_repr: DataRepr::Bytes, threshold: Some(3), byte_multiple: ByteMultiple::KB, sound: Sound::None, @@ -1686,7 +1574,7 @@ fn expire_notifications_timeout(sniffer: &mut Sniffer) { }; let bytes_notification_sound_off_only = DataNotification { - chart_type: ChartType::Bytes, + data_repr: DataRepr::Bytes, threshold: Some(800_000), byte_multiple: ByteMultiple::GB, sound: Sound::None, @@ -1704,36 +1592,36 @@ fn expire_notifications_timeout(sniffer: &mut Sniffer) { }; // initial default state - assert_eq!(sniffer.configs.settings.notifications.volume, 60); - assert_eq!(sniffer.configs.settings.notifications.volume, 60); + assert_eq!(sniffer.conf.settings.notifications.volume, 60); + assert_eq!(sniffer.conf.settings.notifications.volume, 60); assert_eq!( - sniffer.configs.settings.notifications.data_notification, + sniffer.conf.settings.notifications.data_notification, bytes_notification_init ); assert_eq!( - sniffer.configs.settings.notifications.favorite_notification, + sniffer.conf.settings.notifications.favorite_notification, fav_notification_init ); // change volume sniffer.update(Message::ChangeVolume(95)); - assert_eq!(sniffer.configs.settings.notifications.volume, 95); + assert_eq!(sniffer.conf.settings.notifications.volume, 95); assert_eq!( - sniffer.configs.settings.notifications.data_notification, + sniffer.conf.settings.notifications.data_notification, bytes_notification_init, ); assert_eq!( - sniffer.configs.settings.notifications.favorite_notification, + sniffer.conf.settings.notifications.favorite_notification, fav_notification_init, ); assert_eq!( - sniffer.configs.settings.notifications.data_notification, + sniffer.conf.settings.notifications.data_notification, bytes_notification_init ); assert_eq!( - sniffer.configs.settings.notifications.favorite_notification, + sniffer.conf.settings.notifications.favorite_notification, fav_notification_init ); @@ -1745,7 +1633,7 @@ fn expire_notifications_timeout(sniffer: &mut Sniffer) { // Verify that toggling threshold is applied immediately assert_eq!( - sniffer.configs.settings.notifications.data_notification, + sniffer.conf.settings.notifications.data_notification, bytes_notification_toggled_on, ); @@ -1757,19 +1645,19 @@ fn expire_notifications_timeout(sniffer: &mut Sniffer) { // Verify adjusted threshold is not applied before timeout expires, // and rest is applied immediately assert_eq!( - sniffer.configs.settings.notifications.data_notification, + sniffer.conf.settings.notifications.data_notification, bytes_notification_sound_off_only, ); expire_notifications_timeout(&mut sniffer); - assert_eq!(sniffer.configs.settings.notifications.volume, 95); + assert_eq!(sniffer.conf.settings.notifications.volume, 95); assert_eq!( - sniffer.configs.settings.notifications.data_notification, + sniffer.conf.settings.notifications.data_notification, bytes_notification_adjusted_threshold_sound_off ); assert_eq!( - sniffer.configs.settings.notifications.favorite_notification, + sniffer.conf.settings.notifications.favorite_notification, fav_notification_init, ); @@ -1782,18 +1670,18 @@ fn expire_notifications_timeout(sniffer: &mut Sniffer) { // Verify threshold is not applied before timeout expires, // and rest is applied immediately assert_eq!( - sniffer.configs.settings.notifications.favorite_notification, + sniffer.conf.settings.notifications.favorite_notification, fav_notification_new, ); // And the rest is intact - assert_eq!(sniffer.configs.settings.notifications.volume, 95); + assert_eq!(sniffer.conf.settings.notifications.volume, 95); assert_eq!( - sniffer.configs.settings.notifications.data_notification, + sniffer.conf.settings.notifications.data_notification, bytes_notification_adjusted_threshold_sound_off ); assert_eq!( - sniffer.configs.settings.notifications.favorite_notification, + sniffer.conf.settings.notifications.favorite_notification, fav_notification_new ); } @@ -1801,12 +1689,12 @@ fn expire_notifications_timeout(sniffer: &mut Sniffer) { #[test] #[parallel] // needed to not collide with other tests generating configs files fn test_clear_all_notifications() { - let mut sniffer = Sniffer::new(Configs::default()); + let mut sniffer = Sniffer::new(Conf::default()); sniffer.logged_notifications.0 = VecDeque::from([LoggedNotification::DataThresholdExceeded( DataThresholdExceeded { id: 1, - chart_type: ChartType::Packets, + data_repr: DataRepr::Packets, threshold: 0, data_info: DataInfo::default(), timestamp: "".to_string(), @@ -1828,40 +1716,53 @@ fn test_clear_all_notifications() { #[test] #[parallel] // needed to not collide with other tests generating configs files fn test_correctly_switch_running_and_settings_pages() { - let mut sniffer = Sniffer::new(Configs::default()); + let mut sniffer = Sniffer::new(Conf::default()); // initial status assert_eq!(sniffer.settings_page, None); assert_eq!(sniffer.modal, None); - assert_eq!(sniffer.running_page, RunningPage::Init); + assert!(sniffer.running_page.is_none()); // nothing changes sniffer.update(Message::SwitchPage(true)); assert_eq!(sniffer.settings_page, None); + assert_eq!( + sniffer.conf.last_opened_setting, + SettingsPage::Notifications + ); assert_eq!(sniffer.modal, None); - assert_eq!(sniffer.running_page, RunningPage::Init); + assert!(sniffer.running_page.is_none()); // switch settings sniffer.update(Message::OpenLastSettings); assert_eq!(sniffer.settings_page, Some(SettingsPage::Notifications)); - assert_eq!(sniffer.running_page, RunningPage::Init); + assert_eq!( + sniffer.conf.last_opened_setting, + SettingsPage::Notifications + ); + assert!(sniffer.running_page.is_none()); sniffer.update(Message::SwitchPage(false)); assert_eq!(sniffer.settings_page, Some(SettingsPage::General)); + assert_eq!(sniffer.conf.last_opened_setting, SettingsPage::General); assert_eq!(sniffer.modal, None); - assert_eq!(sniffer.running_page, RunningPage::Init); + assert!(sniffer.running_page.is_none()); sniffer.update(Message::SwitchPage(true)); assert_eq!(sniffer.settings_page, Some(SettingsPage::Notifications)); + assert_eq!( + sniffer.conf.last_opened_setting, + SettingsPage::Notifications + ); assert_eq!(sniffer.modal, None); - assert_eq!(sniffer.running_page, RunningPage::Init); + assert!(sniffer.running_page.is_none()); sniffer.update(Message::CloseSettings); assert_eq!(sniffer.settings_page, None); - assert_eq!(sniffer.running_page, RunningPage::Init); + assert!(sniffer.running_page.is_none()); // change state to running - sniffer.running_page = RunningPage::Overview; + sniffer.running_page = Some(RunningPage::Overview); assert_eq!(sniffer.settings_page, None); assert_eq!(sniffer.modal, None); - assert_eq!(sniffer.running_page, RunningPage::Overview); + assert_eq!(sniffer.running_page, Some(RunningPage::Overview)); // switch with closed setting and no packets received => nothing changes sniffer.update(Message::SwitchPage(true)); - assert_eq!(sniffer.running_page, RunningPage::Overview); + assert_eq!(sniffer.running_page, Some(RunningPage::Overview)); assert_eq!(sniffer.settings_page, None); // switch with closed setting and some packets received => change running page sniffer @@ -1869,56 +1770,45 @@ fn test_correctly_switch_running_and_settings_pages() { .tot_data_info .add_packet(0, TrafficDirection::Outgoing); sniffer.update(Message::SwitchPage(true)); - assert_eq!(sniffer.running_page, RunningPage::Inspect); + assert_eq!(sniffer.running_page, Some(RunningPage::Inspect)); assert_eq!(sniffer.settings_page, None); // switch with opened settings => change settings sniffer.update(Message::OpenLastSettings); - assert_eq!(sniffer.running_page, RunningPage::Inspect); + assert_eq!(sniffer.running_page, Some(RunningPage::Inspect)); assert_eq!(sniffer.settings_page, Some(SettingsPage::Notifications)); + assert_eq!( + sniffer.conf.last_opened_setting, + SettingsPage::Notifications + ); sniffer.update(Message::SwitchPage(true)); - assert_eq!(sniffer.running_page, RunningPage::Inspect); + assert_eq!(sniffer.running_page, Some(RunningPage::Inspect)); assert_eq!(sniffer.settings_page, Some(SettingsPage::Appearance)); + assert_eq!(sniffer.conf.last_opened_setting, SettingsPage::Appearance); // focus the window and try to switch => nothing changes sniffer.update(Message::WindowFocused); sniffer.update(Message::SwitchPage(true)); - assert_eq!(sniffer.running_page, RunningPage::Inspect); + assert_eq!(sniffer.running_page, Some(RunningPage::Inspect)); assert_eq!(sniffer.settings_page, Some(SettingsPage::Appearance)); } #[test] #[serial] // needed to not collide with other tests generating configs files - fn test_config_settings() { - let path_string = ConfigSettings::test_path(); + fn test_conf() { + let path_string = Conf::test_path(); let path = Path::new(&path_string); assert!(!path.exists()); - let mut sniffer = Sniffer::new(Configs::load()); + let mut sniffer = Sniffer::new(Conf::load()); assert!(path.exists()); // check that the current settings are the default ones - let settings_start = sniffer.configs.settings.clone(); - assert_eq!( - settings_start, - ConfigSettings { - color_gradient: GradientType::None, - language: Language::EN, - scale_factor: 1.0, - mmdb_country: "".to_string(), - mmdb_asn: "".to_string(), - style_path: "".to_string(), - notifications: Notifications { - volume: 60, - data_notification: Default::default(), - favorite_notification: Default::default() - }, - style: StyleType::Custom(ExtraStyles::A11yDark) - } - ); + let conf_start = sniffer.conf.clone(); + assert_eq!(conf_start, Conf::default(),); - // change some configs by sending messages + // change some conf by sending messages sniffer.update(Message::GradientsSelection(GradientType::Wild)); sniffer.update(Message::LanguageSelection(Language::ZH)); sniffer.update(Message::ChangeScaleFactor(0.0)); @@ -1930,64 +1820,22 @@ fn test_config_settings() { ))); sniffer.update(Message::Style(StyleType::Custom(ExtraStyles::DraculaDark))); sniffer.update(Message::ChangeVolume(100)); - - // quit the app by sending a CloseRequested message - sniffer.update(Message::Quit); - - assert!(path.exists()); - - // check that updated configs are inherited by a new sniffer instance - let settings_end = Sniffer::new(Configs::load()).configs.settings.clone(); - assert_eq!( - settings_end, - ConfigSettings { - color_gradient: GradientType::Wild, - language: Language::ZH, - scale_factor: 1.0, - mmdb_country: "countrymmdb".to_string(), - mmdb_asn: "asnmmdb".to_string(), - style_path: format!( - "{}/resources/themes/catppuccin.toml", - env!("CARGO_MANIFEST_DIR") - ), - notifications: Notifications { - volume: 100, - data_notification: Default::default(), - favorite_notification: Default::default() - }, - style: StyleType::Custom(ExtraStyles::DraculaDark) - } - ); - } - - #[test] - #[serial] // needed to not collide with other tests generating configs files - fn test_config_window() { - let path_string = ConfigWindow::test_path(); - let path = Path::new(&path_string); - - assert!(!path.exists()); - - let mut sniffer = Sniffer::new(Configs::load()); - - assert!(path.exists()); - - // check that the current window properties are the default ones - let window_start = sniffer.configs.window; - assert_eq!( - window_start, - ConfigWindow { - position: PositionTuple(0.0, 0.0), - size: SizeTuple(1190.0, 670.0), - thumbnail_position: PositionTuple(0.0, 0.0), - } - ); - - // change window properties by sending messages sniffer.update(Message::WindowMoved(-10.0, 555.0)); sniffer.update(Message::WindowResized(1000.0, 999.0)); sniffer.thumbnail = true; sniffer.update(Message::WindowMoved(40.0, 40.0)); + sniffer.update(Message::SetCaptureSource(CaptureSourcePicklist::File)); + sniffer.update(Message::ToggleFilters); + sniffer.update(Message::BpfFilter("tcp or udp".to_string())); + sniffer.update(Message::ReportSortSelection(SortType::Ascending)); + sniffer.update(Message::HostSortSelection(SortType::Descending)); + sniffer.update(Message::ServiceSortSelection(SortType::Descending)); + sniffer.update(Message::OpenSettings(SettingsPage::Appearance)); + sniffer.update(Message::ToggleExportPcap); + sniffer.update(Message::OutputPcapFile("test.cap".to_string())); + sniffer.update(Message::OutputPcapDir("/".to_string())); + sniffer.update(Message::SetPcapImport("/test.pcap".to_string())); + sniffer.update(Message::ChangeRunningPage(RunningPage::Notifications)); // quit the app by sending a CloseRequested message sniffer.update(Message::Quit); @@ -1995,13 +1843,49 @@ fn test_config_window() { assert!(path.exists()); // check that updated configs are inherited by a new sniffer instance - let window_end = Sniffer::new(Configs::load()).configs.window.clone(); + let conf_end = Sniffer::new(Conf::load()).conf.clone(); assert_eq!( - window_end, - ConfigWindow { - position: PositionTuple(-10.0, 555.0), - size: SizeTuple(1000.0, 999.0), - thumbnail_position: PositionTuple(40.0, 40.0), + conf_end, + Conf { + settings: Settings { + color_gradient: GradientType::Wild, + language: Language::ZH, + scale_factor: 1.0, + mmdb_country: "countrymmdb".to_string(), + mmdb_asn: "asnmmdb".to_string(), + style_path: format!( + "{}/resources/themes/catppuccin.toml", + env!("CARGO_MANIFEST_DIR") + ), + notifications: Notifications { + volume: 100, + data_notification: Default::default(), + favorite_notification: Default::default() + }, + style: StyleType::Custom(ExtraStyles::DraculaDark), + }, + window: ConfigWindow { + position: PositionTuple(-10.0, 555.0), + size: SizeTuple(1000.0, 999.0), + thumbnail_position: PositionTuple(40.0, 40.0), + }, + device: ConfigDevice::default(), + capture_source_picklist: CaptureSourcePicklist::File, + filters: Filters { + expanded: true, + bpf: "tcp or udp".to_string(), + }, + report_sort_type: SortType::Ascending, + host_sort_type: SortType::Descending, + service_sort_type: SortType::Descending, + last_opened_setting: SettingsPage::Appearance, + last_opened_page: RunningPage::Notifications, + export_pcap: ExportPcap { + enabled: true, + file_name: "test.cap".to_string(), + directory: "/".to_string() + }, + import_pcap_path: "/test.pcap".to_string(), } ); } @@ -2009,95 +1893,95 @@ fn test_config_window() { #[test] #[parallel] // needed to not collide with other tests generating configs files fn test_window_resized() { - let mut sniffer = Sniffer::new(Configs::default()); + let mut sniffer = Sniffer::new(Conf::default()); assert!(!sniffer.thumbnail); - let factor = sniffer.configs.settings.scale_factor; + let factor = sniffer.conf.settings.scale_factor; assert_eq!(factor, 1.0); - assert_eq!(sniffer.configs.window.size, SizeTuple(1190.0, 670.0)); + assert_eq!(sniffer.conf.window.size, SizeTuple(1190.0, 670.0)); assert_eq!( ConfigWindow::thumbnail_size(factor), SizeTuple(360.0, 222.0) ); sniffer.update(Message::WindowResized(850.0, 600.0)); - assert_eq!(sniffer.configs.window.size, SizeTuple(850.0, 600.0)); + assert_eq!(sniffer.conf.window.size, SizeTuple(850.0, 600.0)); sniffer.update(Message::ChangeScaleFactor(0.369)); - let factor = sniffer.configs.settings.scale_factor; + let factor = sniffer.conf.settings.scale_factor; assert_eq!(factor, 1.5); assert_eq!( ConfigWindow::thumbnail_size(factor), SizeTuple(540.0, 333.0) ); sniffer.update(Message::WindowResized(1000.0, 800.0)); - assert_eq!(sniffer.configs.window.size, SizeTuple(1500.0, 1200.0)); + assert_eq!(sniffer.conf.window.size, SizeTuple(1500.0, 1200.0)); sniffer.update(Message::ChangeScaleFactor(-0.631)); - let factor = sniffer.configs.settings.scale_factor; + let factor = sniffer.conf.settings.scale_factor; assert_eq!(factor, 0.5); assert_eq!( ConfigWindow::thumbnail_size(factor), SizeTuple(180.0, 111.0) ); sniffer.update(Message::WindowResized(1000.0, 800.0)); - assert_eq!(sniffer.configs.window.size, SizeTuple(500.0, 400.0)); + assert_eq!(sniffer.conf.window.size, SizeTuple(500.0, 400.0)); } #[test] #[parallel] // needed to not collide with other tests generating configs files fn test_window_moved() { - let mut sniffer = Sniffer::new(Configs::default()); + let mut sniffer = Sniffer::new(Conf::default()); assert!(!sniffer.thumbnail); - assert_eq!(sniffer.configs.settings.scale_factor, 1.0); - assert_eq!(sniffer.configs.window.position, PositionTuple(0.0, 0.0)); + assert_eq!(sniffer.conf.settings.scale_factor, 1.0); + assert_eq!(sniffer.conf.window.position, PositionTuple(0.0, 0.0)); assert_eq!( - sniffer.configs.window.thumbnail_position, + sniffer.conf.window.thumbnail_position, PositionTuple(0.0, 0.0) ); sniffer.update(Message::WindowMoved(850.0, 600.0)); - assert_eq!(sniffer.configs.window.position, PositionTuple(850.0, 600.0)); + assert_eq!(sniffer.conf.window.position, PositionTuple(850.0, 600.0)); assert_eq!( - sniffer.configs.window.thumbnail_position, + sniffer.conf.window.thumbnail_position, PositionTuple(0.0, 0.0) ); sniffer.thumbnail = true; sniffer.update(Message::WindowMoved(400.0, 600.0)); - assert_eq!(sniffer.configs.window.position, PositionTuple(850.0, 600.0)); + assert_eq!(sniffer.conf.window.position, PositionTuple(850.0, 600.0)); assert_eq!( - sniffer.configs.window.thumbnail_position, + sniffer.conf.window.thumbnail_position, PositionTuple(400.0, 600.0) ); sniffer.update(Message::ChangeScaleFactor(0.369)); - assert_eq!(sniffer.configs.settings.scale_factor, 1.5); + assert_eq!(sniffer.conf.settings.scale_factor, 1.5); sniffer.update(Message::WindowMoved(20.0, 40.0)); - assert_eq!(sniffer.configs.window.position, PositionTuple(850.0, 600.0)); + assert_eq!(sniffer.conf.window.position, PositionTuple(850.0, 600.0)); assert_eq!( - sniffer.configs.window.thumbnail_position, + sniffer.conf.window.thumbnail_position, PositionTuple(30.0, 60.0) ); sniffer.thumbnail = false; sniffer.update(Message::WindowMoved(-20.0, 300.0)); - assert_eq!(sniffer.configs.window.position, PositionTuple(-30.0, 450.0)); + assert_eq!(sniffer.conf.window.position, PositionTuple(-30.0, 450.0)); assert_eq!( - sniffer.configs.window.thumbnail_position, + sniffer.conf.window.thumbnail_position, PositionTuple(30.0, 60.0) ); sniffer.update(Message::ChangeScaleFactor(-0.631)); - assert_eq!(sniffer.configs.settings.scale_factor, 0.5); + assert_eq!(sniffer.conf.settings.scale_factor, 0.5); sniffer.update(Message::WindowMoved(500.0, -100.0)); - assert_eq!(sniffer.configs.window.position, PositionTuple(250.0, -50.0)); + assert_eq!(sniffer.conf.window.position, PositionTuple(250.0, -50.0)); assert_eq!( - sniffer.configs.window.thumbnail_position, + sniffer.conf.window.thumbnail_position, PositionTuple(30.0, 60.0) ); sniffer.thumbnail = true; sniffer.update(Message::WindowMoved(-2.0, -34.0)); - assert_eq!(sniffer.configs.window.position, PositionTuple(250.0, -50.0)); + assert_eq!(sniffer.conf.window.position, PositionTuple(250.0, -50.0)); assert_eq!( - sniffer.configs.window.thumbnail_position, + sniffer.conf.window.thumbnail_position, PositionTuple(-1.0, -17.0) ); } @@ -2105,7 +1989,7 @@ fn test_window_moved() { #[test] #[parallel] // needed to not collide with other tests generating configs files fn test_toggle_thumbnail() { - let mut sniffer = Sniffer::new(Configs::default()); + let mut sniffer = Sniffer::new(Conf::default()); assert!(!sniffer.thumbnail); assert!(!sniffer.traffic_chart.thumbnail); @@ -2132,21 +2016,21 @@ fn test_toggle_thumbnail() { #[test] #[parallel] // needed to not collide with other tests generating configs files fn test_scale_factor_shortcut() { - let mut sniffer = Sniffer::new(Configs::default()); - assert_eq!(sniffer.configs.settings.scale_factor, 1.0); + let mut sniffer = Sniffer::new(Conf::default()); + assert_eq!(sniffer.conf.settings.scale_factor, 1.0); sniffer.update(Message::ScaleFactorShortcut(true)); - assert_eq!(sniffer.configs.settings.scale_factor, 1.1); + assert_eq!(sniffer.conf.settings.scale_factor, 1.1); sniffer.update(Message::ScaleFactorShortcut(false)); - assert_eq!(sniffer.configs.settings.scale_factor, 1.0); + assert_eq!(sniffer.conf.settings.scale_factor, 1.0); sniffer.update(Message::ScaleFactorShortcut(false)); - assert_eq!(sniffer.configs.settings.scale_factor, 0.9); + assert_eq!(sniffer.conf.settings.scale_factor, 0.9); for _ in 0..100 { sniffer.update(Message::ScaleFactorShortcut(true)); } assert_eq!( - format!("{:.2}", sniffer.configs.settings.scale_factor), + format!("{:.2}", sniffer.conf.settings.scale_factor), "3.00".to_string() ); @@ -2154,7 +2038,7 @@ fn test_scale_factor_shortcut() { sniffer.update(Message::ScaleFactorShortcut(false)); } assert_eq!( - format!("{:.2}", sniffer.configs.settings.scale_factor), + format!("{:.2}", sniffer.conf.settings.scale_factor), "0.30".to_string() ); } diff --git a/src/gui/styles/button.rs b/src/gui/styles/button.rs index ee75d3fb..899e19e3 100644 --- a/src/gui/styles/button.rs +++ b/src/gui/styles/button.rs @@ -64,7 +64,9 @@ fn active(&self, style: &StyleType) -> Style { radius: match self { ButtonType::Neutral => 0.0.into(), ButtonType::TabActive | ButtonType::TabInactive => Radius::new(0).bottom(30), - ButtonType::BorderedRound | ButtonType::BorderedRoundSelected => 12.0.into(), + ButtonType::BorderedRound + | ButtonType::BorderedRoundSelected + | ButtonType::Gradient(_) => 12.0.into(), ButtonType::Starred | ButtonType::NotStarred => 100.0.into(), _ => BORDER_BUTTON_RADIUS.into(), }, @@ -154,7 +156,9 @@ fn hovered(&self, style: &StyleType) -> Style { radius: match self { ButtonType::Neutral => 0.0.into(), ButtonType::TabActive | ButtonType::TabInactive => Radius::new(0).bottom(30), - ButtonType::BorderedRound | ButtonType::BorderedRoundSelected => 12.0.into(), + ButtonType::BorderedRound + | ButtonType::BorderedRoundSelected + | ButtonType::Gradient(_) => 12.0.into(), ButtonType::Starred | ButtonType::NotStarred => 100.0.into(), _ => BORDER_BUTTON_RADIUS.into(), }, @@ -208,7 +212,7 @@ fn disabled(&self, style: &StyleType) -> Style { _ => Background::Color(ext.buttons_color), }), border: Border { - radius: BORDER_BUTTON_RADIUS.into(), + radius: 12.0.into(), width: BORDER_WIDTH, color: Color { a: ext.alpha_chart_badge, @@ -216,7 +220,7 @@ fn disabled(&self, style: &StyleType) -> Style { }, }, text_color: Color { - a: ext.alpha_chart_badge, + a: 0.5, ..colors.text_headers }, shadow: Shadow::default(), diff --git a/src/gui/styles/donut.rs b/src/gui/styles/donut.rs index 1cb768f8..a63b0251 100644 --- a/src/gui/styles/donut.rs +++ b/src/gui/styles/donut.rs @@ -25,8 +25,7 @@ fn active(&self, style: &StyleType) -> Style { incoming: colors.secondary, outgoing: colors.outgoing, text_color: colors.text_body, - filtered_out: ext.buttons_color, - dropped: ext.red_alert_color, + dropped: ext.buttons_color, } } } @@ -48,7 +47,6 @@ pub struct Style { pub(crate) text_color: Color, pub(crate) incoming: Color, pub(crate) outgoing: Color, - pub(crate) filtered_out: Color, pub(crate) dropped: Color, } diff --git a/src/gui/styles/rule.rs b/src/gui/styles/rule.rs index d44f7cea..f197c6fb 100644 --- a/src/gui/styles/rule.rs +++ b/src/gui/styles/rule.rs @@ -13,7 +13,6 @@ pub enum RuleType { PaletteColor(Color, u16), Incoming, Outgoing, - FilteredOut, Dropped, } @@ -26,8 +25,7 @@ fn appearance(&self, style: &StyleType) -> Style { RuleType::Incoming => colors.secondary, RuleType::Outgoing => colors.outgoing, RuleType::PaletteColor(color, _) => *color, - RuleType::Dropped => ext.red_alert_color, - RuleType::FilteredOut => ext.buttons_color, + RuleType::Dropped => ext.buttons_color, RuleType::Standard => Color { a: ext.alpha_round_borders, ..ext.buttons_color diff --git a/src/gui/styles/text_input.rs b/src/gui/styles/text_input.rs index 0f12efc4..63d0d5b8 100644 --- a/src/gui/styles/text_input.rs +++ b/src/gui/styles/text_input.rs @@ -13,7 +13,7 @@ pub enum TextInputType { #[default] Standard, Badge, - Error, + // Error, } const TEXT_INPUT_BORDER_RADIUS: f32 = 5.0; @@ -25,7 +25,8 @@ fn active(&self, style: &StyleType) -> Style { Style { background: Background::Color(match self { TextInputType::Badge => Color::TRANSPARENT, - _ => Color { + // TextInputType::Error | + TextInputType::Standard => Color { a: ext.alpha_round_borders, ..ext.buttons_color }, @@ -36,7 +37,7 @@ fn active(&self, style: &StyleType) -> Style { color: match self { TextInputType::Badge => Color::TRANSPARENT, TextInputType::Standard => ext.buttons_color, - TextInputType::Error => ext.red_alert_color, + // TextInputType::Error => ext.red_alert_color, }, }, icon: Color { @@ -51,17 +52,17 @@ fn active(&self, style: &StyleType) -> Style { fn focused(&self, style: &StyleType) -> Style { let colors = style.get_palette(); - let ext = style.get_extension(); + // let ext = style.get_extension(); let is_nightly = style.get_extension().is_nightly; Style { background: Background::Color(colors.primary), border: Border { radius: TEXT_INPUT_BORDER_RADIUS.into(), width: BORDER_WIDTH, - color: match self { - TextInputType::Error => ext.red_alert_color, - _ => colors.secondary, - }, + color: colors.secondary, // match self { + // TextInputType::Error => ext.red_alert_color, + // _ => colors.secondary, + // }, }, icon: Color { a: if is_nightly { 0.2 } else { 0.7 }, @@ -114,15 +115,16 @@ fn hovered(&self, style: &StyleType) -> Style { Style { background: Background::Color(match self { TextInputType::Badge => Color::TRANSPARENT, - _ => ext.buttons_color, + // TextInputType::Error | + TextInputType::Standard => ext.buttons_color, }), border: Border { radius: TEXT_INPUT_BORDER_RADIUS.into(), width: BORDER_WIDTH, - color: match self { - TextInputType::Error => ext.red_alert_color, - _ => colors.secondary, - }, + color: colors.secondary, // match self { + // TextInputType::Error => ext.red_alert_color, + // _ => colors.secondary, + // }, }, icon: Color { a: if ext.is_nightly { 0.2 } else { 0.7 }, @@ -140,7 +142,8 @@ fn disabled(&self, style: &StyleType) -> Style { Style { background: Background::Color(match self { TextInputType::Badge => Color::TRANSPARENT, - _ => Color { + // TextInputType::Error | + TextInputType::Standard => Color { a: ext.alpha_round_containers, ..ext.buttons_color }, @@ -154,10 +157,10 @@ fn disabled(&self, style: &StyleType) -> Style { a: ext.alpha_round_borders, ..ext.buttons_color }, - TextInputType::Error => Color { - a: ext.alpha_round_borders, - ..ext.red_alert_color - }, + // TextInputType::Error => Color { + // a: ext.alpha_round_borders, + // ..ext.red_alert_color + // }, }, }, icon: Color { diff --git a/src/gui/types/conf.rs b/src/gui/types/conf.rs new file mode 100644 index 00000000..34417cc8 --- /dev/null +++ b/src/gui/types/conf.rs @@ -0,0 +1,87 @@ +use crate::gui::pages::types::running_page::RunningPage; +use crate::gui::pages::types::settings_page::SettingsPage; +use crate::gui::types::config_window::ConfigWindow; +use crate::gui::types::export_pcap::ExportPcap; +use crate::gui::types::filters::Filters; +use crate::gui::types::settings::Settings; +use crate::networking::types::capture_context::CaptureSourcePicklist; +use crate::networking::types::config_device::ConfigDevice; +use crate::report::types::sort_type::SortType; +#[cfg(not(test))] +use crate::utils::error_logger::{ErrorLogger, Location}; +#[cfg(not(test))] +use crate::{SNIFFNET_LOWERCASE, location}; +#[cfg(not(test))] +use confy::ConfyError; +use serde::{Deserialize, Serialize}; + +pub static CONF: std::sync::LazyLock = std::sync::LazyLock::new(Conf::load); + +#[derive(Serialize, Deserialize, Default, Clone, PartialEq, Debug)] +#[serde(default)] +pub struct Conf { + /// Parameters from settings pages + pub settings: Settings, + /// Last selected network device name + pub device: ConfigDevice, + /// Window configuration, such as size and position + pub window: ConfigWindow, + /// Capture source picklist, to select the source of the capture + pub capture_source_picklist: CaptureSourcePicklist, + /// BPF filter program to be applied to the capture + pub filters: Filters, + /// Report sort type (inspect page) + pub report_sort_type: SortType, + /// Host sort type (overview page) + pub host_sort_type: SortType, + /// Service sort type (overview page) + pub service_sort_type: SortType, + /// Remembers the last opened setting page + pub last_opened_setting: SettingsPage, + /// Remembers the last opened running page + pub last_opened_page: RunningPage, + /// Information about PCAP file export + pub export_pcap: ExportPcap, + /// Import path for PCAP file + pub import_pcap_path: String, +} + +impl Conf { + const FILE_NAME: &'static str = "conf"; + + /// This should only be used directly to load fresh configurations; + /// use `CONF` instead to access the initial instance + #[cfg(not(test))] + pub fn load() -> Self { + if let Ok(conf) = confy::load::(SNIFFNET_LOWERCASE, Self::FILE_NAME) { + conf + } else { + let _ = Conf::default().store(); + Conf::default() + } + } + + #[cfg(not(test))] + pub fn store(self) -> Result<(), ConfyError> { + confy::store(SNIFFNET_LOWERCASE, Self::FILE_NAME, self).log_err(location!()) + } +} + +#[cfg(test)] +mod tests { + use crate::gui::types::conf::Conf; + + impl Conf { + pub fn test_path() -> String { + format!("{}/{}.toml", env!("CARGO_MANIFEST_DIR"), Self::FILE_NAME) + } + + pub fn load() -> Self { + confy::load_path::(Conf::test_path()).unwrap_or_else(|_| Conf::default()) + } + + pub fn store(self) -> Result<(), confy::ConfyError> { + confy::store_path(Conf::test_path(), self) + } + } +} diff --git a/src/configs/types/config_window.rs b/src/gui/types/config_window.rs similarity index 71% rename from src/configs/types/config_window.rs rename to src/gui/types/config_window.rs index 03bb74fc..fa855d3c 100644 --- a/src/configs/types/config_window.rs +++ b/src/gui/types/config_window.rs @@ -1,7 +1,3 @@ -#[cfg(not(test))] -use crate::utils::error_logger::{ErrorLogger, Location}; -#[cfg(not(test))] -use crate::{SNIFFNET_LOWERCASE, location}; use iced::window::Position; use iced::{Point, Size}; use serde::{Deserialize, Serialize}; @@ -12,6 +8,7 @@ pub struct SizeTuple(pub f32, pub f32); #[derive(Serialize, Deserialize, Copy, Clone, PartialEq, Debug)] +#[serde(default)] pub struct ConfigWindow { pub position: PositionTuple, pub size: SizeTuple, @@ -30,23 +27,6 @@ impl ConfigWindow { const MIN_SIZE_X: f32 = 100.0; const MIN_SIZE_Y: f32 = 100.0; - const FILE_NAME: &'static str = "window"; - #[cfg(not(test))] - pub fn load() -> Self { - if let Ok(window) = confy::load::(SNIFFNET_LOWERCASE, Self::FILE_NAME) { - window - } else { - let _ = confy::store(SNIFFNET_LOWERCASE, Self::FILE_NAME, ConfigWindow::default()) - .log_err(location!()); - ConfigWindow::default() - } - } - - #[cfg(not(test))] - pub fn store(self) -> Result<(), confy::ConfyError> { - confy::store(SNIFFNET_LOWERCASE, Self::FILE_NAME, self).log_err(location!()) - } - pub fn thumbnail_size(factor: f64) -> SizeTuple { Self::THUMBNAIL_SIZE.scale_and_check(factor) } @@ -142,23 +122,3 @@ fn scale_and_check(self, factor: f64) -> PositionTuple { PositionTuple(x, y) } } - -#[cfg(test)] -mod tests { - use crate::ConfigWindow; - - impl ConfigWindow { - pub fn test_path() -> String { - format!("{}/{}.toml", env!("CARGO_MANIFEST_DIR"), Self::FILE_NAME) - } - - pub fn load() -> Self { - confy::load_path::(ConfigWindow::test_path()) - .unwrap_or_else(|_| ConfigWindow::default()) - } - - pub fn store(self) -> Result<(), confy::ConfyError> { - confy::store_path(ConfigWindow::test_path(), self) - } - } -} diff --git a/src/gui/types/export_pcap.rs b/src/gui/types/export_pcap.rs index 93420d8b..dd463170 100644 --- a/src/gui/types/export_pcap.rs +++ b/src/gui/types/export_pcap.rs @@ -1,9 +1,12 @@ +use serde::{Deserialize, Serialize}; use std::path::PathBuf; +#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)] +#[serde(default)] pub struct ExportPcap { - enabled: bool, - file_name: String, - directory: String, + pub(crate) enabled: bool, + pub(crate) file_name: String, + pub(crate) directory: String, } impl ExportPcap { diff --git a/src/gui/types/filters.rs b/src/gui/types/filters.rs new file mode 100644 index 00000000..e23e9f15 --- /dev/null +++ b/src/gui/types/filters.rs @@ -0,0 +1,87 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Clone, PartialEq, Debug, Default)] +#[serde(default)] +pub struct Filters { + pub(crate) expanded: bool, + pub(crate) bpf: String, +} + +impl Filters { + pub fn toggle(&mut self) { + self.expanded = !self.expanded; + } + + pub fn set_bpf(&mut self, bpf: String) { + self.bpf = bpf; + } + + pub fn expanded(&self) -> bool { + self.expanded + } + + pub fn bpf(&self) -> &str { + &self.bpf + } + + pub fn is_some_filter_active(&self) -> bool { + self.expanded && !self.bpf.trim().is_empty() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_default() { + let filters = Filters::default(); + assert_eq!(filters.expanded(), false); + assert_eq!(filters.bpf(), ""); + } + + #[test] + fn test_toggle() { + let mut filters = Filters::default(); + assert_eq!(filters.expanded(), false); + + filters.toggle(); + assert_eq!(filters.expanded(), true); + + filters.toggle(); + assert_eq!(filters.expanded(), false); + } + + #[test] + fn test_set_bpf() { + let mut filters = Filters::default(); + assert_eq!(filters.bpf(), ""); + + filters.set_bpf("tcp port 80".to_string()); + assert_eq!(filters.bpf(), "tcp port 80"); + + filters.set_bpf(" udp port 53 ".to_string()); + assert_eq!(filters.bpf(), " udp port 53 "); + } + + #[test] + fn test_is_some_filter_active() { + let mut filters = Filters::default(); + assert_eq!(filters.is_some_filter_active(), false); + + filters.toggle(); + assert_eq!(filters.is_some_filter_active(), false); + + filters.set_bpf("tcp port 80".to_string()); + assert_eq!(filters.is_some_filter_active(), true); + + filters.toggle(); + assert_eq!(filters.is_some_filter_active(), false); + + filters.toggle(); + assert_eq!(filters.is_some_filter_active(), true); + + filters.set_bpf(" \t \n ".to_string()); + assert_eq!(filters.is_some_filter_active(), false); + } +} diff --git a/src/gui/types/message.rs b/src/gui/types/message.rs index 847c2cb4..07689920 100644 --- a/src/gui/types/message.rs +++ b/src/gui/types/message.rs @@ -5,6 +5,8 @@ use crate::gui::pages::types::running_page::RunningPage; use crate::gui::pages::types::settings_page::SettingsPage; use crate::gui::styles::types::gradient_type::GradientType; +use crate::networking::types::capture_context::CaptureSourcePicklist; +use crate::networking::types::data_representation::DataRepr; use crate::networking::types::host::{Host, HostMessage}; use crate::networking::types::info_traffic::InfoTraffic; use crate::notifications::types::notifications::Notification; @@ -12,7 +14,7 @@ use crate::report::types::sort_type::SortType; use crate::utils::types::file_info::FileInfo; use crate::utils::types::web_page::WebPage; -use crate::{ChartType, IpVersion, Language, Protocol, ReportSortType, StyleType}; +use crate::{Language, StyleType}; #[derive(Debug, Clone)] /// Messages types that permit reacting to application interactions/subscriptions @@ -21,20 +23,18 @@ pub enum Message { 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, InfoTraffic, Vec, bool), + /// Capture source selected from the picklist + SetCaptureSource(CaptureSourcePicklist), /// Select network device DeviceSelection(String), - /// Select IP filter - IpVersionSelection(IpVersion, bool), - /// Select protocol filter - ProtocolSelection(Protocol, bool), - /// Changed address filter - AddressFilter(String), - /// Changed port filter - PortFilter(String), - /// Select chart type to be displayed - ChartSelection(ChartType), + /// Toggle BPF filter checkbox + ToggleFilters, + /// Change BPF filter string + BpfFilter(String), + /// Select data representation to use + DataReprSelection(DataRepr), /// Select report sort type to be displayed (inspect page) - ReportSortSelection(ReportSortType), + ReportSortSelection(SortType), /// Select host sort type to be displayed (overview page) HostSortSelection(SortType), /// Select service sort type to be displayed (overview page) diff --git a/src/gui/types/mod.rs b/src/gui/types/mod.rs index 9bc7a238..f96fc44b 100644 --- a/src/gui/types/mod.rs +++ b/src/gui/types/mod.rs @@ -1,3 +1,7 @@ +pub mod conf; +pub mod config_window; pub mod export_pcap; +pub mod filters; pub mod message; +pub mod settings; pub mod timing_events; diff --git a/src/gui/types/settings.rs b/src/gui/types/settings.rs new file mode 100644 index 00000000..d6b7aa33 --- /dev/null +++ b/src/gui/types/settings.rs @@ -0,0 +1,34 @@ +use serde::{Deserialize, Serialize}; + +use crate::gui::styles::types::gradient_type::GradientType; +use crate::notifications::types::notifications::Notifications; +use crate::{Language, StyleType}; + +#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)] +#[serde(default)] +pub struct Settings { + pub color_gradient: GradientType, + pub language: Language, + pub scale_factor: f64, + pub mmdb_country: String, + pub mmdb_asn: String, + pub style_path: String, + pub notifications: Notifications, + // StyleType should be last in order to deserialize as a table properly + pub style: StyleType, +} + +impl Default for Settings { + fn default() -> Self { + Settings { + color_gradient: GradientType::default(), + language: Language::default(), + scale_factor: 1.0, + mmdb_country: String::new(), + mmdb_asn: String::new(), + style_path: String::new(), + notifications: Notifications::default(), + style: StyleType::default(), + } + } +} diff --git a/src/main.rs b/src/main.rs index 04588a65..e7066d6c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,34 +7,29 @@ use iced::advanced::graphics::image::image_rs::ImageFormat; #[cfg(target_os = "linux")] use iced::window::settings::PlatformSpecific; -use iced::{Font, Pixels, Settings, application, window}; +use iced::{Font, Pixels, application, window}; -use chart::types::chart_type::ChartType; use chart::types::traffic_chart::TrafficChart; use cli::handle_cli_args; -use configs::types::config_device::ConfigDevice; -use configs::types::config_settings::ConfigSettings; use gui::pages::types::running_page::RunningPage; use gui::sniffer::Sniffer; use gui::styles::style_constants::FONT_SIZE_BODY; use gui::styles::types::style_type::StyleType; -use networking::types::byte_multiple::ByteMultiple; +use networking::types::data_representation::ByteMultiple; use networking::types::info_traffic::InfoTraffic; use networking::types::ip_version::IpVersion; use networking::types::protocol::Protocol; use networking::types::service::Service; -use report::types::report_sort_type::ReportSortType; use translations::types::language::Language; use utils::formatted_strings::print_cli_welcome_message; -use crate::configs::types::config_window::{ConfigWindow, ToPosition, ToSize}; -use crate::configs::types::configs::{CONFIGS, Configs}; use crate::gui::sniffer::FONT_FAMILY_NAME; use crate::gui::styles::style_constants::{ICONS_BYTES, SARASA_MONO_BOLD_BYTES, SARASA_MONO_BYTES}; +use crate::gui::types::conf::CONF; +use crate::gui::types::config_window::{ConfigWindow, ToPosition, ToSize}; mod chart; mod cli; -mod configs; mod countries; mod gui; mod mmdb; @@ -63,7 +58,7 @@ pub fn main() -> iced::Result { _gag2 = gag2; } - let configs = CONFIGS.clone(); + let conf = CONF.clone(); let boot_task_chain = handle_cli_args(); #[cfg(debug_assertions)] @@ -79,10 +74,10 @@ pub fn main() -> iced::Result { print_cli_welcome_message(); - let ConfigWindow { size, position, .. } = configs.window; + let ConfigWindow { size, position, .. } = conf.window; application(SNIFFNET_TITLECASE, Sniffer::update, Sniffer::view) - .settings(Settings { + .settings(iced::Settings { // id needed for Linux Wayland; should match StartupWMClass in .desktop file; see issue #292 id: Some(String::from(SNIFFNET_LOWERCASE)), fonts: vec![ @@ -115,5 +110,5 @@ pub fn main() -> iced::Result { .subscription(Sniffer::subscription) .theme(Sniffer::theme) .scale_factor(Sniffer::scale_factor) - .run_with(move || (Sniffer::new(configs), boot_task_chain)) + .run_with(move || (Sniffer::new(conf), boot_task_chain)) } diff --git a/src/mmdb/asn.rs b/src/mmdb/asn.rs index 23e341f9..e120d2ee 100644 --- a/src/mmdb/asn.rs +++ b/src/mmdb/asn.rs @@ -77,19 +77,19 @@ fn test_get_asn_with_custom_ipinfo_single_reader() { assert!(matches!(reader, MmdbReader::Custom(_))); // known IP - let res = get_asn(&IpAddr::from([61, 8, 0, 0]), &reader); - assert_eq!(res.code, "AS1221"); - assert_eq!(res.name, "Telstra Limited"); + let res = get_asn(&IpAddr::from([185, 72, 2, 28]), &reader); + assert_eq!(res.code, "AS202583"); + assert_eq!(res.name, "AVATEL TELECOM, SA"); // another known IP - let res = get_asn(&IpAddr::from([206, 180, 34, 99]), &reader); - assert_eq!(res.code, "AS63344"); - assert_eq!(res.name, "The Reynolds and Reynolds Company"); + let res = get_asn(&IpAddr::from([89, 187, 198, 0]), &reader); + assert_eq!(res.code, "AS210367"); + assert_eq!(res.name, "Krajska zdravotni, a.s."); // known IPv6 - let res = get_asn(&IpAddr::from_str("2806:230:2057::").unwrap(), &reader); - assert_eq!(res.code, "AS11888"); - assert_eq!(res.name, "Television Internacional, S.A. de C.V."); + let res = get_asn(&IpAddr::from_str("2408:8957:6280::").unwrap(), &reader); + assert_eq!(res.code, "AS17622"); + assert_eq!(res.name, "China Unicom Guangzhou network"); // unknown IP let res = get_asn(&IpAddr::from([127, 0, 0, 1]), &reader); @@ -106,31 +106,29 @@ fn test_get_asn_with_custom_ipinfo_single_reader() { #[test] fn test_get_asn_with_custom_ipinfo_combined_reader() { let reader_1 = MmdbReader::from( - &String::from("resources/test/ipinfo_country_asn_sample.mmdb"), + &String::from("resources/test/ipinfo_lite_sample.mmdb"), ASN_MMDB, ); - let reader_2 = MmdbReader::from( - &String::from("resources/test/ipinfo_country_asn_sample.mmdb"), - &[], - ); + let reader_2 = + MmdbReader::from(&String::from("resources/test/ipinfo_lite_sample.mmdb"), &[]); for reader in vec![reader_1, reader_2] { assert!(matches!(reader, MmdbReader::Custom(_))); // known IP - let res = get_asn(&IpAddr::from([31, 171, 144, 141]), &reader); - assert_eq!(res.code, "AS197742"); - assert_eq!(res.name, "IBB Energie AG"); + let res = get_asn(&IpAddr::from([1, 0, 65, 1]), &reader); + assert_eq!(res.code, "AS18144"); + assert_eq!(res.name, "Enecom,Inc."); // another known IP - let res = get_asn(&IpAddr::from([103, 112, 220, 111]), &reader); - assert_eq!(res.code, "AS134077"); - assert_eq!(res.name, "Magik Pivot Company Limited"); + let res = get_asn(&IpAddr::from([1, 6, 230, 0]), &reader); + assert_eq!(res.code, "AS4755"); + assert_eq!(res.name, "TATA Communications formerly VSNL is Leading ISP"); // known IPv6 - let res = get_asn(&IpAddr::from_str("2a02:6ea0:f001::").unwrap(), &reader); - assert_eq!(res.code, "AS60068"); - assert_eq!(res.name, "Datacamp Limited"); + // let res = get_asn(&IpAddr::from_str("2a02:6ea0:f001::").unwrap(), &reader); + // assert_eq!(res.code, "AS60068"); + // assert_eq!(res.name, "Datacamp Limited"); // unknown IP let res = get_asn(&IpAddr::from([127, 0, 0, 1]), &reader); diff --git a/src/mmdb/country.rs b/src/mmdb/country.rs index c23e4d69..9e5cd9a0 100644 --- a/src/mmdb/country.rs +++ b/src/mmdb/country.rs @@ -63,11 +63,11 @@ fn test_get_country_with_default_reader() { #[test] fn test_get_country_with_custom_ipinfo_single_reader() { let reader_1 = MmdbReader::from( - &String::from("resources/test/ipinfo_country_sample.mmdb"), + &String::from("resources/test/ipinfo_location_sample.mmdb"), COUNTRY_MMDB, ); let reader_2 = MmdbReader::from( - &String::from("resources/test/ipinfo_country_sample.mmdb"), + &String::from("resources/test/ipinfo_location_sample.mmdb"), &[], ); @@ -75,16 +75,16 @@ fn test_get_country_with_custom_ipinfo_single_reader() { assert!(matches!(reader, MmdbReader::Custom(_))); // known IP - let res = get_country(&IpAddr::from([2, 2, 146, 0]), &reader); - assert_eq!(res, Country::GB); + let res = get_country(&IpAddr::from([1, 0, 6, 99]), &reader); + assert_eq!(res, Country::AU); // another known IP - let res = get_country(&IpAddr::from([23, 193, 112, 81]), &reader); - assert_eq!(res, Country::US); + let res = get_country(&IpAddr::from([1, 0, 8, 0]), &reader); + assert_eq!(res, Country::CN); // known IPv6 - let res = get_country(&IpAddr::from_str("2a0e:1d80::").unwrap(), &reader); - assert_eq!(res, Country::RO); + // let res = get_country(&IpAddr::from_str("2a0e:1d80::").unwrap(), &reader); + // assert_eq!(res, Country::RO); // unknown IP let res = get_country(&IpAddr::from([127, 0, 0, 1]), &reader); @@ -99,28 +99,26 @@ fn test_get_country_with_custom_ipinfo_single_reader() { #[test] fn test_get_country_with_custom_ipinfo_combined_reader() { let reader_1 = MmdbReader::from( - &String::from("resources/test/ipinfo_country_asn_sample.mmdb"), + &String::from("resources/test/ipinfo_lite_sample.mmdb"), COUNTRY_MMDB, ); - let reader_2 = MmdbReader::from( - &String::from("resources/test/ipinfo_country_asn_sample.mmdb"), - &[], - ); + let reader_2 = + MmdbReader::from(&String::from("resources/test/ipinfo_lite_sample.mmdb"), &[]); for reader in vec![reader_1, reader_2] { assert!(matches!(reader, MmdbReader::Custom(_))); // known IP - let res = get_country(&IpAddr::from([31, 171, 144, 141]), &reader); - assert_eq!(res, Country::IT); + let res = get_country(&IpAddr::from([1, 0, 65, 1]), &reader); + assert_eq!(res, Country::JP); // another known IP - let res = get_country(&IpAddr::from([103, 112, 220, 111]), &reader); - assert_eq!(res, Country::TH); + let res = get_country(&IpAddr::from([1, 6, 230, 0]), &reader); + assert_eq!(res, Country::IN); // known IPv6 - let res = get_country(&IpAddr::from_str("2a02:6ea0:f001::").unwrap(), &reader); - assert_eq!(res, Country::AR); + // let res = get_country(&IpAddr::from_str("2a02:6ea0:f001::").unwrap(), &reader); + // assert_eq!(res, Country::AR); // unknown IP let res = get_country(&IpAddr::from([127, 0, 0, 1]), &reader); diff --git a/src/mmdb/types/mmdb_country_entry.rs b/src/mmdb/types/mmdb_country_entry.rs index fffa5b6e..279e0b2e 100644 --- a/src/mmdb/types/mmdb_country_entry.rs +++ b/src/mmdb/types/mmdb_country_entry.rs @@ -30,7 +30,9 @@ fn get_country(&self) -> Country { Self::Standard(StandardCountryEntry { country: Some(StandardCountryEntryInner { iso_code: Some(c) }), }) - | Self::Ipinfo(IpinfoCountryEntry { country: Some(c) }) => Country::from_str(c), + | Self::Ipinfo(IpinfoCountryEntry { + country_code: Some(c), + }) => Country::from_str(c), _ => Country::ZZ, } } @@ -49,5 +51,5 @@ struct StandardCountryEntryInner<'a> { #[derive(Deserialize)] struct IpinfoCountryEntry<'a> { - country: Option<&'a str>, + country_code: Option<&'a str>, } diff --git a/src/networking/manage_packets.rs b/src/networking/manage_packets.rs index 90037db6..a8120356 100644 --- a/src/networking/manage_packets.rs +++ b/src/networking/manage_packets.rs @@ -1,7 +1,9 @@ use std::collections::HashMap; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; -use etherparse::{EtherType, LaxPacketHeaders, LinkHeader, NetHeaders, TransportHeader}; +use etherparse::{ + ArpHardwareId, EtherType, LaxPacketHeaders, LinkHeader, NetHeaders, TransportHeader, +}; use pcap::Address; use crate::networking::types::address_port_pair::AddressPortPair; @@ -81,13 +83,28 @@ fn analyze_link_header( mac_address2: &mut Option, exchanged_bytes: &mut u128, ) { - if let Some(LinkHeader::Ethernet2(header)) = link_header { - *exchanged_bytes += 14; - *mac_address1 = Some(mac_from_dec_to_hex(header.source)); - *mac_address2 = Some(mac_from_dec_to_hex(header.destination)); - } else { - *mac_address1 = None; - *mac_address2 = None; + match link_header { + Some(LinkHeader::Ethernet2(header)) => { + *exchanged_bytes += 14; + *mac_address1 = Some(mac_from_dec_to_hex(header.source)); + *mac_address2 = Some(mac_from_dec_to_hex(header.destination)); + } + Some(LinkHeader::LinuxSll(header)) => { + *exchanged_bytes += 16; + *mac_address1 = if header.sender_address_valid_length == 6 + && header.arp_hrd_type == ArpHardwareId::ETHERNET + && let Ok(sender) = header.sender_address[0..6].try_into() + { + Some(mac_from_dec_to_hex(sender)) + } else { + None + }; + *mac_address2 = None; + } + None => { + *mac_address1 = None; + *mac_address2 = None; + } } } @@ -151,7 +168,7 @@ fn analyze_network_header( *arp_type = ArpType::from_etherparse(arp_packet.operation); true } - _ => false, + None => false, } } @@ -192,7 +209,7 @@ fn analyze_transport_header( *icmp_type = IcmpTypeV6::from_etherparse(&icmpv6_header.icmp_type); true } - _ => false, + None => false, } } @@ -337,14 +354,15 @@ fn get_traffic_direction( .collect(); // first let's handle TCP and UDP loopback - if source_ip.is_loopback() && destination_ip.is_loopback() { - if let (Some(sport), Some(dport)) = (source_port, dest_port) { - return if sport > dport { - TrafficDirection::Outgoing - } else { - TrafficDirection::Incoming - }; - } + if source_ip.is_loopback() + && destination_ip.is_loopback() + && let (Some(sport), Some(dport)) = (source_port, dest_port) + { + return if sport > dport { + TrafficDirection::Outgoing + } else { + TrafficDirection::Incoming + }; } // if interface_addresses is empty, check if the IP is a bogon (useful when importing pcap files) @@ -1499,7 +1517,7 @@ fn test_get_service_different_tcp_udp() { #[test] fn test_get_service_not_applicable() { - for p in Protocol::ALL { + for p in [Protocol::TCP, Protocol::UDP, Protocol::ICMP, Protocol::ARP] { for d in [TrafficDirection::Incoming, TrafficDirection::Outgoing] { for (p1, p2) in [(None, Some(443)), (None, None), (Some(443), None)] { let key = AddressPortPair::new( diff --git a/src/networking/parse_packets.rs b/src/networking/parse_packets.rs index b18ce3a3..fb733279 100644 --- a/src/networking/parse_packets.rs +++ b/src/networking/parse_packets.rs @@ -14,7 +14,6 @@ use crate::networking::types::capture_context::{CaptureContext, 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, HostMessage}; use crate::networking::types::icmp_type::IcmpType; use crate::networking::types::info_traffic::InfoTraffic; @@ -26,10 +25,8 @@ use crate::utils::types::timestamp::Timestamp; use async_channel::Sender; use dns_lookup::lookup_addr; -use etherparse::err::ip::{HeaderError, LaxHeaderSliceError}; -use etherparse::err::{Layer, LenError}; -use etherparse::{LaxPacketHeaders, LenSource}; -use pcap::{Address, Device, Packet}; +use etherparse::{EtherType, LaxPacketHeaders}; +use pcap::{Address, Packet}; use std::collections::HashMap; use std::net::IpAddr; use std::sync::{Arc, Mutex}; @@ -40,7 +37,6 @@ pub fn parse_packets( cap_id: usize, mut cs: CaptureSource, - filters: &Filters, mmdb_readers: &MmdbReaders, capture_context: CaptureContext, tx: &Sender, @@ -97,7 +93,7 @@ pub fn parse_packets( } } Ok(packet) => { - if let Ok(headers) = get_sniffable_headers(&packet, my_link_type) { + if let Some(headers) = get_sniffable_headers(&packet, my_link_type) { #[allow(clippy::useless_conversion)] let secs = i64::from(packet.header.ts.tv_sec); #[allow(clippy::useless_conversion)] @@ -137,148 +133,139 @@ pub fn parse_packets( continue; }; - let passed_filters = filters.matches(&packet_filters_fields); - if passed_filters { - // save this packet to PCAP file - if let Some(file) = savefile.as_mut() { - file.write(&packet); - } - // update the map - let (traffic_direction, service) = modify_or_insert_in_map( - &mut info_traffic_msg, - &key, - &cs, - mac_addresses, - icmp_type, - arp_type, - exchanged_bytes, - ); + // save this packet to PCAP file + if let Some(file) = savefile.as_mut() { + file.write(&packet); + } + // update the map + let (traffic_direction, service) = modify_or_insert_in_map( + &mut info_traffic_msg, + &key, + &cs, + mac_addresses, + icmp_type, + arp_type, + exchanged_bytes, + ); - info_traffic_msg - .tot_data_info - .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); - let mut r_dns_waiting_resolution = false; - let mut resolutions_lock = resolutions_state.lock().unwrap(); - let r_dns_already_resolved = resolutions_lock - .addresses_resolved + // check the rDNS status of this address and act accordingly + let address_to_lookup = get_address_to_lookup(&key, traffic_direction); + let mut r_dns_waiting_resolution = false; + let mut resolutions_lock = resolutions_state.lock().unwrap(); + let r_dns_already_resolved = resolutions_lock + .addresses_resolved + .contains_key(&address_to_lookup); + if !r_dns_already_resolved { + r_dns_waiting_resolution = resolutions_lock + .addresses_waiting_resolution .contains_key(&address_to_lookup); - if !r_dns_already_resolved { - r_dns_waiting_resolution = resolutions_lock - .addresses_waiting_resolution - .contains_key(&address_to_lookup); - } - - match (r_dns_waiting_resolution, r_dns_already_resolved) { - (false, false) => { - // rDNS not requested yet (first occurrence of this address to lookup) - - // Add this address to the map of addresses waiting for a resolution - // Useful to NOT perform again a rDNS lookup for this entry - resolutions_lock.addresses_waiting_resolution.insert( - address_to_lookup, - DataInfo::new_with_first_packet( - exchanged_bytes, - traffic_direction, - ), - ); - drop(resolutions_lock); - - // launch new thread to resolve host name - let key2 = key; - let resolutions_state2 = resolutions_state.clone(); - let new_hosts_to_send2 = new_hosts_to_send.clone(); - let interface_addresses = cs.get_addresses().clone(); - let mmdb_readers_2 = mmdb_readers.clone(); - let tx2 = tx.clone(); - let _ = thread::Builder::new() - .name("thread_reverse_dns_lookup".to_string()) - .spawn(move || { - reverse_dns_lookup( - &resolutions_state2, - &new_hosts_to_send2, - &key2, - traffic_direction, - &interface_addresses, - &mmdb_readers_2, - &tx2, - ); - }) - .log_err(location!()); - } - (true, false) => { - // waiting for a previously requested rDNS resolution - // update the corresponding waiting address data - resolutions_lock - .addresses_waiting_resolution - .entry(address_to_lookup) - .and_modify(|data_info| { - data_info.add_packet(exchanged_bytes, traffic_direction); - }); - drop(resolutions_lock); - } - (_, true) => { - // rDNS already resolved - // update the corresponding host's data info - let host = resolutions_lock - .addresses_resolved - .get(&address_to_lookup) - .unwrap_or(&Host::default()) - .clone(); - drop(resolutions_lock); - info_traffic_msg - .hosts - .entry(host) - .and_modify(|data_info_host| { - data_info_host - .data_info - .add_packet(exchanged_bytes, traffic_direction); - }) - .or_insert_with(|| { - let my_interface_addresses = cs.get_addresses(); - let traffic_type = get_traffic_type( - &address_to_lookup, - my_interface_addresses, - traffic_direction, - ); - let is_loopback = address_to_lookup.is_loopback(); - let is_local = is_local_connection( - &address_to_lookup, - my_interface_addresses, - ); - let is_bogon = is_bogon(&address_to_lookup); - DataInfoHost { - data_info: DataInfo::new_with_first_packet( - exchanged_bytes, - traffic_direction, - ), - is_favorite: false, - is_loopback, - is_local, - is_bogon, - traffic_type, - } - }); - } - } - - //increment the packet count for the sniffed service - info_traffic_msg - .services - .entry(service) - .and_modify(|data_info| { - data_info.add_packet(exchanged_bytes, traffic_direction); - }) - .or_insert_with(|| { - DataInfo::new_with_first_packet(exchanged_bytes, traffic_direction) - }); } - //increment number of sniffed packets and bytes - info_traffic_msg.all_packets += 1; - info_traffic_msg.all_bytes += exchanged_bytes; + match (r_dns_waiting_resolution, r_dns_already_resolved) { + (false, false) => { + // rDNS not requested yet (first occurrence of this address to lookup) + + // Add this address to the map of addresses waiting for a resolution + // Useful to NOT perform again a rDNS lookup for this entry + resolutions_lock.addresses_waiting_resolution.insert( + address_to_lookup, + DataInfo::new_with_first_packet(exchanged_bytes, traffic_direction), + ); + drop(resolutions_lock); + + // launch new thread to resolve host name + let key2 = key; + let resolutions_state2 = resolutions_state.clone(); + let new_hosts_to_send2 = new_hosts_to_send.clone(); + let interface_addresses = cs.get_addresses().clone(); + let mmdb_readers_2 = mmdb_readers.clone(); + let tx2 = tx.clone(); + let _ = thread::Builder::new() + .name("thread_reverse_dns_lookup".to_string()) + .spawn(move || { + reverse_dns_lookup( + &resolutions_state2, + &new_hosts_to_send2, + &key2, + traffic_direction, + &interface_addresses, + &mmdb_readers_2, + &tx2, + ); + }) + .log_err(location!()); + } + (true, false) => { + // waiting for a previously requested rDNS resolution + // update the corresponding waiting address data + resolutions_lock + .addresses_waiting_resolution + .entry(address_to_lookup) + .and_modify(|data_info| { + data_info.add_packet(exchanged_bytes, traffic_direction); + }); + drop(resolutions_lock); + } + (_, true) => { + // rDNS already resolved + // update the corresponding host's data info + let host = resolutions_lock + .addresses_resolved + .get(&address_to_lookup) + .unwrap_or(&Host::default()) + .clone(); + drop(resolutions_lock); + info_traffic_msg + .hosts + .entry(host) + .and_modify(|data_info_host| { + data_info_host + .data_info + .add_packet(exchanged_bytes, traffic_direction); + }) + .or_insert_with(|| { + let my_interface_addresses = cs.get_addresses(); + let traffic_type = get_traffic_type( + &address_to_lookup, + my_interface_addresses, + traffic_direction, + ); + let is_loopback = address_to_lookup.is_loopback(); + let is_local = is_local_connection( + &address_to_lookup, + my_interface_addresses, + ); + let is_bogon = is_bogon(&address_to_lookup); + DataInfoHost { + data_info: DataInfo::new_with_first_packet( + exchanged_bytes, + traffic_direction, + ), + is_favorite: false, + is_loopback, + is_local, + is_bogon, + traffic_type, + } + }); + } + } + + //increment the packet count for the sniffed service + info_traffic_msg + .services + .entry(service) + .and_modify(|data_info| { + data_info.add_packet(exchanged_bytes, traffic_direction); + }) + .or_insert_with(|| { + DataInfo::new_with_first_packet(exchanged_bytes, traffic_direction) + }); + // update dropped packets number if let Ok(stats) = cap.stats() { info_traffic_msg.dropped_packets = stats.dropped; @@ -292,27 +279,23 @@ pub fn parse_packets( fn get_sniffable_headers<'a>( packet: &'a Packet, my_link_type: MyLinkType, -) -> Result, LaxHeaderSliceError> { +) -> Option> { match my_link_type { MyLinkType::Ethernet(_) | MyLinkType::Unsupported(_) | MyLinkType::NotYetAssigned => { - LaxPacketHeaders::from_ethernet(packet).map_err(LaxHeaderSliceError::Len) + LaxPacketHeaders::from_ethernet(packet).ok() } MyLinkType::RawIp(_) | MyLinkType::IPv4(_) | MyLinkType::IPv6(_) => { - LaxPacketHeaders::from_ip(packet) + LaxPacketHeaders::from_ip(packet).ok() } + MyLinkType::LinuxSll(_) => from_linux_sll(packet, true), + MyLinkType::LinuxSll2(_) => from_linux_sll(packet, false), MyLinkType::Null(_) | MyLinkType::Loop(_) => from_null(packet), } } -fn from_null(packet: &[u8]) -> Result { +fn from_null(packet: &[u8]) -> Option> { if packet.len() <= 4 { - return Err(LaxHeaderSliceError::Len(LenError { - required_len: 4, - len: packet.len(), - len_source: LenSource::Slice, - layer: Layer::Ethernet2Header, - layer_start_offset: 0, - })); + return None; } let is_valid_af_inet = { @@ -333,14 +316,31 @@ fn matches(value: u32) -> bool { }; if is_valid_af_inet { - LaxPacketHeaders::from_ip(&packet[4..]) + LaxPacketHeaders::from_ip(&packet[4..]).ok() } else { - Err(LaxHeaderSliceError::Content( - HeaderError::UnsupportedIpVersion { version_number: 0 }, - )) + None } } +fn from_linux_sll(packet: &[u8], is_v1: bool) -> Option> { + let header_len = if is_v1 { 16 } else { 20 }; + if packet.len() <= header_len { + return None; + } + + let protocol_type = u16::from_be_bytes(if is_v1 { + [packet[14], packet[15]] + } else { + [packet[0], packet[1]] + }); + let payload = &packet[header_len..]; + + Some(LaxPacketHeaders::from_ether_type( + EtherType(protocol_type), + payload, + )) +} + fn reverse_dns_lookup( resolutions_state: &Arc>, new_hosts_to_send: &Arc>>, @@ -442,12 +442,7 @@ fn maybe_send_tick_run_live( new_hosts_to_send.lock().unwrap().drain(..).collect(), false, )); - for dev in Device::list().log_err(location!()).unwrap_or_default() { - if dev.name.eq(&cs.get_name()) { - cs.set_addresses(dev.addresses); - break; - } - } + cs.set_addresses(); } } diff --git a/src/networking/types/bogon.rs b/src/networking/types/bogon.rs index e54fe507..d21fa260 100644 --- a/src/networking/types/bogon.rs +++ b/src/networking/types/bogon.rs @@ -1,20 +1,20 @@ -use crate::networking::types::ip_collection::AddressCollection; +use crate::networking::types::ip_collection::IpCollection; use std::net::IpAddr; pub struct Bogon { - pub range: AddressCollection, + pub range: IpCollection, pub description: &'static str, } // IPv4 bogons static THIS_NETWORK: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon { - range: AddressCollection::new("0.0.0.0-0.255.255.255").unwrap(), + range: IpCollection::new("0.0.0.0-0.255.255.255").unwrap(), description: "\"this\" network", }); static PRIVATE_USE: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon { - range: AddressCollection::new( + range: IpCollection::new( "10.0.0.0-10.255.255.255, 172.16.0.0-172.31.255.255, 192.168.0.0-192.168.255.255", ) .unwrap(), @@ -22,112 +22,112 @@ pub struct Bogon { }); static CARRIER_GRADE: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon { - range: AddressCollection::new("100.64.0.0-100.127.255.255").unwrap(), + range: IpCollection::new("100.64.0.0-100.127.255.255").unwrap(), description: "carrier-grade NAT", }); static LOOPBACK: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon { - range: AddressCollection::new("127.0.0.0-127.255.255.255").unwrap(), + range: IpCollection::new("127.0.0.0-127.255.255.255").unwrap(), description: "loopback", }); static LINK_LOCAL: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon { - range: AddressCollection::new("169.254.0.0-169.254.255.255").unwrap(), + range: IpCollection::new("169.254.0.0-169.254.255.255").unwrap(), description: "link local", }); static IETF_PROTOCOL: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon { - range: AddressCollection::new("192.0.0.0-192.0.0.255").unwrap(), + range: IpCollection::new("192.0.0.0-192.0.0.255").unwrap(), description: "IETF protocol assignments", }); static TEST_NET_1: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon { - range: AddressCollection::new("192.0.2.0-192.0.2.255").unwrap(), + range: IpCollection::new("192.0.2.0-192.0.2.255").unwrap(), description: "TEST-NET-1", }); static NETWORK_INTERCONNECT: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon { - range: AddressCollection::new("198.18.0.0-198.19.255.255").unwrap(), + range: IpCollection::new("198.18.0.0-198.19.255.255").unwrap(), description: "network interconnect device benchmark testing", }); static TEST_NET_2: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon { - range: AddressCollection::new("198.51.100.0-198.51.100.255").unwrap(), + range: IpCollection::new("198.51.100.0-198.51.100.255").unwrap(), description: "TEST-NET-2", }); static TEST_NET_3: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon { - range: AddressCollection::new("203.0.113.0-203.0.113.255").unwrap(), + range: IpCollection::new("203.0.113.0-203.0.113.255").unwrap(), description: "TEST-NET-3", }); static MULTICAST: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon { - range: AddressCollection::new("224.0.0.0-239.255.255.255").unwrap(), + range: IpCollection::new("224.0.0.0-239.255.255.255").unwrap(), description: "multicast", }); static FUTURE_USE: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon { - range: AddressCollection::new("240.0.0.0-255.255.255.255").unwrap(), + range: IpCollection::new("240.0.0.0-255.255.255.255").unwrap(), description: "future use", }); // IPv6 bogons static NODE_SCOPE_UNSPECIFIED: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon { - range: AddressCollection::new("::").unwrap(), + range: IpCollection::new("::").unwrap(), description: "node-scope unicast unspecified", }); static NODE_SCOPE_LOOPBACK: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon { - range: AddressCollection::new("::1").unwrap(), + range: IpCollection::new("::1").unwrap(), description: "node-scope unicast loopback", }); static IPV4_MAPPED: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon { - range: AddressCollection::new("::ffff:0.0.0.0-::ffff:255.255.255.255").unwrap(), + range: IpCollection::new("::ffff:0.0.0.0-::ffff:255.255.255.255").unwrap(), description: "IPv4-mapped", }); static IPV4_COMPATIBLE: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon { - range: AddressCollection::new("::-::255.255.255.255").unwrap(), + range: IpCollection::new("::-::255.255.255.255").unwrap(), description: "IPv4-compatible", }); static REMOTELY_TRIGGERED: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon { - range: AddressCollection::new("100::-100::ffff:ffff:ffff:ffff").unwrap(), + range: IpCollection::new("100::-100::ffff:ffff:ffff:ffff").unwrap(), description: "remotely triggered black hole", }); static ORCHID: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon { - range: AddressCollection::new("2001:10::-2001:1f:ffff:ffff:ffff:ffff:ffff:ffff").unwrap(), + range: IpCollection::new("2001:10::-2001:1f:ffff:ffff:ffff:ffff:ffff:ffff").unwrap(), description: "ORCHID", }); static DOCUMENTATION_PREFIX: std::sync::LazyLock = std::sync::LazyLock::new(|| { Bogon { - range: AddressCollection::new("2001:db8::-2001:db8:ffff:ffff:ffff:ffff:ffff:ffff, 3fff::-3fff:fff:ffff:ffff:ffff:ffff:ffff:ffff") + range: IpCollection::new("2001:db8::-2001:db8:ffff:ffff:ffff:ffff:ffff:ffff, 3fff::-3fff:fff:ffff:ffff:ffff:ffff:ffff:ffff") .unwrap(), description: "documentation prefix", } }); static ULA: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon { - range: AddressCollection::new("fc00::-fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").unwrap(), + range: IpCollection::new("fc00::-fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").unwrap(), description: "ULA", }); static LINK_LOCAL_UNICAST: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon { - range: AddressCollection::new("fe80::-febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff").unwrap(), + range: IpCollection::new("fe80::-febf:ffff:ffff:ffff:ffff:ffff:ffff:ffff").unwrap(), description: "link-local unicast", }); static SITE_LOCAL_UNICAST: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon { - range: AddressCollection::new("fec0::-feff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").unwrap(), + range: IpCollection::new("fec0::-feff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").unwrap(), description: "site-local unicast", }); static MULTICAST_V6: std::sync::LazyLock = std::sync::LazyLock::new(|| Bogon { - range: AddressCollection::new("ff00::-ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").unwrap(), + range: IpCollection::new("ff00::-ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff").unwrap(), description: "multicast v6", }); diff --git a/src/networking/types/byte_multiple.rs b/src/networking/types/byte_multiple.rs deleted file mode 100644 index 670d090a..00000000 --- a/src/networking/types/byte_multiple.rs +++ /dev/null @@ -1,225 +0,0 @@ -use std::fmt; - -use serde::{Deserialize, Serialize}; - -/// Enum representing the possible observed values of IP protocol version. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] -pub enum ByteMultiple { - /// A Byte - B, - /// 10^3 Bytes - KB, - /// 10^6 Bytes - MB, - /// 10^9 Bytes - GB, - /// 10^12 Bytes - TB, - /// 10^15 Bytes - PB, -} - -impl fmt::Display for ByteMultiple { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{self:?}") - } -} - -impl ByteMultiple { - pub fn multiplier(self) -> u64 { - match self { - ByteMultiple::B => 1, - ByteMultiple::KB => 1_000, - ByteMultiple::MB => 1_000_000, - ByteMultiple::GB => 1_000_000_000, - ByteMultiple::TB => 1_000_000_000_000, - ByteMultiple::PB => 1_000_000_000_000_000, - } - } - - fn from_num_bytes(bytes: u128) -> Self { - match bytes { - x if (u128::MIN..u128::from(ByteMultiple::KB.multiplier())).contains(&x) => { - ByteMultiple::B - } - x if (u128::from(ByteMultiple::KB.multiplier()) - ..u128::from(ByteMultiple::MB.multiplier())) - .contains(&x) => - { - ByteMultiple::KB - } - x if (u128::from(ByteMultiple::MB.multiplier()) - ..u128::from(ByteMultiple::GB.multiplier())) - .contains(&x) => - { - ByteMultiple::MB - } - x if (u128::from(ByteMultiple::GB.multiplier()) - ..u128::from(ByteMultiple::TB.multiplier())) - .contains(&x) => - { - ByteMultiple::GB - } - x if (u128::from(ByteMultiple::TB.multiplier()) - ..u128::from(ByteMultiple::PB.multiplier())) - .contains(&x) => - { - ByteMultiple::TB - } - _ => ByteMultiple::PB, - } - } - - pub fn get_char(self) -> String { - self.to_string() - .strip_suffix('B') - .unwrap_or_default() - .to_owned() - } - - pub fn from_char(ch: char) -> Self { - match ch.to_ascii_uppercase() { - 'K' => ByteMultiple::KB, - 'M' => ByteMultiple::MB, - 'G' => ByteMultiple::GB, - 'T' => ByteMultiple::TB, - 'P' => ByteMultiple::PB, - _ => ByteMultiple::B, - } - } - - /// Returns a String representing a quantity of bytes with its proper multiple (B, KB, MB, GB, TB) - pub fn formatted_string(bytes: u128) -> String { - #[allow(clippy::cast_precision_loss)] - let mut n = bytes as f32; - - let byte_multiple = ByteMultiple::from_num_bytes(bytes); - - #[allow(clippy::cast_precision_loss)] - let multiplier = byte_multiple.multiplier() as f32; - n /= multiplier; - if n > 999.0 && byte_multiple != ByteMultiple::PB { - // this allows representing e.g. 999_999 as 999 KB instead of 1000 KB - n = 999.0; - } - let precision = usize::from(byte_multiple != ByteMultiple::B && n <= 9.95); - format!("{n:.precision$} {byte_multiple}") - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_interpret_suffix_correctly() { - assert_eq!(ByteMultiple::from_char('B'), ByteMultiple::B); - assert_eq!(ByteMultiple::from_char('k'), ByteMultiple::KB); - assert_eq!(ByteMultiple::from_char('M'), ByteMultiple::MB); - assert_eq!(ByteMultiple::from_char('g'), ByteMultiple::GB); - assert_eq!(ByteMultiple::from_char('t'), ByteMultiple::TB); - assert_eq!(ByteMultiple::from_char('P'), ByteMultiple::PB); - } - - #[test] - fn test_interpret_unknown_suffix_correctly() { - assert_eq!(ByteMultiple::from_char('E'), ByteMultiple::B); - assert_eq!(ByteMultiple::from_char('y'), ByteMultiple::B); - } - - #[test] - fn test_byte_multiple_display() { - assert_eq!(format!("{}", ByteMultiple::B), "B"); - assert_eq!(format!("{}", ByteMultiple::KB), "KB"); - assert_eq!(format!("{}", ByteMultiple::MB), "MB"); - assert_eq!(format!("{}", ByteMultiple::GB), "GB"); - assert_eq!(format!("{}", ByteMultiple::TB), "TB"); - assert_eq!(format!("{}", ByteMultiple::PB), "PB"); - } - - #[test] - fn test_byte_multiple_get_char() { - assert_eq!(ByteMultiple::B.get_char(), ""); - assert_eq!(ByteMultiple::KB.get_char(), "K"); - assert_eq!(ByteMultiple::MB.get_char(), "M"); - assert_eq!(ByteMultiple::GB.get_char(), "G"); - assert_eq!(ByteMultiple::TB.get_char(), "T"); - assert_eq!(ByteMultiple::PB.get_char(), "P"); - } - - #[test] - fn test_byte_multiple_multiplier() { - assert_eq!(ByteMultiple::B.multiplier(), 1); - assert_eq!(ByteMultiple::KB.multiplier(), 1_000); - assert_eq!(ByteMultiple::MB.multiplier(), 1_000_000); - assert_eq!(ByteMultiple::GB.multiplier(), 1_000_000_000); - assert_eq!(ByteMultiple::TB.multiplier(), 1_000_000_000_000); - assert_eq!(ByteMultiple::PB.multiplier(), 1_000_000_000_000_000); - } - - #[test] - fn test_byte_multiple_formatted_string() { - assert_eq!(ByteMultiple::formatted_string(u128::MIN), "0 B"); - assert_eq!(ByteMultiple::formatted_string(1), "1 B"); - assert_eq!(ByteMultiple::formatted_string(82), "82 B"); - assert_eq!(ByteMultiple::formatted_string(999), "999 B"); - assert_eq!(ByteMultiple::formatted_string(1_000), "1.0 KB"); - assert_eq!(ByteMultiple::formatted_string(1_090), "1.1 KB"); - assert_eq!(ByteMultiple::formatted_string(1_990), "2.0 KB"); - assert_eq!(ByteMultiple::formatted_string(9_090), "9.1 KB"); - assert_eq!(ByteMultiple::formatted_string(9_950), "9.9 KB"); - assert_eq!(ByteMultiple::formatted_string(9_951), "10 KB"); - assert_eq!(ByteMultiple::formatted_string(71_324), "71 KB"); - assert_eq!(ByteMultiple::formatted_string(821_789), "822 KB"); - assert_eq!(ByteMultiple::formatted_string(999_499), "999 KB"); - assert_eq!(ByteMultiple::formatted_string(999_999), "999 KB"); - assert_eq!(ByteMultiple::formatted_string(1_000_000), "1.0 MB"); - assert_eq!(ByteMultiple::formatted_string(3_790_000), "3.8 MB"); - assert_eq!(ByteMultiple::formatted_string(9_950_000), "9.9 MB"); - assert_eq!(ByteMultiple::formatted_string(9_951_000), "10 MB"); - assert_eq!(ByteMultiple::formatted_string(49_499_000), "49 MB"); - assert_eq!(ByteMultiple::formatted_string(49_500_000), "50 MB"); - assert_eq!(ByteMultiple::formatted_string(670_900_000), "671 MB"); - assert_eq!(ByteMultiple::formatted_string(998_199_999), "998 MB"); - assert_eq!(ByteMultiple::formatted_string(999_999_999), "999 MB"); - assert_eq!(ByteMultiple::formatted_string(1_000_000_000), "1.0 GB"); - assert_eq!(ByteMultiple::formatted_string(7_770_000_000), "7.8 GB"); - assert_eq!(ByteMultiple::formatted_string(9_950_000_000), "9.9 GB"); - assert_eq!(ByteMultiple::formatted_string(9_951_000_000), "10 GB"); - assert_eq!(ByteMultiple::formatted_string(19_951_000_000), "20 GB"); - assert_eq!(ByteMultiple::formatted_string(399_951_000_000), "400 GB"); - assert_eq!(ByteMultiple::formatted_string(999_999_999_999), "999 GB"); - assert_eq!(ByteMultiple::formatted_string(1_000_000_000_000), "1.0 TB"); - assert_eq!(ByteMultiple::formatted_string(9_950_000_000_000), "9.9 TB"); - assert_eq!(ByteMultiple::formatted_string(9_951_000_000_000), "10 TB"); - assert_eq!( - ByteMultiple::formatted_string(999_950_000_000_000), - "999 TB" - ); - assert_eq!( - ByteMultiple::formatted_string(999_999_999_999_999), - "999 TB" - ); - assert_eq!( - ByteMultiple::formatted_string(1_000_000_000_000_000), - "1.0 PB" - ); - assert_eq!( - ByteMultiple::formatted_string(1_000_000_000_000_000_0), - "10 PB" - ); - assert_eq!( - ByteMultiple::formatted_string(999_999_999_000_000_000), - "1000 PB" - ); - assert_eq!( - ByteMultiple::formatted_string(1_000_000_000_000_000_000_000), - "1000000 PB" - ); - assert_eq!( - ByteMultiple::formatted_string(u128::MAX / 2), - "170141184077655307190272 PB" - ); - assert_eq!(ByteMultiple::formatted_string(u128::MAX), "inf PB"); - } -} diff --git a/src/networking/types/capture_context.rs b/src/networking/types/capture_context.rs index 43e441cf..66cc703c 100644 --- a/src/networking/types/capture_context.rs +++ b/src/networking/types/capture_context.rs @@ -1,10 +1,14 @@ -use pcap::{Active, Address, Capture, Error, Packet, Savefile, Stat}; - +use crate::gui::types::conf::Conf; +use crate::gui::types::filters::Filters; +use crate::location; use crate::networking::types::my_device::MyDevice; use crate::networking::types::my_link_type::MyLinkType; use crate::translations::translations::network_adapter_translation; -use crate::translations::translations_3::file_name_translation; +use crate::translations::translations_4::capture_file_translation; use crate::translations::types::language::Language; +use crate::utils::error_logger::{ErrorLogger, Location}; +use pcap::{Active, Address, Capture, Device, Error, Packet, Savefile, Stat}; +use serde::{Deserialize, Serialize}; pub enum CaptureContext { Live(Live), @@ -14,11 +18,19 @@ pub enum CaptureContext { } impl CaptureContext { - pub fn new(source: &CaptureSource, pcap_out_path: Option<&String>) -> Self { - let cap_type = match CaptureType::from_source(source, pcap_out_path) { + pub fn new(source: &CaptureSource, pcap_out_path: Option<&String>, filters: &Filters) -> Self { + let mut cap_type = match CaptureType::from_source(source, pcap_out_path) { Ok(c) => c, Err(e) => return Self::Error(e.to_string()), }; + + // only apply BPF filter if it is active, and return an error if it fails to apply + if filters.is_some_filter_active() + && let Err(e) = cap_type.set_bpf(filters.bpf()) + { + return Self::Error(e.to_string()); + } + let cap = match cap_type { CaptureType::Live(cap) => cap, CaptureType::Offline(cap) => return Self::new_offline(cap), @@ -97,7 +109,7 @@ pub enum CaptureType { } impl CaptureType { - pub fn next_packet(&mut self) -> Result { + pub fn next_packet(&mut self) -> Result, Error> { match self { Self::Live(on) => on.next_packet(), Self::Offline(off) => off.next_packet(), @@ -123,7 +135,7 @@ fn from_source(source: &CaptureSource, pcap_out_path: Option<&String>) -> Result } else { 200 // limit stored packets slice dimension (to keep more in the buffer) }) - .immediate_mode(true) // parse packets ASAP + .immediate_mode(false) .timeout(150) // ensure UI is updated even if no packets are captured .open()?; Ok(Self::Live(cap)) @@ -131,6 +143,13 @@ fn from_source(source: &CaptureSource, pcap_out_path: Option<&String>) -> Result CaptureSource::File(file) => Ok(Self::Offline(Capture::from_file(&file.path)?)), } } + + fn set_bpf(&mut self, bpf: &str) -> Result<(), Error> { + match self { + Self::Live(cap) => cap.filter(bpf, true), + Self::Offline(cap) => cap.filter(bpf, true), + } + } } #[derive(Clone)] @@ -140,10 +159,23 @@ pub enum CaptureSource { } impl CaptureSource { + pub fn from_conf(conf: &Conf) -> Self { + match conf.capture_source_picklist { + CaptureSourcePicklist::Device => { + let device = conf.device.to_my_device(); + Self::Device(device) + } + CaptureSourcePicklist::File => { + let path = conf.import_pcap_path.clone(); + Self::File(MyPcapImport::new(path)) + } + } + } + pub fn title(&self, language: Language) -> &str { match self { Self::Device(_) => network_adapter_translation(language), - Self::File(_) => file_name_translation(language), + Self::File(_) => capture_file_translation(language), } } @@ -154,9 +186,21 @@ pub fn get_addresses(&self) -> &Vec
{ } } - pub fn set_addresses(&mut self, addresses: Vec
) { - if let Self::Device(device) = self { - device.set_addresses(addresses); + pub fn set_addresses(&mut self) { + if let Self::Device(my_device) = self { + let mut addresses = Vec::new(); + for dev in Device::list().log_err(location!()).unwrap_or_default() { + if matches!( + my_device.get_link_type(), + MyLinkType::LinuxSll(_) | MyLinkType::LinuxSll2(_) + ) { + addresses.extend(dev.addresses); + } else if dev.name.eq(my_device.get_name()) { + addresses.extend(dev.addresses); + break; + } + } + my_device.set_addresses(addresses); } } @@ -206,3 +250,10 @@ pub fn new(path: String) -> Self { } } } + +#[derive(Clone, Eq, PartialEq, Debug, Copy, Default, Serialize, Deserialize)] +pub enum CaptureSourcePicklist { + #[default] + Device, + File, +} diff --git a/src/networking/types/config_device.rs b/src/networking/types/config_device.rs new file mode 100644 index 00000000..c3d993f7 --- /dev/null +++ b/src/networking/types/config_device.rs @@ -0,0 +1,42 @@ +use crate::networking::types::my_device::MyDevice; +use pcap::{Device, DeviceFlags}; +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)] +#[serde(default)] +pub struct ConfigDevice { + pub device_name: String, +} + +impl Default for ConfigDevice { + fn default() -> Self { + Self { + device_name: Device::lookup() + .unwrap_or(None) + .unwrap_or_else(|| Device { + name: String::new(), + desc: None, + addresses: vec![], + flags: DeviceFlags::empty(), + }) + .name, + } + } +} + +impl ConfigDevice { + pub fn to_my_device(&self) -> MyDevice { + for device in Device::list().unwrap_or_default() { + if device.name.eq(&self.device_name) { + return MyDevice::from_pcap_device(device); + } + } + let standard_device = Device::lookup().unwrap_or(None).unwrap_or_else(|| Device { + name: String::new(), + desc: None, + addresses: vec![], + flags: DeviceFlags::empty(), + }); + MyDevice::from_pcap_device(standard_device) + } +} diff --git a/src/networking/types/data_info.rs b/src/networking/types/data_info.rs index 5c5d99af..975e93e1 100644 --- a/src/networking/types/data_info.rs +++ b/src/networking/types/data_info.rs @@ -1,6 +1,6 @@ //! Module defining the `DataInfo` struct, which represents incoming and outgoing packets and bytes. -use crate::chart::types::chart_type::ChartType; +use crate::networking::types::data_representation::DataRepr; use crate::networking::types::traffic_direction::TrafficDirection; use crate::report::types::sort_type::SortType; use std::cmp::Ordering; @@ -23,37 +23,26 @@ pub struct DataInfo { } impl DataInfo { - pub fn incoming_packets(&self) -> u128 { - self.incoming_packets - } - - pub fn outgoing_packets(&self) -> u128 { - self.outgoing_packets - } - - pub fn incoming_bytes(&self) -> u128 { - self.incoming_bytes - } - - pub fn outgoing_bytes(&self) -> u128 { - self.outgoing_bytes - } - - pub fn tot_packets(&self) -> u128 { - self.incoming_packets + self.outgoing_packets - } - - 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 incoming_data(&self, data_repr: DataRepr) -> u128 { + match data_repr { + DataRepr::Packets => self.incoming_packets, + DataRepr::Bytes => self.incoming_bytes, + DataRepr::Bits => self.incoming_bytes * 8, } } + pub fn outgoing_data(&self, data_repr: DataRepr) -> u128 { + match data_repr { + DataRepr::Packets => self.outgoing_packets, + DataRepr::Bytes => self.outgoing_bytes, + DataRepr::Bits => self.outgoing_bytes * 8, + } + } + + pub fn tot_data(&self, data_repr: DataRepr) -> u128 { + self.incoming_data(data_repr) + self.outgoing_data(data_repr) + } + pub fn add_packet(&mut self, bytes: u128, traffic_direction: TrafficDirection) { if traffic_direction.eq(&TrafficDirection::Outgoing) { self.outgoing_packets += 1; @@ -103,18 +92,11 @@ pub fn refresh(&mut self, rhs: Self) { self.final_instant = rhs.final_instant; } - pub fn compare(&self, other: &Self, sort_type: SortType, chart_type: ChartType) -> Ordering { - match chart_type { - ChartType::Packets => match sort_type { - SortType::Ascending => self.tot_packets().cmp(&other.tot_packets()), - SortType::Descending => other.tot_packets().cmp(&self.tot_packets()), - SortType::Neutral => other.final_instant.cmp(&self.final_instant), - }, - ChartType::Bytes => match sort_type { - SortType::Ascending => self.tot_bytes().cmp(&other.tot_bytes()), - SortType::Descending => other.tot_bytes().cmp(&self.tot_bytes()), - SortType::Neutral => other.final_instant.cmp(&self.final_instant), - }, + pub fn compare(&self, other: &Self, sort_type: SortType, data_repr: DataRepr) -> Ordering { + match sort_type { + SortType::Ascending => self.tot_data(data_repr).cmp(&other.tot_data(data_repr)), + SortType::Descending => other.tot_data(data_repr).cmp(&self.tot_data(data_repr)), + SortType::Neutral => other.final_instant.cmp(&self.final_instant), } } @@ -146,3 +128,115 @@ fn default() -> Self { } } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::networking::types::traffic_direction::TrafficDirection; + + #[test] + fn test_data_info() { + // in_packets: 0, out_packets: 0, in_bytes: 0, out_bytes: 0 + let mut data_info_1 = DataInfo::new_with_first_packet(123, TrafficDirection::Incoming); + // 1, 0, 123, 0 + data_info_1.add_packet(100, TrafficDirection::Incoming); + // 2, 0, 223, 0 + data_info_1.add_packet(200, TrafficDirection::Outgoing); + // 2, 1, 223, 200 + data_info_1.add_packets(11, 1200, TrafficDirection::Outgoing); + // 2, 12, 223, 1400 + data_info_1.add_packets(5, 500, TrafficDirection::Incoming); + // 7, 12, 723, 1400 + + assert_eq!(data_info_1.incoming_packets, 7); + assert_eq!(data_info_1.outgoing_packets, 12); + assert_eq!(data_info_1.incoming_bytes, 723); + assert_eq!(data_info_1.outgoing_bytes, 1400); + + assert_eq!(data_info_1.tot_data(DataRepr::Packets), 19); + assert_eq!(data_info_1.tot_data(DataRepr::Bytes), 2123); + assert_eq!(data_info_1.tot_data(DataRepr::Bits), 16984); + + assert_eq!(data_info_1.incoming_data(DataRepr::Packets), 7); + assert_eq!(data_info_1.incoming_data(DataRepr::Bytes), 723); + assert_eq!(data_info_1.incoming_data(DataRepr::Bits), 5784); + + assert_eq!(data_info_1.outgoing_data(DataRepr::Packets), 12); + assert_eq!(data_info_1.outgoing_data(DataRepr::Bytes), 1400); + assert_eq!(data_info_1.outgoing_data(DataRepr::Bits), 11200); + + let mut data_info_2 = DataInfo::new_with_first_packet(100, TrafficDirection::Outgoing); + // 0, 1, 0, 100 + data_info_2.add_packets(19, 300, TrafficDirection::Outgoing); + // 0, 20, 0, 400 + + assert_eq!(data_info_2.incoming_packets, 0); + assert_eq!(data_info_2.outgoing_packets, 20); + assert_eq!(data_info_2.incoming_bytes, 0); + assert_eq!(data_info_2.outgoing_bytes, 400); + + assert_eq!(data_info_2.tot_data(DataRepr::Packets), 20); + assert_eq!(data_info_2.tot_data(DataRepr::Bytes), 400); + assert_eq!(data_info_2.tot_data(DataRepr::Bits), 3200); + + assert_eq!(data_info_2.incoming_data(DataRepr::Packets), 0); + assert_eq!(data_info_2.incoming_data(DataRepr::Bytes), 0); + assert_eq!(data_info_2.incoming_data(DataRepr::Bits), 0); + + assert_eq!(data_info_2.outgoing_data(DataRepr::Packets), 20); + assert_eq!(data_info_2.outgoing_data(DataRepr::Bytes), 400); + assert_eq!(data_info_2.outgoing_data(DataRepr::Bits), 3200); + + // compare data_info_1 and data_info_2 + + assert_eq!( + data_info_1.compare(&data_info_2, SortType::Ascending, DataRepr::Packets), + Ordering::Less + ); + assert_eq!( + data_info_1.compare(&data_info_2, SortType::Descending, DataRepr::Packets), + Ordering::Greater + ); + assert_eq!( + data_info_1.compare(&data_info_2, SortType::Neutral, DataRepr::Packets), + Ordering::Greater + ); + + assert_eq!( + data_info_1.compare(&data_info_2, SortType::Ascending, DataRepr::Bytes), + Ordering::Greater + ); + assert_eq!( + data_info_1.compare(&data_info_2, SortType::Descending, DataRepr::Bytes), + Ordering::Less + ); + assert_eq!( + data_info_1.compare(&data_info_2, SortType::Neutral, DataRepr::Bytes), + Ordering::Greater + ); + + assert_eq!( + data_info_1.compare(&data_info_2, SortType::Ascending, DataRepr::Bits), + Ordering::Greater + ); + assert_eq!( + data_info_1.compare(&data_info_2, SortType::Descending, DataRepr::Bits), + Ordering::Less + ); + assert_eq!( + data_info_1.compare(&data_info_2, SortType::Neutral, DataRepr::Bits), + Ordering::Greater + ); + + // refresh data_info_1 with data_info_2 + assert!(data_info_1.final_instant < data_info_2.final_instant); + data_info_1.refresh(data_info_2); + + // data_info_1 should now contain the sum of both data_info_1 and data_info_2 + assert_eq!(data_info_1.incoming_packets, 7); + assert_eq!(data_info_1.outgoing_packets, 32); + assert_eq!(data_info_1.incoming_bytes, 723); + assert_eq!(data_info_1.outgoing_bytes, 1800); + assert_eq!(data_info_1.final_instant, data_info_2.final_instant); + } +} diff --git a/src/networking/types/data_representation.rs b/src/networking/types/data_representation.rs new file mode 100644 index 00000000..8e289b27 --- /dev/null +++ b/src/networking/types/data_representation.rs @@ -0,0 +1,523 @@ +use crate::translations::translations::{ + bytes_exceeded_translation, bytes_translation, packets_exceeded_translation, + packets_translation, +}; +use crate::translations::translations_4::{bits_exceeded_translation, bits_translation}; +use crate::translations::types::language::Language; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] +pub enum DataRepr { + Packets, + Bytes, + Bits, +} + +impl DataRepr { + pub(crate) const ALL: [DataRepr; 3] = [DataRepr::Bits, DataRepr::Bytes, DataRepr::Packets]; + + pub fn get_label(&self, language: Language) -> &str { + match self { + DataRepr::Packets => packets_translation(language), + DataRepr::Bytes => bytes_translation(language), + DataRepr::Bits => bits_translation(language), + } + } + + /// Returns a String representing a quantity of traffic (packets / bytes / bits) with the proper multiple if applicable + pub fn formatted_string(self, amount: u128) -> String { + if self == DataRepr::Packets { + return amount.to_string(); + } + + #[allow(clippy::cast_precision_loss)] + let mut n = amount as f32; + + let byte_multiple = ByteMultiple::from_amount(amount); + + #[allow(clippy::cast_precision_loss)] + let multiplier = byte_multiple.multiplier() as f32; + n /= multiplier; + if n > 999.0 && byte_multiple != ByteMultiple::PB { + // this allows representing e.g. 999_999 as 999 KB instead of 1000 KB + n = 999.0; + } + let precision = usize::from(byte_multiple != ByteMultiple::B && n <= 9.95); + format!("{n:.precision$} {}", byte_multiple.pretty_print(self)) + .trim() + .to_string() + } + + pub fn data_exceeded_translation(&self, language: Language) -> &str { + match self { + DataRepr::Packets => packets_exceeded_translation(language), + DataRepr::Bytes => bytes_exceeded_translation(language), + DataRepr::Bits => bits_exceeded_translation(language), + } + } +} + +/// Represents a Byte or bit multiple for displaying values in a human-readable format. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] +pub enum ByteMultiple { + /// A Byte + B, + /// 10^3 Bytes + KB, + /// 10^6 Bytes + MB, + /// 10^9 Bytes + GB, + /// 10^12 Bytes + TB, + /// 10^15 Bytes + PB, +} + +impl ByteMultiple { + pub fn multiplier(self) -> u64 { + match self { + ByteMultiple::B => 1, + ByteMultiple::KB => 1_000, + ByteMultiple::MB => 1_000_000, + ByteMultiple::GB => 1_000_000_000, + ByteMultiple::TB => 1_000_000_000_000, + ByteMultiple::PB => 1_000_000_000_000_000, + } + } + + fn from_amount(bytes: u128) -> Self { + match bytes { + x if (u128::MIN..u128::from(ByteMultiple::KB.multiplier())).contains(&x) => { + ByteMultiple::B + } + x if (u128::from(ByteMultiple::KB.multiplier()) + ..u128::from(ByteMultiple::MB.multiplier())) + .contains(&x) => + { + ByteMultiple::KB + } + x if (u128::from(ByteMultiple::MB.multiplier()) + ..u128::from(ByteMultiple::GB.multiplier())) + .contains(&x) => + { + ByteMultiple::MB + } + x if (u128::from(ByteMultiple::GB.multiplier()) + ..u128::from(ByteMultiple::TB.multiplier())) + .contains(&x) => + { + ByteMultiple::GB + } + x if (u128::from(ByteMultiple::TB.multiplier()) + ..u128::from(ByteMultiple::PB.multiplier())) + .contains(&x) => + { + ByteMultiple::TB + } + _ => ByteMultiple::PB, + } + } + + pub fn get_char(self) -> String { + match self { + Self::B => String::new(), + Self::KB => "K".to_string(), + Self::MB => "M".to_string(), + Self::GB => "G".to_string(), + Self::TB => "T".to_string(), + Self::PB => "P".to_string(), + } + } + + pub fn from_char(ch: char) -> Self { + match ch.to_ascii_uppercase() { + 'K' => ByteMultiple::KB, + 'M' => ByteMultiple::MB, + 'G' => ByteMultiple::GB, + 'T' => ByteMultiple::TB, + 'P' => ByteMultiple::PB, + _ => ByteMultiple::B, + } + } + + fn pretty_print(self, repr: DataRepr) -> String { + match repr { + DataRepr::Packets => String::new(), + DataRepr::Bytes => format!("{}B", self.get_char()), + DataRepr::Bits => format!("{}b", self.get_char()), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_interpret_suffix_correctly() { + assert_eq!(ByteMultiple::from_char('B'), ByteMultiple::B); + assert_eq!(ByteMultiple::from_char('k'), ByteMultiple::KB); + assert_eq!(ByteMultiple::from_char('M'), ByteMultiple::MB); + assert_eq!(ByteMultiple::from_char('g'), ByteMultiple::GB); + assert_eq!(ByteMultiple::from_char('t'), ByteMultiple::TB); + assert_eq!(ByteMultiple::from_char('P'), ByteMultiple::PB); + } + + #[test] + fn test_interpret_unknown_suffix_correctly() { + assert_eq!(ByteMultiple::from_char('E'), ByteMultiple::B); + assert_eq!(ByteMultiple::from_char('y'), ByteMultiple::B); + } + + #[test] + fn test_byte_multiple_display() { + assert_eq!( + format!("{}", ByteMultiple::B.pretty_print(DataRepr::Packets)), + "" + ); + assert_eq!( + format!("{}", ByteMultiple::B.pretty_print(DataRepr::Bytes)), + "B" + ); + assert_eq!( + format!("{}", ByteMultiple::B.pretty_print(DataRepr::Bits)), + "b" + ); + assert_eq!( + format!("{}", ByteMultiple::KB.pretty_print(DataRepr::Packets)), + "" + ); + assert_eq!( + format!("{}", ByteMultiple::KB.pretty_print(DataRepr::Bytes)), + "KB" + ); + assert_eq!( + format!("{}", ByteMultiple::KB.pretty_print(DataRepr::Bits)), + "Kb" + ); + assert_eq!( + format!("{}", ByteMultiple::MB.pretty_print(DataRepr::Packets)), + "" + ); + assert_eq!( + format!("{}", ByteMultiple::MB.pretty_print(DataRepr::Bytes)), + "MB" + ); + assert_eq!( + format!("{}", ByteMultiple::MB.pretty_print(DataRepr::Bits)), + "Mb" + ); + assert_eq!( + format!("{}", ByteMultiple::GB.pretty_print(DataRepr::Packets)), + "" + ); + assert_eq!( + format!("{}", ByteMultiple::GB.pretty_print(DataRepr::Bytes)), + "GB" + ); + assert_eq!( + format!("{}", ByteMultiple::GB.pretty_print(DataRepr::Bits)), + "Gb" + ); + assert_eq!( + format!("{}", ByteMultiple::TB.pretty_print(DataRepr::Packets)), + "" + ); + assert_eq!( + format!("{}", ByteMultiple::TB.pretty_print(DataRepr::Bytes)), + "TB" + ); + assert_eq!( + format!("{}", ByteMultiple::TB.pretty_print(DataRepr::Bits)), + "Tb" + ); + assert_eq!( + format!("{}", ByteMultiple::PB.pretty_print(DataRepr::Packets)), + "" + ); + assert_eq!( + format!("{}", ByteMultiple::PB.pretty_print(DataRepr::Bytes)), + "PB" + ); + assert_eq!( + format!("{}", ByteMultiple::PB.pretty_print(DataRepr::Bits)), + "Pb" + ); + } + + #[test] + fn test_byte_multiple_get_char() { + assert_eq!(ByteMultiple::B.get_char(), ""); + assert_eq!(ByteMultiple::KB.get_char(), "K"); + assert_eq!(ByteMultiple::MB.get_char(), "M"); + assert_eq!(ByteMultiple::GB.get_char(), "G"); + assert_eq!(ByteMultiple::TB.get_char(), "T"); + assert_eq!(ByteMultiple::PB.get_char(), "P"); + } + + #[test] + fn test_byte_multiple_multiplier() { + assert_eq!(ByteMultiple::B.multiplier(), 1); + assert_eq!(ByteMultiple::KB.multiplier(), 1_000); + assert_eq!(ByteMultiple::MB.multiplier(), 1_000_000); + assert_eq!(ByteMultiple::GB.multiplier(), 1_000_000_000); + assert_eq!(ByteMultiple::TB.multiplier(), 1_000_000_000_000); + assert_eq!(ByteMultiple::PB.multiplier(), 1_000_000_000_000_000); + } + + #[test] + fn test_byte_multiple_formatted_string() { + assert_eq!(DataRepr::Packets.formatted_string(u128::MIN), "0"); + assert_eq!(DataRepr::Bytes.formatted_string(u128::MIN), "0 B"); + assert_eq!(DataRepr::Bits.formatted_string(u128::MIN), "0 b"); + + assert_eq!(DataRepr::Packets.formatted_string(1), "1"); + assert_eq!(DataRepr::Bytes.formatted_string(1), "1 B"); + assert_eq!(DataRepr::Bits.formatted_string(1), "1 b"); + + assert_eq!(DataRepr::Packets.formatted_string(82), "82"); + assert_eq!(DataRepr::Bytes.formatted_string(82), "82 B"); + assert_eq!(DataRepr::Bits.formatted_string(82), "82 b"); + + assert_eq!(DataRepr::Packets.formatted_string(999), "999"); + assert_eq!(DataRepr::Bytes.formatted_string(999), "999 B"); + assert_eq!(DataRepr::Bits.formatted_string(999), "999 b"); + + assert_eq!(DataRepr::Packets.formatted_string(1_000), "1000"); + assert_eq!(DataRepr::Bytes.formatted_string(1_000), "1.0 KB"); + assert_eq!(DataRepr::Bits.formatted_string(1_000), "1.0 Kb"); + + assert_eq!(DataRepr::Packets.formatted_string(1_090), "1090"); + assert_eq!(DataRepr::Bytes.formatted_string(1_090), "1.1 KB"); + assert_eq!(DataRepr::Bits.formatted_string(1_090), "1.1 Kb"); + + assert_eq!(DataRepr::Packets.formatted_string(1_990), "1990"); + assert_eq!(DataRepr::Bytes.formatted_string(1_990), "2.0 KB"); + assert_eq!(DataRepr::Bits.formatted_string(1_990), "2.0 Kb"); + + assert_eq!(DataRepr::Packets.formatted_string(9_090), "9090"); + assert_eq!(DataRepr::Bytes.formatted_string(9_090), "9.1 KB"); + assert_eq!(DataRepr::Bits.formatted_string(9_090), "9.1 Kb"); + + assert_eq!(DataRepr::Packets.formatted_string(9_950), "9950"); + assert_eq!(DataRepr::Bytes.formatted_string(9_950), "9.9 KB"); + assert_eq!(DataRepr::Bits.formatted_string(9_950), "9.9 Kb"); + + assert_eq!(DataRepr::Packets.formatted_string(9_951), "9951"); + assert_eq!(DataRepr::Bytes.formatted_string(9_951), "10 KB"); + assert_eq!(DataRepr::Bits.formatted_string(9_951), "10 Kb"); + + assert_eq!(DataRepr::Packets.formatted_string(71_324), "71324"); + assert_eq!(DataRepr::Bytes.formatted_string(71_324), "71 KB"); + assert_eq!(DataRepr::Bits.formatted_string(71_324), "71 Kb"); + + assert_eq!(DataRepr::Packets.formatted_string(821_789), "821789"); + assert_eq!(DataRepr::Bytes.formatted_string(821_789), "822 KB"); + assert_eq!(DataRepr::Bits.formatted_string(821_789), "822 Kb"); + + assert_eq!(DataRepr::Packets.formatted_string(999_499), "999499"); + assert_eq!(DataRepr::Bytes.formatted_string(999_499), "999 KB"); + assert_eq!(DataRepr::Bits.formatted_string(999_499), "999 Kb"); + + assert_eq!(DataRepr::Packets.formatted_string(999_999), "999999"); + assert_eq!(DataRepr::Bytes.formatted_string(999_999), "999 KB"); + assert_eq!(DataRepr::Bits.formatted_string(999_999), "999 Kb"); + + assert_eq!(DataRepr::Packets.formatted_string(1_000_000), "1000000"); + assert_eq!(DataRepr::Bytes.formatted_string(1_000_000), "1.0 MB"); + assert_eq!(DataRepr::Bits.formatted_string(1_000_000), "1.0 Mb"); + + assert_eq!(DataRepr::Packets.formatted_string(3_790_000), "3790000"); + assert_eq!(DataRepr::Bytes.formatted_string(3_790_000), "3.8 MB"); + assert_eq!(DataRepr::Bits.formatted_string(3_790_000), "3.8 Mb"); + + assert_eq!(DataRepr::Packets.formatted_string(9_950_000), "9950000"); + assert_eq!(DataRepr::Bytes.formatted_string(9_950_000), "9.9 MB"); + assert_eq!(DataRepr::Bits.formatted_string(9_950_000), "9.9 Mb"); + + assert_eq!(DataRepr::Packets.formatted_string(9_951_000), "9951000"); + assert_eq!(DataRepr::Bytes.formatted_string(9_951_000), "10 MB"); + assert_eq!(DataRepr::Bits.formatted_string(9_951_000), "10 Mb"); + + assert_eq!(DataRepr::Packets.formatted_string(49_499_000), "49499000"); + assert_eq!(DataRepr::Bytes.formatted_string(49_499_000), "49 MB"); + assert_eq!(DataRepr::Bits.formatted_string(49_499_000), "49 Mb"); + + assert_eq!(DataRepr::Packets.formatted_string(49_500_000), "49500000"); + assert_eq!(DataRepr::Bytes.formatted_string(49_500_000), "50 MB"); + assert_eq!(DataRepr::Bits.formatted_string(49_500_000), "50 Mb"); + + assert_eq!(DataRepr::Packets.formatted_string(670_900_000), "670900000"); + assert_eq!(DataRepr::Bytes.formatted_string(670_900_000), "671 MB"); + assert_eq!(DataRepr::Bits.formatted_string(670_900_000), "671 Mb"); + + assert_eq!(DataRepr::Packets.formatted_string(998_199_999), "998199999"); + assert_eq!(DataRepr::Bytes.formatted_string(998_199_999), "998 MB"); + assert_eq!(DataRepr::Bits.formatted_string(998_199_999), "998 Mb"); + + assert_eq!(DataRepr::Packets.formatted_string(999_999_999), "999999999"); + assert_eq!(DataRepr::Bytes.formatted_string(999_999_999), "999 MB"); + assert_eq!(DataRepr::Bits.formatted_string(999_999_999), "999 Mb"); + + assert_eq!( + DataRepr::Packets.formatted_string(1_000_000_000), + "1000000000" + ); + assert_eq!(DataRepr::Bytes.formatted_string(1_000_000_000), "1.0 GB"); + assert_eq!(DataRepr::Bits.formatted_string(1_000_000_000), "1.0 Gb"); + + assert_eq!( + DataRepr::Packets.formatted_string(7_770_000_000), + "7770000000" + ); + assert_eq!(DataRepr::Bytes.formatted_string(7_770_000_000), "7.8 GB"); + assert_eq!(DataRepr::Bits.formatted_string(7_770_000_000), "7.8 Gb"); + + assert_eq!( + DataRepr::Packets.formatted_string(9_950_000_000), + "9950000000" + ); + assert_eq!(DataRepr::Bytes.formatted_string(9_950_000_000), "9.9 GB"); + assert_eq!(DataRepr::Bits.formatted_string(9_950_000_000), "9.9 Gb"); + + assert_eq!( + DataRepr::Packets.formatted_string(9_951_000_000), + "9951000000" + ); + assert_eq!(DataRepr::Bytes.formatted_string(9_951_000_000), "10 GB"); + assert_eq!(DataRepr::Bits.formatted_string(9_951_000_000), "10 Gb"); + + assert_eq!( + DataRepr::Packets.formatted_string(19_951_000_000), + "19951000000" + ); + assert_eq!(DataRepr::Bytes.formatted_string(19_951_000_000), "20 GB"); + assert_eq!(DataRepr::Bits.formatted_string(19_951_000_000), "20 Gb"); + + assert_eq!( + DataRepr::Packets.formatted_string(399_951_000_000), + "399951000000" + ); + assert_eq!(DataRepr::Bytes.formatted_string(399_951_000_000), "400 GB"); + assert_eq!(DataRepr::Bits.formatted_string(399_951_000_000), "400 Gb"); + + assert_eq!( + DataRepr::Packets.formatted_string(999_999_999_999), + "999999999999" + ); + assert_eq!(DataRepr::Bytes.formatted_string(999_999_999_999), "999 GB"); + assert_eq!(DataRepr::Bits.formatted_string(999_999_999_999), "999 Gb"); + + assert_eq!( + DataRepr::Packets.formatted_string(1_000_000_000_000), + "1000000000000" + ); + assert_eq!( + DataRepr::Bytes.formatted_string(1_000_000_000_000), + "1.0 TB" + ); + assert_eq!(DataRepr::Bits.formatted_string(1_000_000_000_000), "1.0 Tb"); + + assert_eq!( + DataRepr::Packets.formatted_string(9_950_000_000_000), + "9950000000000" + ); + assert_eq!( + DataRepr::Bytes.formatted_string(9_950_000_000_000), + "9.9 TB" + ); + assert_eq!(DataRepr::Bits.formatted_string(9_950_000_000_000), "9.9 Tb"); + + assert_eq!( + DataRepr::Packets.formatted_string(9_951_000_000_000), + "9951000000000" + ); + assert_eq!(DataRepr::Bytes.formatted_string(9_951_000_000_000), "10 TB"); + assert_eq!(DataRepr::Bits.formatted_string(9_951_000_000_000), "10 Tb"); + + assert_eq!( + DataRepr::Packets.formatted_string(999_950_000_000_000), + "999950000000000" + ); + assert_eq!( + DataRepr::Bytes.formatted_string(999_950_000_000_000), + "999 TB" + ); + assert_eq!( + DataRepr::Bits.formatted_string(999_950_000_000_000), + "999 Tb" + ); + + assert_eq!( + DataRepr::Packets.formatted_string(999_999_999_999_999), + "999999999999999" + ); + assert_eq!( + DataRepr::Bytes.formatted_string(999_999_999_999_999), + "999 TB" + ); + assert_eq!( + DataRepr::Bits.formatted_string(999_999_999_999_999), + "999 Tb" + ); + + assert_eq!( + DataRepr::Packets.formatted_string(1_000_000_000_000_000), + "1000000000000000" + ); + assert_eq!( + DataRepr::Bytes.formatted_string(1_000_000_000_000_000), + "1.0 PB" + ); + assert_eq!( + DataRepr::Bits.formatted_string(1_000_000_000_000_000), + "1.0 Pb" + ); + + assert_eq!( + DataRepr::Packets.formatted_string(1_000_000_000_000_000_0), + "10000000000000000" + ); + assert_eq!( + DataRepr::Bytes.formatted_string(1_000_000_000_000_000_0), + "10 PB" + ); + assert_eq!( + DataRepr::Bits.formatted_string(1_000_000_000_000_000_0), + "10 Pb" + ); + assert_eq!( + DataRepr::Packets.formatted_string(999_999_999_000_000_000), + "999999999000000000" + ); + assert_eq!( + DataRepr::Bytes.formatted_string(999_999_999_000_000_000), + "1000 PB" + ); + assert_eq!( + DataRepr::Bits.formatted_string(999_999_999_000_000_000), + "1000 Pb" + ); + + assert_eq!( + DataRepr::Packets.formatted_string(u128::MAX / 2), + "170141183460469231731687303715884105727" + ); + assert_eq!( + DataRepr::Bytes.formatted_string(u128::MAX / 2), + "170141184077655307190272 PB" + ); + assert_eq!( + DataRepr::Bits.formatted_string(u128::MAX / 2), + "170141184077655307190272 Pb" + ); + + assert_eq!( + DataRepr::Packets.formatted_string(u128::MAX), + "340282366920938463463374607431768211455" + ); + assert_eq!(DataRepr::Bytes.formatted_string(u128::MAX), "inf PB"); + assert_eq!(DataRepr::Bits.formatted_string(u128::MAX), "inf Pb"); + } +} diff --git a/src/networking/types/filters.rs b/src/networking/types/filters.rs deleted file mode 100644 index 62f7e1e4..00000000 --- a/src/networking/types/filters.rs +++ /dev/null @@ -1,112 +0,0 @@ -//! Module defining the `Filters` struct, which represents the possible filters applicable on network traffic. - -use std::collections::HashSet; - -use crate::networking::types::ip_collection::AddressCollection; -use crate::networking::types::packet_filters_fields::PacketFiltersFields; -use crate::networking::types::port_collection::PortCollection; -use crate::{IpVersion, Protocol}; - -/// Possible filters applicable to network traffic -#[derive(Clone)] -pub struct Filters { - /// Internet Protocol versions - pub ip_versions: HashSet, - /// Protocols - pub protocols: HashSet, - /// IP addresses string in Initial page text input - pub address_str: String, - /// IP address collection to match against traffic - pub address_collection: AddressCollection, - /// Ports string in Initial page text input - pub port_str: String, - /// Port collection to match against traffic - pub port_collection: PortCollection, -} - -impl Default for Filters { - fn default() -> Self { - Self { - ip_versions: HashSet::from(IpVersion::ALL), - protocols: HashSet::from(Protocol::ALL), - address_str: String::new(), - address_collection: AddressCollection::default(), - port_str: String::new(), - port_collection: PortCollection::default(), - } - } -} - -impl Filters { - /// Checks whether the filters match the current packet's protocols - pub fn matches(&self, packet_filters_fields: &PacketFiltersFields) -> bool { - self.ip_versions.contains(&packet_filters_fields.ip_version) - && self.protocols.contains(&packet_filters_fields.protocol) - && (self - .address_collection - .contains(&packet_filters_fields.source) - || self - .address_collection - .contains(&packet_filters_fields.dest)) - && (self.port_collection.contains(packet_filters_fields.sport) - || self.port_collection.contains(packet_filters_fields.dport)) - } - - pub fn are_valid(&self) -> bool { - self.ip_version_valid() - && self.protocol_valid() - && self.address_valid() - && self.port_valid() - } - - pub fn ip_version_valid(&self) -> bool { - !self.ip_versions.is_empty() - } - - pub fn protocol_valid(&self) -> bool { - !self.protocols.is_empty() - } - - pub fn address_valid(&self) -> bool { - AddressCollection::new(&self.address_str).is_some() - } - - pub fn port_valid(&self) -> bool { - PortCollection::new(&self.port_str).is_some() - } - - pub fn none_active(&self) -> bool { - !self.ip_version_active() - && !self.protocol_active() - && !self.address_active() - && !self.port_active() - } - - pub fn ip_version_active(&self) -> bool { - self.ip_versions.len() != IpVersion::ALL.len() - } - - pub fn protocol_active(&self) -> bool { - self.protocols.len() != Protocol::ALL.len() - } - - pub fn address_active(&self) -> bool { - self.address_collection != AddressCollection::default() - } - - pub fn port_active(&self) -> bool { - self.port_collection != PortCollection::default() - } - - pub fn pretty_print_ip(&self) -> String { - format!("{:?}", self.ip_versions) - .replace('{', "") - .replace('}', "") - } - - pub fn pretty_print_protocol(&self) -> String { - format!("{:?}", self.protocols) - .replace('{', "") - .replace('}', "") - } -} diff --git a/src/networking/types/icmp_type.rs b/src/networking/types/icmp_type.rs index ddb2c31f..6859ab6d 100644 --- a/src/networking/types/icmp_type.rs +++ b/src/networking/types/icmp_type.rs @@ -216,15 +216,15 @@ pub fn from_etherparse(icmpv6type: &Icmpv6Type) -> IcmpType { Icmpv6Type::ParameterProblem(_) => Self::ParameterProblem, Icmpv6Type::EchoRequest(_) => Self::EchoRequest, Icmpv6Type::EchoReply(_) => Self::EchoReply, + Icmpv6Type::RouterSolicitation => Self::RouterSolicitation, + Icmpv6Type::RouterAdvertisement(_) => Self::RouterAdvertisement, + Icmpv6Type::NeighborSolicitation => Self::NeighborSolicitation, + Icmpv6Type::NeighborAdvertisement(_) => Self::NeighborAdvertisement, + Icmpv6Type::Redirect => Self::RedirectMessage, Icmpv6Type::Unknown { type_u8: id, .. } => match id { 130 => Self::MulticastListenerQuery, 131 => Self::MulticastListenerReport, 132 => Self::MulticastListenerDone, - 133 => Self::RouterSolicitation, - 134 => Self::RouterAdvertisement, - 135 => Self::NeighborSolicitation, - 136 => Self::NeighborAdvertisement, - 137 => Self::RedirectMessage, 138 => Self::RouterRenumbering, 139 => Self::ICMPNodeInformationQuery, 140 => Self::ICMPNodeInformationResponse, diff --git a/src/networking/types/info_address_port_pair.rs b/src/networking/types/info_address_port_pair.rs index 3e9682d6..a872f85a 100644 --- a/src/networking/types/info_address_port_pair.rs +++ b/src/networking/types/info_address_port_pair.rs @@ -1,12 +1,15 @@ //! Module defining the `InfoAddressPortPair` struct, useful to format the output report file and //! to keep track of statistics about the sniffed traffic. +use std::cmp::Ordering; use std::collections::HashMap; use crate::Service; use crate::networking::types::arp_type::ArpType; +use crate::networking::types::data_representation::DataRepr; use crate::networking::types::icmp_type::IcmpType; use crate::networking::types::traffic_direction::TrafficDirection; +use crate::report::types::sort_type::SortType; use crate::utils::types::timestamp::Timestamp; /// Struct useful to format the output report file and to keep track of statistics about the sniffed traffic. @@ -56,4 +59,94 @@ pub fn refresh(&mut self, other: &Self) { .or_insert(*count); } } + + pub fn transmitted_data(&self, data_repr: DataRepr) -> u128 { + match data_repr { + DataRepr::Packets => self.transmitted_packets, + DataRepr::Bytes => self.transmitted_bytes, + DataRepr::Bits => self.transmitted_bytes * 8, + } + } + + pub fn compare(&self, other: &Self, sort_type: SortType, data_repr: DataRepr) -> Ordering { + match sort_type { + SortType::Ascending => self + .transmitted_data(data_repr) + .cmp(&other.transmitted_data(data_repr)), + SortType::Descending => other + .transmitted_data(data_repr) + .cmp(&self.transmitted_data(data_repr)), + SortType::Neutral => other.final_timestamp.cmp(&self.final_timestamp), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::networking::types::data_representation::DataRepr; + use crate::report::types::sort_type::SortType; + + #[test] + fn test_info_address_port_pair_data() { + let pair1 = InfoAddressPortPair { + transmitted_bytes: 1000, + transmitted_packets: 10, + final_timestamp: Timestamp::new(8, 1300), + ..Default::default() + }; + let pair2 = InfoAddressPortPair { + transmitted_bytes: 1100, + transmitted_packets: 8, + final_timestamp: Timestamp::new(15, 0), + ..Default::default() + }; + + assert_eq!(pair1.transmitted_data(DataRepr::Bytes), 1000); + assert_eq!(pair1.transmitted_data(DataRepr::Packets), 10); + assert_eq!(pair1.transmitted_data(DataRepr::Bits), 8000); + + assert_eq!(pair2.transmitted_data(DataRepr::Bytes), 1100); + assert_eq!(pair2.transmitted_data(DataRepr::Packets), 8); + assert_eq!(pair2.transmitted_data(DataRepr::Bits), 8800); + + assert_eq!( + pair1.compare(&pair2, SortType::Ascending, DataRepr::Bytes), + Ordering::Less + ); + assert_eq!( + pair1.compare(&pair2, SortType::Descending, DataRepr::Bytes), + Ordering::Greater + ); + assert_eq!( + pair1.compare(&pair2, SortType::Neutral, DataRepr::Bytes), + Ordering::Greater + ); + + assert_eq!( + pair1.compare(&pair2, SortType::Ascending, DataRepr::Packets), + Ordering::Greater + ); + assert_eq!( + pair1.compare(&pair2, SortType::Descending, DataRepr::Packets), + Ordering::Less + ); + assert_eq!( + pair1.compare(&pair2, SortType::Neutral, DataRepr::Packets), + Ordering::Greater + ); + + assert_eq!( + pair1.compare(&pair2, SortType::Ascending, DataRepr::Bits), + Ordering::Less + ); + assert_eq!( + pair1.compare(&pair2, SortType::Descending, DataRepr::Bits), + Ordering::Greater + ); + assert_eq!( + pair1.compare(&pair2, SortType::Neutral, DataRepr::Bits), + Ordering::Greater + ); + } } diff --git a/src/networking/types/info_traffic.rs b/src/networking/types/info_traffic.rs index 976e78c7..4d9d16f4 100644 --- a/src/networking/types/info_traffic.rs +++ b/src/networking/types/info_traffic.rs @@ -1,8 +1,8 @@ use crate::Service; -use crate::chart::types::chart_type::ChartType; use crate::networking::types::address_port_pair::AddressPortPair; use crate::networking::types::data_info::DataInfo; use crate::networking::types::data_info_host::DataInfoHost; +use crate::networking::types::data_representation::DataRepr; use crate::networking::types::host::Host; use crate::networking::types::info_address_port_pair::InfoAddressPortPair; use crate::utils::types::timestamp::Timestamp; @@ -13,15 +13,11 @@ pub struct InfoTraffic { /// 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 - 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 + /// Map of the traffic pub map: HashMap, /// Map of the upper layer services with their data info pub services: HashMap, @@ -33,8 +29,6 @@ impl InfoTraffic { pub fn refresh(&mut self, msg: &mut InfoTraffic) { self.tot_data_info.refresh(msg.tot_data_info); - 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 @@ -65,27 +59,20 @@ pub fn refresh(&mut self, msg: &mut InfoTraffic) { } } - pub fn get_thumbnail_data(&self, chart_type: ChartType) -> (u128, u128, u128, u128) { - if chart_type.eq(&ChartType::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(), + pub fn get_thumbnail_data(&self, data_repr: DataRepr) -> (u128, u128, u128) { + let incoming = self.tot_data_info.incoming_data(data_repr); + let outgoing = self.tot_data_info.outgoing_data(data_repr); + let all = incoming + outgoing; + let all_packets = self.tot_data_info.tot_data(DataRepr::Packets); + let dropped = match data_repr { + DataRepr::Packets => u128::from(self.dropped_packets), + DataRepr::Bytes | DataRepr::Bits => { // 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_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), - ) - } + u128::from(self.dropped_packets) * all / all_packets + } + }; + + (incoming, outgoing, dropped) } pub fn take_but_leave_something(&mut self) -> Self { diff --git a/src/networking/types/ip_collection.rs b/src/networking/types/ip_collection.rs index 20b469ac..09b0d327 100644 --- a/src/networking/types/ip_collection.rs +++ b/src/networking/types/ip_collection.rs @@ -3,18 +3,15 @@ use std::str::FromStr; #[derive(Debug, Eq, PartialEq, Clone)] -pub(crate) struct AddressCollection { +pub(crate) struct IpCollection { ips: Vec, ranges: Vec>, } -impl AddressCollection { +impl IpCollection { const SEPARATOR: char = ','; const RANGE_SEPARATOR: char = '-'; - pub const PLACEHOLDER_STR: &'static str = - "0.0.0.0-255.255.255.255, ::-ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"; - pub(crate) fn new(str: &str) -> Option { let str = str.replace(' ', ""); @@ -62,9 +59,9 @@ pub(crate) fn contains(&self, ip: &IpAddr) -> bool { } } -impl Default for AddressCollection { +impl Default for IpCollection { fn default() -> Self { - AddressCollection { + IpCollection { ips: vec![], ranges: vec![ RangeInclusive::new( @@ -89,11 +86,11 @@ mod tests { use std::ops::RangeInclusive; use std::str::FromStr; - use crate::networking::types::ip_collection::AddressCollection; + use crate::networking::types::ip_collection::IpCollection; #[test] fn test_default_collection_contains_everything() { - let collection = AddressCollection::default(); + let collection = IpCollection::default(); assert!(collection.contains(&IpAddr::from_str("1.1.1.1").unwrap())); assert!(collection.contains(&IpAddr::from_str("0.0.0.0").unwrap())); assert!(collection.contains(&IpAddr::from_str("255.255.255.255").unwrap())); @@ -116,8 +113,8 @@ fn test_default_collection_contains_everything() { #[test] fn test_new_collections_1() { assert_eq!( - AddressCollection::new("1.1.1.1,2.2.2.2").unwrap(), - AddressCollection { + IpCollection::new("1.1.1.1,2.2.2.2").unwrap(), + IpCollection { ips: vec![ IpAddr::from_str("1.1.1.1").unwrap(), IpAddr::from_str("2.2.2.2").unwrap() @@ -127,11 +124,9 @@ fn test_new_collections_1() { ); assert_eq!( - AddressCollection::new( - "1.1.1.1, 2.2.2.2, 3.3.3.3 - 5.5.5.5, 10.0.0.1-10.0.0.255,9.9.9.9", - ) - .unwrap(), - AddressCollection { + IpCollection::new("1.1.1.1, 2.2.2.2, 3.3.3.3 - 5.5.5.5, 10.0.0.1-10.0.0.255,9.9.9.9",) + .unwrap(), + IpCollection { ips: vec![ IpAddr::from_str("1.1.1.1").unwrap(), IpAddr::from_str("2.2.2.2").unwrap(), @@ -151,8 +146,8 @@ fn test_new_collections_1() { ); assert_eq!( - AddressCollection::new(" aaaa::ffff,bbbb::1-cccc::2").unwrap(), - AddressCollection { + IpCollection::new(" aaaa::ffff,bbbb::1-cccc::2").unwrap(), + IpCollection { ips: vec![IpAddr::from_str("aaaa::ffff").unwrap(),], ranges: vec![RangeInclusive::new( IpAddr::from_str("bbbb::1").unwrap(), @@ -165,8 +160,8 @@ fn test_new_collections_1() { #[test] fn test_new_collections_2() { assert_eq!( - AddressCollection::new("1.1.1.1,2.2.2.2, 8.8.8.8 ").unwrap(), - AddressCollection { + IpCollection::new("1.1.1.1,2.2.2.2, 8.8.8.8 ").unwrap(), + IpCollection { ips: vec![ IpAddr::from_str("1.1.1.1").unwrap(), IpAddr::from_str("2.2.2.2").unwrap(), @@ -177,8 +172,8 @@ fn test_new_collections_2() { ); assert_eq!( - AddressCollection::new(" 1.1.1.1 -1.1.1.1").unwrap(), - AddressCollection { + IpCollection::new(" 1.1.1.1 -1.1.1.1").unwrap(), + IpCollection { ips: vec![], ranges: vec![RangeInclusive::new( IpAddr::from_str("1.1.1.1").unwrap(), @@ -188,9 +183,9 @@ fn test_new_collections_2() { ); assert_eq!( - AddressCollection::new("1.1.1.1,2.2.2.2,3.3.3.3-5.5.5.5,10.0.0.1-10.0.0.255,9.9.9.9",) + IpCollection::new("1.1.1.1,2.2.2.2,3.3.3.3-5.5.5.5,10.0.0.1-10.0.0.255,9.9.9.9",) .unwrap(), - AddressCollection { + IpCollection { ips: vec![ IpAddr::from_str("1.1.1.1").unwrap(), IpAddr::from_str("2.2.2.2").unwrap(), @@ -210,8 +205,8 @@ fn test_new_collections_2() { ); assert_eq!( - AddressCollection::new("aaaa::ffff,bbbb::1-cccc::2,ff::dd").unwrap(), - AddressCollection { + IpCollection::new("aaaa::ffff,bbbb::1-cccc::2,ff::dd").unwrap(), + IpCollection { ips: vec![ IpAddr::from_str("aaaa::ffff").unwrap(), IpAddr::from_str("ff::dd").unwrap() @@ -227,32 +222,32 @@ fn test_new_collections_2() { #[test] fn test_new_collections_invalid() { assert_eq!( - AddressCollection::new("1.1.1.1,2.2.2.2,3.3.3.3-5.5.5.5,10.0.0.1-10.0.0.255,9.9.9"), + IpCollection::new("1.1.1.1,2.2.2.2,3.3.3.3-5.5.5.5,10.0.0.1-10.0.0.255,9.9.9"), None ); assert_eq!( - AddressCollection::new("1.1.1.1,2.2.2.2,3.3.3.3-5.5.5.5,10.0.0.1:10.0.0.255,9.9.9.9"), + IpCollection::new("1.1.1.1,2.2.2.2,3.3.3.3-5.5.5.5,10.0.0.1:10.0.0.255,9.9.9.9"), None ); - assert_eq!(AddressCollection::new("1.1.1.1-aa::ff"), None); + assert_eq!(IpCollection::new("1.1.1.1-aa::ff"), None); - assert_eq!(AddressCollection::new("aa::ff-1.1.1.1"), None); + assert_eq!(IpCollection::new("aa::ff-1.1.1.1"), None); - assert_eq!(AddressCollection::new("aa::ff-aa::ee"), None); + assert_eq!(IpCollection::new("aa::ff-aa::ee"), None); - assert_eq!(AddressCollection::new("1.1.1.1-1.1.0.1"), None); + assert_eq!(IpCollection::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!(IpCollection::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); + assert_eq!(IpCollection::new("1.1.1.1-2.2.2.2-"), None); } #[test] fn test_ip_collection_contains() { let collection = - AddressCollection::new("1.1.1.1,2.2.2.2,3.3.3.3-5.5.5.5,10.0.0.1-10.0.0.255,9.9.9.9") + IpCollection::new("1.1.1.1,2.2.2.2,3.3.3.3-5.5.5.5,10.0.0.1-10.0.0.255,9.9.9.9") .unwrap(); assert!(collection.contains(&IpAddr::from_str("1.1.1.1").unwrap())); assert!(collection.contains(&IpAddr::from_str("2.2.2.2").unwrap())); @@ -268,12 +263,12 @@ fn test_ip_collection_contains() { assert!(!collection.contains(&IpAddr::from_str("9.9.9.10").unwrap())); assert!(!collection.contains(&IpAddr::from_str("3.3.3.2").unwrap())); - let collection_2 = AddressCollection::new("1.1.1.0-1.1.9.0").unwrap(); + let collection_2 = IpCollection::new("1.1.1.0-1.1.9.0").unwrap(); assert!(!collection_2.contains(&IpAddr::from_str("1.1.100.5").unwrap())); assert!(collection_2.contains(&IpAddr::from_str("1.1.3.255").unwrap())); // check that ipv4 range doesn't contain ipv6 - let collection_3 = AddressCollection::new("0.0.0.0-255.255.255.255").unwrap(); + let collection_3 = IpCollection::new("0.0.0.0-255.255.255.255").unwrap(); assert!(!collection_3.contains(&IpAddr::from_str("::").unwrap())); assert!(!collection_3.contains(&IpAddr::from_str("1111::2222").unwrap())); } @@ -281,7 +276,7 @@ fn test_ip_collection_contains() { #[test] fn test_ip_collection_contains_ipv6() { let collection = - AddressCollection::new( "2001:db8:1234:0000:0000:0000:0000:0000-2001:db8:1234:ffff:ffff:ffff:ffff:ffff,daa::aad,caa::aac").unwrap(); + IpCollection::new( "2001:db8:1234:0000:0000:0000:0000:0000-2001:db8:1234:ffff:ffff:ffff:ffff:ffff,daa::aad,caa::aac").unwrap(); assert!( collection .contains(&IpAddr::from_str("2001:db8:1234:0000:0000:0000:0000:0000").unwrap()) @@ -315,14 +310,14 @@ fn test_ip_collection_contains_ipv6() { assert!(!collection.contains(&IpAddr::from_str("da::aad").unwrap())); assert!(!collection.contains(&IpAddr::from_str("caa::aab").unwrap())); - let collection_2 = AddressCollection::new("aa::bb-aa:1::00").unwrap(); + let collection_2 = IpCollection::new("aa::bb-aa:1::00").unwrap(); assert!(!collection_2.contains(&IpAddr::from_str("aa:11::0").unwrap())); assert!(collection_2.contains(&IpAddr::from_str("aa::bc").unwrap())); assert!(collection_2.contains(&IpAddr::from_str("aa::bbcc").unwrap())); assert!(collection_2.contains(&IpAddr::from_str("00aa:0001::00").unwrap())); // check that ipv6 range doesn't contain ipv4 - let collection_3 = AddressCollection::new("0000::0000-ffff::8888").unwrap(); + let collection_3 = IpCollection::new("0000::0000-ffff::8888").unwrap(); assert!(!collection_3.contains(&IpAddr::from_str("192.168.1.1").unwrap())); assert!(!collection_3.contains(&IpAddr::from_str("0.0.0.0").unwrap())); } diff --git a/src/networking/types/ip_version.rs b/src/networking/types/ip_version.rs index 1b844472..0ec6928b 100644 --- a/src/networking/types/ip_version.rs +++ b/src/networking/types/ip_version.rs @@ -14,29 +14,3 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{self:?}") } } - -impl IpVersion { - pub(crate) const ALL: [IpVersion; 2] = [IpVersion::IPv4, IpVersion::IPv6]; -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_ip_version_display() { - for version in IpVersion::ALL { - match version { - IpVersion::IPv4 => assert_eq!(version.to_string(), "IPv4"), - IpVersion::IPv6 => assert_eq!(version.to_string(), "IPv6"), - } - } - } - - #[test] - fn test_all_ip_versions_collection() { - assert_eq!(IpVersion::ALL.len(), 2); - assert_eq!(IpVersion::ALL.get(0).unwrap(), &IpVersion::IPv4); - assert_eq!(IpVersion::ALL.get(1).unwrap(), &IpVersion::IPv6); - } -} diff --git a/src/networking/types/mod.rs b/src/networking/types/mod.rs index 8553faaf..ab112f21 100644 --- a/src/networking/types/mod.rs +++ b/src/networking/types/mod.rs @@ -2,11 +2,11 @@ pub mod arp_type; pub mod asn; pub mod bogon; -pub mod byte_multiple; pub mod capture_context; +pub mod config_device; pub mod data_info; pub mod data_info_host; -pub mod filters; +pub mod data_representation; pub mod host; pub mod host_data_states; pub mod icmp_type; @@ -17,7 +17,6 @@ pub mod my_device; pub mod my_link_type; pub mod packet_filters_fields; -pub mod port_collection; pub mod protocol; pub mod service; pub mod service_query; diff --git a/src/networking/types/my_link_type.rs b/src/networking/types/my_link_type.rs index d9d74dd4..cf5187f2 100644 --- a/src/networking/types/my_link_type.rs +++ b/src/networking/types/my_link_type.rs @@ -1,11 +1,7 @@ -use iced::Font; -use iced::widget::Column; use pcap::Linktype; -use crate::gui::styles::text::TextType; -use crate::gui::types::message::Message; +use crate::Language; use crate::translations::translations_3::link_type_translation; -use crate::{Language, StyleType}; /// Currently supported link types #[derive(Copy, Clone, Default)] @@ -16,6 +12,8 @@ pub enum MyLinkType { Loop(Linktype), IPv4(Linktype), IPv6(Linktype), + LinuxSll(Linktype), + LinuxSll2(Linktype), Unsupported(Linktype), #[default] NotYetAssigned, @@ -34,6 +32,8 @@ pub fn from_pcap_link_type(link_type: Linktype) -> Self { Linktype::LOOP => Self::Loop(link_type), Linktype::IPV4 => Self::IPv4(link_type), Linktype::IPV6 => Self::IPv6(link_type), + Linktype::LINUX_SLL => Self::LinuxSll(link_type), + Linktype::LINUX_SLL2 => Self::LinuxSll2(link_type), _ => Self::Unsupported(link_type), } } @@ -46,6 +46,8 @@ pub fn full_print_on_one_line(self, language: Language) -> String { | Self::Loop(l) | Self::IPv4(l) | Self::IPv6(l) + | Self::LinuxSll(l) + | Self::LinuxSll2(l) | Self::Unsupported(l) => { format!( "{}: {} ({})", @@ -57,32 +59,4 @@ pub fn full_print_on_one_line(self, language: Language) -> String { Self::NotYetAssigned => String::new(), } } - - pub fn link_type_col<'a>( - self, - language: Language, - font: Font, - ) -> Column<'a, Message, StyleType> { - match self { - Self::Null(l) - | Self::Ethernet(l) - | Self::RawIp(l) - | Self::Loop(l) - | Self::IPv4(l) - | Self::IPv6(l) - | Self::Unsupported(l) => { - let link_info = format!( - "{} ({})", - l.get_name().unwrap_or_else(|_| l.0.to_string()), - l.get_description().unwrap_or_else(|_| String::new()) - ); - TextType::highlighted_subtitle_with_desc( - link_type_translation(language), - &link_info, - font, - ) - } - Self::NotYetAssigned => Column::new().height(0), - } - } } diff --git a/src/networking/types/port_collection.rs b/src/networking/types/port_collection.rs deleted file mode 100644 index 393c35a4..00000000 --- a/src/networking/types/port_collection.rs +++ /dev/null @@ -1,180 +0,0 @@ -use std::ops::RangeInclusive; -use std::str::FromStr; - -#[derive(Debug, Eq, PartialEq, Clone)] -pub(crate) struct PortCollection { - pub(crate) ports: Vec, - pub(crate) ranges: Vec>, -} - -impl PortCollection { - const SEPARATOR: char = ','; - const RANGE_SEPARATOR: char = '-'; - - pub const PLACEHOLDER_STR: &'static str = "0-65535"; - - pub(crate) fn new(str: &str) -> Option { - let str = str.replace(' ', ""); - - if str.is_empty() { - return Some(Self::default()); - } - - let mut ports = Vec::new(); - let mut ranges = Vec::new(); - - let objects: Vec<&str> = str.split(Self::SEPARATOR).collect(); - for object in objects { - 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 = 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; - } - ranges.push(range); - } else { - // individual port - let port = u16::from_str(object).ok()?; - ports.push(port); - } - } - - Some(Self { ports, ranges }) - } - - pub(crate) fn contains(&self, port: Option) -> bool { - // ignore port filter in case of ICMP or ARP - let Some(p) = port else { - return true; - }; - - for range in &self.ranges { - if range.contains(&p) { - return true; - } - } - self.ports.contains(&p) - } -} - -impl Default for PortCollection { - fn default() -> Self { - PortCollection { - ports: vec![], - ranges: vec![RangeInclusive::new(u16::MIN, u16::MAX)], - } - } -} - -#[cfg(test)] -mod tests { - use crate::networking::types::port_collection::PortCollection; - - #[test] - fn test_default_collection_contains_everything() { - let collection = PortCollection::default(); - assert!(collection.contains(Some(0))); - assert!(collection.contains(Some(1))); - assert!(collection.contains(Some(2))); - assert!(collection.contains(Some(80))); - assert!(collection.contains(Some(8080))); - assert!(collection.contains(Some(55333))); - assert!(collection.contains(Some(65535))); - } - - #[test] - fn test_new_port_collections() { - assert_eq!( - PortCollection::new("0").unwrap(), - PortCollection { - ports: vec![0], - ranges: vec![] - } - ); - - assert_eq!( - PortCollection::new(" 0 ").unwrap(), - PortCollection { - ports: vec![0], - ranges: vec![] - } - ); - - assert_eq!( - PortCollection::new("1,2,3,4,999").unwrap(), - PortCollection { - ports: vec![1, 2, 3, 4, 999], - ranges: vec![] - } - ); - - assert_eq!( - PortCollection::new("1, 2, 3, 4, 900-999").unwrap(), - PortCollection { - ports: vec![1, 2, 3, 4], - ranges: vec![900..=999] - } - ); - - assert_eq!( - PortCollection::new("1 - 999").unwrap(), - PortCollection { - ports: vec![], - ranges: vec![1..=999] - } - ); - - assert_eq!( - PortCollection::new(" 1,2,10-20,3,4, 999-1200 ").unwrap(), - PortCollection { - ports: vec![1, 2, 3, 4], - ranges: vec![10..=20, 999..=1200] - } - ); - } - - #[test] - fn test_new_port_collections_invalid() { - assert_eq!(PortCollection::new("1,2,10-20,3,4,-1200"), None); - - assert_eq!(PortCollection::new("1,2,10-20,3,4,999:1200"), None); - - assert_eq!(PortCollection::new("1,2,10-20,3,4,999-1200,"), None); - - 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] - fn test_port_collection_contains() { - let collection = PortCollection::new("1,2,25-30,55,101-117").unwrap(); - assert!(collection.contains(Some(1))); - assert!(collection.contains(Some(2))); - assert!(collection.contains(Some(25))); - assert!(collection.contains(Some(27))); - assert!(collection.contains(Some(30))); - assert!(collection.contains(Some(55))); - assert!(collection.contains(Some(101))); - assert!(collection.contains(Some(109))); - assert!(collection.contains(Some(117))); - assert!(!collection.contains(Some(4))); - assert!(!collection.contains(Some(24))); - assert!(!collection.contains(Some(31))); - assert!(!collection.contains(Some(100))); - assert!(!collection.contains(Some(118))); - assert!(!collection.contains(Some(8080))); - } -} diff --git a/src/networking/types/protocol.rs b/src/networking/types/protocol.rs index 31f20e62..bef8c720 100644 --- a/src/networking/types/protocol.rs +++ b/src/networking/types/protocol.rs @@ -19,33 +19,3 @@ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{self:?}") } } - -impl Protocol { - pub const ALL: [Protocol; 4] = [Protocol::TCP, Protocol::UDP, Protocol::ICMP, Protocol::ARP]; -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_protocol_display() { - for protocol in Protocol::ALL { - match protocol { - Protocol::TCP => assert_eq!(protocol.to_string(), "TCP"), - Protocol::UDP => assert_eq!(protocol.to_string(), "UDP"), - Protocol::ICMP => assert_eq!(protocol.to_string(), "ICMP"), - Protocol::ARP => assert_eq!(protocol.to_string(), "ARP"), - } - } - } - - #[test] - fn test_all_protocols_collection() { - assert_eq!(Protocol::ALL.len(), 4); - assert_eq!(Protocol::ALL.get(0).unwrap(), &Protocol::TCP); - assert_eq!(Protocol::ALL.get(1).unwrap(), &Protocol::UDP); - assert_eq!(Protocol::ALL.get(2).unwrap(), &Protocol::ICMP); - assert_eq!(Protocol::ALL.get(3).unwrap(), &Protocol::ARP); - } -} diff --git a/src/notifications/notify_and_log.rs b/src/notifications/notify_and_log.rs index 93acddd9..67a27a8c 100644 --- a/src/notifications/notify_and_log.rs +++ b/src/notifications/notify_and_log.rs @@ -1,8 +1,8 @@ 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::data_representation::DataRepr; use crate::networking::types::host::Host; use crate::networking::types::service::Service; use crate::notifications::types::logged_notification::{ @@ -31,8 +31,8 @@ pub fn notify_and_log( 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) { + let data_repr = notifications.data_notification.data_repr; + if data_info.tot_data(data_repr) > u128::from(threshold) { //log this notification logged_notifications.1 += 1; if logged_notifications.0.len() >= 30 { @@ -43,13 +43,13 @@ pub fn notify_and_log( .push_front(LoggedNotification::DataThresholdExceeded( DataThresholdExceeded { id: logged_notifications.1, - chart_type, + data_repr, 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), + hosts: hosts_list(info_traffic_msg, data_repr), + services: services_list(info_traffic_msg, data_repr), }, )); if sound_to_play.eq(&Sound::None) { @@ -98,7 +98,7 @@ pub fn notify_and_log( logged_notifications.1 - emitted_notifications_prev } -fn hosts_list(info_traffic_msg: &InfoTraffic, chart_type: ChartType) -> Vec<(Host, DataInfoHost)> { +fn hosts_list(info_traffic_msg: &InfoTraffic, data_repr: DataRepr) -> Vec<(Host, DataInfoHost)> { let mut hosts: Vec<(Host, DataInfoHost)> = info_traffic_msg .hosts .iter() @@ -106,7 +106,7 @@ fn hosts_list(info_traffic_msg: &InfoTraffic, chart_type: ChartType) -> Vec<(Hos .collect(); hosts.sort_by(|(_, a), (_, b)| { a.data_info - .compare(&b.data_info, SortType::Descending, chart_type) + .compare(&b.data_info, SortType::Descending, data_repr) }); let n_entry = min(hosts.len(), 4); hosts @@ -117,17 +117,14 @@ fn hosts_list(info_traffic_msg: &InfoTraffic, chart_type: ChartType) -> Vec<(Hos .collect() } -fn services_list( - info_traffic_msg: &InfoTraffic, - chart_type: ChartType, -) -> Vec<(Service, DataInfo)> { +fn services_list(info_traffic_msg: &InfoTraffic, data_repr: DataRepr) -> 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)); + services.sort_by(|(_, a), (_, b)| a.compare(b, SortType::Descending, data_repr)); let n_entry = min(services.len(), 4); services .get(..n_entry) diff --git a/src/notifications/types/logged_notification.rs b/src/notifications/types/logged_notification.rs index 6a75d2cd..1b354370 100644 --- a/src/notifications/types/logged_notification.rs +++ b/src/notifications/types/logged_notification.rs @@ -1,6 +1,6 @@ -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::data_representation::DataRepr; use crate::networking::types::host::Host; use crate::networking::types::service::Service; @@ -38,7 +38,7 @@ pub fn expand(&mut self, expand: bool) { #[derive(Clone)] pub struct DataThresholdExceeded { pub(crate) id: usize, - pub(crate) chart_type: ChartType, + pub(crate) data_repr: DataRepr, pub(crate) threshold: u64, pub(crate) data_info: DataInfo, pub(crate) timestamp: String, diff --git a/src/notifications/types/notifications.rs b/src/notifications/types/notifications.rs index 8935ad07..7a9c9d05 100644 --- a/src/notifications/types/notifications.rs +++ b/src/notifications/types/notifications.rs @@ -1,11 +1,12 @@ use serde::{Deserialize, Serialize}; use crate::ByteMultiple; -use crate::chart::types::chart_type::ChartType; +use crate::networking::types::data_representation::DataRepr; use crate::notifications::types::sound::Sound; /// Used to contain the notifications configuration set by the user #[derive(Clone, Serialize, Deserialize, Copy, PartialEq, Debug)] +#[serde(default)] pub struct Notifications { pub volume: u8, pub data_notification: DataNotification, @@ -32,9 +33,10 @@ pub enum Notification { } #[derive(Clone, Eq, PartialEq, Serialize, Deserialize, Debug, Copy)] +#[serde(default)] pub struct DataNotification { /// Data representation - pub chart_type: ChartType, + pub data_repr: DataRepr, /// Threshold of received + sent bytes; if exceeded a notification is emitted pub threshold: Option, /// B, KB, MB or GB @@ -48,7 +50,7 @@ pub struct DataNotification { impl Default for DataNotification { fn default() -> Self { DataNotification { - chart_type: ChartType::Bytes, + data_repr: DataRepr::Bytes, threshold: None, byte_multiple: ByteMultiple::KB, sound: Sound::Pop, @@ -101,6 +103,7 @@ pub fn from(value: &str, existing: Option) -> Self { } #[derive(Clone, Eq, PartialEq, Serialize, Deserialize, Debug, Copy)] +#[serde(default)] pub struct FavoriteNotification { /// Flag to determine if this notification is enabled pub notify_on_favorite: bool, diff --git a/src/report/get_report_entries.rs b/src/report/get_report_entries.rs index f7742a7d..1f6646c4 100644 --- a/src/report/get_report_entries.rs +++ b/src/report/get_report_entries.rs @@ -4,10 +4,11 @@ use crate::networking::types::address_port_pair::AddressPortPair; use crate::networking::types::data_info::DataInfo; use crate::networking::types::data_info_host::DataInfoHost; +use crate::networking::types::data_representation::DataRepr; use crate::networking::types::host::Host; use crate::networking::types::info_address_port_pair::InfoAddressPortPair; use crate::report::types::sort_type::SortType; -use crate::{ChartType, InfoTraffic, ReportSortType, Service, Sniffer}; +use crate::{InfoTraffic, Service, Sniffer}; /// Return the elements that satisfy the search constraints and belong to the given page, /// and the total number of elements which satisfy the search constraints, @@ -46,24 +47,12 @@ pub fn get_searched_entries( }) .collect(); - all_results.sort_by(|&(_, a), &(_, b)| match sniffer.report_sort_type { - ReportSortType { - byte_sort, - packet_sort: SortType::Neutral, - } => match byte_sort { - SortType::Ascending => a.transmitted_bytes.cmp(&b.transmitted_bytes), - SortType::Descending => b.transmitted_bytes.cmp(&a.transmitted_bytes), - SortType::Neutral => b.final_timestamp.cmp(&a.final_timestamp), - }, - ReportSortType { - byte_sort: SortType::Neutral, - packet_sort, - } => match packet_sort { - SortType::Ascending => a.transmitted_packets.cmp(&b.transmitted_packets), - SortType::Descending => b.transmitted_packets.cmp(&a.transmitted_packets), - SortType::Neutral => b.final_timestamp.cmp(&a.final_timestamp), - }, - _ => b.final_timestamp.cmp(&a.final_timestamp), + all_results.sort_by(|&(_, a), &(_, b)| { + a.compare( + b, + sniffer.conf.report_sort_type, + sniffer.traffic_chart.data_repr, + ) }); let upper_bound = min(sniffer.page_number * 20, all_results.len()); @@ -82,12 +71,12 @@ pub fn get_searched_entries( pub fn get_host_entries( info_traffic: &InfoTraffic, - chart_type: ChartType, + data_repr: DataRepr, sort_type: SortType, ) -> Vec<(Host, DataInfoHost)> { let mut sorted_vec: Vec<(&Host, &DataInfoHost)> = info_traffic.hosts.iter().collect(); - sorted_vec.sort_by(|&(_, a), &(_, b)| a.data_info.compare(&b.data_info, sort_type, chart_type)); + sorted_vec.sort_by(|&(_, a), &(_, b)| a.data_info.compare(&b.data_info, sort_type, data_repr)); let n_entry = min(sorted_vec.len(), 30); sorted_vec[0..n_entry] @@ -98,7 +87,7 @@ pub fn get_host_entries( pub fn get_service_entries( info_traffic: &InfoTraffic, - chart_type: ChartType, + data_repr: DataRepr, sort_type: SortType, ) -> Vec<(Service, DataInfo)> { let mut sorted_vec: Vec<(&Service, &DataInfo)> = info_traffic @@ -107,7 +96,7 @@ pub fn get_service_entries( .filter(|(service, _)| service != &&Service::NotApplicable) .collect(); - sorted_vec.sort_by(|&(_, a), &(_, b)| a.compare(b, sort_type, chart_type)); + sorted_vec.sort_by(|&(_, a), &(_, b)| a.compare(b, sort_type, data_repr)); let n_entry = min(sorted_vec.len(), 30); sorted_vec[0..n_entry] diff --git a/src/report/types/mod.rs b/src/report/types/mod.rs index ebbc80a9..588104e0 100644 --- a/src/report/types/mod.rs +++ b/src/report/types/mod.rs @@ -1,4 +1,3 @@ pub mod report_col; -pub mod report_sort_type; pub mod search_parameters; pub mod sort_type; diff --git a/src/report/types/report_col.rs b/src/report/types/report_col.rs index 1ec1877f..2e5db7b1 100644 --- a/src/report/types/report_col.rs +++ b/src/report/types/report_col.rs @@ -1,10 +1,8 @@ -use crate::ByteMultiple; use crate::networking::types::address_port_pair::AddressPortPair; +use crate::networking::types::data_representation::DataRepr; use crate::networking::types::info_address_port_pair::InfoAddressPortPair; use crate::report::types::search_parameters::FilterInputType; -use crate::translations::translations::{ - address_translation, bytes_translation, packets_translation, protocol_translation, -}; +use crate::translations::translations::{address_translation, protocol_translation}; use crate::translations::translations_2::{destination_translation, source_translation}; use crate::translations::translations_3::{port_translation, service_translation}; use crate::translations::types::language::Language; @@ -25,36 +23,30 @@ pub enum ReportCol { DstPort, Proto, Service, - Bytes, - Packets, + Data, } impl ReportCol { - pub(crate) const ALL: [ReportCol; 8] = [ + pub(crate) const ALL: [ReportCol; 7] = [ ReportCol::SrcIp, ReportCol::SrcPort, ReportCol::DstIp, ReportCol::DstPort, ReportCol::Proto, ReportCol::Service, - ReportCol::Bytes, - ReportCol::Packets, + ReportCol::Data, ]; pub(crate) const FILTER_COLUMNS_WIDTH: f32 = 4.0 * SMALL_COL_WIDTH + 2.0 * LARGE_COL_WIDTH; - pub(crate) fn get_title(&self, language: Language) -> String { + pub(crate) fn get_title(&self, language: Language, data_repr: DataRepr) -> String { match self { ReportCol::SrcIp | ReportCol::DstIp => address_translation(language).to_string(), ReportCol::SrcPort | ReportCol::DstPort => port_translation(language).to_string(), ReportCol::Proto => protocol_translation(language).to_string(), ReportCol::Service => service_translation(language).to_string(), - ReportCol::Bytes => { - let mut str = bytes_translation(language).to_string(); - str.remove(0).to_uppercase().to_string() + &str - } - ReportCol::Packets => { - let mut str = packets_translation(language).to_string(); + ReportCol::Data => { + let mut str = data_repr.get_label(language).to_string(); str.remove(0).to_uppercase().to_string() + &str } } @@ -72,7 +64,12 @@ pub(crate) fn get_title_direction_info(&self, language: Language) -> String { } } - pub(crate) fn get_value(&self, key: &AddressPortPair, val: &InfoAddressPortPair) -> String { + pub(crate) fn get_value( + &self, + key: &AddressPortPair, + val: &InfoAddressPortPair, + data_repr: DataRepr, + ) -> String { match self { ReportCol::SrcIp => key.address1.to_string(), ReportCol::SrcPort => { @@ -92,8 +89,7 @@ pub(crate) fn get_value(&self, key: &AddressPortPair, val: &InfoAddressPortPair) } ReportCol::Proto => key.protocol.to_string(), ReportCol::Service => val.service.to_string(), - ReportCol::Bytes => ByteMultiple::formatted_string(val.transmitted_bytes), - ReportCol::Packets => val.transmitted_packets.to_string(), + ReportCol::Data => data_repr.formatted_string(val.transmitted_data(data_repr)), } } @@ -126,7 +122,7 @@ pub(crate) fn get_filter_input_type(&self) -> FilterInputType { ReportCol::DstPort => FilterInputType::PortDst, ReportCol::Proto => FilterInputType::Proto, ReportCol::Service => FilterInputType::Service, - ReportCol::Bytes | ReportCol::Packets => FilterInputType::Country, // just to not panic... + ReportCol::Data => FilterInputType::Country, // just to not panic... } } } diff --git a/src/report/types/report_sort_type.rs b/src/report/types/report_sort_type.rs deleted file mode 100644 index 161d79de..00000000 --- a/src/report/types/report_sort_type.rs +++ /dev/null @@ -1,175 +0,0 @@ -use std::fmt::Debug; - -use iced::widget::Text; - -use crate::gui::styles::button::ButtonType; -use crate::gui::styles::types::style_type::StyleType; -use crate::report::types::report_col::ReportCol; -use crate::report::types::sort_type::SortType; -use crate::utils::types::icon::Icon; - -/// Struct representing the possible kinds of sort for displayed relevant connections. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] -pub struct ReportSortType { - pub byte_sort: SortType, - pub packet_sort: SortType, -} - -impl ReportSortType { - pub fn next_sort(self, report_col: &ReportCol) -> Self { - match report_col { - ReportCol::Bytes => Self { - byte_sort: self.byte_sort.next_sort(), - packet_sort: SortType::Neutral, - }, - ReportCol::Packets => Self { - byte_sort: SortType::Neutral, - packet_sort: self.packet_sort.next_sort(), - }, - _ => Self::default(), - } - } - - pub fn icon<'a>(self, report_col: &ReportCol) -> Text<'a, StyleType> { - match report_col { - ReportCol::Bytes => self.byte_sort.icon(), - ReportCol::Packets => self.packet_sort.icon(), - _ => Icon::SortNeutral.to_text(), - } - } - - pub fn button_type(self, report_col: &ReportCol) -> ButtonType { - match report_col { - ReportCol::Bytes => self.byte_sort.button_type(), - ReportCol::Packets => self.packet_sort.button_type(), - _ => ButtonType::SortArrows, - } - } -} - -#[cfg(test)] -mod tests { - use crate::report::types::report_col::ReportCol; - use crate::report::types::report_sort_type::ReportSortType; - use crate::report::types::sort_type::SortType; - - #[test] - fn test_next_report_sort() { - let mut sort = ReportSortType::default(); - assert_eq!( - sort, - ReportSortType { - byte_sort: SortType::Neutral, - packet_sort: SortType::Neutral - } - ); - - sort = sort.next_sort(&ReportCol::Packets); - assert_eq!( - sort, - ReportSortType { - byte_sort: SortType::Neutral, - packet_sort: SortType::Descending - } - ); - - sort = sort.next_sort(&ReportCol::Packets); - assert_eq!( - sort, - ReportSortType { - byte_sort: SortType::Neutral, - packet_sort: SortType::Ascending - } - ); - - sort = sort.next_sort(&ReportCol::Packets); - assert_eq!( - sort, - ReportSortType { - byte_sort: SortType::Neutral, - packet_sort: SortType::Neutral - } - ); - - sort = sort.next_sort(&ReportCol::Packets); - assert_eq!( - sort, - ReportSortType { - byte_sort: SortType::Neutral, - packet_sort: SortType::Descending - } - ); - - sort = sort.next_sort(&ReportCol::Bytes); - assert_eq!( - sort, - ReportSortType { - byte_sort: SortType::Descending, - packet_sort: SortType::Neutral - } - ); - - sort = sort.next_sort(&ReportCol::Packets); - assert_eq!( - sort, - ReportSortType { - byte_sort: SortType::Neutral, - packet_sort: SortType::Descending - } - ); - - sort = sort.next_sort(&ReportCol::Bytes); - assert_eq!( - sort, - ReportSortType { - byte_sort: SortType::Descending, - packet_sort: SortType::Neutral - } - ); - - sort = sort.next_sort(&ReportCol::Bytes); - assert_eq!( - sort, - ReportSortType { - byte_sort: SortType::Ascending, - packet_sort: SortType::Neutral - } - ); - - sort = sort.next_sort(&ReportCol::Packets); - assert_eq!( - sort, - ReportSortType { - byte_sort: SortType::Neutral, - packet_sort: SortType::Descending - } - ); - - sort = sort.next_sort(&ReportCol::Bytes); - assert_eq!( - sort, - ReportSortType { - byte_sort: SortType::Descending, - packet_sort: SortType::Neutral - } - ); - - sort = sort.next_sort(&ReportCol::Bytes); - assert_eq!( - sort, - ReportSortType { - byte_sort: SortType::Ascending, - packet_sort: SortType::Neutral - } - ); - - sort = sort.next_sort(&ReportCol::Bytes); - assert_eq!( - sort, - ReportSortType { - byte_sort: SortType::Neutral, - packet_sort: SortType::Neutral - } - ); - } -} diff --git a/src/report/types/sort_type.rs b/src/report/types/sort_type.rs index 2dec0be5..b991a0e2 100644 --- a/src/report/types/sort_type.rs +++ b/src/report/types/sort_type.rs @@ -1,10 +1,10 @@ -use iced::widget::Text; - use crate::gui::styles::button::ButtonType; use crate::gui::styles::types::style_type::StyleType; use crate::utils::types::icon::Icon; +use iced::widget::Text; +use serde::{Deserialize, Serialize}; -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default, Serialize, Deserialize)] pub enum SortType { Ascending, Descending, diff --git a/src/translations/mod.rs b/src/translations/mod.rs index b55b4908..408e867e 100644 --- a/src/translations/mod.rs +++ b/src/translations/mod.rs @@ -3,4 +3,5 @@ pub mod translations_2; pub mod translations_3; pub mod translations_4; +pub mod translations_5; pub mod types; diff --git a/src/translations/translations.rs b/src/translations/translations.rs index 010bef21..a07389c2 100644 --- a/src/translations/translations.rs +++ b/src/translations/translations.rs @@ -5,33 +5,33 @@ use crate::StyleType; use crate::translations::types::language::Language; -pub fn choose_adapters_translation<'a>(language: Language) -> Text<'a, StyleType> { - Text::new(match language { - Language::EN => "Select network adapter to inspect", - Language::IT => "Seleziona la scheda di rete da ispezionare", - Language::FR => "Sélectionnez une carte réseau à inspecter", - Language::ES => "Seleccione el adaptador de red que desea inspeccionar", - Language::PL => "Wybierz adapter sieciowy do inspekcji", - Language::DE => "Wähle einen Netzwerkadapter zum überwachen aus", - Language::UK => "Виберіть мережевий адаптер для перевірки", - Language::ZH => "选择需要监控的网络适配器", - Language::ZH_TW => "選取要檢視的網路介面卡", - Language::RO => "Selectați adaptor de rețea pentru a inspecta", - Language::KO => "검사할 네트워크 어댑터 선택", - Language::TR => "İncelemek için bir ağ adaptörü seçiniz", - Language::RU => "Выберите сетевой адаптер для инспекции", - Language::PT => "Selecione o adaptador de rede a inspecionar", - Language::EL => "Επίλεξε τον προσαρμογέα δικτύου για επιθεώρηση", - // Language::FA => "مبدل شبکه را برای بازرسی انتخاب کنید", - Language::SV => "Välj nätverksadapter att inspektera", - Language::FI => "Valitse tarkasteltava verkkosovitin", - Language::JA => "使用するネットワーク アダプターを選択してください", - 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", - }) -} +// pub fn choose_adapters_translation<'a>(language: Language) -> Text<'a, StyleType> { +// Text::new(match language { +// Language::EN => "Select network adapter to inspect", +// Language::IT => "Seleziona la scheda di rete da ispezionare", +// Language::FR => "Sélectionnez une carte réseau à inspecter", +// Language::ES => "Seleccione el adaptador de red que desea inspeccionar", +// Language::PL => "Wybierz adapter sieciowy do inspekcji", +// Language::DE => "Wähle einen Netzwerkadapter zum überwachen aus", +// Language::UK => "Виберіть мережевий адаптер для перевірки", +// Language::ZH => "选择需要监控的网络适配器", +// Language::ZH_TW => "選取要檢視的網路介面卡", +// Language::RO => "Selectați adaptor de rețea pentru a inspecta", +// Language::KO => "검사할 네트워크 어댑터 선택", +// Language::TR => "İncelemek için bir ağ adaptörü seçiniz", +// Language::RU => "Выберите сетевой адаптер для инспекции", +// Language::PT => "Selecione o adaptador de rede a inspecionar", +// Language::EL => "Επιλέξτε τον προσαρμογέα δικτύου για επιθεώρηση", +// // Language::FA => "مبدل شبکه را برای بازرسی انتخاب کنید", +// Language::SV => "Välj nätverksadapter att inspektera", +// Language::FI => "Valitse tarkasteltava verkkosovitin", +// Language::JA => "使用するネットワーク アダプターを選択してください", +// 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", +// }) +// } // pub fn application_protocol_translation(language: Language) -> &'static str { // match language { @@ -60,33 +60,33 @@ pub fn choose_adapters_translation<'a>(language: Language) -> Text<'a, StyleType // } // } -pub fn select_filters_translation<'a>(language: Language) -> Text<'a, StyleType> { - Text::new(match language { - Language::EN => "Select filters to be applied on network traffic", - Language::IT => "Seleziona i filtri da applicare al traffico di rete", - Language::FR => "Sélectionnez les filtres à appliquer sur le traffic réseau", - Language::ES => "Seleccionar los filtros que se aplicarán al tráfico de red", - Language::PL => "Wybierz filtry, które mają być zastosowane na ruchu sieciowym", - Language::DE => "Wähle die Filter, die auf den Netzwerkverkehr angewendet werden sollen", - Language::UK => "Виберіть фільтри, які мають бути застосовані до мережевого руху", - Language::ZH => "选择需要监控的目标", - Language::ZH_TW => "選取要套用於網路流量的篩選器", - Language::RO => "Selectați filtre pentru traficul de rețea", - Language::KO => "네트워크 트레픽에 적용할 필터 선택", - Language::TR => "Ağ trafiğine uygulanacak filtreleri seçiniz", - Language::RU => "Выберите фильтры для применения к сетевому трафику", - Language::PT => "Selecione os filtros a serem aplicados no tráfego de rede", - Language::EL => "Επίλεξε τα φίλτρα για εφαρμογή στην κίνηση του δικτύου", - // Language::FA => "صافی ها را جهت اعمال بر آمد و شد شبکه انتخاب کنید", - Language::SV => "Välj filtren som ska appliceras på nätverkstrafiken", - Language::FI => "Valitse suodattimet verkkoliikenteelle", - Language::JA => "トラフィックに適用するフィルターを選択してください", - 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 select_filters_translation<'a>(language: Language) -> Text<'a, StyleType> { +// Text::new(match language { +// Language::EN => "Select filters to be applied on network traffic", +// Language::IT => "Seleziona i filtri da applicare al traffico di rete", +// Language::FR => "Sélectionnez les filtres à appliquer sur le traffic réseau", +// Language::ES => "Seleccionar los filtros que se aplicarán al tráfico de red", +// Language::PL => "Wybierz filtry, które mają być zastosowane na ruchu sieciowym", +// Language::DE => "Wähle die Filter, die auf den Netzwerkverkehr angewendet werden sollen", +// Language::UK => "Виберіть фільтри, які мають бути застосовані до мережевого руху", +// Language::ZH => "选择需要监控的目标", +// Language::ZH_TW => "選取要套用於網路流量的篩選器", +// Language::RO => "Selectați filtre pentru traficul de rețea", +// Language::KO => "네트워크 트레픽에 적용할 필터 선택", +// Language::TR => "Ağ trafiğine uygulanacak filtreleri seçiniz", +// Language::RU => "Выберите фильтры для применения к сетевому трафику", +// Language::PT => "Selecione os filtros a serem aplicados no tráfego de rede", +// Language::EL => "Επιλέξτε τα φίλτρα που θα εφαρμοστούν στην κίνηση του δικτύου", +// // Language::FA => "صافی ها را جهت اعمال بر آمد و شد شبکه انتخاب کنید", +// Language::SV => "Välj filtren som ska appliceras på nätverkstrafiken", +// Language::FI => "Valitse suodattimet verkkoliikenteelle", +// Language::JA => "トラフィックに適用するフィルターを選択してください", +// 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 { @@ -163,33 +163,33 @@ pub fn addresses_translation(language: Language) -> &'static str { } } -pub fn ip_version_translation(language: Language) -> &'static str { - match language { - Language::EN => "IP version", - Language::IT => "Versione IP", - Language::FR => "Version IP", - Language::ES => "Versión IP", - Language::PL => "Wersja IP", - Language::DE => "IP Version", - Language::UK => "Версія IP", - Language::ZH => "目标IP协议版本", - Language::ZH_TW => "IP 版本", - Language::RO => "Versiune IP", - Language::KO => "IP 버전", - Language::TR => "IP versiyonu", - Language::RU => "Версия IP", - Language::PT => "Versão de IP", - Language::EL => "Έκδοση IP", - // Language::FA => "نسخهٔ IP", - Language::SV => "IP-version", - Language::FI => "IP-versio", - Language::JA => "IP バージョン", - Language::UZ => "IP versiyasi", - Language::VI => "Phiên bản IP", - Language::ID => "Versi IP", - Language::NL => "IP versie", - } -} +// pub fn ip_version_translation(language: Language) -> &'static str { +// match language { +// Language::EN => "IP version", +// Language::IT => "Versione IP", +// Language::FR => "Version IP", +// Language::ES => "Versión IP", +// Language::PL => "Wersja IP", +// Language::DE => "IP Version", +// Language::UK => "Версія IP", +// Language::ZH => "目标IP协议版本", +// Language::ZH_TW => "IP 版本", +// Language::RO => "Versiune IP", +// Language::KO => "IP 버전", +// Language::TR => "IP versiyonu", +// Language::RU => "Версия IP", +// Language::PT => "Versão de IP", +// Language::EL => "Έκδοση IP", +// // Language::FA => "نسخهٔ IP", +// Language::SV => "IP-version", +// Language::FI => "IP-versio", +// Language::JA => "IP バージョン", +// Language::UZ => "IP versiyasi", +// Language::VI => "Phiên bản IP", +// Language::ID => "Versi IP", +// Language::NL => "IP versie", +// } +// } // pub fn transport_protocol_translation(language: Language) -> &'static str { // match language { @@ -361,7 +361,7 @@ pub fn ask_quit_translation<'a>(language: Language) -> Text<'a, StyleType> { Language::TR => "Bu analizden çıkmak istediğine emin misin?", Language::RU => "Вы уверены, что хотите выйти из текущего анализа?", Language::PT => "Tem a certeza que deseja sair desta análise?", - Language::EL => "Είσαι σίγουρος ότι θες να κλείσεις την ανάλυση;", + Language::EL => "Είστε βέβαιοι ότι θέλετε να τερματίσετε την ανάλυση;", // Language::FA => "آیا مطمئن هستید می خواهید از این تحلیل خارج شوید؟", Language::SV => "Är du säker på att du vill avsluta analysen?", Language::FI => "Haluatko varmasti lopettaa analyysin?", @@ -417,7 +417,7 @@ pub fn ask_clear_all_translation<'a>(language: Language) -> Text<'a, StyleType> Language::TR => "Bildirimleri temizlemek istediğine emin misin?", Language::RU => "Вы уверены, что хотите удлить все уведомления?", Language::PT => "Tem a certeza que deseja eliminar as notificações?", - Language::EL => "Είσαι σίγουρος ότι θες να κάνεις εκκαθάριση των ειδοποιήσεων;", + Language::EL => "Είστε βέβαιοι ότι θέλετε να εκκαθαρίσετε τις ειδοποιήσεις;", // Language::FA => "آیا مطمئن هستید می خواهید اعلان ها را پاک کنید؟", Language::SV => "Är du säker på att du vill radera notifikationerna?", Language::FI => "Haluatko varmasti tyhjentää ilmoitukset?", @@ -751,124 +751,124 @@ pub fn waiting_translation<'a>(language: Language, adapter: &str) -> Text<'a, St }) } -#[allow(clippy::too_many_lines)] -pub fn some_observed_translation<'a>(language: Language, observed: u128) -> Text<'a, StyleType> { - Text::new(match language { - Language::EN => format!( - "Total intercepted packets: {observed}\n\n\ - Filtered packets: 0\n\n\ - Some packets have been intercepted, but still none has been selected according to the filters you specified..." - ), - Language::IT => format!( - "Totale pacchetti intercettati: {observed}\n\n\ - Pacchetti filtrati: 0\n\n\ - Alcuni pacchetti sono stati intercettati, ma ancora nessuno è stato selezionato secondo i filtri specificati..." - ), - Language::FR => format!( - "Total des paquets interceptés: {observed}\n\n\ - Paquets filtrés: 0\n\n\ - Certains paquets ont été interceptés, mais aucun ne satisfait les critères des filtres sélectionnés..." - ), - Language::ES => format!( - "Total de paquetes interceptados: {observed}\n\n\ - Paquetes filtrados: 0\n\n\ - Se interceptaron algunos paquetes, pero ninguno de ellos cumplía los criterios de los filtros seleccionados..." - ), - Language::PL => format!( - "Suma przechwyconych pakietów: {observed}\n\n\ - Przefiltrowane pakiety: 0\n\n\ - Niektóre pakiety zostały przechwycone, ale żaden nie został wybrany zgodnie z wskazanymi filtrami..." - ), - Language::DE => format!( - "Anzahl der empfangenen Pakete: {observed}\n\n\ - Gefilterte Pakete: 0\n\n\ - Ein Paar Pakete wurden empfangen, aber es entsprechen noch keine den gewählten Filtern..." - ), - Language::UK => format!( - "Сума перехоплених пакетів: {observed}\n\n\ - Відфільтровані пакети: 0\n\n\ - Деякі пакети були перехоплені, але жоден з них не був вибраний відповідно до вказаних фільтрів..." - ), - Language::ZH => format!( - "监测到的数据包总数: {observed}\n\n\ - 目标数据包总数: 0\n\n\ - 当前已监测到一些数据包, 但其中并未包含您的目标数据包......" - ), - Language::ZH_TW => format!( - "攔截到的封包總數: {observed}\n\n\ - 已篩選封包: 0\n\n\ - 已攔截到部分封包,但尚未有任何封包符合您指定的篩選條件..." - ), - Language::RO => format!( - "Total pachete interceptate: {observed}\n\n\ - Pachete filtrate: 0\n\n\ - Unele pachete au fost interceptate, dar încă niciunul nu a fost selectat conform filtrelor pe care le-ați specificat..." - ), - Language::KO => format!( - "감지한 총 패킷: {observed}\n\n\ - 필터링된 패킷: 0\n\n\ - 일부 패킷이 감지되었지만, 지정한 필터에 따라 선택되지 않았습니다..." - ), - Language::TR => format!( - "Toplam yakalanan paketler: {observed}\n\n\ - Filterelenen paketler: 0\n\n\ - Bazı paketler yakalandı, fakat belirttiğiniz filtrelere göre hiç biri seçilmedi..." - ), - Language::RU => format!( - "Всего пакетов перехвачено: {observed}\n\n\ - Фильтровано пакетов: 0\n\n\ - Сетевые пакеты были перехвачены, но ни один из них не соответствует заданным фильтрам..." - ), - Language::PT => format!( - "Total de pacotes interceptados: {observed}\n\n\ - Pacotes filtrados: 0\n\n\ - Alguns pacotes foram interceptados, mas nenhum deles foi selecionado de acordo com os filtros especificados..." - ), - Language::EL => format!( - "Συνολικά αναχαιτισμένα πακέτα: {observed}\n\n\ - Φιλτραρισμένα πακέτα: 0\n\n\ - Κάποια από τα πακέτα έχουν αναχαιτιστεί, αλλά κανένα ακόμη δεν έχει επιλεγεί σύμφωνα με τα φίλτρα που επέλεξες..." - ), - // Language::FA => format!("مجموع بسته های رهگیری شده: {observed}\n\n\ - // بسته های صاف شده: 0\n\n\ - // شماری از بسته ها رهگیری شده اند، ولی هنوز هیچ کدام بر اساس صافی تعیین شده شما انتخاب نشده اند..."), - Language::SV => format!( - "Antal fångade paket: {observed}\n\n\ - Filtrerade paket: 0\n\n\ - Några paket har fångats, men än har inget valts enligt de angivna filtren ..." - ), - Language::FI => format!( - "Siepattuja paketteja yhteensä: {observed}\n\n\ - Suodatettuja paketteja: 0\n\n\ - Joitakin paketteja on siepattu, mutta yhtäkään ei ole valittu määrittämiesi suodattimien mukaan..." - ), - Language::JA => format!( - "取得したパケット数: {observed}\n\n\ - フィルター後のパケット数: 0\n\n\ - パケットは取得できていますが、設定されたフィルタリングにより表示されません..." - ), - Language::UZ => format!( - "Jami ushlangan paketlar: {observed}\n\n\ - Filtrlangan paketlar: 0\n\n\ - Tarmoq paketlari ushlandi, lekin ularning hech biri belgilangan filtrlarga mos kelmadi..." - ), - Language::VI => format!( - "Tổng số gói tin bị chặn: {observed}\n\n\ - Các gói tin đã lọc: 0\n\n\ - Một số gói đã bị chặn, nhưng vẫn chưa có gói tin nào được bắt theo bộ lọc bạn đã chọn..." - ), - Language::ID => format!( - "Total paket yang dilacak: {observed}\n\n\ - 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..." - ), - }) -} +// #[allow(clippy::too_many_lines)] +// pub fn some_observed_translation<'a>(language: Language, observed: u128) -> Text<'a, StyleType> { +// Text::new(match language { +// Language::EN => format!( +// "Total intercepted packets: {observed}\n\n\ +// Filtered packets: 0\n\n\ +// Some packets have been intercepted, but still none has been selected according to the filters you specified..." +// ), +// Language::IT => format!( +// "Totale pacchetti intercettati: {observed}\n\n\ +// Pacchetti filtrati: 0\n\n\ +// Alcuni pacchetti sono stati intercettati, ma ancora nessuno è stato selezionato secondo i filtri specificati..." +// ), +// Language::FR => format!( +// "Total des paquets interceptés: {observed}\n\n\ +// Paquets filtrés: 0\n\n\ +// Certains paquets ont été interceptés, mais aucun ne satisfait les critères des filtres sélectionnés..." +// ), +// Language::ES => format!( +// "Total de paquetes interceptados: {observed}\n\n\ +// Paquetes filtrados: 0\n\n\ +// Se interceptaron algunos paquetes, pero ninguno de ellos cumplía los criterios de los filtros seleccionados..." +// ), +// Language::PL => format!( +// "Suma przechwyconych pakietów: {observed}\n\n\ +// Przefiltrowane pakiety: 0\n\n\ +// Niektóre pakiety zostały przechwycone, ale żaden nie został wybrany zgodnie z wskazanymi filtrami..." +// ), +// Language::DE => format!( +// "Anzahl der empfangenen Pakete: {observed}\n\n\ +// Gefilterte Pakete: 0\n\n\ +// Ein Paar Pakete wurden empfangen, aber es entsprechen noch keine den gewählten Filtern..." +// ), +// Language::UK => format!( +// "Сума перехоплених пакетів: {observed}\n\n\ +// Відфільтровані пакети: 0\n\n\ +// Деякі пакети були перехоплені, але жоден з них не був вибраний відповідно до вказаних фільтрів..." +// ), +// Language::ZH => format!( +// "监测到的数据包总数: {observed}\n\n\ +// 目标数据包总数: 0\n\n\ +// 当前已监测到一些数据包, 但其中并未包含您的目标数据包......" +// ), +// Language::ZH_TW => format!( +// "攔截到的封包總數: {observed}\n\n\ +// 已篩選封包: 0\n\n\ +// 已攔截到部分封包,但尚未有任何封包符合您指定的篩選條件..." +// ), +// Language::RO => format!( +// "Total pachete interceptate: {observed}\n\n\ +// Pachete filtrate: 0\n\n\ +// Unele pachete au fost interceptate, dar încă niciunul nu a fost selectat conform filtrelor pe care le-ați specificat..." +// ), +// Language::KO => format!( +// "감지한 총 패킷: {observed}\n\n\ +// 필터링된 패킷: 0\n\n\ +// 일부 패킷이 감지되었지만, 지정한 필터에 따라 선택되지 않았습니다..." +// ), +// Language::TR => format!( +// "Toplam yakalanan paketler: {observed}\n\n\ +// Filterelenen paketler: 0\n\n\ +// Bazı paketler yakalandı, fakat belirttiğiniz filtrelere göre hiç biri seçilmedi..." +// ), +// Language::RU => format!( +// "Всего пакетов перехвачено: {observed}\n\n\ +// Фильтровано пакетов: 0\n\n\ +// Сетевые пакеты были перехвачены, но ни один из них не соответствует заданным фильтрам..." +// ), +// Language::PT => format!( +// "Total de pacotes interceptados: {observed}\n\n\ +// Pacotes filtrados: 0\n\n\ +// Alguns pacotes foram interceptados, mas nenhum deles foi selecionado de acordo com os filtros especificados..." +// ), +// Language::EL => format!( +// "Συνολικά αναχαιτισμένα πακέτα: {observed}\n\n\ +// Φιλτραρισμένα πακέτα: 0\n\n\ +// Κάποια από τα πακέτα έχουν αναχαιτιστεί, αλλά κανένα ακόμη δεν έχει επιλεγεί σύμφωνα με τα φίλτρα που επέλεξες..." +// ), +// // Language::FA => format!("مجموع بسته های رهگیری شده: {observed}\n\n\ +// // بسته های صاف شده: 0\n\n\ +// // شماری از بسته ها رهگیری شده اند، ولی هنوز هیچ کدام بر اساس صافی تعیین شده شما انتخاب نشده اند..."), +// Language::SV => format!( +// "Antal fångade paket: {observed}\n\n\ +// Filtrerade paket: 0\n\n\ +// Några paket har fångats, men än har inget valts enligt de angivna filtren ..." +// ), +// Language::FI => format!( +// "Siepattuja paketteja yhteensä: {observed}\n\n\ +// Suodatettuja paketteja: 0\n\n\ +// Joitakin paketteja on siepattu, mutta yhtäkään ei ole valittu määrittämiesi suodattimien mukaan..." +// ), +// Language::JA => format!( +// "取得したパケット数: {observed}\n\n\ +// フィルター後のパケット数: 0\n\n\ +// パケットは取得できていますが、設定されたフィルタリングにより表示されません..." +// ), +// Language::UZ => format!( +// "Jami ushlangan paketlar: {observed}\n\n\ +// Filtrlangan paketlar: 0\n\n\ +// Tarmoq paketlari ushlandi, lekin ularning hech biri belgilangan filtrlarga mos kelmadi..." +// ), +// Language::VI => format!( +// "Tổng số gói tin bị chặn: {observed}\n\n\ +// Các gói tin đã lọc: 0\n\n\ +// Một số gói đã bị chặn, nhưng vẫn chưa có gói tin nào được bắt theo bộ lọc bạn đã chọn..." +// ), +// Language::ID => format!( +// "Total paket yang dilacak: {observed}\n\n\ +// 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..." +// ), +// }) +// } // pub fn filtered_packets_translation(language: Language) -> &'static str { // match language { @@ -1038,7 +1038,7 @@ pub fn some_observed_translation<'a>(language: Language, observed: u128) -> Text // }) // } -pub fn error_translation(language: Language, error: &str) -> Text { +pub fn error_translation(language: Language, error: &str) -> Text<'_, StyleType> { Text::new(match language { Language::EN => format!( "An error occurred! \n\n\ @@ -1462,7 +1462,7 @@ pub fn appearance_title_translation<'a>(language: Language) -> Text<'a, StyleTyp Language::TR => "Favori temanızı seçin", Language::RU => "Выберите предпочительную тему", Language::PT => "Escolha o seu tema favorito", - Language::EL => "Επίλεξε το αγαπημένο σου θέμα", + Language::EL => "Επιλέξτε το αγαπημένο σας θέμα", // Language::FA => "زمینه دلخواه خود را انتخاب کنید", Language::SV => "Välj ditt favorittema", Language::FI => "Valitse suosikkiteemasi", @@ -1787,7 +1787,7 @@ pub fn overview_translation(language: Language) -> &'static str { Language::TR => "Ön izleme", Language::RU => "Обзор", Language::PT => "Visão geral", - Language::EL => "επισκόπηση", + Language::EL => "Επισκόπηση", // Language::FA => "نمای کلی", Language::SV => "Översikt", Language::FI => "Yleiskatsaus", @@ -2167,7 +2167,7 @@ pub fn favorite_transmitted_translation(language: Language) -> &'static str { Language::TR => "Favorilerden yeni veri aktarıldı", Language::RU => "Новый обмен данными в избранных соедиениях", Language::PT => "Novos dados trocados dos favoritos", - Language::EL => "Καινούρια δεδομένα έχουν ανταλλαγεί στα αγαπημένα", + Language::EL => "Νέα δεδομένα έχουν ανταλλαγεί στα αγαπημένα", // Language::FA => "مبادله داده جدید از پسندیده ها", Language::SV => "Ny data utbytt av favoriter", Language::FI => "Uusia tietoja vaihdettu suosikeista", @@ -2253,9 +2253,9 @@ pub fn no_notifications_set_translation<'a>(language: Language) -> Text<'a, Styl Pode ativar as notificações nas definições:" } Language::EL => { - "Δεν έχεις ενεργοποιήσει τις ειδοποιήσεις ακόμη!\n\n\ - Αφότου τις ενεργοποιήσεις, αυτή η σελίδα θα απεικονίσει μια καταγραφή των ειδοποιήσεών σου\n\n\ - Μπορείς να ενεργοποιήσεις τις ειδοποιήσεις από τις ρυθμίσεις:" + "Δεν έχετε ενεργοποιήσει τις ειδοποιήσεις ακόμη!\n\n\ + Αφότου τις ενεργοποιήσετε, αυτή η σελίδα θα εμφανίσει ένα αρχείο καταγραφής των ειδοποιήσεών σας\n\n\ + Μπορείτε να ενεργοποιήσετε τις ειδοποιήσεις από τις ρυθμίσεις:" } // Language::FA => "شما هنوز اعلان ها را فعال نکرده اید!\n\n\ // پس از آنکه آن ها را فعال کنید، این صفحه یک کارنامه از اعلان های شما را نمایش خواهد داد\n\n @@ -2357,8 +2357,8 @@ pub fn no_notifications_received_translation<'a>(language: Language) -> Text<'a, Quando receber uma notificação, ela será mostrada aqui" } Language::EL => { - "Δεν υπάρχει κάτι για απεικόνιση αυτή τη στιγμή...\n\n\ - Όταν λάβεις μια ειδοποίηση, αυτή θα εμφανιστεί εδώ" + "Δεν υπάρχουν ειδοποιήσεις αυτή τη στιγμή...\n\n\ + Όταν λάβετε μια ειδοποίηση, θα εμφανιστεί εδώ" } // Language::FA => { // "در حال حاضر هیچ چیزی برای دیدن نیست...\n\n\ diff --git a/src/translations/translations_2.rs b/src/translations/translations_2.rs index e193426a..684b947b 100644 --- a/src/translations/translations_2.rs +++ b/src/translations/translations_2.rs @@ -1,5 +1,3 @@ -#![allow(clippy::match_same_arms, clippy::match_wildcard_for_single_variants)] - use crate::Language; pub fn new_version_available_translation(language: Language) -> &'static str { @@ -54,7 +52,7 @@ pub fn inspect_translation(language: Language) -> &'static str { Language::VI => "Quan sát", Language::ID => "Memeriksa", Language::NL => "Inspecteren", - _ => "Inspect", + Language::EL => "Επιθεώρηση", } } @@ -82,7 +80,7 @@ pub fn connection_details_translation(language: Language) -> &'static str { Language::VI => "Thông tin kết nối", Language::ID => "Rincian koneksi", Language::NL => "Verbindingsdetails", - _ => "Connection details", + Language::EL => "Λεπτομέρειες σύνδεσης", } } @@ -94,7 +92,7 @@ pub fn dropped_translation(language: Language) -> &'static str { Language::RU => "Потеряно", Language::SV => "Tappade", Language::FI => "Pudotetut", - Language::DE => "Verloren", + Language::DE | Language::NL => "Verloren", Language::TR => "Düşen", // Language::FA => "رها شده", Language::ES | Language::PT => "Perdidos", @@ -109,8 +107,7 @@ pub fn dropped_translation(language: Language) -> &'static str { Language::UZ => "Yig'ilgan", Language::VI => "Mất", Language::ID => "Dihapus", - Language::NL => "Verloren", - _ => "Dropped", + Language::EL => "Απορριμμένα", } } @@ -138,7 +135,7 @@ pub fn data_representation_translation(language: Language) -> &'static str { Language::VI => "Miêu tả dữ liệu", Language::ID => "Penyajian ulang data", Language::NL => "Gegevensweergave", - _ => "Data representation", + Language::EL => "Αναπαράσταση δεδομένων", } } @@ -166,7 +163,7 @@ pub fn host_translation(language: Language) -> &'static str { Language::VI => "Máy chủ", Language::ID => "Jaringan asal", Language::NL => "Netwerk host", - _ => "Network host", + Language::EL => "Κόμβος δικτύου", } } @@ -194,7 +191,7 @@ pub fn only_top_30_items_translation(language: Language) -> &'static str { 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", + Language::EL => "Εμφανίζονται μόνο τα κορυφαία 30 στοιχεία", } } @@ -248,7 +245,7 @@ pub fn local_translation(language: Language) -> &'static str { Language::VI => "Mạng nội bộ", Language::ID => "Jaringan lokal", Language::NL => "Lokaal netwerk", - _ => "Local network", + Language::EL => "Τοπικό δίκτυο", } } @@ -276,7 +273,7 @@ pub fn unknown_translation(language: Language) -> &'static str { Language::VI => "Không rõ địa điểm", Language::ID => "Lokasi tidak diketahui", Language::NL => "Onbekende locatie", - _ => "Unknown location", + Language::EL => "Άγνωστη τοποθεσία", } } @@ -304,7 +301,7 @@ pub fn your_network_adapter_translation(language: Language) -> &'static str { Language::VI => "Network adapter của bạn", Language::ID => "Adaptor jaringan kamu", Language::NL => "Uw netwerkadapter", - _ => "Your network adapter", + Language::EL => "Ο προσαρμογέας δικτύου σας", } } @@ -332,7 +329,7 @@ pub fn socket_address_translation(language: Language) -> &'static str { Language::VI => "Địa chỉ socket", Language::ID => "Alamat sambungan", Language::NL => "Socket adres", - _ => "Socket address", + Language::EL => "Διεύθυνση υποδοχής", } } @@ -360,13 +357,13 @@ pub fn mac_address_translation(language: Language) -> &'static str { Language::VI => "Địa chỉ MAC", Language::ID => "Alamat MAC", Language::NL => "MAC-adres", - _ => "MAC address", + Language::EL => "Διεύθυνση MAC", } } pub fn source_translation(language: Language) -> &'static str { match language { - Language::EN => "Source", + Language::EN | Language::FR => "Source", Language::IT => "Sorgente", Language::RU => "Источник", Language::SV => "Källa", @@ -381,20 +378,19 @@ pub fn source_translation(language: Language) -> &'static str { Language::UK => "Джерело", Language::RO => "Sursă", Language::PL => "Źródło", - Language::FR => "Source", Language::JA => "送信元", Language::UZ => "Manba", Language::PT => "Fonte", Language::VI => "Nguồn", Language::ID => "Asal", Language::NL => "Bron", - _ => "Source", + Language::EL => "Πηγή", } } pub fn destination_translation(language: Language) -> &'static str { match language { - Language::EN | Language::SV => "Destination", + Language::EN | Language::SV | Language::FR => "Destination", Language::IT => "Destinazione", Language::RU => "Получатель", Language::FI => "Määränpää", @@ -408,13 +404,12 @@ pub fn destination_translation(language: Language) -> &'static str { Language::UK => "Призначення", Language::RO => "Destinație", Language::PL => "Miejsce docelowe", - Language::FR => "Destination", Language::JA => "送信先", Language::UZ => "Qabul qiluvchi", Language::VI => "Đích", Language::ID => "Tujuan", Language::NL => "Bestemming", - _ => "Destination", + Language::EL => "Προορισμός", } } @@ -430,8 +425,7 @@ pub fn fqdn_translation(language: Language) -> &'static str { // Language::FA => "نام دامنه جامع الشرایط", Language::ES => "Nombre de dominio completo", Language::KO => "절대 도메인 네임", - Language::ZH | Language::JA => "FQDN", - Language::ZH_TW => "FQDN", + Language::ZH | Language::JA | Language::ZH_TW => "FQDN", Language::UK => "Повністю визначене доменне ім'я", Language::RO => "Nume de domeniu complet calificat", Language::PL => "Pełna nazwa domeny", @@ -441,7 +435,7 @@ pub fn fqdn_translation(language: Language) -> &'static str { Language::VI => "Tên miền đầy đủ", Language::ID => "Nama domain yang memenuhi syarat", Language::NL => "Volledig gekwalificeerde domeinnaam", - _ => "Fully qualified domain name", + Language::EL => "Πλήρως προσδιορισμένο όνομα τομέα", } } @@ -469,7 +463,7 @@ pub fn administrative_entity_translation(language: Language) -> &'static str { Language::VI => "Tên Autonomous System", Language::ID => "Nama System Otomatis", Language::NL => "Naam van het autonome systeem", - _ => "Autonomous System name", + Language::EL => "Όνομα αυτόνομου συστήματος", } } @@ -497,7 +491,7 @@ pub fn transmitted_data_translation(language: Language) -> &'static str { Language::VI => "Dữ liệu được truyền", Language::ID => "Data terkirim", Language::NL => "Verzonden gegevens", - _ => "Transmitted data", + Language::EL => "Μεταδιδόμενα δεδομένα", } } @@ -506,9 +500,8 @@ pub fn country_translation(language: Language) -> &'static str { Language::EN => "Country", Language::IT => "Paese", Language::RU => "Страна", - Language::SV => "Land", + Language::SV | Language::DE | Language::NL => "Land", Language::FI => "Maa", - Language::DE => "Land", Language::TR => "Ülke", // Language::FA => "کشور", Language::ES | Language::PT => "País", @@ -523,8 +516,7 @@ pub fn country_translation(language: Language) -> &'static str { Language::UZ => "Davlat", Language::VI => "Quốc gia", Language::ID => "Negara", - Language::NL => "Land", - _ => "Country", + Language::EL => "Χώρα", } } @@ -552,7 +544,7 @@ pub fn domain_name_translation(language: Language) -> &'static str { Language::VI => "Tên miền", Language::ID => "Nama Domain", Language::NL => "Domeinnaam", - _ => "Domain name", + Language::EL => "Όνομα τομέα", } } @@ -580,7 +572,7 @@ pub fn only_show_favorites_translation(language: Language) -> &'static str { Language::VI => "Chỉ hiển thị mục ưa thích", Language::ID => "Hanya tunjukkan favorit", Language::NL => "Toon alleen favorieten", - _ => "Only show favorites", + Language::EL => "Εμφάνιση μόνο αγαπημένων", } } @@ -635,7 +627,9 @@ pub fn no_search_results_translation(language: Language) -> &'static str { 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", + Language::EL => { + "Δεν υπάρχουν διαθέσιμα αποτελέσματα σύμφωνα με τα καθορισμένα φίλτρα αναζήτησης" + } } } @@ -670,11 +664,10 @@ pub fn showing_results_translation( Language::NL => { format!("{start}-{end} van de {total} totale resultaten worden weergegeven") } - _ => format!("Showing {start}-{end} of {total} total results"), + Language::EL => format!("Εμφάνιση {start}-{end} από {total} συνολικά αποτελέσματα"), } } -#[allow(dead_code)] pub fn color_gradients_translation(language: Language) -> &'static str { match language { Language::EN => "Apply color gradients", @@ -699,6 +692,6 @@ pub fn color_gradients_translation(language: Language) -> &'static str { Language::VI => "Áp dụng color gradients", Language::ID => "Aplikasikan gradasi warna", Language::NL => "Kleurverlopen toepassen", - _ => "Apply color gradients", + Language::EL => "Εφαρμογή χρωματικών διαβαθμίσεων", } } diff --git a/src/translations/translations_3.rs b/src/translations/translations_3.rs index 3fd89175..23a0b1a4 100644 --- a/src/translations/translations_3.rs +++ b/src/translations/translations_3.rs @@ -1,4 +1,4 @@ -#![allow(clippy::match_same_arms)] +#![allow(clippy::match_same_arms, clippy::match_wildcard_for_single_variants)] use iced::widget::Text; @@ -28,6 +28,7 @@ pub fn general_translation(language: Language) -> &'static str { Language::UK => "Загальні", Language::ID => "Umum", Language::NL => "Algemeen", + Language::EL => "Γενικά", _ => "General", } } @@ -55,6 +56,7 @@ pub fn zoom_translation(language: Language) -> &'static str { Language::TR => "Yakınlaştırma", Language::UK => "Масштабування", Language::ID => "Perbesar", + Language::EL => "Εστίαση", _ => "Zoom", } } @@ -82,6 +84,7 @@ pub fn mmdb_files_translation(language: Language) -> &'static str { Language::UK => "Файли бази даних", Language::ID => "Berkas database", Language::NL => "Database bestanden", + Language::EL => "Αρχεία βάσης δεδομένων", _ => "Database files", } } @@ -109,6 +112,9 @@ pub fn params_not_editable_translation(language: Language) -> &'static str { Language::UK => "Наступні параметри не можна змінювати під час аналізу трафіку", Language::ID => "Parameter berikut tidak bisa diubah saat dianalisa", Language::NL => "De volgende parameters kunnen niet worden aangepast tijdens de analyse", + Language::EL => { + "Οι ακόλουθες παράμετροι δεν μπορούν να τροποποιηθούν κατά τη διάρκεια της ανάλυσης" + } _ => "The following parameters can't be modified during the analysis", } } @@ -135,6 +141,7 @@ pub fn custom_style_translation(language: Language) -> &'static str { Language::UK => "Власний стиль", Language::ID => "Ubah Model", Language::NL => "Aangepaste stijl", + Language::EL => "Προσαρμοσμένο στυλ", _ => "Custom style", } } @@ -160,6 +167,7 @@ pub fn copy_translation(language: Language) -> &'static str { Language::UK => "Копіювати", Language::ID => "Salin", Language::NL => "Kopiëren", + Language::EL => "Αντιγραφή", _ => "Copy", } } @@ -186,35 +194,37 @@ pub fn port_translation(language: Language) -> &'static str { Language::UK => "Порт", Language::ID => "Port", Language::NL => "Poort", + Language::EL => "Θύρα", _ => "Port", } } -pub fn invalid_filters_translation(language: Language) -> &'static str { - match language { - Language::EN => "Invalid filters", - // Language::FA => "صافی نامعتبر", - Language::ES | Language::PT => "Filtros inválidos", - Language::IT => "Filtri non validi", - Language::FR => "Filtres invalides", - Language::DE => "Ungültige Filter", - Language::PL => "Nieprawidłowe filtry", - Language::RU => "Неверный формат фильтров", - Language::RO => "Filtre invalide", - Language::JA => "無効なフィルター", - Language::UZ => "Noto'g'ri filtrlar", - Language::SV => "Ogiltiga filter", - Language::VI => "Bộ lọc không khả dụng", - Language::ZH => "无效的过滤器", - Language::ZH_TW => "無效的篩選器", - Language::KO => "잘못된 필터", - Language::TR => "Geçersiz filtreler", - Language::UK => "Неправильний формат фільтрів", - Language::ID => "Filter salah", - Language::NL => "Ongeldige filters", - _ => "Invalid filters", - } -} +// pub fn invalid_filters_translation(language: Language) -> &'static str { +// match language { +// Language::EN => "Invalid filters", +// // Language::FA => "صافی نامعتبر", +// Language::ES | Language::PT => "Filtros inválidos", +// Language::IT => "Filtri non validi", +// Language::FR => "Filtres invalides", +// Language::DE => "Ungültige Filter", +// Language::PL => "Nieprawidłowe filtry", +// Language::RU => "Неверный формат фильтров", +// Language::RO => "Filtre invalide", +// Language::JA => "無効なフィルター", +// Language::UZ => "Noto'g'ri filterlar", +// Language::SV => "Ogiltiga filter", +// Language::VI => "Bộ lọc không khả dụng", +// Language::ZH => "无效的过滤器", +// Language::ZH_TW => "無效的篩選器", +// Language::KO => "잘못된 필터", +// Language::TR => "Geçersiz filtreler", +// Language::UK => "Неправильний формат фільтрів", +// Language::ID => "Filter salah", +// Language::NL => "Ongeldige filters", +// Language::EL => "Μη έγκυρα φίλτρα", +// _ => "Invalid filters", +// } +// } pub fn messages_translation(language: Language) -> &'static str { match language { @@ -238,6 +248,7 @@ pub fn messages_translation(language: Language) -> &'static str { Language::UK => "Повідомлення", Language::ID => "Pesan", Language::NL => "Berichten", + Language::EL => "Μηνύματα", _ => "Messages", } } @@ -264,6 +275,7 @@ pub fn link_type_translation(language: Language) -> &'static str { Language::PT => "Tipo de conexão", Language::UK => "Різновид зʼєднання", Language::ID => "Tipe koneksi", + Language::EL => "Τύπος σύνδεσης", _ => "Link type", } } @@ -324,6 +336,9 @@ pub fn unsupported_link_type_translation<'a>( Language::NL => { "Het linktype dat is gekoppeld aan deze adapter wordt nog niet ondersteund door Sniffnet..." } + Language::EL => { + "Ο τύπος σύνδεσης που σχετίζεται με αυτόν τον προσαρμογέα δεν υποστηρίζεται ακόμη από το Sniffnet..." + } _ => "The link type associated with this adapter is not supported by Sniffnet yet...", }; @@ -356,6 +371,7 @@ pub fn style_from_file_translation(language: Language) -> &'static str { Language::UK => "Виберіть стиль з файлу", Language::ID => "Pilih model / gaya dari berkas", Language::NL => "Selecteer stijl vanuit een bestand", + Language::EL => "Επιλογή στυλ από αρχείο", _ => "Select style from a file", } } @@ -383,6 +399,7 @@ pub fn database_from_file_translation(language: Language) -> &'static str { Language::UK => "Виберіть файл бази даних", Language::ID => "Pilih berkas database", Language::NL => "Selecteer database bestand", + Language::EL => "Επιλογή αρχείου βάσης δεδομένων", _ => "Select database file", } } @@ -410,6 +427,7 @@ pub fn filter_by_host_translation(language: Language) -> &'static str { Language::UK => "Фільтр за хостом мережі", Language::ID => "Filter berdasarkan jaringan asal", Language::NL => "Filteren op netwerk host", + Language::EL => "Φίλτρο ανά διακομιστή δικτύου", _ => "Filter by network host", } } @@ -434,6 +452,7 @@ pub fn service_translation(language: Language) -> &'static str { Language::UK => "Сервіс", Language::ID => "Layanan", Language::NL => "Dienst", + Language::EL => "Υπηρεσία", _ => "Service", } } @@ -461,6 +480,7 @@ pub fn export_capture_translation(language: Language) -> &'static str { Language::ID => "Ekspor data tangkapan", Language::ES => "Exportar archivo de captura", Language::NL => "Exporteer capture bestand", + Language::EL => "Εξαγωγή αρχείου καταγραφής", _ => "Export capture file", } } @@ -487,6 +507,7 @@ pub fn directory_translation(language: Language) -> &'static str { Language::ID => "Direktori", Language::ES => "Directorio", Language::NL => "Map", + Language::EL => "Κατάλογος", _ => "Directory", } } @@ -514,6 +535,7 @@ pub fn select_directory_translation(language: Language) -> &'static str { Language::ID => "Pilih direktori tujuan", Language::ES => "Selecciona el directorio de destino", Language::NL => "Selecteer doelmap", + Language::EL => "Επιλογή καταλόγου προορισμού", _ => "Select destination directory", } } @@ -541,6 +563,7 @@ pub fn file_name_translation(language: Language) -> &'static str { Language::ID => "Nama berkas", Language::ES => "Nombre del archivo", Language::NL => "Bestandsnaam", + Language::EL => "Όνομα αρχείου", _ => "File name", } } @@ -567,6 +590,7 @@ pub fn thumbnail_mode_translation(language: Language) -> &'static str { Language::UK => "Режим мініатюри", Language::ID => "Mode gambar kecil", Language::NL => "Miniatuur modus", + Language::EL => "Λειτουργία μικρογραφιών", _ => "Thumbnail mode", } } diff --git a/src/translations/translations_4.rs b/src/translations/translations_4.rs index 30089fff..fffb656e 100644 --- a/src/translations/translations_4.rs +++ b/src/translations/translations_4.rs @@ -9,12 +9,17 @@ pub fn reserved_address_translation(language: Language, info: &str) -> String { match language { Language::EN => format!("Reserved address ({info})"), Language::IT => format!("Indirizzo riservato ({info})"), + Language::JA => format!("予約済みアドレス ({info})"), Language::PT => format!("Endereço reservado ({info})"), Language::UK => format!("Зарезервована адреса ({info})"), + Language::ZH => format!("预留地址 ({info})"), Language::ZH_TW => format!("保留的網路位址 ({info})"), + Language::FR => format!("Adresse réservée ({info})"), Language::NL => format!("Gereserveerd adres ({info})"), + Language::RO => format!("Adresă rezervată ({info})"), Language::DE => format!("Reservierte Adresse ({info})"), Language::UZ => format!("Rezervlangan manzil ({info})"), + Language::EL => format!("Δεσμευμένη διεύθυνση ({info})"), _ => format!("Reserved address ({info})"), } } @@ -23,36 +28,51 @@ pub fn share_feedback_translation(language: Language) -> &'static str { match language { Language::EN => "Share your feedback", Language::IT => "Condividi il tuo feedback", + Language::JA => "フィードバックを共有", + Language::ZH => "分享您的反馈", Language::ZH_TW => "分享您的意見回饋", + Language::FR => "Partagez vos commentaires", Language::NL => "Deel uw feedback", + Language::RO => "Împărtășiți feedback-ul dvs.", Language::DE => "Feedback geben", Language::UZ => "Fikr-mulohazalaringizni ulashing", + Language::EL => "Μοιραστείτε τα σχόλιά σας", _ => "Share your feedback", } } // refers to bytes or packets excluded because of the filters -pub fn excluded_translation(language: Language) -> &'static str { - match language { - Language::EN => "Excluded", - Language::IT => "Esclusi", - Language::UZ => "Chiqarib tashlangan", - Language::ZH_TW => "已排除", - Language::NL => "Uitgesloten", - Language::DE => "Herausgefiltert", - _ => "Excluded", - } -} +// pub fn excluded_translation(language: Language) -> &'static str { +// match language { +// Language::EN => "Excluded", +// Language::IT => "Esclusi", +// Language::JA => "除外", +// Language::ZH => "已被过滤", +// Language::UZ => "Chiqarib tashlangan", +// Language::ZH_TW => "已排除", +// Language::FR => "Exclus", +// Language::NL => "Uitgesloten", +// Language::DE => "Herausgefiltert", +// Language::EL => "Εξαιρούμενα", +// Language::RO => "Excluși", +// _ => "Excluded", +// } +// } -pub fn import_capture_translation(language: Language) -> &'static str { +pub fn capture_file_translation(language: Language) -> &'static str { match language { - Language::EN => "Import capture file", - Language::IT => "Importa file di cattura", - Language::NL => "Importeer capture bestand", - Language::DE => "Aufzeichnungsdatei importieren", - Language::UZ => "Tahlil faylini import qilish", + Language::EN => "Capture file", + Language::IT => "File di cattura", + Language::FR => "Fichier de capture", + Language::JA => "キャプチャファイル", + Language::ZH => "捕获文件", + Language::NL => "Capture bestand", + Language::DE => "Aufzeichnungsdatei", + Language::UZ => "Tahlil faylini", + Language::EL => "Αρχείου καταγραφής", + Language::RO => "Importă fișierul de captură", Language::ZH_TW => "導入擷取文件", - _ => "Import capture file", + _ => "Capture file", } } @@ -60,10 +80,15 @@ pub fn select_capture_translation(language: Language) -> &'static str { match language { Language::EN => "Select capture file", Language::IT => "Seleziona file di cattura", + Language::FR => "Sélectionner un fichier de capture", + Language::JA => "キャプチャファイルを選択", + Language::ZH => "选择捕获文件", Language::NL => "Selecteer capture bestand", + Language::RO => "Selectează fișierul de captură", Language::DE => "Aufzeichnungsdatei auswählen", Language::UZ => "Tahlil faylini tanlang", Language::ZH_TW => "選擇擷取文件", + Language::EL => "Επιλογή αρχείου καταγραφής", _ => "Select capture file", } } @@ -81,11 +106,31 @@ 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::FR => format!( + "Lecture des paquets depuis le fichier...\n\n\ + {file_name_translation}: {file}\n\n\ + Êtes-vous sûr que le fichier sélectionné n'est pas vide?" + ), + Language::JA => format!( + "ファイルからパケットを読み込み中...\n\n\ + {file_name_translation}: {file}\n\n\ + 選択したファイルが空でないことを確認しましたか?" + ), + Language::ZH => format!( + "从文件中读取数据包...\n\n\ + {file_name_translation}: {file}\n\n\ + 您确定选中的文件不是空的吗?" + ), 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?" ), + Language::RO => format!( + "Citirea pachetelor din fișier...\n\n\ + {file_name_translation}: {file}\n\n\ + Ești sigur că fișierul selectat nu este gol?" + ), Language::DE => format!( "Pakete aus Datei laden... \n\n\ {file_name_translation}: {file}\n\n\ @@ -101,10 +146,15 @@ pub fn reading_from_pcap_translation<'a>(language: Language, file: &str) -> Text {file_name_translation}: {file}\n\n\ 您確定您選擇的檔案不是空的嗎?" ), + Language::EL => format!( + "Ανάγνωση πακέτων από αρχείο...\n\n\ + {file_name_translation}: {file}\n\n\ + Είστε βέβαιοι ότι το επιλεγμένο αρχείο δεν είναι κενό;" + ), _ => format!( "Reading packets from file...\n\n\ - {file_name_translation}: {file}\n\n\ - Are you sure the file you selected isn't empty?" + {file_name_translation}: {file}\n\n\ + Are you sure the file you selected isn't empty?" ), }) } @@ -113,45 +163,61 @@ pub fn data_exceeded_translation(language: Language) -> &'static str { match language { Language::EN => "Data threshold exceeded", Language::IT => "Soglia di dati superata", + Language::FR => "Seuil de données dépassé", + Language::JA => "データの閾値を超えました", + Language::ZH => "已超出数据阈值", Language::NL => "Gegevenslimiet overschreden", + Language::RO => "Limita de date depășită", Language::DE => "Datenschwelle überschritten", Language::UZ => "Ma'lumotlar chegarasidan oshib ketdi", Language::ZH_TW => "已排除", + Language::EL => "Υπέρβαση ορίου δεδομένων", _ => "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::FR => "Seuil de bits dépassé", + Language::JA => "ビットの閾値を超えました", + Language::ZH => "已超出比特阈值", Language::NL => "Bits limiet overschreden", + Language::RO => "Limita de biți depășită", Language::DE => "Bitschwelle überschritten", Language::UZ => "Bitlar chegarasidan oshib ketdi", Language::ZH_TW => "超出數據界限", + Language::EL => "Υπέρβαση ορίου δυφίων", _ => "Bits threshold exceeded", } } -#[allow(dead_code)] pub fn bits_translation(language: Language) -> &'static str { match language { - Language::EN | Language::IT | Language::NL | Language::DE => "Bits", + Language::EN | Language::IT | Language::NL | Language::DE | Language::FR => "bits", + Language::JA => "ビット", + Language::ZH => "比特", + Language::UZ => "bitlar", + Language::EL => "Δυφία", + Language::RO => "Biți", Language::ZH_TW => "位元", - Language::UZ => "Bitlar", - _ => "Bits", + _ => "bits", } } #[allow(dead_code)] pub fn pause_translation(language: Language) -> &'static str { match language { - Language::EN | Language::DE => "Pause", + Language::EN | Language::DE | Language::FR => "Pause", Language::IT => "Pausa", + Language::JA => "一時停止", + Language::ZH => "暂停", Language::NL => "Pauzeren", + Language::RO => "Pauză", Language::UZ => "To'xtatish", Language::ZH_TW => "暫停", + Language::EL => "Παύση", _ => "Pause", } } @@ -161,10 +227,15 @@ pub fn resume_translation(language: Language) -> &'static str { match language { Language::EN => "Resume", Language::IT => "Riprendi", + Language::FR => "Reprendre", + Language::JA => "再開", + Language::ZH => "恢复", Language::NL => "Hervatten", + Language::RO => "Continuă", Language::DE => "Fortsetzen", Language::UZ => "Davom ettirish", Language::ZH_TW => "繼續", + Language::EL => "Συνέχεια", _ => "Resume", } } diff --git a/src/translations/translations_5.rs b/src/translations/translations_5.rs new file mode 100644 index 00000000..cfe4516e --- /dev/null +++ b/src/translations/translations_5.rs @@ -0,0 +1,21 @@ +#![allow(clippy::match_same_arms)] + +use crate::translations::types::language::Language; + +pub fn filter_traffic_translation(language: Language) -> String { + match language { + Language::EN => "Filter traffic", + Language::IT => "Filtra il traffico", + _ => "Filter traffic", + } + .to_string() +} + +// the source from which Sniffnet reads network traffic (i.e., a capture file or a network adapter) +pub fn traffic_source_translation(language: Language) -> &'static str { + match language { + Language::EN => "Traffic source", + Language::IT => "Fonte del traffico", + _ => "Traffic source", + } +} diff --git a/src/translations/types/language.rs b/src/translations/types/language.rs index 6092f077..2c7aba79 100644 --- a/src/translations/types/language.rs +++ b/src/translations/types/language.rs @@ -121,7 +121,15 @@ pub fn get_flag<'a>(self) -> Svg<'a, StyleType> { pub fn is_up_to_date(self) -> bool { matches!( self, - Language::EN | Language::IT | Language::NL | Language::DE | Language::UZ + Language::EN + | Language::IT + | Language::NL + | Language::DE + | Language::UZ + | Language::ZH + | Language::JA + | Language::FR + | Language::EL ) } } diff --git a/src/utils/formatted_strings.rs b/src/utils/formatted_strings.rs index da4f9d05..8dcabc23 100644 --- a/src/utils/formatted_strings.rs +++ b/src/utils/formatted_strings.rs @@ -1,15 +1,8 @@ use std::cmp::min; use std::net::IpAddr; -use crate::Language; -use crate::networking::types::filters::Filters; -use crate::translations::translations::{ - address_translation, ip_version_translation, protocol_translation, -}; -use crate::translations::translations_3::{invalid_filters_translation, port_translation}; use crate::utils::types::timestamp::Timestamp; use chrono::{Local, TimeZone}; -use std::fmt::Write; /// Application version number (to be displayed in gui footer) pub const APP_VERSION: &str = env!("CARGO_PKG_VERSION"); @@ -27,61 +20,6 @@ // } // } -pub fn get_invalid_filters_string(filters: &Filters, language: Language) -> String { - let mut ret_val = format!("{}:", invalid_filters_translation(language)); - if !filters.ip_version_valid() { - let _ = write!(ret_val, "\n • {}", ip_version_translation(language)); - } - if !filters.protocol_valid() { - let _ = write!(ret_val, "\n • {}", protocol_translation(language)); - } - if !filters.address_valid() { - let _ = write!(ret_val, "\n • {}", address_translation(language)); - } - if !filters.port_valid() { - let _ = write!(ret_val, "\n • {}", port_translation(language)); - } - ret_val -} - -/// Computes the string representing the active filters -pub fn get_active_filters_string(filters: &Filters, language: Language) -> String { - let mut filters_string = String::new(); - if filters.ip_version_active() { - let _ = writeln!( - filters_string, - "• {}: {}", - ip_version_translation(language), - filters.pretty_print_ip() - ); - } - if filters.protocol_active() { - let _ = writeln!( - filters_string, - "• {}: {}", - protocol_translation(language), - filters.pretty_print_protocol() - ); - } - if filters.address_active() { - let _ = writeln!( - filters_string, - "• {}: {}", - address_translation(language), - filters.address_str - ); - } - if filters.port_active() { - let _ = writeln!( - filters_string, - "• {}: {}", - port_translation(language), - filters.port_str - ); - } - filters_string -} - pub fn print_cli_welcome_message() { let ver = APP_VERSION; print!( diff --git a/src/utils/types/icon.rs b/src/utils/types/icon.rs index 87f16a39..5208aa6a 100644 --- a/src/utils/types/icon.rs +++ b/src/utils/types/icon.rs @@ -39,7 +39,7 @@ pub enum Icon { PacketsThreshold, // Restore, Roadmap, - Rocket, + // Rocket, Settings, Sniffnet, SortAscending, @@ -85,7 +85,7 @@ pub fn codepoint(&self) -> char { Icon::Overview => 'd', Icon::PacketsThreshold => '\\', // Icon::Restore => 'k', - Icon::Rocket => 'S', + // Icon::Rocket => 'S', Icon::Settings => 'a', Icon::Sniffnet => 'A', Icon::Star => 'g',