summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/lib.rs111
1 files 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<components::SignIn>,
     post_editor: Controller<components::PostEditor<micropub::Error>>,
 }
+
 #[derive(Debug)]
 enum AuthState {
     LoggedOut,
@@ -51,6 +52,82 @@ enum AuthState {
     }
 }
 
+impl App {
+    async fn get_login_state(schema: &libsecret::Schema) -> Result<AuthState, glib::Error> {
+        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<Self>,
     ) -> AsyncComponentParts<Self> {
         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();