use anyhow::Context;
use sequoia_openpgp as openpgp;
use openpgp::{
Result,
KeyHandle,
KeyID,
Fingerprint,
armor,
cert::{
Cert,
CertParser,
},
packet::{
UserID,
},
parse::Parse,
serialize::Serialize,
};
use sequoia_net as net;
use net::{
KeyServer,
wkd,
};
use crate::{
Config,
open_or_stdin,
create_or_stdout,
create_or_stdout_pgp,
serialize_keyring,
};
fn parse_network_policy(m: &clap::ArgMatches) -> net::Policy {
match m.value_of("policy").expect("has default value") {
"offline" => net::Policy::Offline,
"anonymized" => net::Policy::Anonymized,
"encrypted" => net::Policy::Encrypted,
"insecure" => net::Policy::Insecure,
_ => unreachable!(),
}
}
pub fn dispatch_keyserver(config: Config, m: &clap::ArgMatches) -> Result<()> {
let network_policy = parse_network_policy(m);
let mut ks = if let Some(uri) = m.value_of("server") {
KeyServer::new(network_policy, &uri)
} else {
KeyServer::keys_openpgp_org(network_policy)
}.context("Malformed keyserver URI")?;
let mut rt = tokio::runtime::Builder::new()
.basic_scheduler()
.enable_io()
.enable_time()
.build()?;
match m.subcommand() {
("get", Some(m)) => {
let query = m.value_of("query").unwrap();
let handle: Option<KeyHandle> = {
let q_fp = query.parse::<Fingerprint>();
let q_id = query.parse::<KeyID>();
if let Ok(Fingerprint::V4(_)) = q_fp {
q_fp.ok().map(Into::into)
} else if let Ok(KeyID::V4(_)) = q_id {
q_fp.ok().map(Into::into)
} else {
None
}
};
if let Some(handle) = handle {
let cert = rt.block_on(ks.get(handle))
.context("Failed to retrieve cert")?;
let mut output =
create_or_stdout(m.value_of("output"), config.force)?;
if ! m.is_present("binary") {
cert.armored().serialize(&mut output)
} else {
cert.serialize(&mut output)
}.context("Failed to serialize cert")?;
} else if let Ok(Some(addr)) = UserID::from(query).email() {
let certs = rt.block_on(ks.search(addr))
.context("Failed to retrieve certs")?;
let mut output =
create_or_stdout_pgp(m.value_of("output"), config.force,
m.is_present("binary"),
armor::Kind::PublicKey)?;
for cert in certs {
cert.serialize(&mut output)
.context("Failed to serialize cert")?;
}
output.finalize()?;
} else {
Err(anyhow::anyhow!(
"Query must be a fingerprint, a keyid, \
or an email address: {:?}", query))?;
}
},
("send", Some(m)) => {
let mut input = open_or_stdin(m.value_of("input"))?;
let cert = Cert::from_reader(&mut input).
context("Malformed key")?;
rt.block_on(ks.send(&cert))
.context("Failed to send key to server")?;
},
_ => unreachable!(),
}
Ok(())
}
pub fn dispatch_wkd(config: Config, m: &clap::ArgMatches) -> Result<()> {
let network_policy = parse_network_policy(m);
let mut rt = tokio::runtime::Builder::new()
.basic_scheduler()
.enable_io()
.enable_time()
.build()?;
match m.subcommand() {
("url", Some(m)) => {
let email_address = m.value_of("input").unwrap();
let wkd_url = wkd::Url::from(email_address)?;
let url = wkd_url.to_url(None)?;
println!("{}", url);
},
("get", Some(m)) => {
network_policy.assert(net::Policy::Encrypted)?;
let email_address = m.value_of("input").unwrap();
let certs = rt.block_on(wkd::get(&email_address))?;
let mut output = create_or_stdout(m.value_of("output"),
config.force)?;
serialize_keyring(&mut output, &certs,
m.is_present("binary"))?;
},
("generate", Some(m)) => {
let domain = m.value_of("domain").unwrap();
let f = open_or_stdin(m.value_of("input"))?;
let base_path =
m.value_of("base_directory").expect("required");
let variant = if m.is_present("direct_method") {
wkd::Variant::Direct
} else {
wkd::Variant::Advanced
};
let parser = CertParser::from_reader(f)?;
let certs: Vec<Cert> = parser.filter_map(|cert| cert.ok())
.collect();
for cert in certs {
wkd::insert(&base_path, domain, variant, &cert)
.context(format!("Failed to generate the WKD in \
{}.", base_path))?;
}
},
_ => unreachable!(),
}
Ok(())
}