mirror of https://git.meli.delivery/meli/meli
add libgpgme feature
parent
b9c07bacef
commit
23ca41e3e8
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,155 @@
|
||||
/*
|
||||
* melib - gpgme module
|
||||
*
|
||||
* Copyright 2020 Manos Pitsidianakis
|
||||
*
|
||||
* This file is part of meli.
|
||||
*
|
||||
* meli is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* meli is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use super::*;
|
||||
use std::io::{self, Read, Seek, Write};
|
||||
|
||||
#[repr(C)]
|
||||
struct TagData {
|
||||
idx: usize,
|
||||
fd: ::std::os::raw::c_int,
|
||||
io_state: Arc<Mutex<IoState>>,
|
||||
}
|
||||
|
||||
pub unsafe extern "C" fn gpgme_register_io_cb(
|
||||
data: *mut ::std::os::raw::c_void,
|
||||
fd: ::std::os::raw::c_int,
|
||||
dir: ::std::os::raw::c_int,
|
||||
fnc: gpgme_io_cb_t,
|
||||
fnc_data: *mut ::std::os::raw::c_void,
|
||||
tag: *mut *mut ::std::os::raw::c_void,
|
||||
) -> gpgme_error_t {
|
||||
let io_state: Arc<Mutex<IoState>> = Arc::from_raw(data as *const _);
|
||||
let io_state_copy = io_state.clone();
|
||||
let mut io_state_lck = io_state.lock().unwrap();
|
||||
let idx = io_state_lck.max_idx;
|
||||
io_state_lck.max_idx += 1;
|
||||
let (sender, receiver) = smol::channel::unbounded();
|
||||
let gpgfd = GpgmeFd {
|
||||
fd,
|
||||
fnc,
|
||||
fnc_data,
|
||||
idx,
|
||||
write: dir == 0,
|
||||
sender,
|
||||
receiver,
|
||||
io_state: io_state_copy.clone(),
|
||||
};
|
||||
let tag_data = Arc::into_raw(Arc::new(TagData {
|
||||
idx,
|
||||
fd,
|
||||
io_state: io_state_copy,
|
||||
}));
|
||||
core::ptr::write(tag, tag_data as *mut _);
|
||||
io_state_lck.ops.insert(idx, gpgfd);
|
||||
drop(io_state_lck);
|
||||
let _ = Arc::into_raw(io_state);
|
||||
0
|
||||
}
|
||||
|
||||
pub unsafe extern "C" fn gpgme_remove_io_cb(tag: *mut ::std::os::raw::c_void) {
|
||||
let tag_data: Arc<TagData> = Arc::from_raw(tag as *const _);
|
||||
let mut io_state_lck = tag_data.io_state.lock().unwrap();
|
||||
let fd = io_state_lck.ops.remove(&tag_data.idx).unwrap();
|
||||
fd.sender.try_send(()).unwrap();
|
||||
drop(io_state_lck);
|
||||
let _ = Arc::into_raw(tag_data);
|
||||
}
|
||||
|
||||
pub unsafe extern "C" fn gpgme_event_io_cb(
|
||||
data: *mut ::std::os::raw::c_void,
|
||||
type_: gpgme_event_io_t,
|
||||
type_data: *mut ::std::os::raw::c_void,
|
||||
) {
|
||||
if type_ == gpgme_event_io_t_GPGME_EVENT_DONE {
|
||||
let err = type_data as gpgme_io_event_done_data_t;
|
||||
let io_state: Arc<Mutex<IoState>> = Arc::from_raw(data as *const _);
|
||||
let mut io_state_lck = io_state.lock().unwrap();
|
||||
io_state_lck.sender.try_send(()).unwrap();
|
||||
*io_state_lck.done.lock().unwrap() = Some(gpgme_error_try(&io_state_lck.lib, (*err).err));
|
||||
drop(io_state_lck);
|
||||
let _ = Arc::into_raw(io_state);
|
||||
} else if type_ == gpgme_event_io_t_GPGME_EVENT_NEXT_KEY {
|
||||
if let Some(inner) = core::ptr::NonNull::new(type_data as gpgme_key_t) {
|
||||
let io_state: Arc<Mutex<IoState>> = Arc::from_raw(data as *const _);
|
||||
let io_state_lck = io_state.lock().unwrap();
|
||||
io_state_lck
|
||||
.key_sender
|
||||
.try_send(KeyInner { inner })
|
||||
.unwrap();
|
||||
drop(io_state_lck);
|
||||
let _ = Arc::into_raw(io_state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Read for Data {
|
||||
#[inline]
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
let result = unsafe {
|
||||
let (buf, len) = (buf.as_mut_ptr() as *mut _, buf.len());
|
||||
call!(self.lib, gpgme_data_read)(self.inner.as_ptr(), buf, len)
|
||||
};
|
||||
if result >= 0 {
|
||||
Ok(result as usize)
|
||||
} else {
|
||||
Err(io::Error::last_os_error().into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for Data {
|
||||
#[inline]
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
let result = unsafe {
|
||||
let (buf, len) = (buf.as_ptr() as *const _, buf.len());
|
||||
call!(self.lib, gpgme_data_write)(self.inner.as_ptr(), buf, len)
|
||||
};
|
||||
if result >= 0 {
|
||||
Ok(result as usize)
|
||||
} else {
|
||||
Err(io::Error::last_os_error().into())
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Seek for Data {
|
||||
#[inline]
|
||||
fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
|
||||
use std::convert::TryInto;
|
||||
let (off, whence) = match pos {
|
||||
io::SeekFrom::Start(off) => (off.try_into().unwrap_or(i64::MAX), libc::SEEK_SET),
|
||||
io::SeekFrom::End(off) => (off.saturating_abs(), libc::SEEK_END),
|
||||
io::SeekFrom::Current(off) => (off, libc::SEEK_CUR),
|
||||
};
|
||||
let result = unsafe { call!(self.lib, gpgme_data_seek)(self.inner.as_ptr(), off, whence) };
|
||||
if result >= 0 {
|
||||
Ok(result as u64)
|
||||
} else {
|
||||
Err(io::Error::last_os_error().into())
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,798 @@
|
||||
/*
|
||||
* melib - gpgme module
|
||||
*
|
||||
* Copyright 2020 Manos Pitsidianakis
|
||||
*
|
||||
* This file is part of meli.
|
||||
*
|
||||
* meli is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* meli is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with meli. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use crate::email::pgp::{DecryptionMetadata, Recipient};
|
||||
use crate::error::{ErrorKind, IntoMeliError, MeliError, Result, ResultIntoMeliError};
|
||||
use futures::FutureExt;
|
||||
use smol::Async;
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::{CStr, CString, OsStr};
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
use std::os::unix::io::{AsRawFd, RawFd};
|
||||
use std::path::Path;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use std::future::Future;
|
||||
|
||||
macro_rules! call {
|
||||
($lib:expr, $func:ty) => {{
|
||||
let func: libloading::Symbol<$func> =
|
||||
$lib.get(stringify!($func).as_bytes()).expect(concat!(
|
||||
"Could not use libgpgme: symbol ",
|
||||
stringify!($func),
|
||||
" not found!"
|
||||
));
|
||||
func
|
||||
}};
|
||||
}
|
||||
mod bindings;
|
||||
use bindings::*;
|
||||
mod io;
|
||||
|
||||
struct IoState {
|
||||
max_idx: usize,
|
||||
ops: HashMap<usize, GpgmeFd>,
|
||||
done: Arc<Mutex<Option<Result<()>>>>,
|
||||
sender: smol::channel::Sender<()>,
|
||||
receiver: smol::channel::Receiver<()>,
|
||||
key_sender: smol::channel::Sender<KeyInner>,
|
||||
key_receiver: smol::channel::Receiver<KeyInner>,
|
||||
lib: Arc<libloading::Library>,
|
||||
}
|
||||
|
||||
unsafe impl Send for IoState {}
|
||||
unsafe impl Sync for IoState {}
|
||||
|
||||
pub struct ContextInner {
|
||||
inner: core::ptr::NonNull<gpgme_context>,
|
||||
lib: Arc<libloading::Library>,
|
||||
}
|
||||
|
||||
unsafe impl Send for ContextInner {}
|
||||
unsafe impl Sync for ContextInner {}
|
||||
|
||||
pub struct Context {
|
||||
inner: Arc<ContextInner>,
|
||||
io_state: Arc<Mutex<IoState>>,
|
||||
}
|
||||
|
||||
unsafe impl Send for Context {}
|
||||
unsafe impl Sync for Context {}
|
||||
|
||||
impl Drop for ContextInner {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe { call!(self.lib, gpgme_release)(self.inner.as_mut()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Context {
|
||||
pub fn new() -> Result<Self> {
|
||||
let version = CString::new("1.12.0").unwrap();
|
||||
let lib = Arc::new(libloading::Library::new("libgpgme.so")?);
|
||||
unsafe { call!(&lib, gpgme_check_version)(version.as_c_str().as_ptr() as *mut _) };
|
||||
let (sender, receiver) = smol::channel::unbounded();
|
||||
let (key_sender, key_receiver) = smol::channel::unbounded();
|
||||
|
||||
let mut ptr = core::ptr::null_mut();
|
||||
let io_state = Arc::new(Mutex::new(IoState {
|
||||
max_idx: 0,
|
||||
ops: HashMap::default(),
|
||||
done: Arc::new(Mutex::new(None)),
|
||||
sender,
|
||||
receiver,
|
||||
key_sender,
|
||||
key_receiver,
|
||||
lib: lib.clone(),
|
||||
}));
|
||||
let add_priv_data = io_state.clone();
|
||||
let event_priv_data = io_state.clone();
|
||||
|
||||
let mut io_cbs = gpgme_io_cbs {
|
||||
add: Some(io::gpgme_register_io_cb),
|
||||
add_priv: Arc::into_raw(add_priv_data) as *mut ::std::os::raw::c_void, //add_priv: *mut ::std::os::raw::c_void,
|
||||
remove: Some(io::gpgme_remove_io_cb),
|
||||
event: Some(io::gpgme_event_io_cb),
|
||||
event_priv: Arc::into_raw(event_priv_data) as *mut ::std::os::raw::c_void, //pub event_priv: *mut ::std::os::raw::c_void,
|
||||
};
|
||||
|
||||
unsafe {
|
||||
gpgme_error_try(&lib, call!(&lib, gpgme_new)(&mut ptr))?;
|
||||
call!(&lib, gpgme_set_io_cbs)(ptr, &mut io_cbs);
|
||||
}
|
||||
Ok(Context {
|
||||
inner: Arc::new(ContextInner {
|
||||
inner: core::ptr::NonNull::new(ptr).ok_or_else(|| {
|
||||
MeliError::new("Could not use libgpgme").set_kind(ErrorKind::Bug)
|
||||
})?,
|
||||
lib,
|
||||
}),
|
||||
io_state,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn new_data_mem(&self, bytes: &[u8]) -> Result<Data> {
|
||||
let mut ptr = core::ptr::null_mut();
|
||||
unsafe {
|
||||
gpgme_error_try(
|
||||
&self.inner.lib,
|
||||
call!(&self.inner.lib, gpgme_data_new_from_mem)(
|
||||
&mut ptr,
|
||||
bytes.as_ptr() as *const ::std::os::raw::c_char,
|
||||
bytes.len(),
|
||||
1,
|
||||
),
|
||||
)?;
|
||||
}
|
||||
|
||||
Ok(Data {
|
||||
lib: self.inner.lib.clone(),
|
||||
kind: DataKind::Memory,
|
||||
inner: core::ptr::NonNull::new(ptr).ok_or_else(|| {
|
||||
MeliError::new("Could not create libgpgme data").set_kind(ErrorKind::Bug)
|
||||
})?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn new_data_file<P: AsRef<Path>>(&self, r: P) -> Result<Data> {
|
||||
let path: &Path = r.as_ref();
|
||||
if !path.exists() {
|
||||
return Err(MeliError::new(format!(
|
||||
"File `{}` doesn't exist.",
|
||||
path.display()
|
||||
)));
|
||||
}
|
||||
let os_str: &OsStr = path.as_ref();
|
||||
let b = CString::new(os_str.as_bytes())?;
|
||||
let mut ptr = core::ptr::null_mut();
|
||||
unsafe {
|
||||
let ret: GpgmeError = call!(&self.inner.lib, gpgme_data_new_from_file)(
|
||||
&mut ptr,
|
||||
b.as_ptr() as *const ::std::os::raw::c_char,
|
||||
1,
|
||||
);
|
||||
gpgme_error_try(&self.inner.lib, ret)?;
|
||||
}
|
||||
|
||||
Ok(Data {
|
||||
lib: self.inner.lib.clone(),
|
||||
kind: DataKind::Memory,
|
||||
inner: core::ptr::NonNull::new(ptr).ok_or_else(|| {
|
||||
MeliError::new("Could not create libgpgme data").set_kind(ErrorKind::Bug)
|
||||
})?,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn verify(
|
||||
&mut self,
|
||||
mut signature: Data,
|
||||
mut text: Data,
|
||||
) -> Result<impl Future<Output = Result<()>> + Send> {
|
||||
unsafe {
|
||||
gpgme_error_try(
|
||||
&self.inner.lib,
|
||||
call!(&self.inner.lib, gpgme_op_verify_start)(
|
||||
self.inner.inner.as_ptr(),
|
||||
signature.inner.as_mut(),
|
||||
text.inner.as_mut(),
|
||||
std::ptr::null_mut(),
|
||||
),
|
||||
)?;
|
||||
}
|
||||
|
||||
let ctx = self.inner.clone();
|
||||
let io_state = self.io_state.clone();
|
||||
let io_state_lck = self.io_state.lock().unwrap();
|
||||
let done = io_state_lck.done.clone();
|
||||
let fut = io_state_lck
|
||||
.ops
|
||||
.values()
|
||||
.map(|a| Async::new(a.clone()).unwrap())
|
||||
.collect::<Vec<Async<GpgmeFd>>>();
|
||||
drop(io_state_lck);
|
||||
Ok(async move {
|
||||
let _s = signature;
|
||||
let _t = text;
|
||||
futures::future::join_all(fut.iter().map(|fut| {
|
||||
let done = done.clone();
|
||||
if fut.get_ref().write {
|
||||
futures::future::select(
|
||||
fut.get_ref().receiver.recv().boxed(),
|
||||
fut.write_with(move |_f| {
|
||||
if done.lock().unwrap().is_some() {
|
||||
return Ok(());
|
||||
}
|
||||
unsafe {
|
||||
(fut.get_ref().fnc.unwrap())(
|
||||
fut.get_ref().fnc_data,
|
||||
fut.get_ref().fd,
|
||||
)
|
||||
};
|
||||
if done.lock().unwrap().is_none() {
|
||||
return Err(std::io::ErrorKind::WouldBlock.into());
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
.boxed(),
|
||||
)
|
||||
.boxed()
|
||||
} else {
|
||||
futures::future::select(
|
||||
fut.get_ref().receiver.recv().boxed(),
|
||||
fut.read_with(move |_f| {
|
||||
if done.lock().unwrap().is_some() {
|
||||
return Ok(());
|
||||
}
|
||||
unsafe {
|
||||
(fut.get_ref().fnc.unwrap())(
|
||||
fut.get_ref().fnc_data,
|
||||
fut.get_ref().fd,
|
||||
)
|
||||
};
|
||||
if done.lock().unwrap().is_none() {
|
||||
return Err(std::io::ErrorKind::WouldBlock.into());
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
.boxed(),
|
||||
)
|
||||
.boxed()
|
||||
}
|
||||
}))
|
||||
.await;
|
||||
debug!("done with fut join");
|
||||
let rcv = {
|
||||
let io_state_lck = io_state.lock().unwrap();
|
||||
io_state_lck.receiver.clone()
|
||||
};
|
||||
let _ = rcv.recv().await;
|
||||
{
|
||||
let verify_result =
|
||||
unsafe { call!(&ctx.lib, gpgme_op_verify_result)(ctx.inner.as_ptr()) };
|
||||
if verify_result.is_null() {
|
||||
return Err(MeliError::new(
|
||||
"Unspecified libgpgme error: gpgme_op_verify_result returned NULL.",
|
||||
)
|
||||
.set_err_kind(ErrorKind::External));
|
||||
}
|
||||
drop(verify_result);
|
||||
}
|
||||
let io_state_lck = io_state.lock().unwrap();
|
||||
let ret = io_state_lck
|
||||
.done
|
||||
.lock()
|
||||
.unwrap()
|
||||
.take()
|
||||
.unwrap_or_else(|| Err(MeliError::new("Unspecified libgpgme error")));
|
||||
ret
|
||||
})
|
||||
}
|
||||
|
||||
pub fn keylist(&mut self) -> Result<impl Future<Output = Result<Vec<String>>>> {
|
||||
unsafe {
|
||||
gpgme_error_try(
|
||||
&self.inner.lib,
|
||||
call!(&self.inner.lib, gpgme_op_keylist_start)(
|
||||
self.inner.inner.as_ptr(),
|
||||
std::ptr::null_mut(),
|
||||
0,
|
||||
),
|
||||
)?;
|
||||
}
|
||||
|
||||
let ctx = self.inner.clone();
|
||||
let io_state = self.io_state.clone();
|
||||
let io_state_lck = self.io_state.lock().unwrap();
|
||||
let done = io_state_lck.done.clone();
|
||||
let fut = io_state_lck
|
||||
.ops
|
||||
.values()
|
||||
.map(|a| Async::new(a.clone()).unwrap())
|
||||
.collect::<Vec<Async<GpgmeFd>>>();
|
||||
drop(io_state_lck);
|
||||
Ok(async move {
|
||||
futures::future::join_all(fut.iter().map(|fut| {
|
||||
let done = done.clone();
|
||||
if fut.get_ref().write {
|
||||
futures::future::select(
|
||||
fut.get_ref().receiver.recv().boxed(),
|
||||
fut.write_with(move |_f| {
|
||||
if done.lock().unwrap().is_some() {
|
||||
return Ok(());
|
||||
}
|
||||
unsafe {
|
||||
(fut.get_ref().fnc.unwrap())(
|
||||
fut.get_ref().fnc_data,
|
||||
fut.get_ref().fd,
|
||||
)
|
||||
};
|
||||
if done.lock().unwrap().is_none() {
|
||||
return Err(std::io::ErrorKind::WouldBlock.into());
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
.boxed(),
|
||||
)
|
||||
.boxed()
|
||||
} else {
|
||||
futures::future::select(
|
||||
fut.get_ref().receiver.recv().boxed(),
|
||||
fut.read_with(move |_f| {
|
||||
if done.lock().unwrap().is_some() {
|
||||
return Ok(());
|
||||
}
|
||||
unsafe {
|
||||
(fut.get_ref().fnc.unwrap())(
|
||||
fut.get_ref().fnc_data,
|
||||
fut.get_ref().fd,
|
||||
)
|
||||
};
|
||||
if done.lock().unwrap().is_none() {
|
||||
return Err(std::io::ErrorKind::WouldBlock.into());
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
.boxed(),
|
||||
)
|
||||
.boxed()
|
||||
}
|
||||
}))
|
||||
.await;
|
||||
let (rcv, key_receiver) = {
|
||||
let io_state_lck = io_state.lock().unwrap();
|
||||
(
|
||||
io_state_lck.receiver.clone(),
|
||||
io_state_lck.key_receiver.clone(),
|
||||
)
|
||||
};
|
||||
let _ = rcv.recv().await;
|
||||
unsafe {
|
||||
gpgme_error_try(
|
||||
&ctx.lib,
|
||||
call!(&ctx.lib, gpgme_op_keylist_end)(ctx.inner.as_ptr()),
|
||||
)?;
|
||||
}
|
||||
let io_state_lck = io_state.lock().unwrap();
|
||||
io_state_lck
|
||||
.done
|
||||
.lock()
|
||||
.unwrap()
|
||||
.take()
|
||||
.unwrap_or_else(|| Err(MeliError::new("Unspecified libgpgme error")))?;
|
||||
let mut keys = vec![];
|
||||
while let Ok(key) = key_receiver.try_recv() {
|
||||
unsafe {
|
||||
if (*(key.inner.as_ptr())).uids.is_null() {
|
||||
keys.push("null".to_string());
|
||||
} else {
|
||||
keys.push(format!(
|
||||
"{} <{}>",
|
||||
CStr::from_ptr((*((*(key.inner.as_ptr())).uids)).name)
|
||||
.to_string_lossy(),
|
||||
CStr::from_ptr((*((*(key.inner.as_ptr())).uids)).email)
|
||||
.to_string_lossy()
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(keys)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn sign<'d>(
|
||||
&mut self,
|
||||
text: &'d mut Data,
|
||||
) -> Result<impl Future<Output = Result<()>> + 'd> {
|
||||
let sig = std::ptr::null_mut();
|
||||
unsafe {
|
||||
gpgme_error_try(
|
||||
&self.inner.lib,
|
||||
call!(&self.inner.lib, gpgme_op_sign_start)(
|
||||
self.inner.inner.as_ptr(),
|
||||
text.inner.as_mut(),
|
||||
sig,
|
||||
gpgme_sig_mode_t_GPGME_SIG_MODE_DETACH,
|
||||
),
|
||||
)?;
|
||||
}
|
||||
|
||||
let io_state = self.io_state.clone();
|
||||
let io_state_lck = self.io_state.lock().unwrap();
|
||||
let done = io_state_lck.done.clone();
|
||||
let fut = io_state_lck
|
||||
.ops
|
||||
.values()
|
||||
.map(|a| Async::new(a.clone()).unwrap())
|
||||
.collect::<Vec<Async<GpgmeFd>>>();
|
||||
drop(io_state_lck);
|
||||
Ok(async move {
|
||||
futures::future::join_all(fut.iter().map(|fut| {
|
||||
let done = done.clone();
|
||||
if fut.get_ref().write {
|
||||
futures::future::select(
|
||||
fut.get_ref().receiver.recv().boxed(),
|
||||
fut.write_with(move |_f| {
|
||||
if done.lock().unwrap().is_some() {
|
||||
return Ok(());
|
||||
}
|
||||
unsafe {
|
||||
(fut.get_ref().fnc.unwrap())(
|
||||
fut.get_ref().fnc_data,
|
||||
fut.get_ref().fd,
|
||||
)
|
||||
};
|
||||
if done.lock().unwrap().is_none() {
|
||||
return Err(std::io::ErrorKind::WouldBlock.into());
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
.boxed(),
|
||||
)
|
||||
.boxed()
|
||||
} else {
|
||||
futures::future::select(
|
||||
fut.get_ref().receiver.recv().boxed(),
|
||||
fut.read_with(move |_f| {
|
||||
if done.lock().unwrap().is_some() {
|
||||
return Ok(());
|
||||
}
|
||||
unsafe {
|
||||
(fut.get_ref().fnc.unwrap())(
|
||||
fut.get_ref().fnc_data,
|
||||
fut.get_ref().fd,
|
||||
)
|
||||
};
|
||||
if done.lock().unwrap().is_none() {
|
||||
return Err(std::io::ErrorKind::WouldBlock.into());
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
.boxed(),
|
||||
)
|
||||
.boxed()
|
||||
}
|
||||
}))
|
||||
.await;
|
||||
let rcv = {
|
||||
let io_state_lck = io_state.lock().unwrap();
|
||||
io_state_lck.receiver.clone()
|
||||
};
|
||||
let _ = rcv.recv().await;
|
||||
let io_state_lck = io_state.lock().unwrap();
|
||||
let ret = io_state_lck
|
||||
.done
|
||||
.lock()
|
||||
.unwrap()
|
||||
.take()
|
||||
.unwrap_or_else(|| Err(MeliError::new("Unspecified libgpgme error")));
|
||||
ret
|
||||
})
|
||||
}
|
||||
|
||||
pub fn decrypt(
|
||||
&mut self,
|
||||
mut cipher: Data,
|
||||
) -> Result<impl Future<Output = Result<(DecryptionMetadata, Vec<u8>)>> + Send> {
|
||||
let mut plain: gpgme_data_t = std::ptr::null_mut();
|
||||
unsafe {
|
||||
gpgme_error_try(
|
||||
&self.inner.lib,
|
||||
call!(&self.inner.lib, gpgme_data_new)(&mut plain),
|
||||
)?;
|
||||
gpgme_error_try(
|
||||
&self.inner.lib,
|
||||
call!(&self.inner.lib, gpgme_op_decrypt_start)(
|
||||
self.inner.inner.as_ptr(),
|
||||
cipher.inner.as_mut(),
|
||||
plain,
|
||||
),
|
||||
)?;
|
||||
}
|
||||
let mut plain = Data {
|
||||
lib: self.inner.lib.clone(),
|
||||
kind: DataKind::Memory,
|
||||
inner: core::ptr::NonNull::new(plain).ok_or_else(|| {
|
||||
MeliError::new("internal libgpgme error").set_kind(ErrorKind::Bug)
|
||||
})?,
|
||||
};
|
||||
|
||||
let ctx = self.inner.clone();
|
||||
let io_state = self.io_state.clone();
|
||||
let io_state_lck = self.io_state.lock().unwrap();
|
||||
let done = io_state_lck.done.clone();
|
||||
let fut = io_state_lck
|
||||
.ops
|
||||
.values()
|
||||
.map(|a| Async::new(a.clone()).unwrap())
|
||||
.collect::<Vec<Async<GpgmeFd>>>();
|
||||
drop(io_state_lck);
|
||||
Ok(async move {
|
||||
let _c = cipher;
|
||||
futures::future::join_all(fut.iter().map(|fut| {
|
||||
let done = done.clone();
|
||||
if fut.get_ref().write {
|
||||
futures::future::select(
|
||||
fut.get_ref().receiver.recv().boxed(),
|
||||
fut.write_with(move |_f| {
|
||||
if done.lock().unwrap().is_some() {
|
||||
return Ok(());
|
||||
}
|
||||
unsafe {
|
||||
(fut.get_ref().fnc.unwrap())(
|
||||
fut.get_ref().fnc_data,
|
||||
fut.get_ref().fd,
|
||||
)
|
||||
};
|
||||
if done.lock().unwrap().is_none() {
|
||||
return Err(std::io::ErrorKind::WouldBlock.into());
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
.boxed(),
|
||||
)
|
||||
.boxed()
|
||||
} else {
|
||||
futures::future::select(
|
||||
fut.get_ref().receiver.recv().boxed(),
|
||||
fut.read_with(move |_f| {
|
||||
if done.lock().unwrap().is_some() {
|
||||
return Ok(());
|
||||
}
|
||||
unsafe {
|
||||
(fut.get_ref().fnc.unwrap())(
|
||||
fut.get_ref().fnc_data,
|
||||
fut.get_ref().fd,
|
||||
)
|
||||
};
|
||||
if done.lock().unwrap().is_none() {
|
||||
return Err(std::io::ErrorKind::WouldBlock.into());
|
||||
}
|
||||
Ok(())
|
||||
})
|
||||
.boxed(),
|
||||
)
|
||||
.boxed()
|
||||
}
|
||||
}))
|
||||
.await;
|
||||
let rcv = {
|
||||
let io_state_lck = io_state.lock().unwrap();
|
||||
io_state_lck.receiver.clone()
|
||||
};
|
||||
let _ = rcv.recv().await;
|
||||
let io_state_lck = io_state.lock().unwrap();
|
||||
io_state_lck
|
||||
.done
|
||||
.lock()
|
||||
.unwrap()
|
||||
.take()
|
||||
.unwrap_or_else(|| Err(MeliError::new("Unspecified libgpgme error")))?;
|
||||
|
||||
let decrypt_result =
|
||||
unsafe { call!(&ctx.lib, gpgme_op_decrypt_result)(ctx.inner.as_ptr()) };
|
||||
if decrypt_result.is_null() {
|
||||
return Err(MeliError::new(
|
||||
"Unspecified libgpgme error: gpgme_op_decrypt_result returned NULL.",
|
||||
)
|
||||
.set_err_kind(ErrorKind::External));
|
||||
}
|
||||
let mut recipients = vec![];
|
||||
let is_mime;
|
||||
let file_name;
|
||||
let session_key;
|
||||
unsafe {
|
||||
is_mime = (*decrypt_result).is_mime() > 0;
|
||||
file_name = if !(*decrypt_result).file_name.is_null() {
|
||||
Some(
|
||||
CStr::from_ptr((*decrypt_result).file_name)
|
||||
.to_string_lossy()
|
||||
.to_string(),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
session_key = if !(*decrypt_result).session_key.is_null() {
|
||||
Some(
|
||||
CStr::from_ptr((*decrypt_result).session_key)
|
||||
.to_string_lossy()
|
||||
.to_string(),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let mut recipient_iter = (*decrypt_result).recipients;
|
||||
while !recipient_iter.is_null() {
|
||||
recipients.push(Recipient {
|
||||
keyid: if !(*recipient_iter).keyid.is_null() {
|
||||
Some(
|
||||
CStr::from_ptr((*recipient_iter).keyid)
|
||||
.to_string_lossy()
|
||||
.to_string(),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
status: gpgme_error_try(&ctx.lib, (*recipient_iter).status),
|
||||
});
|
||||
recipient_iter = (*recipient_iter).next;
|
||||
}
|
||||
}
|
||||
use std::io::Seek;
|
||||
/* Rewind cursor */
|
||||
plain
|
||||
.seek(std::io::SeekFrom::Start(0))
|
||||
.chain_err_summary(|| "libgpgme error: could not perform seek on plain text")?;
|
||||
Ok((
|
||||
DecryptionMetadata {
|
||||
recipients,
|
||||
file_name,
|
||||
session_key,
|
||||
is_mime,
|
||||
},
|
||||
plain.into_bytes()?,
|
||||
))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn gpgme_error_try(lib: &libloading::Library, error_code: GpgmeError) -> Result<()> {
|
||||
const ERR_MAX_LEN: usize = 256;
|
||||
if error_code == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
let mut buf: Vec<u8> = vec![0; ERR_MAX_LEN];
|
||||
unsafe {
|
||||
call!(lib, gpgme_strerror_r)(
|
||||
error_code,
|
||||
buf.as_mut_ptr() as *mut ::std::os::raw::c_char,
|
||||
ERR_MAX_LEN,
|
||||
);
|
||||
}
|
||||
while buf.ends_with(&b"\0"[..]) {
|
||||
buf.pop();
|
||||
}
|
||||
Err(MeliError::from(
|
||||
String::from_utf8(buf)
|
||||
.unwrap_or_else(|err| String::from_utf8_lossy(&err.into_bytes()).to_string()),
|
||||
)
|
||||
.set_summary(format!("libgpgme error {}", error_code)))
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum DataKind {
|
||||
Memory,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Data {
|
||||
inner: core::ptr::NonNull<bindings::gpgme_data>,
|
||||
kind: DataKind,
|
||||
lib: Arc<libloading::Library>,
|
||||
}
|
||||
|
||||
impl Data {
|
||||
pub fn into_bytes(mut self) -> Result<Vec<u8>> {
|
||||
use std::io::Read;
|
||||
let mut buf = vec![];
|
||||
self.read_to_end(&mut buf)?;
|
||||
Ok(buf)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for Data {}
|
||||
unsafe impl Sync for Data {}
|
||||
|
||||
impl Drop for Data {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
if !self.inner.as_ptr().is_null() {
|
||||
match self.kind {
|
||||
DataKind::Memory => unsafe {
|
||||
call!(self.lib, gpgme_data_release)(self.inner.as_mut())
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone)]
|
||||
struct GpgmeFd {
|
||||
fd: RawFd,
|
||||
fnc: GpgmeIOCb,
|
||||
fnc_data: *mut ::std::os::raw::c_void,
|
||||
idx: usize,
|
||||
write: bool,
|
||||
sender: smol::channel::Sender<()>,
|
||||
receiver: smol::channel::Receiver<()>,
|
||||
io_state: Arc<Mutex<IoState>>,
|
||||
}
|
||||
|
||||
unsafe impl Send for GpgmeFd {}
|
||||
unsafe impl Sync for GpgmeFd {}
|
||||
|
||||
impl AsRawFd for GpgmeFd {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.fd
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct KeyInner {
|
||||
inner: core::ptr::NonNull<_gpgme_key>,
|
||||
}
|
||||
|
||||
unsafe impl Send for KeyInner {}
|
||||
unsafe impl Sync for KeyInner {}
|
||||
|
||||
pub struct Key {
|
||||
inner: KeyInner,
|
||||
lib: Arc<libloading::Library>,
|
||||
}
|
||||
unsafe impl Send for Key {}
|
||||
unsafe impl Sync for Key {}
|
||||
|
||||
impl Clone for Key {
|
||||
fn clone(&self) -> Self {
|
||||
let lib = self.lib.clone();
|
||||
unsafe {
|
||||
call!(&self.lib, gpgme_key_ref)(self.inner.inner.as_ptr());
|
||||
}
|
||||
Key {
|
||||
inner: self.inner.clone(),
|
||||
lib,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Key {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
call!(&self.lib, gpgme_key_unref)(self.inner.inner.as_ptr());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//#[test]
|
||||
//fn test_gpgme() {
|
||||
// std::thread::spawn(move || {
|
||||
// let ex = smol::Executor::new();
|
||||
// futures::executor::block_on(ex.run(futures::future::pending::<()>()));
|
||||
// });
|
||||
// let mut ctx = Context::new().unwrap();
|
||||
// //let sig = ctx.new_data_mem("sign").unwrap();
|
||||
// //let text = ctx.new_data_mem("file").unwrap();
|
||||
// let sig = ctx.new_data_mem(include_bytes!("/tmp/sig")).unwrap();
|
||||
// let text = ctx.new_data_mem(include_bytes!("/tmp/data")).unwrap();
|
||||
//
|
||||
// futures::executor::block_on(ctx.verify(sig, text).unwrap()).unwrap();
|
||||
// println!(
|
||||
// "keys = {:#?}",
|
||||
// futures::executor::block_on(ctx.keylist().unwrap()).unwrap()
|
||||
// );
|
||||
// let cipher = ctx.new_data_file("/tmp/msg.asc").unwrap();
|
||||
// let plain = futures::executor::block_on(ctx.decrypt(cipher).unwrap()).unwrap();
|
||||
// println!(
|
||||
// "buf: {}",
|
||||
// String::from_utf8_lossy(&plain.into_bytes().unwrap())
|
||||
// );
|
||||
//}
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue