summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJustus Winter <justus@sequoia-pgp.org>2021-01-15 10:16:10 +0100
committerJustus Winter <justus@sequoia-pgp.org>2021-01-15 11:34:49 +0100
commitc69380439fe2dbe0d4ced968a273e21cd474f15e (patch)
treefd60cec4aaee6c02bd7e8f3685e1f1453601cc8b
parentde649ea58966273392f4ab09f2bb72bb75650ae2 (diff)
sq: Move the store-related functionality to its own file.
-rw-r--r--sq/src/commands/mappings.rs222
-rw-r--r--sq/src/commands/mod.rs37
-rw-r--r--sq/src/sq.rs211
3 files changed, 265 insertions, 205 deletions
diff --git a/sq/src/commands/mappings.rs b/sq/src/commands/mappings.rs
new file mode 100644
index 00000000..0846cb52
--- /dev/null
+++ b/sq/src/commands/mappings.rs
@@ -0,0 +1,222 @@
+use anyhow::Context;
+
+use prettytable::{Table, Cell, Row, row, cell};
+
+use sequoia_openpgp as openpgp;
+use openpgp::{
+ Result,
+ cert::{
+ Cert,
+ },
+ parse::Parse,
+ serialize::Serialize,
+};
+use sequoia_store as store;
+use store::{
+ Mapping,
+ LogIter,
+};
+
+use crate::{
+ Config,
+ help_warning,
+ commands::dump::Convert,
+ open_or_stdin,
+ create_or_stdout,
+};
+
+pub fn dispatch_mapping(config: Config, m: &clap::ArgMatches) -> Result<()> {
+ let mapping = Mapping::open(&config.context, config.network_policy,
+ &config.realm_name, &config.mapping_name)
+ .context("Failed to open the mapping")?;
+
+ match m.subcommand() {
+ ("list", Some(_)) => {
+ list_bindings(&mapping, &config.realm_name, &config.mapping_name)?;
+ },
+ ("add", Some(m)) => {
+ let fp = m.value_of("fingerprint").unwrap().parse()
+ .expect("Malformed fingerprint");
+ mapping.add(m.value_of("label").unwrap(), &fp)?;
+ },
+ ("import", Some(m)) => {
+ let label = m.value_of("label").unwrap();
+ help_warning(label);
+ let mut input = open_or_stdin(m.value_of("input"))?;
+ let cert = Cert::from_reader(&mut input)?;
+ mapping.import(label, &cert)?;
+ },
+ ("export", Some(m)) => {
+ let cert = mapping.lookup(m.value_of("label").unwrap())?.cert()?;
+ let mut output = create_or_stdout(m.value_of("output"),
+ config.force)?;
+ if m.is_present("binary") {
+ cert.serialize(&mut output)?;
+ } else {
+ cert.armored().serialize(&mut output)?;
+ }
+ },
+ ("delete", Some(m)) => {
+ if m.is_present("label") == m.is_present("the-mapping") {
+ return Err(anyhow::anyhow!(
+ "Please specify either a label or --the-mapping."));
+ }
+
+ if m.is_present("the-mapping") {
+ mapping.delete().context("Failed to delete the mapping")?;
+ } else {
+ let binding = mapping.lookup(m.value_of("label").unwrap())
+ .context("Failed to get key")?;
+ binding.delete().context("Failed to delete the binding")?;
+ }
+ },
+ ("stats", Some(m)) => {
+ mapping_print_stats(&mapping,
+ m.value_of("label").unwrap())?;
+ },
+ ("log", Some(m)) => {
+ if m.is_present("label") {
+ let binding = mapping.lookup(m.value_of("label").unwrap())
+ .context("No such key")?;
+ print_log(binding.log().context("Failed to get log")?, false);
+ } else {
+ print_log(mapping.log().context("Failed to get log")?, true);
+ }
+ },
+ _ => unreachable!(),
+ }
+
+ Ok(())
+}
+
+pub fn dispatch_list(config: Config, m: &clap::ArgMatches) -> Result<()> {
+ match m.subcommand() {
+ ("mappings", Some(m)) => {
+ let mut table = Table::new();
+ table.set_format(*prettytable::format::consts::FORMAT_NO_LINESEP_WITH_TITLE);
+ table.set_titles(row!["realm", "name", "network policy"]);
+
+ for (realm, name, network_policy, _)
+ in Mapping::list(&config.context, m.value_of("prefix").unwrap_or(""))? {
+ table.add_row(Row::new(vec![
+ Cell::new(&realm),
+ Cell::new(&name),
+ Cell::new(&format!("{:?}", network_policy))
+ ]));
+ }
+
+ table.printstd();
+ },
+ ("bindings", Some(m)) => {
+ for (realm, name, _, mapping)
+ in Mapping::list(&config.context, m.value_of("prefix").unwrap_or(""))? {
+ list_bindings(&mapping, &realm, &name)?;
+ }
+ },
+ ("keys", Some(_)) => {
+ let mut table = Table::new();
+ table.set_format(*prettytable::format::consts::FORMAT_NO_LINESEP_WITH_TITLE);
+ table.set_titles(row!["fingerprint", "updated", "status"]);
+
+ for (fingerprint, key) in store::Store::list_keys(&config.context)? {
+ let stats = key.stats()
+ .context("Failed to get key stats")?;
+ table.add_row(Row::new(vec![
+ Cell::new(&fingerprint.to_string()),
+ if let Some(t) = stats.updated {
+ Cell::new(&t.convert().to_string())
+ } else {
+ Cell::new("")
+ },
+ Cell::new("")
+ ]));
+ }
+
+ table.printstd();
+ },
+ ("log", Some(_)) => {
+ print_log(store::Store::server_log(&config.context)?, true);
+ },
+ _ => unreachable!(),
+ }
+
+ Ok(())
+}
+
+fn list_bindings(mapping: &Mapping, realm: &str, name: &str)
+ -> Result<()> {
+ if mapping.iter()?.count() == 0 {
+ println!("No label-key bindings in the \"{}/{}\" mapping.",
+ realm, name);
+ return Ok(());
+ }
+
+ println!("Realm: {:?}, mapping: {:?}:", realm, name);
+
+ let mut table = Table::new();
+ table.set_format(*prettytable::format::consts::FORMAT_NO_LINESEP_WITH_TITLE);
+ table.set_titles(row!["label", "fingerprint"]);
+ for (label, fingerprint, _) in mapping.iter()? {
+ table.add_row(Row::new(vec![
+ Cell::new(&label),
+ Cell::new(&fingerprint.to_string())]));
+ }
+ table.printstd();
+ Ok(())
+}
+
+fn print_log(iter: LogIter, with_slug: bool) {
+ let mut table = Table::new();
+ table.set_format(*prettytable::format::consts::FORMAT_NO_LINESEP_WITH_TITLE);
+ let mut head = row!["timestamp", "message"];
+ if with_slug {
+ head.insert_cell(1, Cell::new("slug"));
+ }
+ table.set_titles(head);
+
+ for entry in iter {
+ let mut row = row![&entry.timestamp.convert().to_string(),
+ &entry.short()];
+ if with_slug {
+ row.insert_cell(1, Cell::new(&entry.slug));
+ }
+ table.add_row(row);
+ }
+
+ table.printstd();
+}
+
+pub fn mapping_print_stats(mapping: &store::Mapping, label: &str) -> Result<()> {
+ fn print_stamps(st: &store::Stamps) -> Result<()> {
+ println!("{} messages using this key", st.count);
+ if let Some(t) = st.first {
+ println!(" First: {}", t.convert());
+ }
+ if let Some(t) = st.last {
+ println!(" Last: {}", t.convert());
+ }
+ Ok(())
+ }
+
+ fn print_stats(st: &store::Stats) -> Result<()> {
+ if let Some(t) = st.created {
+ println!(" Created: {}", t.convert());
+ }
+ if let Some(t) = st.updated {
+ println!(" Updated: {}", t.convert());
+ }
+ print!(" Encrypted ");
+ print_stamps(&st.encryption)?;
+ print!(" Verified ");
+ print_stamps(&st.verification)?;
+ Ok(())
+ }
+
+ let binding = mapping.lookup(label)?;
+ println!("Binding {:?}", label);
+ print_stats(&binding.stats().context("Failed to get stats")?)?;
+ let key = binding.key().context("Failed to get key")?;
+ println!("Key");
+ print_stats(&key.stats().context("Failed to get stats")?)?;
+ Ok(())
+}
diff --git a/sq/src/commands/mod.rs b/sq/src/commands/mod.rs
index 2cd73fe4..0c4728ef 100644
--- a/sq/src/commands/mod.rs
+++ b/sq/src/commands/mod.rs
@@ -33,7 +33,6 @@ pub use self::decrypt::decrypt;
mod sign;
pub use self::sign::sign;
pub mod dump;
-use dump::Convert;
pub use self::dump::dump;
mod inspect;
pub use self::inspect::inspect;
@@ -41,6 +40,7 @@ pub mod key;
pub mod merge_signatures;
pub use self::merge_signatures::merge_signatures;
pub mod certring;
+pub mod mappings;
/// Returns suitable signing keys from a given list of Certs.
fn get_signing_keys(certs: &[openpgp::Cert], p: &dyn Policy,
@@ -507,38 +507,3 @@ pub fn join(inputs: Option<clap::Values>, output: &mut dyn io::Write)
}
Ok(())
}
-
-pub fn mapping_print_stats(mapping: &store::Mapping, label: &str) -> Result<()> {
- fn print_stamps(st: &store::Stamps) -> Result<()> {
- println!("{} messages using this key", st.count);
- if let Some(t) = st.first {
- println!(" First: {}", t.convert());
- }
- if let Some(t) = st.last {
- println!(" Last: {}", t.convert());
- }
- Ok(())
- }
-
- fn print_stats(st: &store::Stats) -> Result<()> {
- if let Some(t) = st.created {
- println!(" Created: {}", t.convert());
- }
- if let Some(t) = st.updated {
- println!(" Updated: {}", t.convert());
- }
- print!(" Encrypted ");
- print_stamps(&st.encryption)?;
- print!(" Verified ");
- print_stamps(&st.verification)?;
- Ok(())
- }
-
- let binding = mapping.lookup(label)?;
- println!("Binding {:?}", label);
- print_stats(&binding.stats().context("Failed to get stats")?)?;
- let key = binding.key().context("Failed to get key")?;
- println!("Key");
- print_stats(&key.stats().context("Failed to get stats")?)?;
- Ok(())
-}
diff --git a/sq/src/sq.rs b/sq/src/sq.rs
index 2a6f1c65..d84c51e8 100644
--- a/sq/src/sq.rs
+++ b/sq/src/sq.rs
@@ -4,7 +4,6 @@ use crossterm;
use crossterm::terminal;
use anyhow::Context as _;
-use prettytable::{Table, Cell, Row, row, cell};
use std::fs::OpenOptions;
use std::io::{self, Write};
use std::path::{Path, PathBuf};
@@ -15,7 +14,7 @@ use buffered_reader::File;
use sequoia_openpgp as openpgp;
use sequoia_core;
use sequoia_net;
-use sequoia_store as store;
+use sequoia_store::Mapping;
use openpgp::{
Result,
@@ -37,11 +36,9 @@ use crate::openpgp::policy::StandardPolicy as P;
use sequoia_core::Context;
use sequoia_net as net;
use sequoia_net::{KeyServer, wkd};
-use store::{Mapping, LogIter};
mod sq_cli;
mod commands;
-use commands::dump::Convert;
fn open_or_stdin(f: Option<&str>) -> Result<Box<dyn io::Read + Send + Sync>> {
match f {
@@ -238,6 +235,14 @@ fn help_warning(arg: &str) {
}
}
+pub struct Config {
+ force: bool,
+ network_policy: net::Policy,
+ context: sequoia_core::Context,
+ realm_name: String,
+ mapping_name: String,
+}
+
fn main() -> Result<()> {
let policy = &mut P::new();
@@ -273,6 +278,16 @@ fn main() -> Result<()> {
builder = builder.home(dir);
}
let ctx = builder.build()?;
+
+ let config = Config {
+ force,
+ network_policy,
+ context: ctx,
+ realm_name: realm_name.into(),
+ mapping_name: mapping_name.into(),
+ };
+
+
let mut rt = tokio::runtime::Builder::new()
.basic_scheduler()
.enable_io()
@@ -291,18 +306,22 @@ fn main() -> Result<()> {
let secrets = m.values_of("secret-key-file")
.map(load_keys)
.unwrap_or(Ok(vec![]))?;
- let mut mapping = Mapping::open(&ctx, network_policy, realm_name,
- mapping_name)
+ let mut mapping = Mapping::open(&config.context,
+ config.network_policy,
+ &config.realm_name,
+ &config.mapping_name)
.context("Failed to open the mapping")?;
- commands::decrypt(&ctx, policy, &mut mapping,
+ commands::decrypt(&config.context, policy, &mut mapping,
&mut input, &mut output,
signatures, certs, secrets,
m.is_present("dump-session-key"),
m.is_present("dump"), m.is_present("hex"))?;
},
("encrypt", Some(m)) => {
- let mapping = Mapping::open(&ctx, network_policy, realm_name,
- mapping_name)
+ let mapping = Mapping::open(&config.context,
+ config.network_policy,
+ &config.realm_name,
+ &config.mapping_name)
.context("Failed to open the mapping")?;
let mut recipients = m.values_of("recipients-cert-file")
.map(load_certs)
@@ -386,10 +405,12 @@ fn main() -> Result<()> {
let certs = m.values_of("sender-cert-file")
.map(load_certs)
.unwrap_or(Ok(vec![]))?;
- let mut mapping = Mapping::open(&ctx, network_policy, realm_name,
- mapping_name)
+ let mut mapping = Mapping::open(&config.context,
+ config.network_policy,
+ &config.realm_name,
+ &config.mapping_name)
.context("Failed to open the mapping")?;
- commands::verify(&ctx, policy, &mut mapping, &mut input,
+ commands::verify(&config.context, policy, &mut mapping, &mut input,
detached.as_mut().map(|r| r as &mut (dyn io::Read + Sync + Send)),
&mut output, signatures, certs)?;
},
@@ -482,11 +503,13 @@ fn main() -> Result<()> {
let secrets = m.values_of("secret-key-file")
.map(load_keys)
.unwrap_or(Ok(vec![]))?;
- let mut mapping = Mapping::open(&ctx, network_policy,
- realm_name, mapping_name)
+ let mut mapping = Mapping::open(&config.context,
+ config.network_policy,
+ &config.realm_name,
+ &config.mapping_name)
.context("Failed to open the mapping")?;
commands::decrypt::decrypt_unwrap(
- &ctx, policy, &mut mapping,
+ &config.context, policy, &mut mapping,
&mut input, &mut output,
secrets, m.is_present("dump-session-key"))?;
output.finalize()?;
@@ -585,117 +608,10 @@ fn main() -> Result<()> {
_ => unreachable!(),
}
},
- ("mapping", Some(m)) => {
- let mapping = Mapping::open(&ctx, network_policy, realm_name,
- mapping_name)
- .context("Failed to open the mapping")?;
-
- match m.subcommand() {
- ("list", Some(_)) => {
- list_bindings(&mapping, realm_name, mapping_name)?;
- },
- ("add", Some(m)) => {
- let fp = m.value_of("fingerprint").unwrap().parse()
- .expect("Malformed fingerprint");
- mapping.add(m.value_of("label").unwrap(), &fp)?;
- },
- ("import", Some(m)) => {
- let label = m.value_of("label").unwrap();
- help_warning(label);
- let mut input = open_or_stdin(m.value_of("input"))?;
- let cert = Cert::from_reader(&mut input)?;
- mapping.import(label, &cert)?;
- },
- ("export", Some(m)) => {
- let cert = mapping.lookup(m.value_of("label").unwrap())?.cert()?;
- let mut output = create_or_stdout(m.value_of("output"), force)?;
- if m.is_present("binary") {
- cert.serialize(&mut output)?;
- } else {
- cert.armored().serialize(&mut output)?;
- }
- },
- ("delete", Some(m)) => {
- if m.is_present("label") == m.is_present("the-mapping") {
- eprintln!("Please specify either a label or --the-mapping.");
- exit(1);
- }
-
- if m.is_present("the-mapping") {
- mapping.delete().context("Failed to delete the mapping")?;
- } else {
- let binding = mapping.lookup(m.value_of("label").unwrap())
- .context("Failed to get key")?;
- binding.delete().context("Failed to delete the binding")?;
- }
- },
- ("stats", Some(m)) => {
- commands::mapping_print_stats(&mapping,
- m.value_of("label").unwrap())?;
- },
- ("log", Some(m)) => {
- if m.is_present("label") {
- let binding = mapping.lookup(m.value_of("label").unwrap())
- .context("No such key")?;
- print_log(binding.log().context("Failed to get log")?, false);
- } else {
- print_log(mapping.log().context("Failed to get log")?, true);
- }
- },
- _ => unreachable!(),
- }
- },
- ("list", Some(m)) => {
- match m.subcommand() {
- ("mappings", Some(m)) => {
- let mut table = Table::new();
- table.set_format(*prettytable::format::consts::FORMAT_NO_LINESEP_WITH_TITLE);
- table.set_titles(row!["realm", "name", "network policy"]);
-
- for (realm, name, network_policy, _)
- in Mapping::list(&ctx, m.value_of("prefix").unwrap_or(""))? {
- table.add_row(Row::new(vec![
- Cell::new(&realm),
- Cell::new(&name),
- Cell::new(&format!("{:?}", network_policy))
- ]));
- }
-
- table.printstd();
- },
- ("bindings", Some(m)) => {
- for (realm, name, _, mapping)
- in Mapping::list(&ctx, m.value_of("prefix").unwrap_or(""))? {
- list_bindings(&mapping, &realm, &name)?;
- }
- },
- ("keys", Some(_)) => {
- let mut table = Table::new();
- table.set_format(*prettytable::format::consts::FORMAT_NO_LINESEP_WITH_TITLE);
- table.set_titles(row!["fingerprint", "updated", "status"]);
-
- for (fingerprint, key) in store::Store::list_keys(&ctx)? {
- let stats = key.stats()
- .context("Failed to get key stats")?;
- table.add_row(Row::new(vec![
- Cell::new(&fingerprint.to_string()),
- if let Some(t) = stats.updated {
- Cell::new(&t.convert().to_string())
- } else {
- Cell::new("")
- },
- Cell::new("")
- ]));
- }
-
- table.printstd();
- },
- ("log", Some(_)) => {
- print_log(store::Store::server_log(&ctx)?, true);
- },
- _ => unreachable!(),
- }
- },
+ ("mapping", Some(m)) =>
+ commands::mappings::dispatch_mapping(config, m)?,
+ ("list", Some(m)) =>
+ commands::mappings::dispatch_list(config, m)?,
("key", Some(m)) => match m.subcommand() {
("generate", Some(m)) => commands::key::generate(m, force)?,
("adopt", Some(m)) => commands::key::adopt(m, policy)?,
@@ -758,49 +674,6 @@ fn main() -> Result<()> {
return Ok(())
}
-fn list_bindings(mapping: &Mapping, realm: &str, name: &str)
- -> Result<()> {
- if mapping.iter()?.count() == 0 {
- println!("No label-key bindings in the \"{}/{}\" mapping.",
- realm, name);
- return Ok(());
- }
-
- println!("Realm: {:?}, mapping: {:?}:", realm, name);
-
- let mut table = Table::new();
- table.set_format(*prettytable::format::consts::FORMAT_NO_LINESEP_WITH_TITLE);
- table.set_titles(row!["label", "fingerprint"]);
- for (label, fingerprint, _) in mapping.iter()? {
- table.add_row(Row::new(vec![
- Cell::new(&label),
- Cell::new(&fingerprint.to_string())]));
- }
- table.printstd();
- Ok(())
-}
-
-fn print_log(iter: LogIter, with_slug: bool) {
- let mut table = Table::new();
- table.set_format(*prettytable::format::consts::FORMAT_NO_LINESEP_WITH_TITLE);
- let mut head = row!["timestamp", "message"];
- if with_slug {
- head.insert_cell(1, Cell::new("slug"));
- }
- table.set_titles(head);
-
- for entry in iter {
- let mut row = row![&entry.timestamp.convert().to_string(),
- &entry.short()];
- if with_slug {
- row.insert_cell(1, Cell::new(&entry.slug));
- }
- table.add_row(row);
- }
-
- table.printstd();
-}
-
/// Parses the given string depicting a ISO 8601 timestamp.
fn parse_iso8601(s: &str, pad_date_with: chrono::NaiveTime)
-> Result<DateTime<Utc>>