//! 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::{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 openpgp::serialize::Serialize;
use crate::Result;
use crate::assuan;
use crate::Keygrip;
use crate::sexp::Sexp;
/// 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 ephemeral_dir = ephemeral.as_ref().map(|tmp| tmp.path());
let homedir = ephemeral_dir.or(homedir).map(PathBuf::from);
for fields in Self::gpgconf(&homedir, &["--list-components"], 3)? {
components.insert(String::from_utf8(fields[0].clone())?,
String::from_utf8(fields[2].clone())?.into());
}
for fields in Self::gpgconf(&homedir, &["--list-dirs"], 2)? {
// NOTE: Directories and socket paths are percent-encoded if no
// argument to "--list-dirs" is given
let key = std::str::from_utf8(&fields[0])?;
let mut value = std::str::from_utf8(&fields[1])?.to_owned();
// FIXME: Percent-decode everything, but for now at least decode
// colons to support Windows drive letters
value = value.replace("%3a", ":");
match key.strip_suffix("-socket") {
None => directories.insert(key.into(), value.into()),
Some(key) => sockets.insert(key.into(), value.into()),
};
}
Ok(Context {
homedir,
components,
directories,
sockets,
ephemeral,
})
}