//! GnuPG RPC support.
#![warn(missing_docs)]
use std::collections::BTreeMap;
use std::convert::TryFrom;
use std::ops::{Deref, DerefMut};
use std::path::{Path, PathBuf};
use std::process::Command;
use futures::{Async, Future, Stream};
use sequoia_openpgp as openpgp;
use openpgp::types::HashAlgorithm;
use openpgp::fmt::hex;
use openpgp::crypto;
use openpgp::crypto::sexp::Sexp;
use openpgp::packet::prelude::*;
use openpgp::parse::Parse;
use openpgp::serialize::Serialize;
use crate::Result;
use crate::assuan;
/// A GnuPG context.
#[derive(Debug)]
pub struct Context {
homedir: Option<PathBuf>,
components: BTreeMap<String, PathBuf>,
directories: BTreeMap<String, PathBuf>,
sockets: BTreeMap<String, PathBuf>,
#[allow(dead_code)] // We keep it around for the cleanup.
ephemeral: Option<tempfile::TempDir>,
}
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 components: BTreeMap<String, PathBuf> = Default::default();
let mut directories: BTreeMap<String, PathBuf> = Default::default();
let mut sockets: BTreeMap<String, PathBuf> = Default::default();
let homedir: Option<PathBuf> =
ephemeral.as_ref().map(|tmp| tmp.path()).or(homedir)
.map(|p| p.into());
for fields in Self::gpgconf(
&homedir, &["--list-components"], 3)?.into_iter()
{
components.insert(String::from_utf8(fields[0].clone())?,
String::from_utf8(fields[2].clone())?.into());
}
for fields in Self::gpgconf(&homedir, &["--list-dirs"], 2)?.into_iter()
{
let (mut key, value) = (fields[0].clone(), fields[1].clone());
if key.ends_with(b"-socket") {
let l = key.len();
key.truncate(l - b"-socket".len());
sockets.insert(String::from_utf8(key)?,
String::from_utf8(value)?.into());
} else {
directories.insert(String::from_utf8(key)?,
String::from_utf8(value)?.into());
}
}
Ok(Context {
homedir,
components,
directories,
sockets,
ephemeral,
})
}
fn gpgconf(homedir: &Option<PathBuf>, arguments: &[&str], nfields: usize)