summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJustus Winter <justus@sequoia-pgp.org>2021-01-06 12:38:13 +0100
committerJustus Winter <justus@sequoia-pgp.org>2021-01-06 12:38:13 +0100
commitc3eed60467ebb02df55d90eecdc08827b2f485c8 (patch)
tree0cb44646bd12b885f3862746bc3359493bafee7f
parent1bbad042ec6047737e214d37aeb261431f170d39 (diff)
sq: Implement 'certring join'.
-rw-r--r--sq/src/commands/certring.rs37
-rw-r--r--sq/src/sq-usage.rs21
-rw-r--r--sq/src/sq.rs2
-rw-r--r--sq/src/sq_cli.rs14
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<clap::Values>, 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 <FILE> Sets the output file to use
+//!
+//! ARGS:
+//! <FILE>... 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
@@ -517,6 +517,20 @@ pub fn build() -> App<'static, 'static> {
.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")
.arg(Arg::with_name("input").value_name("FILE")