/*
* 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 } ,
Address ,
} ;
use crate ::error ::{ ErrorKind , IntoMeliError , MeliError , Result , ResultIntoMeliError } ;
use futures ::FutureExt ;
use serde ::{
de ::{ self , Deserialize } ,
Deserializer , Serialize , Serializer ,
} ;
use smol ::Async ;
use std ::borrow ::Cow ;
use std ::collections ::HashMap ;
use std ::ffi ::{ CStr , CString , OsStr } ;
use std ::future ::Future ;
use std ::io ::Seek ;
use std ::os ::unix ::ffi ::OsStrExt ;
use std ::os ::unix ::io ::{ AsRawFd , RawFd } ;
use std ::path ::Path ;
use std ::sync ::{ Arc , Mutex } ;
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
} } ;
}
macro_rules! c_string_literal {
( $lit :literal ) = > { {
unsafe {
CStr ::from_bytes_with_nul_unchecked ( concat! ( $lit , "\0" ) . as_bytes ( ) ) . as_ptr ( )
as * const ::std ::os ::raw ::c_char
}
} } ;
}
mod bindings ;
use bindings ::* ;
mod io ;
#[ derive(Debug, Clone, Copy, PartialEq) ]
pub enum GpgmeFlag {
///"auto-key-retrieve"
AutoKeyRetrieve ,
OfflineMode ,
AsciiArmor ,
}
bitflags ! {
pub struct LocateKey : u8 {
/// Locate a key using DNS CERT, as specified in RFC-4398.
const CERT = 0b1 ;
/// Locate a key using DNS PKA.
const PKA = 0b10 ;
/// Locate a key using DANE, as specified in draft-ietf-dane-openpgpkey-05.txt.
const DANE = 0b100 ;
/// Locate a key using the Web Key Directory protocol.
const WKD = 0b1000 ;
/// Using DNS Service Discovery, check the domain in question for any LDAP keyservers to use. If this fails, attempt to locate the key using the PGP Universal method of checking ‘ ldap://keys.(thedomain)’ .
const LDAP = 0b10000 ;
/// Locate a key using a keyserver.
const KEYSERVER = 0b100000 ;
/// In addition, a keyserver URL as used in the dirmngr configuration may be used here to query that particular keyserver.
const KEYSERVER_URL = 0b1000000 ;
/// Locate the key using the local keyrings. This mechanism allows the user to select the order a local key lookup is done. Thus using ‘ --auto-key-locate local’ is identical to --no-auto-key-locate.
const LOCAL = 0b10000000 ;
/// This flag disables the standard local key lookup, done before any of the mechanisms defined by the --auto-key-locate are tried. The position of this mechanism in the list does not matter. It is not required if local is also used.
const NODEFAULT = 0 ;
}
}
impl < ' de > Deserialize < ' de > for LocateKey {
fn deserialize < D > ( deserializer : D ) -> std ::result ::Result < Self , D ::Error >
where
D : Deserializer < ' de > ,
{
if let Ok ( s ) = < String > ::deserialize ( deserializer ) {
LocateKey ::from_string_de ::< ' de , D , String > ( s )
} else {
Err ( de ::Error ::custom ( "LocateKey value must be a string." ) )
}
}
}
impl Serialize for LocateKey {
fn serialize < S > ( & self , serializer : S ) -> std ::result ::Result < S ::Ok , S ::Error >
where
S : Serializer ,
{
serializer . serialize_str ( & self . to_string ( ) )
}
}
impl LocateKey {
pub fn from_string_de < ' de , D , T : AsRef < str > > ( s : T ) -> std ::result ::Result < Self , D ::Error >
where
D : Deserializer < ' de > ,
{
Ok ( match s . as_ref ( ) . trim ( ) {
s if s . eq_ignore_ascii_case ( "cert" ) = > LocateKey ::CERT ,
s if s . eq_ignore_ascii_case ( "pka" ) = > LocateKey ::PKA ,
s if s . eq_ignore_ascii_case ( "dane" ) = > LocateKey ::DANE ,
s if s . eq_ignore_ascii_case ( "wkd" ) = > LocateKey ::WKD ,
s if s . eq_ignore_ascii_case ( "ldap" ) = > LocateKey ::LDAP ,
s if s . eq_ignore_ascii_case ( "keyserver" ) = > LocateKey ::KEYSERVER ,
s if s . eq_ignore_ascii_case ( "keyserver-url" ) = > LocateKey ::KEYSERVER_URL ,
s if s . eq_ignore_ascii_case ( "local" ) = > LocateKey ::LOCAL ,
combination if combination . contains ( "," ) = > {
let mut ret = LocateKey ::NODEFAULT ;
for c in combination . trim ( ) . split ( "," ) {
ret | = Self ::from_string_de ::< ' de , D , & str > ( c . trim ( ) ) ? ;
}
ret
}
_ = > {
return Err ( de ::Error ::custom (
r#"Takes valid auto-key-locate GPG values: "cert", "pka", "dane", "wkd", "ldap", "keyserver", "keyserver-URL", "local", "nodefault""# ,
) )
}
} )
}
}
impl std ::fmt ::Display for LocateKey {
fn fmt ( & self , fmt : & mut std ::fmt ::Formatter ) -> std ::fmt ::Result {
if * self = = LocateKey ::NODEFAULT {
write! ( fmt , "clear,nodefault" )
} else {
let mut accum = String ::new ( ) ;
macro_rules! is_set {
( $flag :expr , $string :literal ) = > { {
if self . intersects ( $flag ) {
accum . push_str ( $string ) ;
accum . push_str ( "," ) ;
}
} } ;
}
is_set ! ( LocateKey ::CERT , "cert" ) ;
is_set ! ( LocateKey ::PKA , "pka" ) ;
is_set ! ( LocateKey ::WKD , "wkd" ) ;
is_set ! ( LocateKey ::LDAP , "ldap" ) ;
is_set ! ( LocateKey ::KEYSERVER , "keyserver" ) ;
is_set ! ( LocateKey ::KEYSERVER_URL , "keyserver-url" ) ;
is_set ! ( LocateKey ::LOCAL , "local" ) ;
accum . pop ( ) ;
write! ( fmt , "{}" , accum )
}
}
}
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 lib = Arc ::new ( libloading ::Library ::new ( libloading ::library_filename (
"gpgme" ,
) ) ? ) ;
if unsafe { call ! ( & lib , gpgme_check_version ) ( GPGME_VERSION . as_bytes ( ) . as_ptr ( ) as * mut _ ) }
. is_null ( )
{
return Err ( MeliError ::new ( format! (
"Could not use libgpgme: requested version compatible with {} but got {}" ,
GPGME_VERSION ,
unsafe {
CStr ::from_ptr ( call ! ( & lib , gpgme_check_version ) ( std ::ptr ::null_mut ( ) ) )
. to_string_lossy ( )
} ,
) )
. set_kind ( ErrorKind ::External ) ) ;
} ;
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 ) ;
}
let ret = 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 ,
} ;
ret . set_flag ( GpgmeFlag ::AutoKeyRetrieve , false ) ? ;
ret . set_flag ( GpgmeFlag ::OfflineMode , true ) ? ;
ret . set_flag ( GpgmeFlag ::AsciiArmor , true ) ? ;
ret . set_auto_key_locate ( LocateKey ::LOCAL ) ? ;
Ok ( ret )
}
fn set_flag_inner (
& self ,
raw_flag : * const ::std ::os ::raw ::c_char ,
raw_value : * const ::std ::os ::raw ::c_char ,
) -> Result < ( ) > {
unsafe {
gpgme_error_try (
& self . inner . lib ,
call ! ( & self . inner . lib , gpgme_set_ctx_flag ) (
self . inner . inner . as_ptr ( ) ,
raw_flag ,
raw_value ,
) ,
) ? ;
}
Ok ( ( ) )
}
pub fn set_flag ( & self , flag : GpgmeFlag , value : bool ) -> Result < ( ) > {
match flag {
GpgmeFlag ::AutoKeyRetrieve = > { }
GpgmeFlag ::OfflineMode = > {
unsafe {
call ! ( & self . inner . lib , gpgme_set_offline ) (
self . inner . inner . as_ptr ( ) ,
if value { 1 } else { 0 } ,
) ;
} ;
return Ok ( ( ) ) ;
}
GpgmeFlag ::AsciiArmor = > {
unsafe {
call ! ( & self . inner . lib , gpgme_set_armor ) (
self . inner . inner . as_ptr ( ) ,
if value { 1 } else { 0 } ,
) ;
} ;
return Ok ( ( ) ) ;
}
} ;
const VALUE_ON : & [ u8 ; 2 ] = b" 1 \0 " ;
const VALUE_OFF : & [ u8 ; 2 ] = b" 0 \0 " ;
let raw_flag = match flag {
GpgmeFlag ::AutoKeyRetrieve = > c_string_literal ! ( "auto-key-retrieve" ) ,
GpgmeFlag ::AsciiArmor | GpgmeFlag ::OfflineMode = > unreachable! ( ) ,
} ;
self . set_flag_inner (
raw_flag ,
if value { VALUE_ON } else { VALUE_OFF } . as_ptr ( ) as * const ::std ::os ::raw ::c_char ,
)
}
fn get_flag_inner (
& self ,
raw_flag : * const ::std ::os ::raw ::c_char ,
) -> * const ::std ::os ::raw ::c_char {
unsafe { call ! ( & self . inner . lib , gpgme_get_ctx_flag ) ( self . inner . inner . as_ptr ( ) , raw_flag ) }
}
pub fn get_flag ( & self , flag : GpgmeFlag ) -> Result < bool > {
let raw_flag = match flag {
GpgmeFlag ::AutoKeyRetrieve = > c_string_literal ! ( "auto-key-retrieve" ) ,
GpgmeFlag ::OfflineMode = > {
return Ok ( unsafe {
call ! ( & self . inner . lib , gpgme_get_offline ) ( self . inner . inner . as_ptr ( ) ) > 0
} ) ;
}
GpgmeFlag ::AsciiArmor = > {
return Ok ( unsafe {
call ! ( & self . inner . lib , gpgme_get_armor ) ( self . inner . inner . as_ptr ( ) ) > 0
} ) ;
}
} ;
let val = self . get_flag_inner ( raw_flag ) ;
Ok ( ! val . is_null ( ) )
}
pub fn set_auto_key_locate ( & self , val : LocateKey ) -> Result < ( ) > {
let auto_key_locate : * const ::std ::os ::raw ::c_char = c_string_literal ! ( "auto-key-locate" ) ;
if val = = LocateKey ::NODEFAULT {
self . set_flag_inner ( auto_key_locate , c_string_literal ! ( "clear,nodefault" ) )
} else {
let mut accum = val . to_string ( ) ;
accum . push ( '\0' ) ;
self . set_flag_inner (
auto_key_locate ,
CStr ::from_bytes_with_nul ( accum . as_bytes ( ) )
. expect ( accum . as_str ( ) )
. as_ptr ( ) as * const _ ,
)
}
}
pub fn get_auto_key_locate ( & self ) -> Result < LocateKey > {
let auto_key_locate : * const ::std ::os ::raw ::c_char = c_string_literal ! ( "auto-key-locate" ) ;
let raw_value =
unsafe { CStr ::from_ptr ( self . get_flag_inner ( auto_key_locate ) ) } . to_string_lossy ( ) ;
let mut val = LocateKey ::NODEFAULT ;
if ! raw_value . contains ( "nodefault" ) {
for mechanism in raw_value . split ( "," ) {
match mechanism {
"cert" = > val . set ( LocateKey ::CERT , true ) ,
"pka" = > {
val . set ( LocateKey ::PKA , true ) ;
}
"wkd" = > {
val . set ( LocateKey ::WKD , true ) ;
}
"ldap" = > {
val . set ( LocateKey ::LDAP , true ) ;
}
"keyserver" = > {
val . set ( LocateKey ::KEYSERVER , true ) ;
}
"keyserver-url" = > {
val . set ( LocateKey ::KEYSERVER_URL , true ) ;
}
"local" = > {
val . set ( LocateKey ::LOCAL , true ) ;
}
unknown = > {
debug ! ( "unknown mechanism: {}" , unknown ) ;
}
}
}
}
Ok ( val )
}
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 ,
secret : bool ,
pattern : Option < String > ,
) -> Result < impl Future < Output = Result < Vec < Key > > > > {
let pattern = if let Some ( pattern ) = pattern {
Some ( CString ::new ( pattern ) ? )
} else {
None
} ;
unsafe {
gpgme_error_try (
& self . inner . lib ,
call ! ( & self . inner . lib , gpgme_op_keylist_start ) (
self . inner . inner . as_ptr ( ) ,
pattern
. as_ref ( )
. map ( | cs | cs . as_ptr ( ) )
. unwrap_or ( std ::ptr ::null_mut ( ) )
as * const ::std ::os ::raw ::c_char ,
if secret { 1 } else { 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 ( inner ) = key_receiver . try_recv ( ) {
let key = Key ::new ( inner , ctx . lib . clone ( ) ) ;
keys . push ( key ) ;
}
Ok ( keys )
} )
}
pub fn sign (
& mut self ,
sign_keys : Vec < Key > ,
mut text : Data ,
) -> Result < impl Future < Output = Result < Vec < u8 > > > > {
if sign_keys . is_empty ( ) {
return Err (
MeliError ::new ( "gpgme: Call to sign() with zero keys." ) . set_kind ( ErrorKind ::Bug )
) ;
}
let mut sig : gpgme_data_t = std ::ptr ::null_mut ( ) ;
unsafe {
gpgme_error_try (
& self . inner . lib ,
call ! ( & self . inner . lib , gpgme_data_new ) ( & mut sig ) ,
) ? ;
call ! ( & self . inner . lib , gpgme_signers_clear ) ( self . inner . inner . as_ptr ( ) ) ;
for k in sign_keys {
gpgme_error_try (
& self . inner . lib ,
call ! ( & self . inner . lib , gpgme_signers_add ) (
self . inner . inner . as_ptr ( ) ,
k . inner . inner . as_ptr ( ) ,
) ,
) ? ;
}
}
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 mut sig = Data {
lib : self . inner . lib . clone ( ) ,
kind : DataKind ::Memory ,
inner : core ::ptr ::NonNull ::new ( sig ) . ok_or_else ( | | {
MeliError ::new ( "internal libgpgme error" ) . set_kind ( ErrorKind ::Bug )
} ) ? ,
} ;
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 ( ) ;
io_state_lck
. done
. lock ( )
. unwrap ( )
. take ( )
. unwrap_or_else ( | | Err ( MeliError ::new ( "Unspecified libgpgme error" ) ) ) ? ;
sig . seek ( std ::io ::SeekFrom ::Start ( 0 ) )
. chain_err_summary ( | | {
"libgpgme error: could not perform seek on signature data object"
} ) ? ;
Ok ( sig . into_bytes ( ) ? )
} )
}
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 ;
}
}
/* 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 ( ) ? ,
) )
} )
}
pub fn encrypt (
& mut self ,
sign_keys : Option < Vec < Key > > ,
encrypt_keys : Vec < Key > ,
mut plain : Data ,
) -> Result < impl Future < Output = Result < Vec < u8 > > > + Send > {
if encrypt_keys . is_empty ( ) {
return Err (
MeliError ::new ( "gpgme: Call to encrypt() with zero keys." ) . set_kind ( ErrorKind ::Bug )
) ;
}
unsafe {
call ! ( & self . inner . lib , gpgme_signers_clear ) ( self . inner . inner . as_ptr ( ) ) ;
}
let also_sign : bool = if let Some ( keys ) = sign_keys {
if keys . is_empty ( ) {
false
} else {
for k in keys {
unsafe {
gpgme_error_try (
& self . inner . lib ,
call ! ( & self . inner . lib , gpgme_signers_add ) (
self . inner . inner . as_ptr ( ) ,
k . inner . inner . as_ptr ( ) ,
) ,
) ? ;
}
}
true
}
} else {
false
} ;
let mut cipher : gpgme_data_t = std ::ptr ::null_mut ( ) ;
let mut raw_keys : Vec < gpgme_key_t > = Vec ::with_capacity ( encrypt_keys . len ( ) + 1 ) ;
raw_keys . extend ( encrypt_keys . iter ( ) . map ( | k | k . inner . inner . as_ptr ( ) ) ) ;
raw_keys . push ( std ::ptr ::null_mut ( ) ) ;
unsafe {
gpgme_error_try (
& self . inner . lib ,
call ! ( & self . inner . lib , gpgme_data_new ) ( & mut cipher ) ,
) ? ;
gpgme_error_try (
& self . inner . lib ,
if also_sign {
call ! ( & self . inner . lib , gpgme_op_encrypt_sign_start ) (
self . inner . inner . as_ptr ( ) ,
raw_keys . as_mut_ptr ( ) ,
gpgme_encrypt_flags_t_GPGME_ENCRYPT_NO_ENCRYPT_TO
| gpgme_encrypt_flags_t_GPGME_ENCRYPT_NO_COMPRESS ,
plain . inner . as_mut ( ) ,
cipher ,
)
} else {
call ! ( & self . inner . lib , gpgme_op_encrypt_start ) (
self . inner . inner . as_ptr ( ) ,
raw_keys . as_mut_ptr ( ) ,
gpgme_encrypt_flags_t_GPGME_ENCRYPT_NO_ENCRYPT_TO
| gpgme_encrypt_flags_t_GPGME_ENCRYPT_NO_COMPRESS ,
plain . inner . as_mut ( ) ,
cipher ,
)
} ,
) ? ;
}
let mut cipher = Data {
lib : self . inner . lib . clone ( ) ,
kind : DataKind ::Memory ,
inner : core ::ptr ::NonNull ::new ( cipher ) . 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 {
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 encrypt_result =
unsafe { call ! ( & ctx . lib , gpgme_op_encrypt_result ) ( ctx . inner . as_ptr ( ) ) } ;
if encrypt_result . is_null ( ) {
return Err ( MeliError ::new (
"Unspecified libgpgme error: gpgme_op_encrypt_result returned NULL." ,
)
. set_err_kind ( ErrorKind ::External ) ) ;
}
/* Rewind cursor */
cipher
. seek ( std ::io ::SeekFrom ::Start ( 0 ) )
. chain_err_summary ( | | "libgpgme error: could not perform seek on plain text" ) ? ;
Ok ( cipher . 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 Key {
#[ inline(always) ]
fn new ( inner : KeyInner , lib : Arc < libloading ::Library > ) -> Self {
Key { inner , lib }
}
pub fn primary_uid ( & self ) -> Option < Address > {
unsafe {
if ( * ( self . inner . inner . as_ptr ( ) ) ) . uids . is_null ( ) {
return None ;
}
let uid = ( * ( self . inner . inner . as_ptr ( ) ) ) . uids ;
if ( * uid ) . name . is_null ( ) & & ( * uid ) . email . is_null ( ) {
None
} else if ( * uid ) . name . is_null ( ) {
Some ( Address ::new (
None ,
CStr ::from_ptr ( ( * uid ) . email ) . to_string_lossy ( ) . to_string ( ) ,
) )
} else if ( * uid ) . email . is_null ( ) {
Some ( Address ::new (
None ,
CStr ::from_ptr ( ( * uid ) . name ) . to_string_lossy ( ) . to_string ( ) ,
) )
} else {
Some ( Address ::new (
Some ( CStr ::from_ptr ( ( * uid ) . name ) . to_string_lossy ( ) . to_string ( ) ) ,
CStr ::from_ptr ( ( * uid ) . email ) . to_string_lossy ( ) . to_string ( ) ,
) )
}
}
}
pub fn revoked ( & self ) -> bool {
unsafe { ( * self . inner . inner . as_ptr ( ) ) . revoked ( ) > 0 }
}
pub fn expired ( & self ) -> bool {
unsafe { ( * self . inner . inner . as_ptr ( ) ) . expired ( ) > 0 }
}
pub fn disabled ( & self ) -> bool {
unsafe { ( * self . inner . inner . as_ptr ( ) ) . disabled ( ) > 0 }
}
pub fn invalid ( & self ) -> bool {
unsafe { ( * self . inner . inner . as_ptr ( ) ) . invalid ( ) > 0 }
}
pub fn can_encrypt ( & self ) -> bool {
unsafe { ( * self . inner . inner . as_ptr ( ) ) . can_encrypt ( ) > 0 }
}
pub fn can_sign ( & self ) -> bool {
unsafe { ( * self . inner . inner . as_ptr ( ) ) . can_sign ( ) > 0 }
}
pub fn secret ( & self ) -> bool {
unsafe { ( * self . inner . inner . as_ptr ( ) ) . secret ( ) > 0 }
}
pub fn fingerprint ( & self ) -> Cow < ' _ , str > {
( unsafe { CStr ::from_ptr ( ( * ( self . inner . inner . as_ptr ( ) ) ) . fpr ) } ) . to_string_lossy ( )
}
}
impl std ::fmt ::Debug for Key {
fn fmt ( & self , fmt : & mut std ::fmt ::Formatter ) -> std ::fmt ::Result {
fmt . debug_struct ( "Key" )
. field ( "fingerprint" , & self . fingerprint ( ) )
. field ( "uid" , & self . primary_uid ( ) )
. field ( "can_encrypt" , & self . can_encrypt ( ) )
. field ( "can_sign" , & self . can_sign ( ) )
. field ( "secret" , & self . secret ( ) )
. field ( "revoked" , & self . revoked ( ) )
. field ( "expired" , & self . expired ( ) )
. field ( "invalid" , & self . invalid ( ) )
. finish ( )
}
}
impl std ::cmp ::PartialEq for Key {
fn eq ( & self , other : & Key ) -> bool {
self . fingerprint ( ) = = other . fingerprint ( )
}
}
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())
// );
//}