From 3d7e143ec9dfce5ed97e238d5304e900ad67bb8d Mon Sep 17 00:00:00 2001 From: Vika Date: Sun, 25 Aug 2024 15:31:25 +0300 Subject: "Better" error handling from libsecret I had to commit a minor safety crime (but not an unsoundness crime) to get libsecret's `glib::Error` to be compatible with `glib::Error` from the newer GLib version. This could be dropped later when libsecret updates. --- src/lib.rs | 111 +++++++++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 78 insertions(+), 33 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 253f8b8..d178e07 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -42,6 +42,7 @@ pub struct App { signin: AsyncController, post_editor: Controller>, } + #[derive(Debug)] enum AuthState { LoggedOut, @@ -51,6 +52,82 @@ enum AuthState { } } +impl App { + async fn get_login_state(schema: &libsecret::Schema) -> Result { + let mut retrievables = libsecret::password_search_future(Some(schema), { + let mut attrs = std::collections::HashMap::default(); + attrs.insert(crate::secrets::TOKEN_KIND, crate::secrets::ACCESS_TOKEN); + attrs + }, libsecret::SearchFlags::ALL).await + // SAFETY: the representation of `glib::Error` is the same, since we're + // building against the same C library, so while this is a safety crime, it's + // probably a minor one. + // + // Additionally I'm ensuring the original wrapper is forgotten in case it has + // a drop handler that decrements a reference counter or something. + // + // This is only a workaround for libsecret not being updated in time. + .map_err(|e| unsafe { + let ptr = e.as_ptr(); + std::mem::forget(e); + glib::translate::from_glib_full::<_, glib::Error>( + // We can't name the original type here. + #[allow(clippy::missing_transmute_annotations)] + std::mem::transmute::<_, *mut glib::ffi::GError>(ptr) + ) + })?; + + if retrievables.is_empty() { + Ok(AuthState::LoggedOut) + } else { + retrievables.sort_by_key(|s| s.created()); + let iterable = retrievables.iter().rev(); + for retrievable in iterable { + let attrs = retrievable.attributes(); + let attrs_ref = attrs + .iter() + .map(|(k, v)| (k.as_ref(), v.as_ref())) + .collect(); + let micropub_uri = match attrs + .get(crate::secrets::MICROPUB) + .and_then(|v| glib::Uri::parse( + v, glib::UriFlags::NONE + ).ok()) { + Some(uri) => uri, + None => { + let _ = libsecret::password_clear_future(Some(schema), attrs_ref).await; + continue + }, + }; + + return Ok(AuthState::LoggedIn { + micropub: crate::micropub::Client::new( + micropub_uri, + retrievable.retrieve_secret_future().await + // SAFETY: see above + .map_err(|e| unsafe { + let ptr = e.as_ptr(); + std::mem::forget(e); + glib::translate::from_glib_full::<_, glib::Error>( + // We can't name the original type here. + #[allow(clippy::missing_transmute_annotations)] + std::mem::transmute::<_, *mut glib::ffi::GError>(ptr) + ) + })? + .unwrap() + .text() + .unwrap() + .to_string() + ), + submit_busy_guard: None + }) + } + + Ok(AuthState::LoggedOut) + } + } +} + #[derive(Debug)] #[doc(hidden)] pub enum Input { @@ -111,39 +188,7 @@ impl AsyncComponent for App { sender: AsyncComponentSender, ) -> AsyncComponentParts { let schema = crate::secrets::get_schema(); - let state = match libsecret::password_search_future(Some(&schema), { - let mut attrs = std::collections::HashMap::default(); - attrs.insert(crate::secrets::TOKEN_KIND, crate::secrets::ACCESS_TOKEN); - attrs - }, libsecret::SearchFlags::ALL).await { - Ok(mut retrievables) => { - if retrievables.is_empty() { - AuthState::LoggedOut - } else { - retrievables.sort_by_key(|s| s.created()); - let retrievable = retrievables.last().unwrap(); - let attrs = retrievable.attributes(); - - let micropub_uri = attrs - .get(crate::secrets::MICROPUB) - .and_then(|v| glib::Uri::parse(v, glib::UriFlags::NONE).ok()) - .unwrap(); - - AuthState::LoggedIn { - micropub: crate::micropub::Client::new( - micropub_uri, - retrievable.retrieve_secret_future().await.unwrap().unwrap().text().unwrap().to_string() - ), - submit_busy_guard: None - } - } - }, - Err(err) => { - log::warn!("Error retrieving secrets: {}", err); - AuthState::LoggedOut - }, - - }; + let state = App::get_login_state(&schema).await.unwrap(); let http = soup::Session::builder() .user_agent(concat!(env!("CARGO_PKG_NAME"),"/",env!("CARGO_PKG_VERSION")," ")) .build(); -- cgit 1.4.1