* add configurable destroy_on_exit to single instance plugin
* add change file
* document destroy_on_exit
* move the destroy to rely on the Resource close
* add integration test
* fix android/ios
---------
Co-authored-by: Lucas Nogueira <lucas@tauri.app>
On Linux, `update-desktop-database` (from `desktop-file-utils`) may not
be installed, especially on KDE systems. Instead of failing with an IO
error, log a warning when the command is not found and continue.
Non-NotFound errors still propagate as before.
Fixes#2265
Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: Lucas Nogueira <lucas@tauri.app>
* fix(deep-link): filter out web links instead of app links in deep link domains
This fixes a bug where on ios, deeplinks just don't work because the final Info.plist file doesn't have them.
And also used a better id for CFBundleURLName
Fixes regression from 2804803949
* feat(deep-link): add `webcredentials:` for iOS entitlements file
* chore: added changelog for deep-link patch
* add tests
* remove webcredentials
* -js
---------
Co-authored-by: Lucas Nogueira <lucas@tauri.app>
* chore(deps): update rust crate zip to v9
* chore(updater): add change file for zip v9
---------
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Lucas Nogueira <lucas@tauri.app>
* chore(geolocation): update gradle
* feat(geolocation): add support for android devices without GMS
* fix(geolocation): watchPosition does not resolve on android
* feat(example): add watch support for geolocation
* chore: add changeset
* Update .changes/geolocation-non-gms.md
Co-authored-by: Fabian-Lars <30730186+FabianLars@users.noreply.github.com>
* fix(geolocation): only use Android Location API on API Level >= 31
* fix resolve
* fix(geolocation): use LocationManagerCompat for non-GMS devices
- support API 24+ instead of only API 31+
- pick the best enabled provider (fused, gps, network) instead of GPS only
- honor enableHighAccuracy in getCurrentPosition
- clear the previous listener before starting a new watch
- report an error when the provider is disabled during a watch
- share the provider/priority logic between both code paths
- drop the geolocation-js bump, no JS changes
* fix(examples): clear existing geolocation watch before starting a new one
* chore(geolocation): add change file for watchPosition resolve fix
* fix(geolocation): honor enableHighAccuracy in getCurrentPosition on Android 12+ without GMS
* fix(geolocation): fall back to the next provider when one returns no fix on Android without GMS
* fix(geolocation): honor timeout in getCurrentPosition on Android
* fix(geolocation): keep multiple watchPosition watches active on Android
* chore(geolocation): bump geolocation-js alongside geolocation in change files
* refactor(geolocation): report provider errors from the callers of androidProviders
* e2e tests
---------
Co-authored-by: Tony <68118705+Legend-Master@users.noreply.github.com>
Co-authored-by: Fabian-Lars <30730186+FabianLars@users.noreply.github.com>
Co-authored-by: Lucas Nogueira <lucas@tauri.app>
* feat(clipboard-manager): add support for writing secrets
add `write_secret` command that writes a text into the clipboard and
hints clipboard managers to not save the text.
uses `arboard`'s `SetExtLinux`, `SetExtWindows`, and `SetExtApple`
extensions for each platform.
* fix(clipboard-manager): require arboard 3.6
* chore(clipboard-manager): add license header to secret.rs
* fix(clipboard-manager): only exclude from monitoring on Windows
* refactor(clipboard-manager): replace ExcludeSecret trait with a function
* chore(clipboard-manager): drop redundant cfg(desktop) in desktop.rs
* refactor(clipboard-manager): share clipboard access in a helper
* feat(clipboard-manager): implement write_secret on Android and iOS
* docs(clipboard-manager): document writeSecret
* chore(clipboard-manager): add change file for writeSecret
* test(clipboard-manager): cover writeSecret in e2e
---------
Co-authored-by: Lucas Nogueira <lucas@tauri.app>
* fix(opener): avoid blocking-runtime panic in reveal_item_in_dir
* fix(opener): run zbus reveal on a dedicated thread for all callers
Move the runtime-panic fix into the Linux/BSD implementation so the
public Rust APIs are covered too, not just the JS command, and propagate
the spawn_blocking join error via the existing Tauri error variant.
---------
Co-authored-by: Lucas Nogueira <lucas@tauri.app>
* fix(http): decode response header values as ISO-8859-1
Response header values were decoded with String::from_utf8 before being
sent over IPC. Raw UTF-8 bytes produced JS strings with code points
above 255, which new Headers() rejects as invalid ByteStrings
("Type error" on WebKit), and bytes that were not valid UTF-8 failed
the fetch outright. Decode as ISO-8859-1 instead so every byte maps to a
char 0-255, matching what browsers expose for the same response.
Fixes#2898
* fix(http): add tests for ISO-8859-1 response header decoding
Cover the #2898 Content-Disposition case and non-UTF-8 header bytes on
the same conversion fetch_send uses, so a revert to UTF-8 fails the suite.
* request headers, e2e test
---------
Co-authored-by: Lucas Nogueira <lucas@tauri.app>
Updates quinn-proto from 0.11.14 to 0.11.19 to resolve recent security advisories (RUSTSEC-2026-0338, RUSTSEC-2026-0339, RUSTSEC-2026-0340, RUSTSEC-2026-0342).
* feat(sql): add autoCreate option to Database.load
Add utoCreate option to Database.load allowing users to disable implicit database creation when loading a database.
Closes#299
* fix(sql): fix unused variable without a database feature
* refactor(sql): share the database existence check between drivers
* fix(sql): don't report a failed existence check as a missing database
* fix(sql): name the database as passed, without password, in the missing database error
* fix(sql): don't create the app config dir when autoCreate is false
* refactor(sql): use the LoadOptions default for missing options
* test(sql): cover autoCreate and password redaction
---------
Co-authored-by: Lucas Nogueira <lucas@tauri.app>
* feat: adding the ability to encrypt/decrypt data based on the biometric auth
Adding the method biometricCipher() that allows to use the biometric authentication to use a generated key from android that can be accessed only through the biometric authentication. The method uses this key to encrypt/decrypt the data provided by the caller in the "options" parameter, with "dataToEncrypt" or "dataToDecrypt" filled.
Changes were done both on Kotlin, Rust and JS sides.
* Improving the way we encrypt data by allowing only if the biometric was successful.
* Adding a warning on the JS side
* Adding doc changes in readme.md
* Adding doc changes in readme.md
* Update README.md - it was updated incomplete
* update docs
* enhance error handling
* update example
---------
Co-authored-by: Lucas Nogueira <lucas@tauri.app>
On Android, cancel() called destroy() first, and destroy() sets
savedInvoke to null, so the savedInvoke?.reject("cancelled") that
follows was a no-op. The pending scan() promise was never settled and
callers awaiting it were stranded, which is especially bad in windowed
mode where the page background stays transparent.
Reject the pending invoke before tearing the camera down, matching the
order the iOS implementation already uses.
When initializing the plugin's runtime scope in .setup, pass
equire_literal_leading_dot from config to FsScope::Scope instead of using FsScope::default() so that paths allowed dynamically via s_scope().allow_directory() inherit the configured value.
Closes#3699
* experimenting...
Signed-off-by: BestCodes <106822363+The-Best-Codes@users.noreply.github.com>
* omit unconfirmed fact from .changes
Signed-off-by: BestCodes <106822363+The-Best-Codes@users.noreply.github.com>
* fix(notification): send untracked Linux notifications on the shared connection
Holding notify-rust's handle with `on_close` blocked an async runtime
worker until the server closed the notification, and opened a D-Bus
connection per notification. Since GNOME keeps notifications in its tray,
a handful of them could stall every async task of the app.
Send every notification on the connection the plugin already keeps open
for its action listener instead: it keeps the sender's name alive for
GNOME at the cost of a single connection and thread, and the listener
runs the `on_close` callbacks.
* fix(notification): offer `on_close` on the BSDs too
The BSDs share the D-Bus implementation with Linux, including the GNOME
issue, so gate `NotificationBuilder::on_close` like the rest of that
implementation and document when the callback runs.
* fix(notification): run `on_close` without the tracker lock held
The callback ran on the listener thread while the guard of the callback
map was alive, and a panic in it ended that thread, which then reported
no action of any notification for the rest of the app's life. Take the
callback out of the map before running it and contain its panics.
* fix(notification): use the covector package names in the change files
`tauri-plugin-notification` and `@tauri-apps/plugin-notification` are
not packages of .changes/config.json, so covector refused the change
file. Use `notification` and `notification-js`, and record the new
`on_close` API as a feature of its own.
Also let `on_close` take an `FnOnce`, since it runs at most once.
---------
Signed-off-by: BestCodes <106822363+The-Best-Codes@users.noreply.github.com>
Co-authored-by: Lucas Nogueira <lucas@tauri.app>
* Fix incorrect error being reported in certain cases with fallback allowed
* Add change file
* Rework original solution due to overlooking authenticators logic on API >= `Build.VERSION_CODES.R`
* fix(biometric): don't cast the Android error message to String
* fix(biometric): report a missing device credential before showing the Android prompt
Replaces the override of ERROR_NO_BIOMETRICS in the prompt callback, which keyed on
the caller's flag rather than the device state and missed the no-hardware errors.
* fix(biometric): load the missing device credential message from a string resource
* chore(biometric): drop stray semicolon
* docs(biometric): mention the passcodeNotSet error code rename in the change file
* test(e2e): cover the biometric passcodeNotSet rejection on Android
---------
Co-authored-by: Lucas Nogueira <lucas@tauri.app>
`convertToRustHeaders` replaced `options.headers` of the object passed to
`check`, `download` and `downloadAndInstall` with an array of entries. The
headers are now converted into the invoke payload only (`toRustHeaders`).
E2E: `check` leaves the options object untouched.
plugins/updater/guest-js/index.ts
- `tempdir_in(..)?` and `metadata()?` returned early on the first location
that failed (e.g. a non-writable `/tmp`), so the cache dir and the
directory next to the AppImage were never tried. Failures now move on to
the next location, as the deb/rpm path already did.
- The candidate list was duplicated between the AppImage and deb/rpm paths;
both now use `tmp_dir_locations`, which also no longer unwraps the parent
of the app path (`extract_path.parent().unwrap()`).
Compile-checked with clippy in a Linux container (default features).
plugins/updater/src/updater.rs
* fix(updater): derive the Windows installer launch error from ShellExecuteW
A failed `ShellExecuteW` was reported with `std::io::Error::last_os_error()`,
but `ShellExecuteW` reports failures through its return value (`SE_ERR_*`),
not reliably through `GetLastError`, so the message could be meaningless. The
return value is now mapped to an error (the codes shared with Win32 errors,
like `SE_ERR_ACCESSDENIED` when the UAC prompt is declined, keep their OS
message).
Running `on_before_exit` only once the installer launched changes when a
public hook runs and is left for v3.
Compile-checked only (clippy for x86_64-pc-windows-msvc).
plugins/updater/src/updater.rs
* use constants
Each `Update.download()` overwrote `this.downloadedBytes` without closing the
previous resource, so the whole previous update payload stayed in the webview
resources table. The previous resource is now closed once the new download
succeeded (so a failed retry still leaves the earlier download installable).
Not covered by e2e: the fixture manifest has no valid signature, so a
download can never succeed there.
plugins/updater/guest-js/index.ts
* fix(updater): warn when the update download URL is not https
`validate_endpoints` enforces `https` for the endpoints, but not for the `url`
of the manifest, so the artifact can be downloaded in plaintext. Enforcing it
would break existing releases (left for v3); `Update::download` now logs a
warning instead, unless `dangerousInsecureTransportProtocol` is enabled.
plugins/updater/src/updater.rs
* Apply suggestion from @lucasfernog
`#[cfg(mobile)] fn install_inner` returned `Ok(())` without doing anything.
`check` already fails with `UnsupportedOs` there unless a Rust caller sets a
custom target, in which case a download was followed by a successful
"install" that did nothing. It now returns `Error::UnsupportedOs`, the error
`check` uses.
Verified with clippy for aarch64-linux-android (the iOS build needs the Swift
package, which does not build here).
plugins/updater/src/updater.rs
* fix(updater): check the AppImage update payload before installing it
Without the `zip` feature the gz check was compiled out and a signed
`.tar.gz` was written verbatim over the AppImage, and any other payload
without the gz magic (e.g. a `.deb` from the same release, or the fallthrough
when `bundle_type()` is `None`) was written the same way, corrupting the app.
The payload is now checked before the current AppImage is moved: a gzip
archive requires the `zip` feature, and anything else must be an ELF
executable (which every AppImage is); otherwise `InvalidUpdaterFormat` is
returned. The `Cursor`/`OsStr` imports are gated so that the crate builds
warning-free without `zip` on Linux too.
Compile-checked with clippy on macOS, Linux (container) and Windows, each
with and without the `zip` feature.
plugins/updater/src/updater.rs
* tests
* fix(deep-link): honor XDG failures and unregister associations
* test(deep-link): isolate other application handlers
* chore: satisfy release metadata and test header checks
* fix(deep-link): gate XDG helpers to Linux
The plugin only supports Linux, so FreeBSD builds warned about dead code.
* refactor(deep-link): add run_command helper
Share the spawn, status check and logging for XDG commands.
* refactor(deep-link): share desktop file list helpers
register and unregister now edit MimeType lists the same way.
* fix(deep-link): keep desktop file quoting on register
Parse raw values so a quoted Exec matches and isn't rewritten each time.
* fix(deep-link): clean mimeapps.list first on unregister
A failing update-desktop-database no longer leaves the app as the default.
* fix(deep-link): keep mimeapps.list comments on unregister
Edit it line by line instead of re-serializing it through rust-ini.
* fix(deep-link): skip cache refresh when nothing to unregister
Only run update-desktop-database if the desktop file changed or the cache still lists the app.
* fix(deep-link): treat no default handler as unregistered
xdg-mime exits with 4 on KDE and LXQt when no handler is set.
* fix(deep-link): tolerate padded xdg-mime query output
Compare the trimmed first line instead of the raw output.
* test(deep-link): run XDG tests in CI
Drop #[ignore], skip when the tools are missing, and install them on Linux CI.
---------
Co-authored-by: Lucas Nogueira <lucas@tauri.app>
* fix(single-instance): propagate D-Bus setup and handoff errors
* chore: satisfy release metadata and test header checks
* fix(single-instance): launch normally without a D-Bus session bus
Log a warning instead of failing setup when the session bus is unreachable, and add context to handoff errors.
* ci: run the single-instance D-Bus test under dbus-run-session
Run it in test-rust on Linux and keep it out of the updater integration job, which has no session bus.
---------
Co-authored-by: Lucas Nogueira <lucas@tauri.app>
* fix(fs): allow open, seek and fstat in the write permission sets
The stream path of writeFile calls `open`, which only the nested command
list of `allow-write-file` granted (plugins/fs/permissions/write-all.toml,
write-files.toml), so `fs:allow-appdata-write-recursive` and the other
write sets rejected streamed writes; seek/fstat were missing for
FileHandles created under these sets.
Adds open, seek and fstat to write-all and write-files. This widens the
write sets slightly (open is already implied by allow-write-file); the
generated reference does not list set members, so nothing to regenerate.
* use dependencies instead
In plugins/fs/src/file_path.rs SafeFilePath::from_str, any string that
parsed as a URL with a scheme of 2+ characters was a URL, so a relative
path such as `notes:2024.txt` became a cannot-be-a-base URL and failed
with InvalidPathUrl.
Cannot-be-a-base URLs are now parsed as paths. The scheme-agnostic
url.to_file_path() stays: its result is scope checked (not a bypass).
Unit test for the parser; e2e spec writes and reads such a file (not on
Windows, where `:` is not allowed in file names).
* fix(updater): skip a backup location the AppImage cannot be renamed into
Two mounts of one file system share st_dev, so install_appimage could
pick a bind-mounted or private temp dir and then fail every install with
EXDEV. Move on to the next location instead; the last one is the
AppImage's own directory.
* Apply suggestion from @lucasfernog
---------
Co-authored-by: Lucas Fernandes Nogueira <lucas@tauri.app>
* fix(ios): make `init_plugin_*` entry points public so release builds link on Xcode 27
Xcode 27's SwiftPM (Swift Build) gives non-public @_cdecl functions local
visibility in release builds, so each plugin's init_plugin_* is undefined
when Rust links. Mark them public in the 11 plugins with an iOS part.
See tauri-apps/tauri#16130
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
* Apply suggestion from @lucasfernog
---------
Co-authored-by: takakix2 <1370181+takakix2@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: Lucas Fernandes Nogueira <lucas@tauri.app>