//! GnuPG RPC support.
#![warn(missing_docs)]
use std::collections::BTreeMap;
use std::convert::TryFrom;
use std::ffi::OsStr;
use std::ops::{Deref, DerefMut};
use std::path::{Path, PathBuf};
use futures::{Future, Stream};
use std::task::{Poll, self};
use std::pin::Pin;
use sequoia_openpgp as openpgp;
use openpgp::types::HashAlgorithm;
use openpgp::fmt::hex;
use openpgp::crypto;
use openpgp::packet::prelude::*;
use openpgp::parse::Parse;
use crate::Result;
use crate::assuan;
use crate::Keygrip;
use crate::sexp::Sexp;
/// A GnuPG context.
#[derive(Debug)]
pub struct Context {
homedir: Option<PathBuf>,
sockets: BTreeMap<String, PathBuf>,
#[allow(dead_code)] // We keep it around for the cleanup.
ephemeral: Option<tempfile::TempDir>,
// XXX: Remove me once hack for Cygwin won't be necessary.
#[cfg(windows)]
cygwin: bool,
}
impl Context {
/// Creates a new context for the default GnuPG home directory.
pub fn new() -> Result<Self> {
Self::make(None, None)
}
/// Creates a new context for the given GnuPG home directory.
pub fn with_homedir<P>(homedir: P) -> Result<Self>
where P: AsRef<Path>
{
Self::make(Some(homedir.as_ref()), None)
}
/// Creates a new ephemeral context.
///
/// The created home directory will be deleted once this object is
/// dropped.
pub fn ephemeral() -> Result<Self> {
Self::make(None, Some(tempfile::tempdir()?))
}
fn make(homedir: Option<&Path>, ephemeral: Option<tempfile::TempDir>)
-> Result<Self> {
let mut sockets: BTreeMap<String, PathBuf> = Default::default();
let ephemeral_dir = ephemeral.as_ref().map(|tmp| tmp.path());
let homedir = ephemeral_dir.or(homedir);
// Guess if we're dealing with Unix/Cygwin or native Windows variant
// We need to do that in order to pass paths in correct style to gpgconf
let a_gpg_path = Self::gpgconf(&None, &["--list-dirs", "homedir"], 1)?;
let first_byte = a_gpg_path.get(0).and_then(|c| c.get(0)).and_then(|c| c.get(0));
let gpg_style = match first_byte {
Some(b'/') => Mode::Unix,
_ => Mode::native(),
};
let homedir = homedir.map(|dir|
convert_path(dir, gpg_style)
.unwrap_or_else(|_| PathBuf::from(dir))
);
for fields in Self::gpgconf(&homedir, &["--list-dirs"], 2)? {
let key = std::str::from_utf8(&fields[0])?;
// For now, we're only interested in sockets.
let socket = match key.strip_suffix("-socket"