A backend for the p≡p Engine built on Sequoia.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

350 lines
9.8 KiB

//! A wrapper for pep's pEp_identity and identity_list structures.
//!
//! See pEpEngine.h and identity_list.c for details.
//!
//! This is needed by, for instance:
//!
//! - pgp_generate_keypair
//! - pgp_import_keydata
//!
//! We need to reimplement the following functions:
//!
//! - new_identity
//! - new_identity_list
//! - identity_list_add
//!
//! The following fields of pEp_identity are used:
//!
//! - address
//! - username
//! - fpr
//! - flags
//!
//! PepCommType is also needed by:
//!
//! - pgp_get_key_rating
use std::ffi::CStr;
use std::mem;
use std::os::raw::{
c_char,
c_uint,
};
use std::ptr;
use sequoia_openpgp as openpgp;
use openpgp::Fingerprint;
use crate::buffer::{
malloc_cleared,
rust_str_to_c_str,
};
use crate::ffi::MM;
use crate::pep::{
PepCommType,
PepEncFormat,
PepIdentityFlags,
};
/// A `PepIdentity` template.
///
/// Unlike `PepIdentity`, the object is managed by Rust.
#[derive(Debug)]
pub struct PepIdentityTemplate {
address: String,
fpr: Fingerprint,
username: Option<String>,
}
impl PepIdentityTemplate {
/// Returns a new identity.
///
/// The memory is allocated using the libc allocator. The caller
/// is responsible for freeing it explicitly.
pub fn new<S1, S2>(email: S1, fingerprint: Fingerprint, username: Option<S2>)
-> Self
where S1: AsRef<str>, S2: AsRef<str>
{
Self {
address: email.as_ref().into(),
fpr: fingerprint,
username: username.map(|s| s.as_ref().into()),
}
}
}
// See pEpEngine/src/pEpEngine.h:pEp_identity.
//
// https://gitea.pep.foundation/pEp.foundation/pEpEngine/src/branch/master/src/pEpEngine.h#L788
#[repr(C)]
pub struct PepIdentity {
pub address: *mut c_char,
pub fpr: *mut c_char,
pub userid: *mut c_char,
pub username: *mut c_char,
pub comm_type: PepCommType,
pub lang: [u8; 3],
pub me: bool,
pub major_ver: c_uint,
pub minor_ver: c_uint,
pub enc_format: PepEncFormat,
pub flags: PepIdentityFlags,
}
impl PepIdentity {
/// Returns a new identity.
///
/// The memory is allocated using the libc allocator. The caller
/// is responsible for freeing it explicitly.
pub fn new(mm: MM, template: &PepIdentityTemplate)
-> &'static mut Self
{
let buffer = if let Ok(buffer) = malloc_cleared::<Self>(mm) {
buffer
} else {
panic!("Out of memory allocating a PepIdentity");
};
let ident = unsafe { &mut *(buffer as *mut Self) };
ident.address = rust_str_to_c_str(mm, &template.address);
ident.fpr = rust_str_to_c_str(mm, &template.fpr.to_hex());
if let Some(username) = template.username.as_ref() {
ident.username = rust_str_to_c_str(mm, username);
}
ident
}
/// Converts the raw pointer to a Rust reference.
///
/// This does not take ownership of the object.
pub fn as_mut(ptr: *mut Self) -> Option<&'static mut Self> {
unsafe { ptr.as_mut() }
}
/// Returns the address.
pub fn address(&self) -> Option<&CStr> {
if self.address.is_null() {
None
} else {
Some(unsafe { CStr::from_ptr(self.address) })
}
}
/// Returns the fingerprint.
pub fn fingerprint(&self) -> Option<&CStr> {
if self.fpr.is_null() {
None
} else {
Some(unsafe { CStr::from_ptr(self.fpr) })
}
}
/// Replaces the fingerprint.
pub fn set_fingerprint(&mut self, mm: MM, fpr: Fingerprint) {
unsafe { libc::free(self.fpr as *mut _) };
self.fpr = rust_str_to_c_str(mm, fpr.to_hex());
}
/// Returns the username (in RFC 2822 speak: the display name).
pub fn username(&self) -> Option<&CStr> {
if self.username.is_null() {
None
} else {
Some(unsafe { CStr::from_ptr(self.username) })
}
}
/// Returns the identity flags.
pub fn identity_flags(&self) -> PepIdentityFlags {
self.flags
}
/// Returns whether the specified identity flag is set.
pub fn identity_flag(&self, flag: PepIdentityFlags) -> bool {
self.identity_flags().is_set(flag)
}
}
impl std::fmt::Debug for PepIdentity {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PepIdentity")
.field("address", &self.address())
.field("fingerprint", &self.fingerprint())
.field("username", &self.username())
.finish()
}
}
// See pEpEngine/src/pEpEngine.h:identity_list.
//
// https://gitea.pep.foundation/pEp.foundation/pEpEngine/src/branch/master/src/pEpEngine.h#L817
#[repr(C)]
pub struct PepIdentityListItem {
ident: *mut PepIdentity,
next: *mut PepIdentityListItem,
}
impl PepIdentityListItem {
/// Returns a new PepIdentityListItem containing `ident`.
///
/// `next` is set to NULL.
///
/// The memory is allocated using the libc allocator. The caller
/// is responsible for freeing it explicitly.
fn new(mm: MM, ident: &'static mut PepIdentity) -> &'static mut Self
{
let buffer = if let Ok(buffer) = malloc_cleared::<Self>(mm) {
buffer
} else {
panic!("Out of memory allocating a PepIdentityListItem");
};
let item = unsafe { &mut *(buffer as *mut Self) };
item.ident = ident as *mut _;
item
}
/// Converts the raw pointer to a Rust reference.
///
/// This does not take ownership of the object.
fn as_mut(ptr: *mut Self) -> Option<&'static mut Self> {
unsafe { ptr.as_mut() }
}
}
/// A wrapper structure for a `PepIdentity` list.
pub struct PepIdentityList {
head: *mut PepIdentityListItem,
owned: bool,
mm: MM,
}
impl PepIdentityList {
/// Converts the raw pointer to a Rust object.
///
/// `owned` indicates whether the rust code should own the items.
/// If so, when the `PepIdentityList` is dropped, the items will
/// also be freed.
pub fn to_rust(mm: MM, l: *mut PepIdentityListItem, owned: bool) -> Self
{
Self {
head: l,
owned,
mm,
}
}
/// Converts the Rust object to a raw pointer.
///
/// The items are owned by the raw pointer and need to be freed
/// explicitly using libc's `free`.
pub fn to_c(mut self) -> *mut PepIdentityListItem {
mem::replace(&mut self.head, ptr::null_mut())
}
/// Creates a new, empty PepIdentityList.
///
/// Any added items are owned by the `PepIdentityList`, and when
/// it is dropped, they are freed. To take ownership of the
/// items, call `PepIdentityList::to_c`.
pub fn empty(mm: MM) -> Self {
Self {
head: ptr::null_mut(),
owned: true,
mm,
}
}
/// Prepends the item to the list.
///
/// The item's ownership is determined by the list's ownership
/// property.
pub fn add(&mut self, ident: &PepIdentityTemplate) {
let ident = PepIdentityListItem::new(
self.mm, PepIdentity::new(self.mm, ident));
ident.next = self.head;
self.head = ident;
}
}
impl Drop for PepIdentityList {
fn drop(&mut self) {
let free = self.mm.free;
let mut curr: *mut PepIdentityListItem = self.head;
self.head = ptr::null_mut();
if self.owned {
loop {
let next = if let Some(curr) = PepIdentityListItem::as_mut(curr) {
let next = curr.next;
unsafe { free(curr.ident as *mut _) };
curr.ident = ptr::null_mut();
next
} else {
break;
};
unsafe { free(curr as *mut _) };
curr = next;
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::str::FromStr;
#[test]
fn identity() {
let mm = MM { malloc: libc::malloc, free: libc::free };
let address = "addr@ess";
let fpr = Fingerprint::from_str(
"0123 4567 89AB CDEF 0000 0123 4567 89ab cdef 0000").unwrap();
let username = "User Name";
let template = PepIdentityTemplate::new(address, fpr, Some(username));
let ident = PepIdentity::new(mm, &template);
assert_eq!(ident.address().map(|s| s.to_bytes()),
Some(address.as_bytes()));
// Be careful: the fingerprint is normalized.
assert_eq!(ident.fingerprint().map(|s| s.to_bytes()),
Some("0123456789ABCDEF00000123456789ABCDEF0000".as_bytes()));
assert_eq!(ident.username().map(|s| s.to_bytes()),
Some(username.as_bytes()));
}
#[test]
fn list() {
let mm = MM { malloc: libc::malloc, free: libc::free };
let mut list = PepIdentityList::empty(mm);
assert!(list.head.is_null());
let address = "addr@ess";
let fpr = Fingerprint::from_str(
"0123 4567 89AB CDEF 0000 0123 4567 89ab cdef 0000").unwrap();
let username = "User Name";
let template = PepIdentityTemplate::new(address, fpr, Some(username));
list.add(&template);
assert!(! list.head.is_null());
let item = PepIdentityListItem::as_mut(list.head).unwrap();
assert!(! item.ident.is_null());
let ident = PepIdentity::as_mut(item.ident).unwrap();
assert!(item.next.is_null());
assert_eq!(ident.address().map(|s| s.to_bytes()),
Some(address.as_bytes()));
// Be careful: the fingerprint is normalized.
assert_eq!(ident.fingerprint().map(|s| s.to_bytes()),
Some("0123456789ABCDEF00000123456789ABCDEF0000".as_bytes()));
assert_eq!(ident.username().map(|s| s.to_bytes()),
Some(username.as_bytes()));
}
}