diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 0efc524..915356a 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -1,9 +1,9 @@ # Prova il codice e pubblica l'immagine Docker su GitHub Container Registry (ghcr.io). # -# - Pull request: prova i test e la compilazione dell'immagine (amd64), senza pubblicare. -# - Push su main: pubblica `latest` e `sha-` (solo amd64: è la compilazione veloce). +# - Pull request: esegue i test Linux e Windows e compila il Dockerfile per amd64, senza pubblicare immagini. +# - Push su main: pubblica `latest` e `sha-` per amd64 e arm64. # - Tag `vX.Y.Z` (per esempio `git tag v0.2.0 && git push --tags`): pubblica `X.Y.Z`, `X.Y` e -# `latest`, per amd64 e arm64 (l'arm64 gira sotto emulazione: richiede parecchi minuti). +# `latest`, per amd64 e arm64 (build nativi paralleli, senza QEMU). # # L'immagine: ghcr.io//altkeeper. Se il repo è privato lo è anche il pacchetto: per # usarlo da un server serve `docker login ghcr.io` con un token che possa leggere i pacchetti, @@ -28,6 +28,8 @@ jobs: contents: read steps: - uses: actions/checkout@v7 + - name: Check localized web UI + run: node scripts/check-web-ui.cjs - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 - run: cargo test --locked @@ -44,6 +46,8 @@ jobs: contents: read steps: - uses: actions/checkout@v7 + - name: Check localized web UI + run: node scripts/check-web-ui.cjs - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 - run: cargo test --locked @@ -60,43 +64,121 @@ jobs: $output = cargo run --locked -- --help if ($LASTEXITCODE -ne 0 -or -not ($output -match '^uso: altkeeper')) { throw "Italian CLI help failed: $output" } - image: - needs: test + build-pr: + if: github.event_name == 'pull_request' + needs: [test, test-windows] runs-on: ubuntu-latest permissions: contents: read - packages: write steps: - uses: actions/checkout@v7 - - - uses: docker/setup-qemu-action@v4 - uses: docker/setup-buildx-action@v4 + - uses: docker/build-push-action@v7 + with: + context: . + platforms: linux/amd64 + push: false + cache-from: type=gha,scope=altkeeper-amd64 - # Il login serve solo per pubblicare: le pull request non hanno il permesso di scrivere. + image: + # Build architectures concurrently on native runners for publishing. + if: github.event_name != 'pull_request' + needs: [test, test-windows] + strategy: + fail-fast: false + matrix: + include: + - arch: amd64 + platform: linux/amd64 + runner: ubuntu-latest + - arch: arm64 + platform: linux/arm64 + runner: ubuntu-24.04-arm + runs-on: ${{ matrix.runner }} + permissions: + contents: read + packages: write + steps: + - uses: actions/checkout@v7 + # No QEMU: Rust is compiled natively for each target. + - uses: docker/setup-buildx-action@v4 - uses: docker/login-action@v4 - if: github.event_name != 'pull_request' with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} + - id: image-name + run: echo "image=ghcr.io/${GITHUB_REPOSITORY,,}" >> "$GITHUB_OUTPUT" + - id: meta + uses: docker/metadata-action@v6 + with: + images: ${{ steps.image-name.outputs.image }} + - id: build + uses: docker/build-push-action@v7 + with: + context: . + platforms: ${{ matrix.platform }} + # Immutable per-arch pushes; only the final manifest receives tags. + outputs: type=image,name=${{ steps.image-name.outputs.image }},push-by-digest=true,name-canonical=true,push=true + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha,scope=altkeeper-${{ matrix.arch }} + cache-to: type=gha,scope=altkeeper-${{ matrix.arch }},mode=max + - name: Export image digest + run: | + mkdir -p "${RUNNER_TEMP}/digests" + digest="${{ steps.build.outputs.digest }}" + test -n "${digest}" + touch "${RUNNER_TEMP}/digests/${digest#sha256:}" + - uses: actions/upload-artifact@v4 + with: + name: digests-${{ matrix.arch }} + path: ${{ runner.temp }}/digests/* + if-no-files-found: error + retention-days: 1 + image-manifest: + # Publish tags only after both architecture builds succeed. + if: github.event_name != 'pull_request' + needs: [image] + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + steps: + - uses: actions/download-artifact@v4 + with: + path: ${{ runner.temp }}/digests + pattern: digests-* + merge-multiple: true + - uses: docker/setup-buildx-action@v4 + - uses: docker/login-action@v4 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - id: image-name + run: echo "image=ghcr.io/${GITHUB_REPOSITORY,,}" >> "$GITHUB_OUTPUT" - id: meta uses: docker/metadata-action@v6 with: - images: ghcr.io/${{ github.repository }} + images: ${{ steps.image-name.outputs.image }} tags: | type=semver,pattern={{version}} type=semver,pattern={{major}}.{{minor}} type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v') }} type=sha - - - uses: docker/build-push-action@v7 - with: - context: . - # I tag di versione pubblicano anche arm64; il resto solo amd64 (più rapido). - platforms: ${{ startsWith(github.ref, 'refs/tags/v') && 'linux/amd64,linux/arm64' || 'linux/amd64' }} - push: ${{ github.event_name != 'pull_request' }} - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - cache-from: type=gha - cache-to: type=gha,mode=max + - name: Create multi-platform manifest + working-directory: ${{ runner.temp }}/digests + env: + DOCKER_METADATA_OUTPUT_JSON: ${{ steps.meta.outputs.json }} + run: | + # Never publish a partial multi-architecture manifest. + test "$(find . -maxdepth 1 -type f | wc -l)" -eq 2 + docker buildx imagetools create \ + $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ + $(printf '${{ steps.image-name.outputs.image }}@sha256:%s ' *) + - name: Inspect published manifest + env: + DOCKER_METADATA_OUTPUT_JSON: ${{ steps.meta.outputs.json }} + run: | + docker buildx imagetools inspect "$(jq -r '.tags[0]' <<< "$DOCKER_METADATA_OUTPUT_JSON")" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..dfa1300 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,105 @@ +# Compila AltKeeper per Windows, Linux e macOS e pubblica tutto in una GitHub Release. +# +# - Windows: `altkeeper.exe` + `altkeeper.exe.sha256`. L'aggiornamento dall'icona nella barra di +# sistema scarica proprio questi due file: i nomi non vanno cambiati. +# - Linux (x86-64 e arm64) e macOS (Apple Silicon e Intel): `altkeeper---.tar.gz` +# con il suo `.sha256`. Sono download manuali: l'auto-aggiornamento è solo per Windows. +# +# Si lancia con un tag, per esempio: git tag v0.3.1 && git push origin v0.3.1 +# Il numero del tag deve coincidere con `version` in Cargo.toml. +name: release + +on: + push: + tags: ["v*"] + +jobs: + build: + strategy: + fail-fast: true + matrix: + include: + - name: windows + runner: windows-latest + test: true + # ubuntu-22.04: glibc 2.35, così il binario gira anche su distribuzioni non recentissime. + - name: linux-x86_64 + runner: ubuntu-22.04 + test: true + - name: linux-aarch64 + runner: ubuntu-22.04-arm + test: true + - name: macos-aarch64 + runner: macos-latest + test: false + - name: macos-x86_64 + runner: macos-15-intel + test: false + runs-on: ${{ matrix.runner }} + permissions: + contents: read + defaults: + run: + shell: bash + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 + - uses: dtolnay/rust-toolchain@b725eda525362e7cf34af9a7243cac1e3f01a7ec # stable + - uses: Swatinem/rust-cache@d1527c8f67ef9af357290475d4af4b0fab8f12e0 # v2 + - name: Check that the tag matches Cargo.toml + run: | + version=$(sed -n 's/^version = "\(.*\)"/\1/p' Cargo.toml | head -n1) + if [ "v$version" != "${GITHUB_REF_NAME}" ]; then + echo "tag ${GITHUB_REF_NAME} != Cargo.toml v$version"; exit 1 + fi + - name: Test + if: matrix.test + run: cargo test --locked + - run: cargo build --release --locked + - name: Package (Windows) + if: matrix.name == 'windows' + run: | + mkdir dist + cp target/release/altkeeper.exe dist/altkeeper.exe + cd dist + hash=$(sha256sum altkeeper.exe | cut -d' ' -f1) + printf '%s altkeeper.exe' "$hash" > altkeeper.exe.sha256 + - name: Package (Linux, macOS) + if: matrix.name != 'windows' + run: | + pkg="altkeeper-${GITHUB_REF_NAME}-${{ matrix.name }}" + mkdir -p "dist/$pkg" + cp target/release/altkeeper LICENSE README.md README.it.md "dist/$pkg/" + cp -r examples "dist/$pkg/examples" + cd dist + tar -czf "$pkg.tar.gz" "$pkg" + rm -rf "$pkg" + if command -v sha256sum >/dev/null; then + sha256sum "$pkg.tar.gz" > "$pkg.tar.gz.sha256" + else + shasum -a 256 "$pkg.tar.gz" > "$pkg.tar.gz.sha256" + fi + - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + with: + name: dist-${{ matrix.name }} + path: dist/* + if-no-files-found: error + retention-days: 1 + + publish: + # Una sola release, creata solo se tutte le build sono riuscite. + needs: [build] + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4 + with: + path: dist + pattern: dist-* + merge-multiple: true + - name: List files + run: ls -l dist + - uses: softprops/action-gh-release@3bb12739c298aeb8a4eeaf626c5b8d85266b0e65 # v2 + with: + files: dist/* + fail_on_unmatched_files: true diff --git a/.gitignore b/.gitignore index f30d0ff..4ae32c4 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,6 @@ notify.conf renew.log .ultimo-errore /profili-salvati/ +altkeeper.log +altkeeper.log.1 +altkeeper.new.exe diff --git a/CHANGELOG.md b/CHANGELOG.md index c1550fe..ca11e1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,11 @@ # Changelog -## Unreleased +## 0.3.0 ### Fixed +- **USB on Windows without configuration.** When `USBMUXD_SOCKET_ADDRESS` is not set, Windows now + uses the usbmuxd of iTunes / Apple Devices on `127.0.0.1:27015` instead of failing to find the + phone. Ported from the USB-fix branch. - **Install hung forever over USB on iOS 27.** On USB, installing an IPA now goes through AFC + installation_proxy via usbmuxd (same path as `pymobiledevice3 apps install`) instead of the CoreDeviceProxy software tunnel, and the profile client prefers the lockdown service too. App install and profile install now have timeouts, so a stuck phone produces an error instead of a silent hang. The status line says "over USB" or "over Wi-Fi" according to the real transport. Not compiled in this environment: please run `cargo build` and test. ### Changed @@ -17,6 +20,20 @@ unattended renewals (profiles + App IDs) work again. ### Added +- **Windows tray icon (`altkeeper tray`, also what a double-click on `altkeeper.exe` starts).** + AltServer and the web page run without a terminal, with an icon next to the clock (AltKeeper + logo, grey while the iPhone is unreachable), a menu (*Open AltKeeper*, *Show logs*, *Start with + Windows*, *Check for updates*, *Quit*), a window with the web page (WebView2) and toast + notifications for renewed profiles, installed apps and failures. Ported from AltKeeperPlus. +- **Updates from the tray and `release.yml`.** The icon checks the fork's GitHub releases once a + day; *Update now* verifies the SHA-256, restarts and rolls back if the new version does not + start. `release.yml` builds `altkeeper.exe` for Windows on a `v*` tag. Ported from AltKeeperPlus. +- **Persistent logs.** Server lines (AltServer, web page, DNS-SD) carry a timestamp and are + also written to `altkeeper.log` (rotated at 5 MB) and shown in a new **Logs** section of the + web page (`/api/logs`). Ported from AltKeeperPlus. +- **AltKeeper logo as the icon of `altkeeper.exe`** (embedded by the new `build.rs` on Windows builds, from `assets/altkeeper.ico`), besides the tray and window icons. +- **App / App ID counters** next to "Your apps" in the web page (3 active apps and 10 App IDs for + a free account; an estimate from the profiles on the phone). Ported from the USB-fix branch. - **USB when the iPhone is plugged into the machine running AltKeeper.** Every connection (check, renew, sign and install, AltServer) first looks for an iPhone on USB through the local `usbmuxd` (or the one in `USBMUXD_SOCKET_ADDRESS`) and talks to it through the CoreDeviceProxy tunnel; if there is none, or it fails, it falls back to Wi-Fi as before. Much faster for installing IPAs. Needs the phone to already trust the computer (the usual "Trust" prompt), not the remote pairing. `--phone ` forces Wi-Fi; `ALTKEEPER_NO_USB=1` turns USB off. In Docker the usbmuxd socket must be mounted into the container. - `ALTKEEPER_REVOKE_CERTS=1`: when installing an IPA from the web page fails with Apple error 7460 (the Apple ID already has a development certificate, e.g. from AltStore/SideStore), AltKeeper can revoke it and create its own. Off by default; without it you now get a clear explanation instead of a generic error. - **Windows support (native, no Docker).** `cargo build --release` on Windows 10/11 produces diff --git a/Cargo.lock b/Cargo.lock index 332f00a..14d24f6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -75,19 +75,40 @@ dependencies = [ [[package]] name = "altkeeper" -version = "0.2.0" +version = "0.3.0" dependencies = [ "axum", + "chrono", "idevice", "isideload", "mdns-sd", "plist", + "reqwest", "rpassword", "rustls", "serde", "serde_json", + "sha2 0.10.9", + "tao", + "tauri-winrt-notification", + "tempfile", "tokio", "tracing-subscriber", + "tray-icon", + "windows-sys 0.61.2", + "winreg", + "winresource", + "wry", + "zip", +] + +[[package]] +name = "android_system_properties" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae221649c9976a6f6c56ae1facf410f3ddb33cc661c4b7b61020a912d4237fbc" +dependencies = [ + "libc", ] [[package]] @@ -115,7 +136,7 @@ dependencies = [ "sha2 0.10.9", "sha2 0.11.0", "spki 0.7.3", - "thiserror", + "thiserror 2.0.20", "x509-cert 0.2.5", ] @@ -131,7 +152,7 @@ dependencies = [ "nom", "num-traits", "rusticata-macros", - "thiserror", + "thiserror 2.0.20", "time", ] @@ -214,11 +235,34 @@ dependencies = [ "crc32fast", "futures-lite", "pin-project", - "thiserror", + "thiserror 2.0.20", "tokio", "tokio-util", ] +[[package]] +name = "atk" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241b621213072e993be4f6f3a9e4b45f65b7e6faad43001be957184b7bb1824b" +dependencies = [ + "atk-sys", + "glib", + "libc", +] + +[[package]] +name = "atk-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e48b684b0ca77d2bbadeef17424c2ea3c897d44d566a1617e7e8f30614d086" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + [[package]] name = "atomic-waker" version = "1.1.2" @@ -310,6 +354,21 @@ version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec 0.8.0", +] + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + [[package]] name = "bit-vec" version = "0.9.1" @@ -319,6 +378,12 @@ dependencies = [ "serde", ] +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + [[package]] name = "bitflags" version = "2.13.2" @@ -361,6 +426,15 @@ dependencies = [ "hybrid-array", ] +[[package]] +name = "block2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" +dependencies = [ + "objc2", +] + [[package]] name = "bumpalo" version = "3.20.3" @@ -379,6 +453,31 @@ version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" +[[package]] +name = "cairo-rs" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" +dependencies = [ + "bitflags 2.13.2", + "cairo-sys-rs", + "glib", + "libc", + "once_cell", + "thiserror 1.0.69", +] + +[[package]] +name = "cairo-sys-rs" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + [[package]] name = "cbc" version = "0.1.2" @@ -407,6 +506,22 @@ dependencies = [ "shlex", ] +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cfg-expr" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" +dependencies = [ + "smallvec", + "target-lexicon", +] + [[package]] name = "cfg-if" version = "1.0.5" @@ -454,8 +569,10 @@ version = "0.4.45" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" dependencies = [ + "iana-time-zone", "num-traits", "serde", + "windows-link", ] [[package]] @@ -558,6 +675,16 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" +[[package]] +name = "cookie" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a373e3602691c3cdea496d2f0ee5935151e6168fe87739483c463db1b2f2f87" +dependencies = [ + "time", + "version_check", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -584,6 +711,30 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "core-graphics" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "064badf302c3194842cf2c5d61f56cc88e54a759313879cdf03abdd27d0c3b97" +dependencies = [ + "bitflags 2.13.2", + "core-foundation 0.10.1", + "core-graphics-types", + "foreign-types 0.5.0", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" +dependencies = [ + "bitflags 2.13.2", + "core-foundation 0.10.1", + "libc", +] + [[package]] name = "core_detect" version = "1.0.0" @@ -623,6 +774,15 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crossbeam-channel" +version = "0.5.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98b0cc327b5bc766e7fda9c9260cc0fa81b43a8e240440422dff70788e3f9ef1" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-deque" version = "0.8.8" @@ -684,6 +844,29 @@ dependencies = [ "rand_core 0.10.1", ] +[[package]] +name = "cssparser" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c9cdaae01d5ed7882b04d795e7f752f46ff52d2fa3b50a20d28c464510bba98" +dependencies = [ + "cssparser-macros", + "dtoa-short", + "itoa", + "phf", + "smallvec", +] + +[[package]] +name = "cssparser-macros" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d045de693cb712d0b22c6a64be5b953f67b3ce00ab5ad3dd5d8b441886ab8e1a" +dependencies = [ + "quote", + "syn 3.0.6", +] + [[package]] name = "ctr" version = "0.10.1" @@ -735,6 +918,17 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4583a4551df46e2792f82ceeac45e850d2e2d5debba0b91f102385cda5b11f06" +[[package]] +name = "dbus" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ab69f03cc8c4340c9c8e315114e1658e6775a9b16a04357973aa21cec22b32e" +dependencies = [ + "libc", + "libdbus-sys", + "windows-sys 0.61.2", +] + [[package]] name = "der" version = "0.7.10" @@ -803,6 +997,27 @@ version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +[[package]] +name = "derive_more" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.119", +] + [[package]] name = "des" version = "0.9.0" @@ -836,6 +1051,39 @@ dependencies = [ "ctutils", ] +[[package]] +name = "dirs" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d57d423b3c82e89b9a24ca3091fee61f456a26edbd28d26c65906f4bc1dcd8f" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.61.2", +] + +[[package]] +name = "dispatch2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" +dependencies = [ + "bitflags 2.13.2", + "block2", + "libc", + "objc2", +] + [[package]] name = "displaydoc" version = "0.2.7" @@ -847,6 +1095,71 @@ dependencies = [ "syn 3.0.6", ] +[[package]] +name = "dlopen2" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e2c5bd4158e66d1e215c49b837e11d62f3267b30c92f1d171c4d3105e3dc4d4" +dependencies = [ + "dlopen2_derive", + "libc", + "once_cell", + "winapi", +] + +[[package]] +name = "dlopen2_derive" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fbbb781877580993a8707ec48672673ec7b81eeba04cfd2310bd28c08e47c8f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "dom_query" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fac5fca71e65e94cc718a6e2af65d6e0f9c6027751c2aa562fbb5087fda639bc" +dependencies = [ + "bit-set", + "cssparser", + "foldhash", + "html5ever", + "precomputed-hash", + "selectors", + "tendril", +] + +[[package]] +name = "dpi" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" + +[[package]] +name = "dtoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c3cf4824e2d5f025c7b531afcb2325364084a16806f6d47fbc1f5fbd9960590" + +[[package]] +name = "dtoa-short" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd1511a7b6a56299bd043a9c167a6d2bfb37bf84a6dfceaba651168adfb43c87" +dependencies = [ + "dtoa", +] + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + [[package]] name = "ed25519" version = "2.2.3" @@ -915,12 +1228,31 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + [[package]] name = "fiat-crypto" version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" +[[package]] +name = "field-offset" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" +dependencies = [ + "memoffset", + "rustc_version", +] + [[package]] name = "find-msvc-tools" version = "0.1.13" @@ -940,7 +1272,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e634e2e0ebac1ee034020da1ca582e17ffe4e0f5e985823721e168928136dcb" dependencies = [ "crc32fast", - "miniz_oxide", + "miniz_oxide 0.9.1", "zlib-rs", ] @@ -961,13 +1293,40 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "foreign-types" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" dependencies = [ - "foreign-types-shared", + "foreign-types-shared 0.1.1", +] + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared 0.3.1", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea5190182e6915eb873ddbc16e23b711b6eb1f9c00a0d0a3a91b5f6228475225" +dependencies = [ + "proc-macro2", + "quote", + "syn 3.0.6", ] [[package]] @@ -976,6 +1335,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + [[package]] name = "form_urlencoded" version = "1.2.2" @@ -1087,52 +1452,151 @@ dependencies = [ ] [[package]] -name = "generic-array" -version = "0.14.7" +name = "gdk" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +checksum = "d9f245958c627ac99d8e529166f9823fb3b838d1d41fd2b297af3075093c2691" dependencies = [ - "typenum", - "version_check", + "cairo-rs", + "gdk-pixbuf", + "gdk-sys", + "gio", + "glib", + "libc", + "pango", ] [[package]] -name = "getrandom" -version = "0.2.17" +name = "gdk-pixbuf" +version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +checksum = "50e1f5f1b0bfb830d6ccc8066d18db35c487b1b2b1e8589b5dfe9f07e8defaec" dependencies = [ - "cfg-if", - "js-sys", + "gdk-pixbuf-sys", + "gio", + "glib", "libc", - "wasi", - "wasm-bindgen", + "once_cell", ] [[package]] -name = "getrandom" -version = "0.3.4" +name = "gdk-pixbuf-sys" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7" dependencies = [ - "cfg-if", - "js-sys", + "gio-sys", + "glib-sys", + "gobject-sys", "libc", - "r-efi 5.3.0", - "wasip2", - "wasm-bindgen", + "system-deps", ] [[package]] -name = "getrandom" -version = "0.4.3" +name = "gdk-sys" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" +checksum = "5c2d13f38594ac1e66619e188c6d5a1adb98d11b2fcf7894fc416ad76aa2f3f7" dependencies = [ - "cfg-if", + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gio-sys", + "glib-sys", + "gobject-sys", "libc", - "r-efi 6.0.0", - "rand_core 0.10.1", + "pango-sys", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gdkwayland-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "140071d506d223f7572b9f09b5e155afbd77428cd5cc7af8f2694c41d98dfe69" +dependencies = [ + "gdk-sys", + "glib-sys", + "gobject-sys", + "libc", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gdkx11" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3caa00e14351bebbc8183b3c36690327eb77c49abc2268dd4bd36b856db3fbfe" +dependencies = [ + "gdk", + "gdkx11-sys", + "gio", + "glib", + "libc", + "x11", +] + +[[package]] +name = "gdkx11-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e7445fe01ac26f11601db260dd8608fe172514eb63b3b5e261ea6b0f4428d" +dependencies = [ + "gdk-sys", + "glib-sys", + "libc", + "system-deps", + "x11", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi 5.3.0", + "wasip2", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" +dependencies = [ + "cfg-if", + "libc", + "r-efi 6.0.0", + "rand_core 0.10.1", ] [[package]] @@ -1144,6 +1608,148 @@ dependencies = [ "polyval", ] +[[package]] +name = "gio" +version = "0.18.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "gio-sys", + "glib", + "libc", + "once_cell", + "pin-project-lite", + "smallvec", + "thiserror 1.0.69", +] + +[[package]] +name = "gio-sys" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", + "winapi", +] + +[[package]] +name = "glib" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" +dependencies = [ + "bitflags 2.13.2", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "gio-sys", + "glib-macros", + "glib-sys", + "gobject-sys", + "libc", + "memchr", + "once_cell", + "smallvec", + "thiserror 1.0.69", +] + +[[package]] +name = "glib-macros" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" +dependencies = [ + "heck 0.4.1", + "proc-macro-crate 2.0.2", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "glib-sys" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898" +dependencies = [ + "libc", + "system-deps", +] + +[[package]] +name = "gobject-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gtk" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd56fb197bfc42bd5d2751f4f017d44ff59fbb58140c6b49f9b3b2bdab08506a" +dependencies = [ + "atk", + "cairo-rs", + "field-offset", + "futures-channel", + "gdk", + "gdk-pixbuf", + "gio", + "glib", + "gtk-sys", + "gtk3-macros", + "libc", + "pango", + "pkg-config", +] + +[[package]] +name = "gtk-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f29a1c21c59553eb7dd40e918be54dccd60c52b049b75119d5d96ce6b624414" +dependencies = [ + "atk-sys", + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "system-deps", +] + +[[package]] +name = "gtk3-macros" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ff3c5b21f14f0736fed6dcfc0bfb4225ebf5725f3c0209edeec181e4d73e9d" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.119", +] + [[package]] name = "h2" version = "0.4.19" @@ -1175,6 +1781,18 @@ version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hex" version = "0.4.3" @@ -1208,6 +1826,16 @@ dependencies = [ "digest 0.11.3", ] +[[package]] +name = "html5ever" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46a1761807faccc9a19e86944bbf40610014066306f96edcdedc2fb714bcb7b8" +dependencies = [ + "log", + "markup5ever", +] + [[package]] name = "http" version = "1.5.0" @@ -1340,6 +1968,30 @@ dependencies = [ "windows-registry", ] +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "icu_collections" version = "2.3.0" @@ -1454,7 +2106,7 @@ dependencies = [ "sha1 0.10.7", "sha2 0.10.9", "siphasher", - "thiserror", + "thiserror 2.0.20", "tokio", "tokio-rustls", "tracing", @@ -1579,7 +2231,7 @@ dependencies = [ "sha1 0.11.0", "sha2 0.11.0", "srp", - "thiserror", + "thiserror 2.0.20", "tokio", "tokio-tungstenite", "tracing", @@ -1609,7 +2261,7 @@ dependencies = [ "rc2", "sha1 0.11.0", "sha2 0.11.0", - "thiserror", + "thiserror 2.0.20", "x509-parser", ] @@ -1625,6 +2277,29 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" +[[package]] +name = "javascriptcore-rs" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca5671e9ffce8ffba57afc24070e906da7fc4b1ba66f2cabebf61bf2ea257fcc" +dependencies = [ + "bitflags 1.3.2", + "glib", + "javascriptcore-rs-sys", +] + +[[package]] +name = "javascriptcore-rs-sys" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1be78d14ffa4b75b66df31840478fef72b51f8c2465d4ca7c194da9f7a5124" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + [[package]] name = "jktcp" version = "0.1.7" @@ -1640,6 +2315,22 @@ dependencies = [ "wasmtimer", ] +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys 0.3.1", + "log", + "thiserror 1.0.69", + "walkdir", + "windows-sys 0.45.0", +] + [[package]] name = "jni" version = "0.22.4" @@ -1649,10 +2340,10 @@ dependencies = [ "cfg-if", "combine", "jni-macros", - "jni-sys", + "jni-sys 0.4.1", "log", "simd_cesu8", - "thiserror", + "thiserror 2.0.20", "walkdir", "windows-link", ] @@ -1670,6 +2361,15 @@ dependencies = [ "syn 2.0.119", ] +[[package]] +name = "jni-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258" +dependencies = [ + "jni-sys 0.4.1", +] + [[package]] name = "jni-sys" version = "0.4.1" @@ -1709,6 +2409,16 @@ dependencies = [ "cpufeatures 0.2.17", ] +[[package]] +name = "keyboard-types" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fbe853b403ae61a04233030ae8a79d94975281ed9770a1f9e246732b534b28d" +dependencies = [ + "bitflags 2.13.2", + "serde", +] + [[package]] name = "lazy_static" version = "1.5.0" @@ -1718,18 +2428,70 @@ dependencies = [ "spin", ] +[[package]] +name = "libappindicator" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03589b9607c868cc7ae54c0b2a22c8dc03dd41692d48f2d7df73615c6a95dc0a" +dependencies = [ + "glib", + "gtk", + "gtk-sys", + "libappindicator-sys", + "log", +] + +[[package]] +name = "libappindicator-sys" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e9ec52138abedcc58dc17a7c6c0c00a2bdb4f3427c7f63fa97fd0d859155caf" +dependencies = [ + "gtk-sys", + "libloading", + "once_cell", +] + [[package]] name = "libc" version = "0.2.189" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" +[[package]] +name = "libdbus-sys" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "328c4789d42200f1eeec05bd86c9c13c7f091d2ba9a6ea35acdf51f31bc0f043" +dependencies = [ + "pkg-config", +] + +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + [[package]] name = "libm" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" +[[package]] +name = "libredox" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61ff90caf6077a803a240f62fdbe88645a890bbca49ef8174c3cb0404362171d" +dependencies = [ + "libc", +] + [[package]] name = "linux-raw-sys" version = "0.12.1" @@ -1757,6 +2519,17 @@ version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" +[[package]] +name = "markup5ever" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7122d987ec5f704ee56f6e5b41a7d93722e9aae27ae07cafa4036c4d3f9757de" +dependencies = [ + "log", + "tendril", + "web_atoms", +] + [[package]] name = "matchers" version = "0.2.0" @@ -1813,17 +2586,36 @@ dependencies = [ ] [[package]] -name = "mime" -version = "0.3.17" +name = "memoffset" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - -[[package]] +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] name = "minimal-lexical" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + [[package]] name = "miniz_oxide" version = "0.9.1" @@ -1846,6 +2638,26 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "muda" +version = "0.21.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4340e89c178916e62879c5c36b7f699019369be413e496178a6983ce91b8bc03" +dependencies = [ + "crossbeam-channel", + "dpi", + "gtk", + "keyboard-types", + "libc", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", + "png", + "thiserror 2.0.20", + "windows-sys 0.61.2", +] + [[package]] name = "multer" version = "3.1.0" @@ -1907,6 +2719,42 @@ dependencies = [ "tempfile", ] +[[package]] +name = "ndk" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" +dependencies = [ + "bitflags 2.13.2", + "jni-sys 0.3.1", + "log", + "ndk-sys", + "num_enum", + "raw-window-handle", + "thiserror 1.0.69", +] + +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + +[[package]] +name = "ndk-sys" +version = "0.6.0+11769913" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" +dependencies = [ + "jni-sys 0.3.1", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + [[package]] name = "nom" version = "7.1.3" @@ -1987,6 +2835,222 @@ dependencies = [ "libm", ] +[[package]] +name = "num_enum" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26" +dependencies = [ + "num_enum_derive", + "rustversion", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8" +dependencies = [ + "proc-macro-crate 3.5.0", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "objc2" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08849bbd4767dfae9457696856ae1c84fe4e0281bbe4a7abff2d0e06fb7981f8" +dependencies = [ + "objc2-encode", + "objc2-exception-helper", +] + +[[package]] +name = "objc2-app-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" +dependencies = [ + "bitflags 2.13.2", + "objc2", + "objc2-core-foundation", + "objc2-foundation", +] + +[[package]] +name = "objc2-cloud-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ad74d880bb43877038da939b7427bba67e9dd42004a18b809ba7d87cee241c" +dependencies = [ + "bitflags 2.13.2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-data" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b402a653efbb5e82ce4df10683b6b28027616a2715e90009947d50b8dd298fa" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags 2.13.2", + "dispatch2", + "objc2", +] + +[[package]] +name = "objc2-core-graphics" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" +dependencies = [ + "bitflags 2.13.2", + "dispatch2", + "objc2", + "objc2-core-foundation", + "objc2-io-surface", +] + +[[package]] +name = "objc2-core-image" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d563b38d2b97209f8e861173de434bd0214cf020e3423a52624cd1d989f006" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-location" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca347214e24bc973fc025fd0d36ebb179ff30536ed1f80252706db19ee452009" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-text" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d" +dependencies = [ + "bitflags 2.13.2", + "objc2", + "objc2-core-foundation", + "objc2-core-graphics", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-exception-helper" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7a1c5fbb72d7735b076bb47b578523aedc40f3c439bea6dfd595c089d79d98a" +dependencies = [ + "cc", +] + +[[package]] +name = "objc2-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" +dependencies = [ + "bitflags 2.13.2", + "block2", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-io-surface" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" +dependencies = [ + "bitflags 2.13.2", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" +dependencies = [ + "bitflags 2.13.2", + "objc2", + "objc2-core-foundation", + "objc2-foundation", +] + +[[package]] +name = "objc2-ui-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" +dependencies = [ + "bitflags 2.13.2", + "block2", + "objc2", + "objc2-cloud-kit", + "objc2-core-data", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-core-image", + "objc2-core-location", + "objc2-core-text", + "objc2-foundation", + "objc2-quartz-core", + "objc2-user-notifications", +] + +[[package]] +name = "objc2-user-notifications" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9df9128cbbfef73cda168416ccf7f837b62737d748333bfe9ab71c245d76613e" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-web-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2e5aaab980c433cf470df9d7af96a7b46a9d892d521a2cbbb2f8a4c16751e7f" +dependencies = [ + "bitflags 2.13.2", + "block2", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", +] + [[package]] name = "oid-registry" version = "0.8.1" @@ -2014,9 +3078,9 @@ version = "0.10.81" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77823a27f0babb03091cb9ed9ef80af3b39dbc82f97e8fa530374b7dafd87a45" dependencies = [ - "bitflags", + "bitflags 2.13.2", "cfg-if", - "foreign-types", + "foreign-types 0.3.2", "libc", "openssl-macros", "openssl-sys", @@ -2051,6 +3115,37 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "pango" +version = "0.18.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4" +dependencies = [ + "gio", + "glib", + "libc", + "once_cell", + "pango-sys", +] + +[[package]] +name = "pango-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + [[package]] name = "parking" version = "2.2.1" @@ -2124,6 +3219,59 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +[[package]] +name = "phf" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" +dependencies = [ + "phf_macros", + "phf_shared", + "serde", +] + +[[package]] +name = "phf_codegen" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49aa7f9d80421bca176ca8dbfebe668cc7a2684708594ec9f3c0db0805d5d6e1" +dependencies = [ + "phf_generator", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" +dependencies = [ + "fastrand", + "phf_shared", +] + +[[package]] +name = "phf_macros" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "phf_shared" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" +dependencies = [ + "siphasher", +] + [[package]] name = "pin-project" version = "1.1.13" @@ -2246,6 +3394,19 @@ dependencies = [ "plist", ] +[[package]] +name = "png" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" +dependencies = [ + "bitflags 2.13.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide 0.8.9", +] + [[package]] name = "poly1305" version = "0.8.0" @@ -2292,6 +3453,65 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-crate" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" +dependencies = [ + "toml_datetime 0.6.3", + "toml_edit 0.20.2", +] + +[[package]] +name = "proc-macro-crate" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" +dependencies = [ + "toml_edit 0.25.17+spec-1.1.0", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + [[package]] name = "proc-macro2" version = "1.0.107" @@ -2407,6 +3627,12 @@ version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" +[[package]] +name = "raw-window-handle" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" + [[package]] name = "rayon" version = "1.12.0" @@ -2456,7 +3682,17 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags", + "bitflags 2.13.2", +] + +[[package]] +name = "redox_users" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60dc65c0ff1a7ae1294b0c67b9f14baf70b644404010370171787bfac1038fc0" +dependencies = [ + "libredox", + "thiserror 2.0.20", ] [[package]] @@ -2485,7 +3721,9 @@ dependencies = [ "base64 0.23.1", "bytes", "encoding_rs", + "futures-channel", "futures-core", + "futures-util", "h2", "http", "http-body", @@ -2528,7 +3766,7 @@ dependencies = [ "async-trait", "http", "reqwest", - "thiserror", + "thiserror 2.0.20", "tower-service", ] @@ -2640,7 +3878,7 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "891efababe418670775f199f0d233d84843c227a0949a883ce15b37c78d6629d" dependencies = [ - "bitflags", + "bitflags 2.13.2", "errno", "libc", "linux-raw-sys", @@ -2690,7 +3928,7 @@ checksum = "26d1e2536ce4f35f4846aa13bff16bd0ff40157cdb14cc056c7b14ba41233ba0" dependencies = [ "core-foundation 0.10.1", "core-foundation-sys", - "jni", + "jni 0.22.4", "log", "once_cell", "rustls", @@ -2778,7 +4016,7 @@ version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ - "bitflags", + "bitflags 2.13.2", "core-foundation 0.10.1", "core-foundation-sys", "libc", @@ -2795,6 +4033,25 @@ dependencies = [ "libc", ] +[[package]] +name = "selectors" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8adfa1c298912827b8a28b223b3b874357397ae706e6190acd9bf28cee99114d" +dependencies = [ + "bitflags 2.13.2", + "cssparser", + "derive_more", + "log", + "new_debug_unreachable", + "phf", + "phf_codegen", + "precomputed-hash", + "rustc-hash", + "servo_arc", + "smallvec", +] + [[package]] name = "semver" version = "1.0.28" @@ -2855,6 +4112,24 @@ dependencies = [ "serde_core", ] +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_spanned" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7523beb55eece201a2356bee0bbca0d1ab466c14c07703b2e0ee6d42cb0c2c" +dependencies = [ + "serde_core", +] + [[package]] name = "serdect" version = "0.4.3" @@ -2865,6 +4140,15 @@ dependencies = [ "serde", ] +[[package]] +name = "servo_arc" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "170fb83ab34de17dc69aa7c67482b22218ddb85da56546f9bd6b929e32a05930" +dependencies = [ + "stable_deref_trait", +] + [[package]] name = "sha1" version = "0.10.7" @@ -3015,6 +4299,32 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "soup3" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "471f924a40f31251afc77450e781cb26d55c0b650842efafc9c6cbd2f7cc4f9f" +dependencies = [ + "futures-channel", + "gio", + "glib", + "libc", + "soup3-sys", +] + +[[package]] +name = "soup3-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebe8950a680a12f24f15ebe1bf70db7af98ad242d9db43596ad3108aab86c27" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + [[package]] name = "spin" version = "0.9.9" @@ -3062,12 +4372,46 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" +[[package]] +name = "string_cache" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a18596f8c785a729f2819c0f6a7eae6ebeebdfffbfe4214ae6b087f690e31901" +dependencies = [ + "new_debug_unreachable", + "parking_lot", + "phf_shared", + "precomputed-hash", +] + +[[package]] +name = "string_cache_codegen" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585635e46db231059f76c5849798146164652513eb9e8ab2685939dd90f29b69" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", +] + [[package]] name = "subtle" version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.119" @@ -3127,7 +4471,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" dependencies = [ - "bitflags", + "bitflags 2.13.2", "core-foundation 0.9.4", "system-configuration-sys", ] @@ -3142,6 +4486,87 @@ dependencies = [ "libc", ] +[[package]] +name = "system-deps" +version = "6.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" +dependencies = [ + "cfg-expr", + "heck 0.5.0", + "pkg-config", + "toml 0.8.2", + "version-compare", +] + +[[package]] +name = "tao" +version = "0.37.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5777b06fe6435126ae8d816eba269e050e1e10d302cf5461e9c49eccabc3548" +dependencies = [ + "bitflags 2.13.2", + "block2", + "core-foundation 0.10.1", + "core-graphics", + "crossbeam-channel", + "dbus", + "dispatch2", + "dlopen2", + "dpi", + "gdkwayland-sys", + "gdkx11-sys", + "gtk", + "jni 0.21.1", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "objc2-ui-kit", + "parking_lot", + "percent-encoding", + "raw-window-handle", + "tao-macros", + "unicode-segmentation", + "url", + "windows", + "windows-core", + "windows-version", + "x11-dl", +] + +[[package]] +name = "tao-macros" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f7eeb6d99155545da6150a1795945f16ac9c178deb2a5f2e74d776107bd5849" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "target-lexicon" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" + +[[package]] +name = "tauri-winrt-notification" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f37a6c354fd28fc9e322ed9bd47e3959576dad28c9d58ea1cf888cce1c7ccb36" +dependencies = [ + "thiserror 2.0.20", + "windows", + "windows-version", +] + [[package]] name = "tempfile" version = "3.27.0" @@ -3155,13 +4580,42 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "tendril" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fed54709c5b3a53d09bb1c113ea4f5ceafd1e772ddcb0030a82e1d56c087b08" +dependencies = [ + "new_debug_unreachable", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + [[package]] name = "thiserror" version = "2.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f" dependencies = [ - "thiserror-impl", + "thiserror-impl 2.0.20", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", ] [[package]] @@ -3324,6 +4778,102 @@ dependencies = [ "tokio", ] +[[package]] +name = "toml" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d" +dependencies = [ + "serde", + "serde_spanned 0.6.9", + "toml_datetime 0.6.3", + "toml_edit 0.20.2", +] + +[[package]] +name = "toml" +version = "1.1.8+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20489e00e4d8741d6be680764cc12e270655e375a20d1011e844a9c3379e678d" +dependencies = [ + "indexmap", + "serde_core", + "serde_spanned 1.1.2", + "toml_datetime 1.1.2+spec-1.1.0", + "toml_parser", + "toml_writer", + "winnow 1.0.4", +] + +[[package]] +name = "toml_datetime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_datetime" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b86d767906c6c42421dcba507eb9d203e779497710a47782a224bb871653053" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap", + "toml_datetime 0.6.3", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" +dependencies = [ + "indexmap", + "serde", + "serde_spanned 0.6.9", + "toml_datetime 0.6.3", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.25.17+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3641d5bbb5349a79e1020a242d251efbc546ad8048d133958323ce9c40a9c9c" +dependencies = [ + "indexmap", + "toml_datetime 1.1.2+spec-1.1.0", + "toml_parser", + "winnow 1.0.4", +] + +[[package]] +name = "toml_parser" +version = "1.1.5+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baa693a8032d7e1cada7d0041e96126df243179ff061456783ac7f12bda4744c" +dependencies = [ + "winnow 1.0.4", +] + +[[package]] +name = "toml_writer" +version = "1.1.3+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06bdbd8cfc056b8d2e2e85f29b56a3bdbecb527cef81eb39e3e7b98af4652770" + [[package]] name = "tower" version = "0.5.3" @@ -3346,7 +4896,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" dependencies = [ "async-compression", - "bitflags", + "bitflags 2.13.2", "bytes", "futures-core", "futures-util", @@ -3435,6 +4985,27 @@ dependencies = [ "tracing-log", ] +[[package]] +name = "tray-icon" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f5cc5858ecf9574ddd8f0032a2ff7ffd6f7724d2b81e48b9374984b3fd6ddc9" +dependencies = [ + "crossbeam-channel", + "dirs", + "libappindicator", + "muda", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation", + "once_cell", + "png", + "thiserror 2.0.20", + "windows-sys 0.61.2", +] + [[package]] name = "triomphe" version = "0.1.16" @@ -3462,7 +5033,7 @@ dependencies = [ "rustls", "rustls-pki-types", "sha1 0.10.7", - "thiserror", + "thiserror 2.0.20", ] [[package]] @@ -3483,6 +5054,12 @@ version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d245f478577f809a851594d02313b640fb437e0bb33866753cff937863096954" +[[package]] +name = "unicode-segmentation" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" + [[package]] name = "universal-hash" version = "0.5.1" @@ -3558,6 +5135,12 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "version-compare" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c2856837ef78f57382f06b2b8563a2f512f7185d732608fd9176cb3b8edf0e" + [[package]] name = "version_check" version = "0.9.5" @@ -3686,6 +5269,62 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web_atoms" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba8b815c1b593dc0baf78dd0f4fc8fdb2de53198fb1163738093e9a311c33fb3" +dependencies = [ + "phf", + "phf_codegen", + "string_cache", + "string_cache_codegen", +] + +[[package]] +name = "webkit2gtk" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1027150013530fb2eaf806408df88461ae4815a45c541c8975e61d6f2fc4793" +dependencies = [ + "bitflags 1.3.2", + "cairo-rs", + "gdk", + "gdk-sys", + "gio", + "gio-sys", + "glib", + "glib-sys", + "gobject-sys", + "gtk", + "gtk-sys", + "javascriptcore-rs", + "libc", + "once_cell", + "soup3", + "webkit2gtk-sys", +] + +[[package]] +name = "webkit2gtk-sys" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "916a5f65c2ef0dfe12fff695960a2ec3d4565359fdbb2e9943c974e06c734ea5" +dependencies = [ + "bitflags 1.3.2", + "cairo-sys-rs", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "gtk-sys", + "javascriptcore-rs-sys", + "libc", + "pkg-config", + "soup3-sys", + "system-deps", +] + [[package]] name = "webpki-root-certs" version = "1.0.9" @@ -3713,6 +5352,56 @@ dependencies = [ "rustls-pki-types", ] +[[package]] +name = "webview2-com" +version = "0.39.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f89fca7a704cee10dcb3654c1dbb8941d1783132f1917358af75bec37a7d7e6" +dependencies = [ + "webview2-com-macros", + "webview2-com-sys", + "windows", + "windows-core", +] + +[[package]] +name = "webview2-com-macros" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a921c1b6914c367b2b823cd4cde6f96beec77d30a939c8199bb377cf9b9b54" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "webview2-com-sys" +version = "0.39.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3a07132775117d6065853d9d1178157b8c90e228de47129d6bce2c7edebedfb" +dependencies = [ + "thiserror 2.0.20", + "windows", + "windows-core", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + [[package]] name = "winapi-util" version = "0.1.11" @@ -3722,12 +5411,95 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +dependencies = [ + "windows-collections", + "windows-core", + "windows-future", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +dependencies = [ + "windows-core", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core", + "windows-link", + "windows-threading", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + [[package]] name = "windows-link" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" +[[package]] +name = "windows-numerics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +dependencies = [ + "windows-core", + "windows-link", +] + [[package]] name = "windows-registry" version = "0.6.1" @@ -3757,13 +5529,22 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets", + "windows-targets 0.52.6", ] [[package]] @@ -3775,34 +5556,85 @@ dependencies = [ "windows-link", ] +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + [[package]] name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-version" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4060a1da109b9d0326b7262c8e12c84df67cc0dbc9e33cf49e01ccc2eb63631" +dependencies = [ + "windows-link", ] +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -3815,30 +5647,92 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.56.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d6f32a0ff4a9f6f01231eb2059cc85479330739333e0e58cadf03b6af2cca10" +dependencies = [ + "cfg-if", + "windows-sys 0.61.2", +] + +[[package]] +name = "winresource" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2bc6d36480b829272a5bdabf08e6dca8004761c1da7e35efef800540c66221b" +dependencies = [ + "toml 1.1.8+spec-1.1.0", + "version_check", +] + [[package]] name = "wit-bindgen" version = "0.57.1" @@ -3851,6 +5745,70 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc" +[[package]] +name = "wry" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "759c50ddad0ef9913bb5496cb9d509a50433a3b78cb0238d61105526589cf0e9" +dependencies = [ + "base64 0.23.1", + "block2", + "cookie", + "crossbeam-channel", + "dirs", + "dom_query", + "dpi", + "dunce", + "gdkx11", + "gtk", + "http", + "javascriptcore-rs", + "jni 0.21.1", + "libc", + "ndk", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", + "objc2-ui-kit", + "objc2-web-kit", + "percent-encoding", + "raw-window-handle", + "sha2 0.10.9", + "soup3", + "tao-macros", + "thiserror 2.0.20", + "url", + "webkit2gtk", + "webkit2gtk-sys", + "webview2-com", + "windows", + "windows-core", + "windows-version", + "x11-dl", +] + +[[package]] +name = "x11" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "x11-dl" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" +dependencies = [ + "libc", + "once_cell", + "pkg-config", +] + [[package]] name = "x25519-dalek" version = "2.0.1" @@ -3902,7 +5860,7 @@ dependencies = [ "oid-registry", "ring", "rusticata-macros", - "thiserror", + "thiserror 2.0.20", "time", ] @@ -3912,7 +5870,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5f6765e852b9b4dc8e2a76843e4d64d1cea8e79bcde0b6901aea8e7c7f08282" dependencies = [ - "bit-vec", + "bit-vec 0.9.1", "time", ] diff --git a/Cargo.toml b/Cargo.toml index b817b89..76ae2df 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,13 +1,14 @@ [package] default-run = "altkeeper" name = "altkeeper" -version = "0.2.0" +version = "0.3.0" edition = "2021" description = "AltServer for iOS 17+ that runs on your home server (Docker): renews and installs sideloaded iPhone apps over Wi-Fi, no computer or VPN" license = "MIT" readme = "README.md" [dependencies] +chrono = { version = "0.4", default-features = false, features = ["clock"] } axum = { version = "0.8.9", default-features = false, features = ["http1", "tokio", "json", "multipart"] } idevice = { version = "0.1.68", default-features = false, features = [ "ring", @@ -33,8 +34,23 @@ rustls = { version = "0.23", default-features = false, features = ["ring", "std" serde = { version = "1", features = ["derive"] } serde_json = "1" tokio = { version = "1", features = ["full"] } +tempfile = "3.27" +zip = { version = "8.6", default-features = false, features = ["deflate"] } tracing-subscriber = { version = "0.3", features = ["env-filter"] } +[target.'cfg(windows)'.build-dependencies] +winresource = "0.1" + +[target.'cfg(windows)'.dependencies] +tao = "0.37" +sha2 = "0.10" +tauri-winrt-notification = "0.8" +reqwest = { version = "0.13", default-features = false, features = ["blocking", "json", "rustls-no-provider"] } +tray-icon = "0.26" +winreg = "0.56" +windows-sys = { version = "0.61", features = ["Win32_Foundation", "Win32_System_Console", "Win32_System_Threading", "Win32_UI_WindowsAndMessaging"] } +wry = "0.57" + # La copia in vendor/ è isideload 0.4.6 (upstream 8ca39b62) con due modifiche locali: # gli accessor di AnisetteData usati dal ponte AltStore e il logging ALTKEEPER_DEBUG / # le impostazioni di trasporto HTTP/1.1 portate dalla vecchia copia 0.3.17. diff --git a/Dockerfile b/Dockerfile index 9b5fd76..36fa38e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,7 +15,7 @@ RUN cargo build --release --locked FROM debian:bookworm-slim RUN apt-get update \ - && apt-get install -y --no-install-recommends ca-certificates tzdata \ + && apt-get install -y --no-install-recommends ca-certificates tzdata curl \ && rm -rf /var/lib/apt/lists/* COPY --from=build /src/target/release/altkeeper /usr/local/bin/altkeeper WORKDIR /data diff --git a/README.it.md b/README.it.md index 09210c4..503c5b8 100644 --- a/README.it.md +++ b/README.it.md @@ -106,6 +106,15 @@ i JSON o la pagina web. I comandi del menu 2FA restano `d`, `r` (se disponibile) ### 1. Ottieni il programma +**Già compilato:** ogni [release su GitHub](https://github.com/BartolomeoRusso9/altkeeper/releases) ha +`altkeeper--linux-x86_64.tar.gz`, `-linux-aarch64`, `-macos-aarch64` (Apple Silicon) e +`-macos-x86_64` (Intel), ciascuno con il suo file `.sha256` (verifica con `sha256sum -c`). Decomprimi e +usa il binario `altkeeper`. Le build Linux richiedono glibc 2.35 o più recente; su sistemi più vecchi +o Alpine usa Docker o compila da te. Le build macOS non sono firmate: se Gatekeeper le blocca, lancia +`xattr -d com.apple.quarantine altkeeper`. Si aggiorna da solo solo l'`.exe` di Windows. + +**Oppure compilalo tu:** + ```sh git clone https://github.com/BartolomeoRusso9/altkeeper cd altkeeper @@ -347,6 +356,25 @@ end-to-end. notifica d'errore al massimo una volta al giorno di `renew.sh`. Il PC deve essere acceso e non in sospensione a quell'ora. +5. **Icona nella barra di sistema (senza terminale).** Fai doppio clic su `altkeeper.exe`, oppure + lancia `.\altkeeper.exe tray`: avvia AltServer e la pagina web come `serve --altserver`, ma con + un'icona accanto all'orologio (il logo di AltKeeper; grigio finché l'iPhone non è raggiungibile) + e senza finestra di console. Doppio clic sull'icona, oppure dal menu: *Open AltKeeper* (la pagina + in una finestra propria; chiuderla la nasconde soltanto), *Show logs*, *Start with Windows* + (attivo di default alla prima esecuzione), *Check for updates* e *Quit*. Le notifiche toast + avvisano quando i profili sono rinnovati, un'app è installata o qualcosa fallisce. La finestra + richiede il runtime Microsoft Edge WebView2, già presente in Windows 11 e nei Windows 10 + recenti. `--console` tiene la console, `--dir` sceglie la cartella dei dati (di default quella + dell'`.exe`). + + L'icona controlla anche, una volta al giorno, le release GitHub; *Update now* scarica il + nuovo `altkeeper.exe`, ne verifica lo SHA-256, si riavvia e torna alla versione vecchia se la + nuova non parte. Le release (Windows, Linux e macOS) le compila `.github/workflows/release.yml` quando pubblichi un tag + uguale alla `version` di `Cargo.toml` (per esempio `v0.3.0`). + + Ogni riga del server (AltServer, pagina web, DNS-SD) viene scritta con data e ora anche in + `altkeeper.log` (ruotato a 5 MB) e mostrata nella sezione **Logs** della pagina web. + Tutto il resto di questa guida (comandi, variabili d'ambiente, uso da fuori casa) vale uguale; cambia solo che i percorsi usano `\`, e che `cron` e `renew.sh` si sostituiscono con l'Utilità di pianificazione e `renew.ps1`. @@ -518,6 +546,7 @@ quale nome sta chiedendo il telefono. | `serve [--bind ip:porta] [--pin PIN] [--dir cartella] [--phone ip:porta] [--altserver] [--altserver-port N]` | pagina web (e, con `--altserver`, AltServer) | | `profile-remove ` | toglie un profilo dal telefono, salvandone una copia in `profili-salvati/` | | `profile-restore ` | rimette un profilo salvato | +| `tray [--dir cartella] [--phone ip:porta] [--console]` | solo Windows: AltServer + pagina web con icona nella barra di sistema, senza terminale (è quello che parte con il doppio clic sull'`.exe`) | `ALTKEEPER_PHONE=ip:porta` può sostituire `--phone`; senza nessuno dei due il telefono si trova da solo (vedi il passo 2). `ALTKEEPER_NO_MDNS=1` spegne Bonjour (reti che bloccano il multicast, o per diff --git a/README.md b/README.md index 28eb3c8..2e677c8 100644 --- a/README.md +++ b/README.md @@ -103,6 +103,15 @@ and server logs, **not** the HTML web page, protocol or JSON. The same 2FA menu ### 1. Get the program +**Prebuilt:** each [GitHub release](https://github.com/BartolomeoRusso9/altkeeper/releases) has +`altkeeper--linux-x86_64.tar.gz`, `-linux-aarch64`, `-macos-aarch64` (Apple Silicon) and +`-macos-x86_64` (Intel), each with a `.sha256` file (check with `sha256sum -c`). Unpack it and use the +`altkeeper` binary inside. The Linux builds need glibc 2.35 or newer; on older systems or Alpine, use +Docker or build it yourself. The macOS builds are not signed: if Gatekeeper blocks it, run +`xattr -d com.apple.quarantine altkeeper`. Only the Windows `.exe` updates itself. + +**Or build it yourself:** + ```sh git clone https://github.com/BartolomeoRusso9/altkeeper cd altkeeper @@ -200,6 +209,79 @@ answers with errors, wait before trying again instead of repeating it. `renew` looks at each app's newest profile, renews the ones with `--min-days` days left or fewer (default 3), and signs in to Apple **only if something is due**. +**Installed-app safety filter (default for scheduled/CLI `renew` and web “Renew expiring apps”):** +Before checking expiry, AltKeeper queries the phone's installed user apps through installation_proxy. +Profiles that do not correspond to installed apps are skipped. **If that lookup fails (including +a timeout), AltKeeper explicitly falls back to renewing *all* known profiles for the configured +Apple team, ignoring the expiry threshold.** This fallback is logged as WARNING/FALLBACK. +A successful lookup returning zero apps skips all renewals. + +To renew a specific app, use `altkeeper renew --bundle --force` or the per-app +**Renew** button on the web page. The manual `--force` and web “Renew all now” operations +do not check installed apps; `--installed-only` can be used with CLI `--force` to opt in. +No unattended schedule is enabled by merely running the Docker container (default: OFF). + +**Optional built-in scheduler in the web UI:** Use **Automatic renewal** to enable/disable a +once-daily job and choose a time in **UTC**. The default clock +time is 06:00 UTC and the switch is initially OFF. When enabled, AltKeeper runs +the same installed-app-aware renewal as **Renew expiring apps**, with the documented +all-profile fallback if installed-app listing fails. The server needs a saved +Apple ID password, must be running, and the iPhone must be reachable. +Only one scheduled *attempt* is made per UTC calendar day (including failures). +A server that starts after the scheduled time on a day with no attempt will run +that day's job when it starts; enabling or changing the scheduled time after +today's slot does **not** trigger an immediate refresh. The next slot is tomorrow. +The schedule and last attempted day are stored in `state/auto-renew.json` under +the persistent `/data` volume, surviving Docker restarts. No host systemd, +cron, privileged host socket, or extra package is needed. **Do not enable +an external cron/systemd renewal job at the same time** unless deliberate. + + +The web page also lists **individual provisioning profiles** (including older versions) with a +**Delete profile** button. Every removal requires confirmation, is limited to the saved Apple team, +and saves then verifies a private `profili-salvati/.mobileprovision` backup before removal. +Removing a profile currently needed by an installed app can prevent that app from opening. +To restore a backup: `altkeeper profile-restore profili-salvati/.mobileprovision`. +Neither profile deletion nor renewal removes Apple Developer App IDs. + +**Grouped app management and retry controls:** The web UI groups extension profiles with +their host app when their bundle ID contains the saved team's extension marker +(e.g. `com.kdt.livecontainer.TEAM.LiveProcess` under `com.kdt.livecontainer`). +The expandable **Your apps** card renews *every* member profile in a group, rather +than only the selected host profile. Installed-app filtering also checks the +host app for an extension; the earliest-expiring member determines when the entire +group is due. The web preview lists groups that appear due, but the actual run +performs a fresh phone check and may fall back to all team profiles if the +installed-app inventory fails. + +The web scheduler's **renewal threshold** is adjustable from **1 to 6 days**, +default **3 days**. On automatic renewal failure it retries up to **two** more +times, **30 minutes apart** (up to 3 attempts in total). Authentication and +rate-limit failures are not automatically retried. Enabling Discord failure +notifications requires saving a webhook URL in the web UI. The secret is saved in +the private `state/discord-webhook` file and is never returned to the browser; +only a boolean indicates configuration. The Docker image includes `curl` to send +an alert only after the last failed attempt (or a non-retryable failure). +Successful automatic renewals do not send Discord messages. + +The web UI also lists previously saved profile backups and offers an explicit +**Restore** action after confirmation. Restoration verifies the saved profile +UUID and Apple Team ID and does not change its expiry date. The original file +is retained. The app summary shows the last known per-group profile-install +result from `state/app-renew-status.json`, and **Preview renewals** provides a +non-authoritative overview without signing in to Apple. + + +**Monthly backup cleanup:** When the web service (`serve`, including Docker `serve --altserver`) +is running, it checks once a day whether a cleanup is due. Every **30 days after the last +successful sweep** (including the first startup), it removes only saved `.mobileprovision` +files whose embedded expiry date is at least **30 days in the past**. A verified UUID/file name +match is required; malformed, unreadable, future-dated or unrelated files are preserved. +A cleanup log line reports the number removed. The last successful sweep is recorded at +`state/profile-backup-last-cleanup` so Docker restarts do not reset the schedule. +No iPhone communication, Apple login, cron job, or new systemd timer is required. +This cleanup does not touch live iPhone profiles or Apple Developer App IDs. + ### 6. Automatic renewal (optional) A safety net that does not depend on the iPhone running AltStore. @@ -260,6 +342,20 @@ The image holds nothing secret: pairing, account and state live in the `/data` v uses the host network, because it needs Bonjour to find the iPhone and to be found by AltStore; ports 8787 (web) and 49500 (AltServer) must be free. + +**Fast IPA upload over Wi-Fi (experimental):** After signing, AltKeeper repackages the +signed `.app` (including any custom name/Bundle ID and selected extensions) into one +temporary IPA, stages that file over AFC, then asks iOS to install it. This avoids +thousands of remote AFC operations for apps with many small files. The temporary +IPA is removed automatically; peak memory is bounded by streaming both packaging +and upload. USB installs keep the existing directory-based path. + +For an immediate rollback on Linux, set `ALTKEEPER_INSTALL_MODE=directory` in the +AltKeeper container's `environment:` in `docker-compose.yml`, then recreate +the container. This restores the previous Wi-Fi directory-transfer behavior +without re-signing existing apps. Test on a disposable app first; compatibility +with a real iPhone must be confirmed before considering it production-verified. + A GitHub Actions workflow (`.github/workflows/docker.yml`) runs the tests on Linux and Windows, checks `--help` in both CLI languages, and publishes the image to `ghcr.io//altkeeper` on pushes to `main` (amd64) and on `vX.Y.Z` tags (amd64 and arm64). If the repository is private, the @@ -340,6 +436,24 @@ prove Windows 11, other shell hosts, AltServer installation or unattended renewa `renew.ps1` keeps the same `renew.log`, the same optional `notify.conf` (ntfy) and the same once-a-day error notification as `renew.sh`. It needs the PC to be on and awake at that time. +5. **System tray icon (no terminal).** Double-click `altkeeper.exe`, or run + `.\altkeeper.exe tray`: it starts AltServer and the web page like `serve --altserver`, but + with an icon next to the clock (the AltKeeper logo; grey while the iPhone is not reachable) + and no console window. Double-click the icon, or use the menu: *Open AltKeeper* (the page in + its own window; closing it only hides it), *Show logs*, *Start with Windows* (on by default + the first time), *Check for updates* and *Quit*. Toast notifications tell you when profiles + are renewed, an app is installed or something fails. The window needs the Microsoft Edge + WebView2 runtime, which Windows 11 and recent Windows 10 already have. `--console` keeps the + console, `--dir` chooses the data folder (default: the folder of the `.exe`). + + The icon also checks the GitHub releases once a day; *Update now* downloads the new + `altkeeper.exe`, verifies its SHA-256, restarts, and goes back to the old version if the new + one does not start. Releases (Windows, Linux and macOS) are built by `.github/workflows/release.yml` when you push a + tag equal to the `version` in `Cargo.toml` (for example `v0.3.0`). + + Every server line (AltServer, web page, DNS-SD) is also written, with date and time, to + `altkeeper.log` (rotated at 5 MB) and shown in the **Logs** section of the web page. + Everything else in this guide (commands, environment variables, outside-the-home setup) is the same; only the paths use `\`, and `cron` and `renew.sh` are replaced by the Task Scheduler and `renew.ps1`. @@ -502,11 +616,12 @@ If it says it cannot find the server, watch the DNS with | `pair-usb [file]` | creates the remote pairing over USB (once) | | `login ` | signs in with 2FA and saves the account | | `apps` | lists your App IDs and their expiry dates | -| `renew [--dry-run] [--force] [--min-days N] [--phone ip:port]` | renews the profiles that are due | +| `renew [--dry-run] [--force] [--min-days N] [--phone ip:port] [--bundle ID] [--installed-only]` | renews profiles, optionally one bundle or with installed-app filtering | | `altserver [--port N] [--dir folder] [--phone ip:port] [--dns-domain D --dns-address IP]` | acts as AltServer for AltStore | | `serve [--bind ip:port] [--pin PIN] [--dir folder] [--phone ip:port] [--altserver] [--altserver-port N]` | web page (and, with `--altserver`, AltServer) | | `profile-remove ` | takes one profile off the phone, saving a copy in `profili-salvati/` | | `profile-restore ` | puts a saved profile back | +| `tray [--dir folder] [--phone ip:port] [--console]` | Windows only: AltServer + web page with a system tray icon, no terminal (the default when you double-click the `.exe`) | `ALTKEEPER_PHONE=ip:port` can replace `--phone`; without either the phone is found by itself (see step 2). `ALTKEEPER_NO_MDNS=1` turns Bonjour off (networks that block multicast, or to test the scan). diff --git a/assets/altkeeper.ico b/assets/altkeeper.ico new file mode 100644 index 0000000..3b5ae7a Binary files /dev/null and b/assets/altkeeper.ico differ diff --git a/assets/logo-32.rgba b/assets/logo-32.rgba new file mode 100644 index 0000000..ff809b7 Binary files /dev/null and b/assets/logo-32.rgba differ diff --git a/assets/logo-64.rgba b/assets/logo-64.rgba new file mode 100644 index 0000000..269aa91 Binary files /dev/null and b/assets/logo-64.rgba differ diff --git a/assets/logo-grey-32.rgba b/assets/logo-grey-32.rgba new file mode 100644 index 0000000..3bc8306 Binary files /dev/null and b/assets/logo-grey-32.rgba differ diff --git a/assets/logo.png b/assets/logo.png new file mode 100644 index 0000000..17e730a Binary files /dev/null and b/assets/logo.png differ diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..a6b6f9b --- /dev/null +++ b/build.rs @@ -0,0 +1,23 @@ +//! Su Windows incorpora il logo (assets/altkeeper.ico) nell'exe: è l'icona che si vede in +//! Esplora file, sul desktop e nella barra delle applicazioni. Sugli altri sistemi non fa niente. + +fn main() { + println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-changed=assets/altkeeper.ico"); + + #[cfg(windows)] + { + // Compilare da Windows verso un altro sistema non richiede nessuna risorsa. + if std::env::var("CARGO_CFG_TARGET_OS").as_deref() != Ok("windows") { + return; + } + let mut res = winresource::WindowsResource::new(); + res.set_icon("assets/altkeeper.ico"); + res.set("ProductName", "AltKeeper"); + res.set("FileDescription", "AltKeeper"); + if let Err(e) = res.compile() { + // L'icona è solo estetica: se manca il compilatore di risorse (rc.exe) si va avanti. + println!("cargo:warning=icon not embedded in altkeeper.exe: {e}"); + } + } +} diff --git a/docs/KOREAN_UI_REVIEW.md b/docs/KOREAN_UI_REVIEW.md new file mode 100644 index 0000000..e4e0905 --- /dev/null +++ b/docs/KOREAN_UI_REVIEW.md @@ -0,0 +1,107 @@ +# AltKeeper 한국어 웹 UI 번역 — 제안 + +> 상태: **미적용 번역 제안** — 아래 한국어 문구는 검토를 위한 제안이며, 현재 웹 UI는 영어로 표시됩니다. + +## 번역 원칙 + +- UI의 **AltKeeper**, **Apple ID**, **Discord**, **IPA**, **UUID**, **UTC** 등 식별자와 제품명은 번역하지 않음. +- `Renew`는 '갱신', `Install`은 '설치', `Provisioning profile`은 '프로비저닝 프로파일'로 통일. +- 이 앱 목록은 휴대전화의 프로파일을 기반으로 하므로, 설치 앱 조회에 실패했을 때도 표시할 수 있음. 따라서 '설치된 앱' 대신 **앱 목록** 사용. +- 실패 로그·Apple 원문 오류는 정확한 문제 진단을 위해 번역보다는 원문 유지 + 짧은 한국어 설명 병기를 권장. +- 번역 적용 시 서버 API 및 영어 진단 로그 내용은 보존합니다. + +## 주요 UI 문구 + +| 영어 원문 | 제안 한국어 | +|---|---| +| Keeps your iPhone apps alive by renewing their profiles. | 아이폰 앱의 프로파일을 갱신해 사용 기간을 유지합니다. | +| iPhone | 아이폰 | +| Checking… | 확인 중… | +| iPhone reachable | 아이폰 연결됨 | +| iPhone unreachable | 아이폰 연결 안 됨 | +| Not checked yet | 아직 확인하지 않음 | +| Check again | 다시 확인 | +| Your apps | 앱 목록 | +| Renew app | 이 앱 갱신 | +| Details | 상세 정보 | +| Less | 접기 | +| Renew expiring apps | 만료 임박 앱 갱신 | +| Renew all now | 전체 갱신 | +| Preview renewals | 갱신 대상 미리보기 | +| Automatic renewal | 자동 갱신 | +| OFF | 꺼짐 | +| ON | 켜짐 | +| Automatic renewal disabled | 자동 갱신이 꺼져 있습니다 | +| Daily time (UTC) | 매일 실행 시간 (UTC) | +| Renew when days remaining | 만료까지 남은 기간이 | +| 1 day / 2 days / … / 6 days | 1일 / 2일 / … / 6일 | +| Enable auto-renew | 자동 갱신 켜기 | +| Save settings | 설정 저장 | +| Turn off | 끄기 | +| Discord failure notifications | Discord 실패 알림 | +| Not configured | 설정 안 됨 | +| Configured | 설정됨 | +| Save webhook | 웹훅 저장 | +| Clear webhook | 웹훅 삭제 | +| Send test notification | 테스트 알림 보내기 | +| Test notification sent. Check your Discord channel. | 테스트 알림을 보냈습니다. Discord 채널을 확인하세요. | +| Configure a Discord webhook first. | 먼저 Discord 웹훅을 설정하세요. | +| Provisioning profiles on iPhone | 아이폰의 프로비저닝 프로파일 | +| Delete profile | 프로파일 삭제 | +| Show | 펼치기 | +| Hide | 접기 | +| Saved profile backups | 저장된 프로파일 백업 | +| Restore | 복원 | +| Install an app from this PC | 이 PC에서 IPA 설치 | +| IPA file | IPA 파일 | +| Sign and install | 서명 후 설치 | +| Uploading… | 업로드 중… | +| Apple ID (email) | Apple ID (이메일) | +| Password | 비밀번호 | +| Show password | 비밀번호 표시 | +| Change account | 계정 변경 | +| Sign in | 로그인 | +| Code | 인증 코드 | +| Submit code | 코드 확인 | +| Resend code | 코드 다시 보내기 | +| Recent renewals | 최근 갱신 기록 | +| For use on your home network only. | 가정 내 네트워크에서만 사용하세요. | + +## 설명 문구 (의미 보존 필요) + +| 위치 | 제안 한국어 | +|---|---| +| 자동 갱신 안내 | 서버가 실행 중일 때 하루 한 번 만료가 임박한 앱을 갱신합니다. 설치된 앱을 조회하지 못하면 저장된 Apple Team의 모든 프로파일을 갱신 대상으로 사용합니다. 기본값은 꺼짐입니다. | +| 자동 갱신 재시도 | 예약된 갱신에 실패하면 30분 간격으로 최대 2회 재시도합니다. 모든 시도가 실패했거나 재시도할 수 없는 인증 오류일 때만 Discord 알림을 보냅니다. | +| Discord 알림 안내 | 자동 갱신이 최종 실패했을 때만 알림을 보냅니다. | +| 프로파일 삭제 안내 | 갱신 후에도 이전 프로파일이 남을 수 있습니다. 사용 중인 프로파일을 삭제하면 앱이 실행되지 않을 수 있습니다. 삭제 전에 서버에 검증된 백업을 저장합니다. | +| 프로파일 복원 안내 | UUID로 백업 프로파일을 복원합니다. 복원해도 원래 만료일은 연장되지 않으며, 백업 파일은 유지됩니다. | +| IPA 설치 안내 | IPA를 선택하면 저장된 Apple ID로 서명한 뒤 Wi-Fi를 통해 아이폰에 설치합니다. Apple ID 비밀번호를 서버에 저장해야 합니다. 설치하는 동안 아이폰 화면을 잠금 해제한 상태로 유지하는 것을 권장합니다. | +| Apple ID 저장 안내 | 자동 갱신을 위해 서버에 비밀번호를 저장합니다. 비밀번호는 접근이 제한된 서버 파일에 평문으로 저장됩니다. | +| Apple 로그인 주의 | 한 번에 한 번만 로그인하세요. 반복된 실패는 Apple의 일시적인 로그인 제한을 유발할 수 있습니다. | +| 최근 갱신 기록 없음 | 아직 기록이 없습니다. 자동 갱신을 설정하면 매일 지정된 시간에 실행됩니다. | +| 페어링 누락 | 아이폰 페어링 정보가 없습니다. USB 연결로 페어링한 뒤 README 안내를 따르세요. | + +## 동적 상태 메시지 제안 + +| 영어 문구 | 한국어 제안 | +|---|---| +| expires in 6 days | 6일 후 만료 | +| expires today | 오늘 만료 | +| expired 1 day ago | 1일 전 만료 | +| 2 profiles | 프로파일 2개 | +| Renewing… | 갱신 중… | +| Another operation is in progress. | 다른 작업이 진행 중입니다. | +| The iPhone must be reachable on Wi-Fi. | 아이폰이 Wi-Fi로 연결되어 있어야 합니다. | +| Nothing to renew | 갱신할 항목이 없습니다 | +| Installation completed. | 설치가 완료되었습니다. | +| Error: Installation failed: … | 설치 실패: … (원본 오류 보존) | + +## 검수 결과 및 구현 참고 + +1. '프로비저닝 프로파일'은 기술적으로 정확하지만 길어 UI에선 '프로파일'로 줄일지 여부. +2. 한국어 기본 표시 vs 브라우저 언어 자동 감지 vs 수동 선택(영어로 다시 바꿀 수 있도록). +3. '앱 목록'의 앱 표시는 프로파일 기반이며, 실제 설치 여부를 항상 확정하지 않는다는 설명을 보일지. +4. 일본어/영어로 출력되는 기기 진단 로그는 **원문 보존**할지, 한국어 요약을 함께 제공할지. + +**정적·동적 웹 문구의 한국어 번역은 아직 적용되지 않았습니다.** 원본 진단 로그와 서버 오류 전문은 정확한 디버깅을 위해 보존할 것을 제안합니다. 현재 같은 버튼 영역의 버튼은 같은 너비로 배치되고 문구는 가운데 정렬됩니다. 좁은 화면에서도 버튼은 줄바꿈되지 않고 한 줄에 유지됩니다. diff --git a/scripts/check-web-ui.cjs b/scripts/check-web-ui.cjs new file mode 100644 index 0000000..653eca0 --- /dev/null +++ b/scripts/check-web-ui.cjs @@ -0,0 +1,210 @@ +"use strict"; +const assert = require("node:assert/strict"); +const fs = require("node:fs"); +const vm = require("node:vm"); +const path = require("node:path"); + +// This dashboard is shipped as include_str!(\"web/index.html\"), not a separate JS file. +// Compile its exact embedded script to catch JavaScript regressions that cargo test misses. +const html = fs.readFileSync(path.join(__dirname, "../src/web/index.html"), "utf8"); +const embedded = html.match(/ + diff --git a/tests/localized_identity.rs b/tests/localized_identity.rs new file mode 100644 index 0000000..6450a35 --- /dev/null +++ b/tests/localized_identity.rs @@ -0,0 +1,41 @@ +#![cfg(unix)] + +use isideload::sideload::{application::Application, bundle::Bundle}; +use std::{fs, os::unix::fs::symlink}; + +#[test] +fn display_name_rewrite_skips_symlinks_and_non_regular_files() { + let temp = tempfile::tempdir().unwrap(); + let app = temp.path().join("Test.app"); + fs::create_dir(&app).unwrap(); + let mut info = plist::Dictionary::new(); + info.insert("CFBundleIdentifier".into(), "com.example.test".into()); + info.insert("CFBundleName".into(), "Old".into()); + plist::to_file_xml(app.join("Info.plist"), &info).unwrap(); + let old = b"\"CFBundleDisplayName\" = \"Old\";\n"; + + let localized = app.join("en.lproj"); + fs::create_dir(&localized).unwrap(); + fs::write(localized.join("InfoPlist.strings"), old).unwrap(); + let external_dir = temp.path().join("external"); + fs::create_dir(&external_dir).unwrap(); + fs::write(external_dir.join("InfoPlist.strings"), old).unwrap(); + symlink(&external_dir, app.join("linked.lproj")).unwrap(); + let linked_file_dir = app.join("fr.lproj"); + fs::create_dir(&linked_file_dir).unwrap(); + symlink(external_dir.join("InfoPlist.strings"), linked_file_dir.join("InfoPlist.strings")).unwrap(); + fs::write(app.join("file.lproj"), b"not a directory").unwrap(); + fs::create_dir_all(app.join("directory.lproj/InfoPlist.strings")).unwrap(); + fs::create_dir(app.join("unrelated")).unwrap(); + fs::write(app.join("unrelated/InfoPlist.strings"), old).unwrap(); + + let mut application = Application { bundle: Bundle::new(app.clone()).unwrap() }; + application.apply_install_identity(Some("New"), None).unwrap(); + + assert!(fs::read_to_string(localized.join("InfoPlist.strings")).unwrap().contains("\"New\"")); + assert_eq!(fs::read(external_dir.join("InfoPlist.strings")).unwrap(), old); + assert_eq!(fs::read(app.join("unrelated/InfoPlist.strings")).unwrap(), old); + assert!(fs::symlink_metadata(app.join("linked.lproj")).unwrap().is_symlink()); + assert!(fs::symlink_metadata(linked_file_dir.join("InfoPlist.strings")).unwrap().is_symlink()); + assert!(app.join("directory.lproj/InfoPlist.strings").is_dir()); +} diff --git a/vendor/isideload/src/sideload/application.rs b/vendor/isideload/src/sideload/application.rs index 49e1c58..9696b92 100644 --- a/vendor/isideload/src/sideload/application.rs +++ b/vendor/isideload/src/sideload/application.rs @@ -16,8 +16,9 @@ use rootcause::option_ext::OptionExt; use rootcause::prelude::*; use sha1::Digest; use sha2::Sha256; -use std::io::Write; -use std::path::PathBuf; +use std::collections::HashSet; +use std::io::{Read, Write}; +use std::path::{Path, PathBuf}; use tracing::{info, warn}; use zip::ZipArchive; @@ -28,6 +29,166 @@ pub struct Application { //pub temp_path: PathBuf, } +/// Installation metadata, read from IPA Info.plist files without unpacking the archive. +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct IpaExtensionInfo { + pub bundle_id: String, + pub name: String, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct IpaManifest { + pub bundle_id: String, + pub name: String, + pub extensions: Vec, +} + +fn display_name(info: &plist::Dictionary, fallback: &str) -> String { + info.get("CFBundleDisplayName") + .or_else(|| info.get("CFBundleName")) + .and_then(|v| v.as_string()) + .unwrap_or(fallback) + .to_string() +} + +/// Read the main bundle identifier and extension manifests. This is read-only: +/// neither signing nor Apple registration happens until the user confirms. +pub fn inspect_ipa(path: &Path) -> Result { + let file = File::open(path).context("Could not open IPA for inspection")?; + let mut archive = ZipArchive::new(file).context("IPA is not a valid ZIP archive")?; + let mut main = None; + let mut extensions = Vec::new(); + let mut ids = HashSet::new(); + for i in 0..archive.len() { + let mut entry = archive.by_index(i)?; + let entry_name = entry.name().to_owned(); + let parts: Vec<_> = entry_name.split('/').collect(); + let is_main = parts.len() == 3 && parts[0] == "Payload" + && parts[1].ends_with(".app") && parts[2] == "Info.plist"; + let is_extension = parts.len() == 5 && parts[0] == "Payload" + && parts[1].ends_with(".app") && parts[2] == "PlugIns" + && parts[3].ends_with(".appex") && parts[4] == "Info.plist"; + if !is_main && !is_extension { continue; } + if entry.size() > 512 * 1024 { + bail!(SideloadError::InvalidBundle("Info.plist exceeds 512 KiB".into())); + } + let mut bytes = Vec::new(); + entry.read_to_end(&mut bytes)?; + let info: plist::Dictionary = plist::from_bytes(&bytes) + .context("Cannot parse Info.plist")?; + let bundle_id = info.get("CFBundleIdentifier") + .and_then(|v| v.as_string()).ok_or_report() + .context("Missing CFBundleIdentifier")?.to_string(); + if !ids.insert(bundle_id.clone()) { + bail!(SideloadError::InvalidBundle("Duplicate bundle identifier".into())); + } + let name = display_name(&info, if is_main { + parts[1].trim_end_matches(".app") + } else { + parts[3].trim_end_matches(".appex") + }); + if is_main { + if main.replace((bundle_id, name)).is_some() { + bail!(SideloadError::InvalidBundle("Multiple apps in IPA".into())); + } + } else { + extensions.push(IpaExtensionInfo { bundle_id, name }); + } + } + let (bundle_id, name) = main.ok_or_report() + .context("IPA is missing Payload/*.app/Info.plist")?; + extensions.sort_by(|a, b| a.bundle_id.cmp(&b.bundle_id)); + Ok(IpaManifest { bundle_id, name, extensions }) +} + +/// Replace quoted display-name values in OpenStep InfoPlist.strings without +/// disturbing other localized strings. Binary plist is handled separately. +fn replace_localized_display_name(text: &str, display_name: &str) -> String { + let quoted_name = display_name.replace('\\', "\\\\").replace('"', "\\\""); + let mut value = text.to_string(); + for key in ["CFBundleDisplayName", "CFBundleName"] { + for prefix in [format!("\"{key}\""), key.to_string()] { + let mut cursor = 0; + while let Some(offset) = value[cursor..].find(&prefix) { + let end_key = cursor + offset + prefix.len(); + let bytes = value.as_bytes(); + let mut p = end_key; + while p < bytes.len() && bytes[p].is_ascii_whitespace() { p += 1; } + if bytes.get(p) != Some(&b'=') { cursor = end_key; continue; } + p += 1; + while p < bytes.len() && bytes[p].is_ascii_whitespace() { p += 1; } + if bytes.get(p) != Some(&b'"') { cursor = end_key; continue; } + let first = p + 1; + let mut last = first; + let mut escaped = false; + while last < bytes.len() { + let current = bytes[last]; + if current == b'"' && !escaped { break; } + if current == b'\\' && !escaped { escaped = true; } + else { escaped = false; } + last += 1; + } + if last == bytes.len() { cursor = end_key; continue; } + value.replace_range(first..last, "ed_name); + cursor = first + quoted_name.len() + 1; + } + } + } + value +} + +fn localized_name_bytes(original: &[u8], name: &str) -> Result, Report> { + if let Ok(mut dict) = plist::from_bytes::(original) { + for key in ["CFBundleDisplayName", "CFBundleName"] { + if dict.contains_key(key) { + dict.insert(key.into(), plist::Value::String(name.into())); + } + } + let mut result = Vec::new(); + plist::to_writer_binary(&mut result, &dict)?; + return Ok(result); + } + let le = original.starts_with(&[0xff, 0xfe]); + let be = original.starts_with(&[0xfe, 0xff]); + let text = if le || be { + let mut chunks = original[2..].chunks_exact(2); + let words: Vec = chunks.by_ref().map(|c| if le { + u16::from_le_bytes([c[0], c[1]]) + } else { + u16::from_be_bytes([c[0], c[1]]) + }).collect(); + if !chunks.remainder().is_empty() { + bail!(SideloadError::InvalidBundle("Invalid UTF-16 localization file".into())); + } + String::from_utf16(&words)? + } else { + String::from_utf8(original.to_vec())? + }; + let updated = replace_localized_display_name(&text, name); + if updated == text { return Ok(original.to_vec()); } + if le || be { + let mut out = if le { vec![0xff, 0xfe] } else { vec![0xfe, 0xff] }; + for c in updated.encode_utf16() { + let encoded = if le { c.to_le_bytes() } else { c.to_be_bytes() }; + out.extend_from_slice(&encoded); + } + Ok(out) + } else { + Ok(updated.into_bytes()) + } +} + +/// Accept a conservative reverse-DNS identifier, without the signer-added team suffix. +pub fn valid_custom_bundle_id(id: &str) -> bool { + if id.len() > 160 || id.len() < 3 || !id.contains('.') { return false; } + id.split('.').all(|part| { + !part.is_empty() && part.len() <= 63 + && part.as_bytes()[0].is_ascii_alphanumeric() + && part.bytes().all(|b| b.is_ascii_alphanumeric() || b == b'-') + }) +} + + impl Application { pub fn new(path: PathBuf) -> Result { if !isideload_vfs::fs::metadata(&path).is_ok() { @@ -108,6 +269,79 @@ impl Application { }) } + /// Exclude unchecked .appex bundles before registering App IDs or signing. + /// This mutates only the extracted temporary copy, never the original IPA. + pub fn retain_extensions(&mut self, keep_ids: &[String]) -> Result<(), Report> { + let keep: HashSet<&str> = keep_ids.iter().map(String::as_str).collect(); + let known: HashSet<&str> = self.bundle.app_extensions().iter() + .filter_map(|e| e.bundle_identifier()).collect(); + for id in &keep { + if !known.contains(id) { + bail!(SideloadError::InvalidBundle( + format!("Selected extension is not present in the IPA: {id}"), + )); + } + } + let unwanted: Vec = self.bundle.app_extensions().iter() + .filter(|e| !e.bundle_identifier().is_some_and(|id| keep.contains(id))) + .map(|e| e.bundle_dir.clone()).collect(); + for path in &unwanted { + isideload_vfs::fs::remove_dir_all(path) + .context("Could not remove an excluded extension")?; + } + // Re-scan the bundle so removed extensions' nested frameworks and + // dylibs are not left in the signing tree. + self.bundle = Bundle::new(self.bundle.bundle_dir.clone())?; + Ok(()) + } + + /// Apply requested new-install identity before the existing team-ID signing rewrite. + /// The original IPA is untouched; this only edits the extracted temp bundle. + pub fn apply_install_identity( + &mut self, + display_name: Option<&str>, + custom_bundle_id: Option<&str>, + ) -> Result<(), Report> { + if let Some(custom_id) = custom_bundle_id { + if !valid_custom_bundle_id(custom_id) { + bail!(SideloadError::InvalidBundle("Invalid custom Bundle ID".into())); + } + let original = self.main_bundle_id()?; + if original != custom_id { + self.update_bundle_id(&original, custom_id)?; + for ext in self.bundle.app_extensions_mut() { + ext.write_info()?; + } + } + } + if let Some(name) = display_name { + if name.is_empty() || name.chars().count() > 40 || name.chars().any(char::is_control) { + bail!(SideloadError::InvalidBundle("Invalid custom display name".into())); + } + self.bundle.app_info.insert("CFBundleDisplayName".into(), plist::Value::String(name.into())); + self.bundle.app_info.insert("CFBundleName".into(), plist::Value::String(name.into())); + // iOS prefers localized InfoPlist.strings names over CFBundleDisplayName. + // Update the temporary IPA extraction only, not any installed app. + for entry in isideload_vfs::fs::read_dir(&self.bundle.bundle_dir)? { + let entry = entry?; + let localized = entry.path(); + if localized.extension().is_none_or(|ext| ext != "lproj") { continue; } + if !entry.metadata().is_ok_and(|meta| meta.is_dir()) { continue; } + let path = localized.join("InfoPlist.strings"); + if !isideload_vfs::fs::symlink_metadata(&path).is_ok_and(|meta| meta.is_file()) { continue; } + let old = isideload_vfs::fs::read(&path)?; + if old.len() > 1024 * 1024 { + bail!(SideloadError::InvalidBundle("Localized display-name file too large".into())); + } + let new = localized_name_bytes(&old, name)?; + if old != new { + isideload_vfs::fs::write(&path, &new)?; + } + } + } + Ok(()) + } + pub fn get_special_app(&self) -> Option { let bundle_id = self.bundle.bundle_identifier().unwrap_or(""); let special_app = match bundle_id { diff --git a/vendor/isideload/src/sideload/sideloader.rs b/vendor/isideload/src/sideload/sideloader.rs index e0b7934..f7789bd 100644 --- a/vendor/isideload/src/sideload/sideloader.rs +++ b/vendor/isideload/src/sideload/sideloader.rs @@ -75,6 +75,9 @@ impl Sideloader { progress_callback: Option, pairing_file: Option<&[u8]>, device_udid: Option<&str>, + selected_extensions: Option<&[String]>, + display_name: Option<&str>, + custom_bundle_id: Option<&str>, ) -> Result<(PathBuf, Option), Report> where F: Fn(f32) -> Fut, @@ -100,7 +103,15 @@ impl Sideloader { } let mut app = Application::new(app_path)?; + if let Some(selected) = selected_extensions { + app.retain_extensions(selected)?; + } + // Detect special app type before changing its identity. let special = app.get_special_app(); + if custom_bundle_id.is_some() && special.is_some() { + bail!("Custom Bundle IDs are disabled for special apps (AltStore, SideStore, LiveContainer)."); + } + app.apply_install_identity(display_name, custom_bundle_id)?; let main_bundle_id = app.main_bundle_id()?; let main_app_name = app.main_app_name()?; @@ -273,6 +284,9 @@ impl Sideloader { progress_callback, pairing_file, Some(&device_info.udid), + None, + None, + None, ) .await?;