chore(deps,core,gui): upgrade dependencies to latest

- keyring 3 -> keyring-core 1 + dbus-secret-service-keyring-store 1
  (crypto-rust). Same libdbus Secret Service backend; with no target the
  store matches items keyring 3 wrote, so stored sessions survive.
- ksni 0.2 -> 0.3 via the blocking API, with assume_sni_available so a
  tray that starts before the SNI watcher waits instead of failing. Drops
  the clap 2 / atty chain (Dependabot atty advisory).
- gtk4 0.11, libadwaita 0.9, webkit6 0.6, rusqlite 0.40, fuser 0.18,
  directories 6, sha1/sha2 0.11, base64 0.23.
This commit is contained in:
nvrl committed 2026-09-15 15:07:00 +02:00
1 parent dcb2ab7fac
commit b2f62f70e1
8 files changed
+547 -802

No files matched your search

Generated
+511 -777
View File
File diff suppressed because it is too large. Load diff
+11 -7
View File
@@ -25,7 +25,7 @@ pdfs-fuse = { path = "crates/pdfs-fuse" }
tokio = { version = "1", features = ["rt-multi-thread", "macros", "sync", "fs", "io-util", "time", "signal"] } tokio = { version = "1", features = ["rt-multi-thread", "macros", "sync", "fs", "io-util", "time", "signal"] }
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }
serde_json = "1" serde_json = "1"
base64 = "0.22" base64 = "0.23"
thiserror = "2" thiserror = "2"
anyhow = "1" anyhow = "1"
# Non-poisoning mutexes. A handler that panics while holding a lock must not # Non-poisoning mutexes. A handler that panics while holding a lock must not
@@ -35,24 +35,28 @@ anyhow = "1"
parking_lot = "0.12" parking_lot = "0.12"
tracing = "0.1" tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] } tracing-subscriber = { version = "0.3", features = ["env-filter"] }
directories = "5" directories = "6"
ignore = "0.4" ignore = "0.4"
sha2 = "0.10" sha2 = "0.11"
# Google Takeout exports are zip sets; the importer reads their central # Google Takeout exports are zip sets; the importer reads their central
# directories and streams entries out without extracting to disk first. # directories and streams entries out without extracting to disk first.
zip = { version = "8", default-features = false, features = ["deflate"] } zip = { version = "8", default-features = false, features = ["deflate"] }
# Proton's photo duplicate detection is keyed on the plaintext SHA-1, so an # Proton's photo duplicate detection is keyed on the plaintext SHA-1, so an
# import has to speak SHA-1 whatever its age. # import has to speak SHA-1 whatever its age.
sha1 = "0.10" sha1 = "0.11"
# Thumbnail generation for photos the server has none for. Decoders only for the # Thumbnail generation for photos the server has none for. Decoders only for the
# formats a camera or phone actually produces — the rest of the crate's codecs # formats a camera or phone actually produces — the rest of the crate's codecs
# would be dead weight in the daemon. # would be dead weight in the daemon.
image = { version = "0.25", default-features = false, features = ["jpeg", "png", "webp", "gif", "tiff", "bmp", "rayon"] } image = { version = "0.25", default-features = false, features = ["jpeg", "png", "webp", "gif", "tiff", "bmp", "rayon"] }
rusqlite = { version = "0.32", features = ["bundled"] } rusqlite = { version = "0.40", features = ["bundled"] }
keyring = { version = "3", features = ["sync-secret-service", "crypto-rust"] } # keyring 4 split into `keyring-core` plus per-platform stores. The libdbus
# Secret Service store is the one keyring 3's `sync-secret-service` wrapped, and
# with no `target` it matches items v3 wrote, so existing logins survive.
keyring-core = "1"
dbus-secret-service-keyring-store = { version = "1", features = ["crypto-rust"] }
clap = { version = "4", features = ["derive"] } clap = { version = "4", features = ["derive"] }
rpassword = "7" rpassword = "7"
async-trait = "0.1" async-trait = "0.1"
futures = "0.3" futures = "0.3"
fuser = "0.17" fuser = "0.18"
libc = "0.2" libc = "0.2"
+2 -1
View File
@@ -23,7 +23,8 @@ sha2.workspace = true
sha1.workspace = true sha1.workspace = true
zip.workspace = true zip.workspace = true
rusqlite.workspace = true rusqlite.workspace = true
keyring.workspace = true keyring-core.workspace = true
dbus-secret-service-keyring-store.workspace = true
parking_lot.workspace = true parking_lot.workspace = true
async-trait.workspace = true async-trait.workspace = true
libc.workspace = true libc.workspace = true
+8 -3
View File
@@ -6,7 +6,7 @@
//! interactive step; the refresh token auto-renews via the HTTP client's 401 //! interactive step; the refresh token auto-renews via the HTTP client's 401
//! path, so no fresh 2FA is required until the refresh token itself expires. //! path, so no fresh 2FA is required until the refresh token itself expires.
use keyring::Entry; use keyring_core::Entry;
use proton_drive_rs::{KeySalt, ProtonDriveClient}; use proton_drive_rs::{KeySalt, ProtonDriveClient};
use proton_sdk::api::{HumanVerification, HumanVerificationCredential}; use proton_sdk::api::{HumanVerification, HumanVerificationCredential};
use proton_sdk::cache::EncryptedCacheRepository; use proton_sdk::cache::EncryptedCacheRepository;
@@ -81,6 +81,11 @@ fn client_config() -> ProtonClientConfiguration {
} }
fn keyring_entry() -> Result<Entry> { fn keyring_entry() -> Result<Entry> {
// Installed lazily rather than once at startup so a session bus that was not
// up yet on the first attempt is retried on the next one.
if keyring_core::get_default_store().is_none() {
keyring_core::set_default_store(dbus_secret_service_keyring_store::Store::new()?);
}
Ok(Entry::new(KEYRING_SERVICE, KEYRING_USER)?) Ok(Entry::new(KEYRING_SERVICE, KEYRING_USER)?)
} }
@@ -215,7 +220,7 @@ pub fn load() -> Result<StoredSession> {
let entry = keyring_entry()?; let entry = keyring_entry()?;
match entry.get_password() { match entry.get_password() {
Ok(json) => Ok(serde_json::from_str(&json)?), Ok(json) => Ok(serde_json::from_str(&json)?),
Err(keyring::Error::NoEntry) => Err(Error::NotLoggedIn), Err(keyring_core::Error::NoEntry) => Err(Error::NotLoggedIn),
Err(e) => Err(e.into()), Err(e) => Err(e.into()),
} }
} }
@@ -238,7 +243,7 @@ pub fn logout() -> Result<()> {
} }
} }
match keyring_entry()?.delete_credential() { match keyring_entry()?.delete_credential() {
Ok(()) | Err(keyring::Error::NoEntry) => Ok(()), Ok(()) | Err(keyring_core::Error::NoEntry) => Ok(()),
Err(e) => Err(e.into()), Err(e) => Err(e.into()),
} }
} }
+1 -1
View File
@@ -11,7 +11,7 @@ pub enum Error {
Proton(#[from] proton_sdk::ProtonError), Proton(#[from] proton_sdk::ProtonError),
#[error("keyring: {0}")] #[error("keyring: {0}")]
Keyring(#[from] keyring::Error), Keyring(#[from] keyring_core::Error),
#[error("serialization: {0}")] #[error("serialization: {0}")]
Serde(#[from] serde_json::Error), Serde(#[from] serde_json::Error),
+4 -4
View File
@@ -29,9 +29,9 @@ anyhow.workspace = true
tokio.workspace = true tokio.workspace = true
serde.workspace = true serde.workspace = true
serde_json.workspace = true serde_json.workspace = true
ksni = "0.2" ksni = { version = "0.3", features = ["blocking"] }
gtk4 = { version = "0.9", features = ["v4_12"] } gtk4 = { version = "0.11", features = ["v4_12"] }
adw = { package = "libadwaita", version = "0.7", features = ["v1_5"] } adw = { package = "libadwaita", version = "0.9", features = ["v1_5"] }
async-channel = "2" async-channel = "2"
exif = { version = "0.6", package = "kamadak-exif" } exif = { version = "0.6", package = "kamadak-exif" }
webkit6 = "0.4" webkit6 = "0.6"
+9 -8
View File
@@ -14,8 +14,9 @@ use std::process::Command;
use std::time::Duration; use std::time::Duration;
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use ksni::blocking::TrayMethods;
use ksni::menu::StandardItem; use ksni::menu::StandardItem;
use ksni::{MenuItem, Tray, TrayService}; use ksni::{MenuItem, Tray};
use pdfs_core::auth; use pdfs_core::auth;
use pdfs_core::config::AppDirs; use pdfs_core::config::AppDirs;
use pdfs_core::control::{ use pdfs_core::control::{
@@ -282,13 +283,13 @@ fn main() -> Result<()> {
state: poll_state(&socket, &default_mountpoint), state: poll_state(&socket, &default_mountpoint),
}; };
let service = TrayService::new(tray); // The tray autostarts with the session and can beat the desktop's SNI
let handle = service.handle(); // watcher onto the bus. Without `assume_sni_available` that race is a hard
std::thread::spawn(move || { // spawn error; with it the item waits and registers once the watcher shows.
if let Err(e) = service.run() { let handle = tray
tracing::error!("tray service stopped: {e:?}"); .assume_sni_available(true)
} .spawn()
}); .context("start tray service")?;
// Poll the daemon forever, pushing each fresh snapshot into the tray so the // Poll the daemon forever, pushing each fresh snapshot into the tray so the
// menu reflects mount/login changes made elsewhere (e.g. via the CLI). // menu reflects mount/login changes made elsewhere (e.g. via the CLI).
+1 -1
View File
@@ -1364,7 +1364,7 @@ fn icon_for(name: &str, is_dir: bool) -> gio::Icon {
if is_dir { if is_dir {
return gio::ThemedIcon::new("folder").upcast(); return gio::ThemedIcon::new("folder").upcast();
} }
let (content_type, _uncertain) = gio::functions::content_type_guess(Some(name), &[]); let (content_type, _uncertain) = gio::functions::content_type_guess(Some(name), None);
gio::functions::content_type_get_icon(&content_type) gio::functions::content_type_get_icon(&content_type)
} }