summaryrefslogtreecommitdiffstats
path: root/sq/src/commands/net.rs
diff options
context:
space:
mode:
Diffstat (limited to 'sq/src/commands/net.rs')
-rw-r--r--sq/src/commands/net.rs168
1 files changed, 168 insertions, 0 deletions
diff --git a/sq/src/commands/net.rs b/sq/src/commands/net.rs
new file mode 100644
index 00000000..c531f69e
--- /dev/null
+++ b/sq/src/commands/net.rs
@@ -0,0 +1,168 @@
+//! Network services.
+
+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,
+};
+
+pub fn dispatch_keyserver(config: Config, m: &clap::ArgMatches) -> Result<()> {
+ let mut ks = if let Some(uri) = m.value_of("server") {
+ KeyServer::new(config.network_policy, &uri)
+ } else {
+ KeyServer::keys_openpgp_org(config.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 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)?;
+ // XXX: Add other subcomand to specify whether it should be
+ // created with the advanced or the direct method.
+ let url = wkd_url.to_url(None)?;
+ println!("{}", url);
+ },
+ ("get", Some(m)) => {
+ let email_address = m.value_of("input").unwrap();
+ // XXX: EmailAddress could be created here to
+ // check it's a valid email address, print the error to
+ // stderr and exit.
+ // Because it might be created a WkdServer struct, not
+ // doing it for now.
+ let certs = rt.block_on(wkd::get(&email_address))?;
+ // ```text
+ // The HTTP GET method MUST return the binary representation of the
+ // OpenPGP key for the given mail address.
+ // [draft-koch]: https://datatracker.ietf.org/doc/html/draft-koch-openpgp-webkey-service-07
+ // ```
+ // But to keep the parallelism with `store export` and `keyserver get`,
+ // The output is armored if not `--binary` option is given.
+ 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(())
+}