diff --git a/credential/cargo-credential-gnome-secret/build.rs b/credential/cargo-credential-gnome-secret/build.rs index 9283535af36..8bb86ee434d 100644 --- a/credential/cargo-credential-gnome-secret/build.rs +++ b/credential/cargo-credential-gnome-secret/build.rs @@ -1,3 +1,8 @@ fn main() { - pkg_config::probe_library("libsecret-1").unwrap(); + if cfg!(target_os = "linux") { + // TODO: Consider ignoring errors when libsecret is not installed and + // switching the impl to UnsupportedCredential (possibly along with a + // warning?). + pkg_config::probe_library("libsecret-1").unwrap(); + } } diff --git a/credential/cargo-credential-gnome-secret/src/libsecret.rs b/credential/cargo-credential-gnome-secret/src/libsecret.rs new file mode 100644 index 00000000000..c584eeecf68 --- /dev/null +++ b/credential/cargo-credential-gnome-secret/src/libsecret.rs @@ -0,0 +1,190 @@ +//! Implementation of the libsecret credential helper. + +use cargo_credential::{Credential, Error}; +use std::ffi::{CStr, CString}; +use std::os::raw::{c_char, c_int}; +use std::ptr::{null, null_mut}; + +#[allow(non_camel_case_types)] +type gchar = c_char; + +#[allow(non_camel_case_types)] +type gboolean = c_int; + +type GQuark = u32; + +#[repr(C)] +struct GError { + domain: GQuark, + code: c_int, + message: *mut gchar, +} + +#[repr(C)] +struct GCancellable { + _private: [u8; 0], +} + +#[repr(C)] +struct SecretSchema { + name: *const gchar, + flags: SecretSchemaFlags, + attributes: [SecretSchemaAttribute; 32], +} + +#[repr(C)] +#[derive(Copy, Clone)] +struct SecretSchemaAttribute { + name: *const gchar, + attr_type: SecretSchemaAttributeType, +} + +#[repr(C)] +enum SecretSchemaFlags { + None = 0, +} + +#[repr(C)] +#[derive(Copy, Clone)] +enum SecretSchemaAttributeType { + String = 0, +} + +extern "C" { + fn secret_password_store_sync( + schema: *const SecretSchema, + collection: *const gchar, + label: *const gchar, + password: *const gchar, + cancellable: *mut GCancellable, + error: *mut *mut GError, + ... + ) -> gboolean; + fn secret_password_clear_sync( + schema: *const SecretSchema, + cancellable: *mut GCancellable, + error: *mut *mut GError, + ... + ) -> gboolean; + fn secret_password_lookup_sync( + schema: *const SecretSchema, + cancellable: *mut GCancellable, + error: *mut *mut GError, + ... + ) -> *mut gchar; +} + +pub struct GnomeSecret; + +fn label(index_url: &str) -> CString { + CString::new(format!("cargo-registry:{}", index_url)).unwrap() +} + +fn schema() -> SecretSchema { + let mut attributes = [SecretSchemaAttribute { + name: null(), + attr_type: SecretSchemaAttributeType::String, + }; 32]; + attributes[0] = SecretSchemaAttribute { + name: b"url\0".as_ptr() as *const gchar, + attr_type: SecretSchemaAttributeType::String, + }; + SecretSchema { + name: b"org.rust-lang.cargo.registry\0".as_ptr() as *const gchar, + flags: SecretSchemaFlags::None, + attributes, + } +} + +impl Credential for GnomeSecret { + fn name(&self) -> &'static str { + env!("CARGO_PKG_NAME") + } + + fn get(&self, index_url: &str) -> Result { + let mut error: *mut GError = null_mut(); + let attr_url = CString::new("url").unwrap(); + let index_url_c = CString::new(index_url).unwrap(); + let schema = schema(); + unsafe { + let token_c = secret_password_lookup_sync( + &schema, + null_mut(), + &mut error, + attr_url.as_ptr(), + index_url_c.as_ptr(), + null() as *const gchar, + ); + if !error.is_null() { + return Err(format!( + "failed to get token: {}", + CStr::from_ptr((*error).message).to_str()? + ) + .into()); + } + if token_c.is_null() { + return Err(format!("cannot find token for {}", index_url).into()); + } + let token = CStr::from_ptr(token_c) + .to_str() + .map_err(|e| format!("expected utf8 token: {}", e))? + .to_string(); + Ok(token) + } + } + + fn store(&self, index_url: &str, token: &str, name: Option<&str>) -> Result<(), Error> { + let label = label(name.unwrap_or(index_url)); + let token = CString::new(token).unwrap(); + let mut error: *mut GError = null_mut(); + let attr_url = CString::new("url").unwrap(); + let index_url_c = CString::new(index_url).unwrap(); + let schema = schema(); + unsafe { + secret_password_store_sync( + &schema, + b"default\0".as_ptr() as *const gchar, + label.as_ptr(), + token.as_ptr(), + null_mut(), + &mut error, + attr_url.as_ptr(), + index_url_c.as_ptr(), + null() as *const gchar, + ); + if !error.is_null() { + return Err(format!( + "failed to store token: {}", + CStr::from_ptr((*error).message).to_str()? + ) + .into()); + } + } + Ok(()) + } + + fn erase(&self, index_url: &str) -> Result<(), Error> { + let schema = schema(); + let mut error: *mut GError = null_mut(); + let attr_url = CString::new("url").unwrap(); + let index_url_c = CString::new(index_url).unwrap(); + unsafe { + secret_password_clear_sync( + &schema, + null_mut(), + &mut error, + attr_url.as_ptr(), + index_url_c.as_ptr(), + null() as *const gchar, + ); + if !error.is_null() { + return Err(format!( + "failed to erase token: {}", + CStr::from_ptr((*error).message).to_str()? + ) + .into()); + } + } + Ok(()) + } +} diff --git a/credential/cargo-credential-gnome-secret/src/main.rs b/credential/cargo-credential-gnome-secret/src/main.rs index 40972b05dcc..1d2ecc61fbc 100644 --- a/credential/cargo-credential-gnome-secret/src/main.rs +++ b/credential/cargo-credential-gnome-secret/src/main.rs @@ -1,193 +1,11 @@ //! Cargo registry gnome libsecret credential process. -use cargo_credential::{Credential, Error}; -use std::ffi::{CStr, CString}; -use std::os::raw::{c_char, c_int}; -use std::ptr::{null, null_mut}; - -#[allow(non_camel_case_types)] -type gchar = c_char; - -#[allow(non_camel_case_types)] -type gboolean = c_int; - -type GQuark = u32; - -#[repr(C)] -struct GError { - domain: GQuark, - code: c_int, - message: *mut gchar, -} - -#[repr(C)] -struct GCancellable { - _private: [u8; 0], -} - -#[repr(C)] -struct SecretSchema { - name: *const gchar, - flags: SecretSchemaFlags, - attributes: [SecretSchemaAttribute; 32], -} - -#[repr(C)] -#[derive(Copy, Clone)] -struct SecretSchemaAttribute { - name: *const gchar, - attr_type: SecretSchemaAttributeType, -} - -#[repr(C)] -enum SecretSchemaFlags { - None = 0, -} - -#[repr(C)] -#[derive(Copy, Clone)] -enum SecretSchemaAttributeType { - String = 0, -} - -extern "C" { - fn secret_password_store_sync( - schema: *const SecretSchema, - collection: *const gchar, - label: *const gchar, - password: *const gchar, - cancellable: *mut GCancellable, - error: *mut *mut GError, - ... - ) -> gboolean; - fn secret_password_clear_sync( - schema: *const SecretSchema, - cancellable: *mut GCancellable, - error: *mut *mut GError, - ... - ) -> gboolean; - fn secret_password_lookup_sync( - schema: *const SecretSchema, - cancellable: *mut GCancellable, - error: *mut *mut GError, - ... - ) -> *mut gchar; -} - -struct GnomeSecret; - -fn label(index_url: &str) -> CString { - CString::new(format!("cargo-registry:{}", index_url)).unwrap() -} - -fn schema() -> SecretSchema { - let mut attributes = [SecretSchemaAttribute { - name: null(), - attr_type: SecretSchemaAttributeType::String, - }; 32]; - attributes[0] = SecretSchemaAttribute { - name: b"url\0".as_ptr() as *const gchar, - attr_type: SecretSchemaAttributeType::String, - }; - SecretSchema { - name: b"org.rust-lang.cargo.registry\0".as_ptr() as *const gchar, - flags: SecretSchemaFlags::None, - attributes, - } -} - -impl Credential for GnomeSecret { - fn name(&self) -> &'static str { - env!("CARGO_PKG_NAME") - } - - fn get(&self, index_url: &str) -> Result { - let mut error: *mut GError = null_mut(); - let attr_url = CString::new("url").unwrap(); - let index_url_c = CString::new(index_url).unwrap(); - let schema = schema(); - unsafe { - let token_c = secret_password_lookup_sync( - &schema, - null_mut(), - &mut error, - attr_url.as_ptr(), - index_url_c.as_ptr(), - null() as *const gchar, - ); - if !error.is_null() { - return Err(format!( - "failed to get token: {}", - CStr::from_ptr((*error).message).to_str()? - ) - .into()); - } - if token_c.is_null() { - return Err(format!("cannot find token for {}", index_url).into()); - } - let token = CStr::from_ptr(token_c) - .to_str() - .map_err(|e| format!("expected utf8 token: {}", e))? - .to_string(); - Ok(token) - } - } - - fn store(&self, index_url: &str, token: &str, name: Option<&str>) -> Result<(), Error> { - let label = label(name.unwrap_or(index_url)); - let token = CString::new(token).unwrap(); - let mut error: *mut GError = null_mut(); - let attr_url = CString::new("url").unwrap(); - let index_url_c = CString::new(index_url).unwrap(); - let schema = schema(); - unsafe { - secret_password_store_sync( - &schema, - b"default\0".as_ptr() as *const gchar, - label.as_ptr(), - token.as_ptr(), - null_mut(), - &mut error, - attr_url.as_ptr(), - index_url_c.as_ptr(), - null() as *const gchar, - ); - if !error.is_null() { - return Err(format!( - "failed to store token: {}", - CStr::from_ptr((*error).message).to_str()? - ) - .into()); - } - } - Ok(()) - } - - fn erase(&self, index_url: &str) -> Result<(), Error> { - let schema = schema(); - let mut error: *mut GError = null_mut(); - let attr_url = CString::new("url").unwrap(); - let index_url_c = CString::new(index_url).unwrap(); - unsafe { - secret_password_clear_sync( - &schema, - null_mut(), - &mut error, - attr_url.as_ptr(), - index_url_c.as_ptr(), - null() as *const gchar, - ); - if !error.is_null() { - return Err(format!( - "failed to erase token: {}", - CStr::from_ptr((*error).message).to_str()? - ) - .into()); - } - } - Ok(()) - } -} +#[cfg(target_os = "linux")] +mod libsecret; +#[cfg(not(target_os = "linux"))] +use cargo_credential::UnsupportedCredential as GnomeSecret; +#[cfg(target_os = "linux")] +use libsecret::GnomeSecret; fn main() { cargo_credential::main(GnomeSecret);