From 4d5791f8173ca6c4ef54894dfca92bbbc8dc7941 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Commaille?= Date: Sat, 22 Oct 2022 10:48:23 +0200 Subject: [PATCH] secret: Use oo7 instead of libsecret --- Cargo.lock | 103 +++++++++++------ Cargo.toml | 8 +- meson.build | 2 - src/error_page.rs | 28 ++--- src/login/mod.rs | 10 +- src/main.rs | 10 +- src/secret.rs | 280 ++++++++++++++++++++++++--------------------- src/session/mod.rs | 4 +- src/window.rs | 16 +-- 9 files changed, 260 insertions(+), 201 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 235bee5b..c66b41ed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -693,6 +693,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", + "rand_core 0.6.3", "typenum", ] @@ -707,9 +708,9 @@ dependencies = [ [[package]] name = "curve25519-dalek" -version = "3.2.1" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90f9d052967f590a76e62eb387bd0bbb1b000182c3cefe5364db6b7211651bc0" +checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" dependencies = [ "byteorder", "digest 0.9.0", @@ -1085,7 +1086,6 @@ dependencies = [ "image 0.24.4", "indexmap", "libadwaita", - "libsecret", "libshumate", "log", "matrix-sdk", @@ -1093,6 +1093,7 @@ dependencies = [ "mime_guess", "num_enum", "once_cell", + "oo7", "pulldown-cmark", "qrcode", "rand 0.8.5", @@ -2352,34 +2353,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "libsecret" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4af5a2342942fa42d706a424e9f9914287fb8317132750fd73a241140ac38c1" -dependencies = [ - "bitflags", - "gio", - "glib", - "libc", - "libsecret-sys", - "once_cell", -] - -[[package]] -name = "libsecret-sys" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b630bef24b542dc1609a14c56b9267c147dbef1ee7ad08fb1a852a07c17d492d" -dependencies = [ - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "pkg-config", - "system-deps", -] - [[package]] name = "libshumate" version = "0.1.1" @@ -2890,6 +2863,40 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "num" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43db66d1170d347f9a065114077f7dccb00c1b9478c89384490a3425279a4606" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational 0.4.1", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ae39348c8bc5fbd7f40c727a9925f03517afd2ab27d46702108b6a7e5414c19" +dependencies = [ + "num-traits", +] + [[package]] name = "num-integer" version = "0.1.45" @@ -2929,6 +2936,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0" dependencies = [ "autocfg", + "num-bigint", "num-integer", "num-traits", ] @@ -3008,6 +3016,32 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" +[[package]] +name = "oo7" +version = "0.1.0-alpha.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e614140b3625a16ddde5880e5c6429d4ef901883256edf496cceb62af1c7b6f8" +dependencies = [ + "aes", + "byteorder", + "cbc", + "cipher 0.4.3", + "digest 0.10.3", + "dirs", + "futures", + "hkdf", + "hmac", + "num", + "once_cell", + "pbkdf2", + "rand 0.8.5", + "serde", + "sha2 0.10.5", + "tokio", + "zbus", + "zeroize", +] + [[package]] name = "opaque-debug" version = "0.3.0" @@ -5040,8 +5074,7 @@ dependencies = [ [[package]] name = "x25519-dalek" version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2392b6b94a576b4e2bf3c5b2757d63f10ada8020a2e4d08ac849ebcf6ea8e077" +source = "git+https://github.com/A6GibKm/x25519-dalek?rev=9f19028c34107eea87d37bcee2eb2b350ec34cfe#9f19028c34107eea87d37bcee2eb2b350ec34cfe" dependencies = [ "curve25519-dalek", "rand_core 0.5.1", @@ -5126,9 +5159,9 @@ dependencies = [ [[package]] name = "zeroize" -version = "1.3.0" +version = "1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd" +checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f" dependencies = [ "zeroize_derive", ] diff --git a/Cargo.toml b/Cargo.toml index 4dfa4fad..c0a3f3b5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,7 +28,9 @@ serde = "1.0.130" serde_json = "1.0" tokio = { version = "1.15", features = ["rt", "rt-multi-thread", "sync"] } url = "2.2" -libsecret = { version = "0.1.4", features = ["v0_19"] } +oo7 = { version = "0.1.0-alpha.5", default-features = false, features = [ + "tokio", +] } html2pango = "0.5.0" futures = "0.3" rand = "0.8" @@ -93,3 +95,7 @@ features = [ "unstable-msc3440", "unstable-sanitize", ] + +[patch.crates-io.x25519-dalek] +git = "https://github.com/A6GibKm/x25519-dalek" +rev = "9f19028c34107eea87d37bcee2eb2b350ec34cfe" diff --git a/meson.build b/meson.build index f5b895c1..69a7e718 100644 --- a/meson.build +++ b/meson.build @@ -26,8 +26,6 @@ dependency('gstreamer-1.0', version: '>= 1.18') dependency('gstreamer-base-1.0', version: '>= 1.18') dependency('gstreamer-plugins-base-1.0', version: '>= 1.18') dependency('gstreamer-video-1.0', version: '>= 1.18') -dependency('libsecret-1', version: '>= 0.19', - default_options: ['gtk_doc=false', 'gir=false', 'vapi=false']) glib_compile_resources = find_program('glib-compile-resources', required: true) glib_compile_schemas = find_program('glib-compile-schemas', required: true) diff --git a/src/error_page.rs b/src/error_page.rs index 86766db3..b6a11336 100644 --- a/src/error_page.rs +++ b/src/error_page.rs @@ -3,7 +3,7 @@ use gettextrs::gettext; use gtk::{self, glib, glib::clone, prelude::*, subclass::prelude::*, CompositeTemplate}; use log::error; -use crate::{secret, secret::SecretError, spawn, toast, window::Window}; +use crate::{spawn, toast, window::Window}; pub enum ErrorSubpage { SecretErrorSession, @@ -33,7 +33,7 @@ mod imp { pub page: TemplateChild, #[template_child] pub stack: TemplateChild, - pub secret_error: RefCell>, + pub secret_item: RefCell>, } #[glib::object_subclass] @@ -82,22 +82,24 @@ impl ErrorPage { glib::Object::new(&[]).expect("Failed to create ErrorPage") } - pub fn display_secret_error(&self, message: &str, error: SecretError) { + pub fn display_secret_error(&self, message: &str, item: Option) { let priv_ = self.imp(); - self.action_set_enabled( - "error-page.remove-secret-error-session", - matches!(error, SecretError::CorruptSession(_)), - ); + self.action_set_enabled("error-page.remove-secret-error-session", item.is_some()); priv_.page.set_description(Some(message)); - priv_ - .stack - .set_visible_child_name(error.error_subpage().as_ref()); - priv_.secret_error.replace(Some(error)); + + let error_subpage = if item.is_some() { + ErrorSubpage::SecretErrorSession + } else { + ErrorSubpage::SecretErrorOther + }; + + priv_.stack.set_visible_child_name(error_subpage.as_ref()); + priv_.secret_item.replace(item); } async fn remove_secret_error_session(&self) { - if let Some(SecretError::CorruptSession((_, item))) = self.imp().secret_error.take() { - match secret::remove_item(&item).await { + if let Some(item) = self.imp().secret_item.take() { + match item.delete().await { Ok(_) => { self.action_set_enabled("error-page.remove-secret-error-session", false); if let Some(window) = self diff --git a/src/login/mod.rs b/src/login/mod.rs index 14d188de..617dc347 100644 --- a/src/login/mod.rs +++ b/src/login/mod.rs @@ -595,12 +595,16 @@ impl Login { let session = Session::new(); if is_new { - if let Err(error) = secret::store_session(&session_info).await { + let session_info = session_info.clone(); + let handle = spawn_tokio!(async move { secret::store_session(&session_info).await }); + + if let Err(error) = handle.await.unwrap() { error!("Couldn't store session: {:?}", error); + let (message, item) = error.into_parts(); self.parent_window().switch_to_error_page( - &format!("{}\n\n{}", gettext("Unable to store session"), error), - error, + &format!("{}\n\n{}", gettext("Unable to store session"), message), + item, ); return; } diff --git a/src/main.rs b/src/main.rs index 5030ee64..9ed36b7c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -27,14 +27,8 @@ use gtk::{gdk::Display, gio, IconTheme}; use once_cell::sync::Lazy; use self::{ - application::Application, - error_page::{ErrorPage, ErrorSubpage}, - greeter::Greeter, - i18n::*, - login::Login, - session::Session, - user_facing_error::UserFacingError, - window::Window, + application::Application, error_page::ErrorPage, greeter::Greeter, i18n::*, login::Login, + session::Session, user_facing_error::UserFacingError, window::Window, }; /// The default tokio runtime to be used for async tasks diff --git a/src/secret.rs b/src/secret.rs index e8f5f124..4509b4aa 100644 --- a/src/secret.rs +++ b/src/secret.rs @@ -1,68 +1,104 @@ use std::{collections::HashMap, ffi::OsStr, fmt, path::PathBuf, string::FromUtf8Error}; use gettextrs::gettext; -use gtk::{gio, glib}; -use libsecret::{ - password_clear_future, password_search_sync, password_store_binary_future, prelude::*, - Retrievable, Schema, SchemaAttributeType, SchemaFlags, SearchFlags, Value, COLLECTION_DEFAULT, -}; use log::error; use matrix_sdk::ruma::{DeviceId, OwnedDeviceId, OwnedUserId, UserId}; +use oo7::{is_sandboxed, Item, Keyring}; use serde::{Deserialize, Serialize}; use serde_json::error::Error as JsonError; +use thiserror::Error; use url::Url; -use crate::{config::APP_ID, gettext_f, ErrorSubpage}; +use crate::{config::APP_ID, gettext_f, user_facing_error::UserFacingError}; + +const SCHEMA_ATTRIBUTE: &str = "xdg:schema"; /// Any error that can happen when interacting with the secret service. -#[derive(Debug, Clone)] +#[derive(Debug, Error)] pub enum SecretError { - CorruptSession((String, Retrievable)), - Libsecret(glib::Error), - Unknown, + /// A corrupted session was found. + #[error("{0}")] + CorruptSession(String, Item), + + /// An error occurred interacting with the secret service. + #[error(transparent)] + Oo7(#[from] oo7::Error), } impl SecretError { - /// Get the error subpage that matches `self`. - pub fn error_subpage(&self) -> ErrorSubpage { + /// Split `self` between its message and its optional `Item`. + pub fn into_parts(self) -> (String, Option) { match self { - Self::CorruptSession(_) => ErrorSubpage::SecretErrorSession, - _ => ErrorSubpage::SecretErrorOther, + SecretError::CorruptSession(message, item) => (message, Some(item)), + SecretError::Oo7(error) => (error.to_user_facing(), None), } } } -impl From for SecretError { - fn from(error: glib::Error) -> Self { - Self::Libsecret(error) +impl UserFacingError for oo7::Error { + fn to_user_facing(self) -> String { + match self { + oo7::Error::Portal(error) => error.to_user_facing(), + oo7::Error::DBus(error) => error.to_user_facing(), + } } } -impl fmt::Display for SecretError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!( - f, - "{}", - match self { - Self::CorruptSession((message, _)) => message.to_owned(), - Self::Libsecret(error) if error.is::() => { - match error.kind::() { - Some(libsecret::Error::Protocol) => error.message().to_owned(), - Some(libsecret::Error::IsLocked) => { - gettext("Could not unlock the secret storage") - } - _ => gettext( - "An unknown error occurred when interacting with the secret storage", - ), - } - } - _ => gettext("An unknown error occurred when interacting with the secret storage"), - } - ) +impl UserFacingError for oo7::portal::Error { + fn to_user_facing(self) -> String { + match self { + oo7::portal::Error::FileHeaderMismatch(_) | + oo7::portal::Error::VersionMismatch(_) | + oo7::portal::Error::NoData | + oo7::portal::Error::MacError | + oo7::portal::Error::HashedAttributeMac(_) | + oo7::portal::Error::GVariantDeserialization(_) => gettext( + "The secret storage file is corrupted.", + ), + oo7::portal::Error::NoParentDir(_) | + oo7::portal::Error::NoDataDir => gettext( + "Could not access the secret storage file location.", + ), + oo7::portal::Error::Io(_) => gettext( + "An unknown error occurred when accessing the secret storage file.", + ), + oo7::portal::Error::TargetFileChanged(_) => gettext( + "The secret storage file has been changed by another process.", + ), + oo7::portal::Error::PortalBus(_) => gettext( + "An unknown error occurred when interacting with the D-Bus Secret Service.", + ), + oo7::portal::Error::CancelledPortalRequest => gettext( + "The request to the Flatpak Secret Portal was cancelled. Make sure to accept any prompt asking to access it.", + ), + oo7::portal::Error::PortalNotAvailable => gettext( + "The Flatpak Secret Portal is not available. Make sure xdg-desktop-portal is installed, and it is at least at version 1.5.0.", + ), + } } } -#[derive(Debug, Clone)] +impl UserFacingError for oo7::dbus::Error { + fn to_user_facing(self) -> String { + match self { + oo7::dbus::Error::Deleted => gettext( + "The item was deleted.", + ), + oo7::dbus::Error::Dismissed => gettext( + "The request to the D-Bus Secret Service was cancelled. Make sure to accept any prompt asking to access it.", + ), + oo7::dbus::Error::NotFound(_) => gettext( + "Could not access the default collection. Make sure a keyring was created and set as default.", + ), + oo7::dbus::Error::Zbus(_) | + oo7::dbus::Error::IO(_) => gettext( + "An unknown error occurred when interacting with the D-Bus Secret Service.", + ), + } + } +} + +#[derive(Clone)] pub struct StoredSession { pub homeserver: Url, pub user_id: OwnedUserId, @@ -71,10 +107,21 @@ pub struct StoredSession { pub secret: Secret, } +impl fmt::Debug for StoredSession { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("StoredSession") + .field("homeserver", &self.homeserver) + .field("user_id", &self.user_id) + .field("device_id", &self.device_id) + .field("path", &self.path) + .finish() + } +} + impl StoredSession { /// Build self from a secret. - pub async fn try_from_secret_item(item: Retrievable) -> Result { - let attr = item.attributes(); + pub async fn try_from_secret_item(item: Item) -> Result { + let attr = item.attributes().await?; let homeserver = match attr.get("homeserver") { Some(string) => match Url::parse(string) { @@ -84,17 +131,17 @@ impl StoredSession { "Could not parse 'homeserver' attribute in stored session: {:?}", err ); - return Err(SecretError::CorruptSession(( + return Err(SecretError::CorruptSession( gettext("Malformed homeserver in stored session"), item, - ))); + )); } }, None => { - return Err(SecretError::CorruptSession(( + return Err(SecretError::CorruptSession( gettext("Could not find homeserver in stored session"), item, - ))); + )); } }; let user_id = match attr.get("user") { @@ -105,60 +152,54 @@ impl StoredSession { "Could not parse 'user' attribute in stored session: {:?}", err ); - return Err(SecretError::CorruptSession(( + return Err(SecretError::CorruptSession( gettext("Malformed user ID in stored session"), item, - ))); + )); } }, None => { - return Err(SecretError::CorruptSession(( + return Err(SecretError::CorruptSession( gettext("Could not find user ID in stored session"), item, - ))); + )); } }; let device_id = match attr.get("device-id") { Some(string) => <&DeviceId>::from(string.as_str()).to_owned(), None => { - return Err(SecretError::CorruptSession(( + return Err(SecretError::CorruptSession( gettext("Could not find device ID in stored session"), item, - ))); + )); } }; let path = match attr.get("db-path") { Some(string) => PathBuf::from(string), None => { - return Err(SecretError::CorruptSession(( + return Err(SecretError::CorruptSession( gettext("Could not find database path in stored session"), item, - ))); + )); } }; - let secret = match item.retrieve_secret_future().await { - Ok(Some(value)) => match Secret::from_utf8(value.get()) { + let secret = match item.secret().await { + Ok(secret) => match Secret::from_utf8(&secret) { Ok(secret) => secret, Err(err) => { error!("Could not parse secret in stored session: {:?}", err); - return Err(SecretError::CorruptSession(( + return Err(SecretError::CorruptSession( gettext("Malformed secret in stored session"), item, - ))); + )); } }, - Ok(None) => { - return Err(SecretError::CorruptSession(( - gettext("No secret in stored session"), - item, - ))); - } Err(err) => { error!("Could not get secret in stored session: {:?}", err); - return Err(SecretError::CorruptSession(( + return Err(SecretError::CorruptSession( gettext("Could not get secret in stored session"), item, - ))); + )); } }; @@ -171,20 +212,14 @@ impl StoredSession { }) } - /// Build a secret from `self`. - /// - /// Returns an (attributes, secret) tuple. - pub fn to_secret_item(&self) -> (HashMap<&str, &str>, Value) { - let attributes = HashMap::from([ + /// Get the attributes from `self`. + pub fn attributes(&self) -> HashMap<&str, &str> { + HashMap::from([ ("homeserver", self.homeserver.as_str()), ("user", self.user_id.as_str()), ("device-id", self.device_id.as_str()), ("db-path", self.path.to_str().unwrap()), - ]); - - let secret = Value::new(&self.secret.to_string(), "application/json"); - - (attributes, secret) + ]) } /// Get the unique ID for this `StoredSession`. @@ -219,7 +254,7 @@ impl From for FromUtf8SecretError { } /// A `Secret` that can be stored in the `SecretService`. -#[derive(Debug, Clone, Deserialize, Serialize)] +#[derive(Clone, Deserialize, Serialize)] pub struct Secret { pub access_token: String, pub passphrase: String, @@ -227,38 +262,24 @@ pub struct Secret { impl Secret { /// Converts a vector of bytes to a `Secret`. - pub fn from_utf8(vec: Vec) -> Result { - let s = String::from_utf8(vec)?; + pub fn from_utf8(slice: &[u8]) -> Result { + let s = String::from_utf8(slice.to_owned())?; Ok(serde_json::from_str(&s)?) } } -impl fmt::Display for Secret { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", serde_json::to_string(self).unwrap()) - } -} - -/// The `Schema` of the items in the `SecretService`. -fn schema() -> Schema { - let attributes = HashMap::from([ - ("homeserver", SchemaAttributeType::String), - ("user", SchemaAttributeType::String), - ("device-id", SchemaAttributeType::String), - ("db-path", SchemaAttributeType::String), - ]); - - Schema::new(APP_ID, SchemaFlags::NONE, attributes) -} - /// Retrieves all sessions stored to the `SecretService` pub async fn restore_sessions() -> Result, SecretError> { - let items = password_search_sync( - Some(&schema()), - HashMap::new(), - SearchFlags::ALL | SearchFlags::UNLOCK | SearchFlags::LOAD_SECRETS, - gio::Cancellable::NONE, - )?; + let keyring = Keyring::new().await?; + + let items = if is_sandboxed() { + keyring.items().await? + } else { + keyring + .search_items(HashMap::from([(SCHEMA_ATTRIBUTE, APP_ID)])) + .await? + }; + let mut sessions = Vec::with_capacity(items.len()); for item in items { @@ -271,43 +292,40 @@ pub async fn restore_sessions() -> Result, SecretError> { /// Writes a session to the `SecretService`, overwriting any previously stored /// session with the same `homeserver`, `username` and `device-id`. pub async fn store_session(session: &StoredSession) -> Result<(), SecretError> { - let (attributes, secret) = session.to_secret_item(); - - password_store_binary_future( - Some(&schema()), - attributes, - Some(&COLLECTION_DEFAULT), - &gettext_f( - // Translators: Do NOT translate the content between '{' and '}', this is a variable - // name. - "Fractal: Matrix credentials for {user_id}", - &[("user_id", session.user_id.as_str())], - ), - &secret, - ) - .await?; + let keyring = Keyring::new().await?; - Ok(()) -} + let mut attributes = session.attributes(); -/// Removes a session from the `SecretService` -pub async fn remove_session(session: &StoredSession) -> Result<(), SecretError> { - let (attributes, _) = session.to_secret_item(); + if !is_sandboxed() { + attributes.insert(SCHEMA_ATTRIBUTE, APP_ID); + } - password_clear_future(Some(&schema()), attributes).await?; + let secret = serde_json::to_string(&session.secret).unwrap(); + + keyring + .create_item( + &gettext_f( + // Translators: Do NOT translate the content between '{' and '}', this is a + // variable name. + "Fractal: Matrix credentials for {user_id}", + &[("user_id", session.user_id.as_str())], + ), + attributes, + secret, + true, + ) + .await?; Ok(()) } -/// Removes an item from the `SecretService` -pub async fn remove_item(item: &Retrievable) -> Result<(), SecretError> { - let attributes = item.attributes(); - let mut attr = HashMap::with_capacity(attributes.len()); +/// Removes a session from the `SecretService` +pub async fn remove_session(session: &StoredSession) -> Result<(), SecretError> { + let keyring = Keyring::new().await?; - for (key, value) in attributes.iter() { - attr.insert(key.as_str(), value.as_str()); - } - password_clear_future(Some(&schema()), attr).await?; + let attributes = session.attributes(); + + keyring.delete(attributes).await?; Ok(()) } diff --git a/src/session/mod.rs b/src/session/mod.rs index d080addf..99512ede 100644 --- a/src/session/mod.rs +++ b/src/session/mod.rs @@ -709,7 +709,9 @@ impl Session { settings.delete_settings(); } - if let Err(error) = secret::remove_session(info).await { + let session_info = info.clone(); + let handle = spawn_tokio!(async move { secret::remove_session(&session_info).await }); + if let Err(error) = handle.await.unwrap() { error!( "Failed to remove credentials from SecretService after logout: {}", error diff --git a/src/window.rs b/src/window.rs index 48fb776c..c9394912 100644 --- a/src/window.rs +++ b/src/window.rs @@ -7,8 +7,7 @@ use log::{info, warn}; use crate::{ account_switcher::AccountSwitcher, config::{APP_ID, PROFILE}, - secret::{self, SecretError}, - spawn, Application, ErrorPage, Greeter, Login, Session, + secret, spawn, spawn_tokio, Application, ErrorPage, Greeter, Login, Session, }; mod imp { @@ -212,7 +211,8 @@ impl Window { } pub async fn restore_sessions(&self) { - match secret::restore_sessions().await { + let handle = spawn_tokio!(secret::restore_sessions()); + match handle.await.unwrap() { Ok(sessions) => { if sessions.is_empty() { self.switch_to_greeter_page(); @@ -237,13 +237,15 @@ impl Window { } Err(error) => { warn!("Failed to restore previous sessions: {:?}", error); + + let (message, item) = error.into_parts(); self.switch_to_error_page( &format!( "{}\n\n{}", gettext("Failed to restore previous sessions"), - error, + message, ), - error, + item, ); } } @@ -316,9 +318,9 @@ impl Window { priv_.main_stack.set_visible_child(&*priv_.greeter); } - pub fn switch_to_error_page(&self, message: &str, error: SecretError) { + pub fn switch_to_error_page(&self, message: &str, item: Option) { let priv_ = self.imp(); - priv_.error_page.display_secret_error(message, error); + priv_.error_page.display_secret_error(message, item); priv_.main_stack.set_visible_child(&*priv_.error_page); }