blob: c584eeecf68506e292d018e7fbfedabf43a9c33d [file] [log] [blame]
//! 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<String, Error> {
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(())
}
}