From c3eed60467ebb02df55d90eecdc08827b2f485c8 Mon Sep 17 00:00:00 2001 From: Justus Winter Date: Wed, 6 Jan 2021 12:38:13 +0100 Subject: sq: Implement 'certring join'. --- sq/src/commands/certring.rs | 37 ++++++++++++++++++++++++++++++++++++- sq/src/sq-usage.rs | 21 +++++++++++++++++++++ sq/src/sq.rs | 2 +- sq/src/sq_cli.rs | 14 ++++++++++++++ 4 files changed, 72 insertions(+), 2 deletions(-) diff --git a/sq/src/commands/certring.rs b/sq/src/commands/certring.rs index 2971ce3f..d8a08fa3 100644 --- a/sq/src/commands/certring.rs +++ b/sq/src/commands/certring.rs @@ -8,6 +8,7 @@ use anyhow::Context; use sequoia_openpgp as openpgp; use openpgp::{ Result, + armor, cert::CertParser, parse::Parse, serialize::Serialize, @@ -15,10 +16,24 @@ use openpgp::{ use crate::{ open_or_stdin, + create_or_stdout_pgp, }; -pub fn dispatch(m: &clap::ArgMatches) -> Result<()> { +pub fn dispatch(m: &clap::ArgMatches, force: bool) -> Result<()> { match m.subcommand() { + ("join", Some(m)) => { + // XXX: Armor type selection is a bit problematic. If any + // of the certificates contain a secret key, it would be + // better to use Kind::SecretKey here. However, this + // requires buffering all certs, which has its own + // problems. + let mut output = create_or_stdout_pgp(m.value_of("output"), + force, + m.is_present("binary"), + armor::Kind::PublicKey)?; + join(m.values_of("input"), &mut output)?; + output.finalize() + }, ("split", Some(m)) => { let mut input = open_or_stdin(m.value_of("input"))?; let prefix = @@ -42,6 +57,26 @@ pub fn dispatch(m: &clap::ArgMatches) -> Result<()> { } } +/// Joins cert(ring)s into a certring. +fn join(inputs: Option, output: &mut dyn io::Write) + -> Result<()> { + if let Some(inputs) = inputs { + for name in inputs { + for cert in CertParser::from_file(name)? { + let cert = cert.context( + format!("Malformed certificate in certring {:?}", name))?; + cert.serialize(output)?; + } + } + } else { + for cert in CertParser::from_reader(io::stdin())? { + let cert = cert.context("Malformed certificate in certring")?; + cert.serialize(output)?; + } + } + Ok(()) +} + /// Splits a certring into individual certs. fn split(input: &mut (dyn io::Read + Sync + Send), prefix: &str) -> Result<()> { diff --git a/sq/src/sq-usage.rs b/sq/src/sq-usage.rs index d257b58a..7a909bb6 100644 --- a/sq/src/sq-usage.rs +++ b/sq/src/sq-usage.rs @@ -443,9 +443,30 @@ //! //! SUBCOMMANDS: //! help Prints this message or the help of the given subcommand(s) +//! join Joins certs into a certring //! split Splits a certring into individual certs //! ``` //! +//! ### Subcommand certring join +//! +//! ```text +//! Joins certs into a certring +//! +//! USAGE: +//! sq certring join [FLAGS] [OPTIONS] [FILE]... +//! +//! FLAGS: +//! -B, --binary Don't ASCII-armor the certring +//! -h, --help Prints help information +//! -V, --version Prints version information +//! +//! OPTIONS: +//! -o, --output Sets the output file to use +//! +//! ARGS: +//! ... Sets the input files to use +//! ``` +//! //! ### Subcommand certring split //! //! ```text diff --git a/sq/src/sq.rs b/sq/src/sq.rs index 62d3c5ff..09f89a88 100644 --- a/sq/src/sq.rs +++ b/sq/src/sq.rs @@ -452,7 +452,7 @@ fn main() -> Result<()> { commands::inspect(m, policy, &mut output)?; }, - ("certring", Some(m)) => commands::certring::dispatch(m)?, + ("certring", Some(m)) => commands::certring::dispatch(m, force)?, ("packet", Some(m)) => match m.subcommand() { ("dump", Some(m)) => { diff --git a/sq/src/sq_cli.rs b/sq/src/sq_cli.rs index cb3176ed..6e4428ed 100644 --- a/sq/src/sq_cli.rs +++ b/sq/src/sq_cli.rs @@ -516,6 +516,20 @@ pub fn build() -> App<'static, 'static> { SubCommand::with_name("certring") .about("Manipulates certificate rings") .setting(AppSettings::SubcommandRequiredElseHelp) + .subcommand( + SubCommand::with_name("join") + .about("Joins certs into a certring") + .arg(Arg::with_name("input").value_name("FILE") + .multiple(true) + .help("Sets the input files to use")) + .arg(Arg::with_name("output").value_name("FILE") + .long("output") + .short("o") + .help("Sets the output file to use")) + .arg(Arg::with_name("binary") + .long("binary") + .short("B") + .help("Don't ASCII-armor the certring"))) .subcommand( SubCommand::with_name("split") .about("Splits a certring into individual certs") -- cgit v1.2.3