From 782291af1426e35ae36a3a52ea5c551bd2348c67 Mon Sep 17 00:00:00 2001 From: Justus Winter Date: Thu, 21 Jan 2021 23:36:46 +0100 Subject: sq: Align sq armor with SOP. - Use --label, use the labels from SOP. Make auto-detection the default. - Likewise for sq packet join. --- sq/src/commands/mod.rs | 56 ++++++++++++++++++++++--- sq/src/sq-usage.rs | 12 +++--- sq/src/sq.rs | 112 +++++++++++++++++++++++++++++++++++++++---------- sq/src/sq_cli.rs | 19 +++++---- 4 files changed, 155 insertions(+), 44 deletions(-) diff --git a/sq/src/commands/mod.rs b/sq/src/commands/mod.rs index 45c1201c..0150441b 100644 --- a/sq/src/commands/mod.rs +++ b/sq/src/commands/mod.rs @@ -7,6 +7,9 @@ use std::time::SystemTime; use rpassword; use sequoia_openpgp as openpgp; +use crate::openpgp::{ + armor, +}; use crate::openpgp::types::{ CompressionAlgorithm, }; @@ -28,6 +31,8 @@ use crate::openpgp::policy::Policy; use crate::{ Config, + parse_armor_kind, + create_or_stdout_pgp, }; pub mod decrypt; @@ -438,17 +443,54 @@ pub fn split(input: &mut (dyn io::Read + Sync + Send), prefix: &str) } /// Joins the given files. -pub fn join(inputs: Option, output: &mut dyn io::Write) +pub fn join(config: Config, m: &clap::ArgMatches) -> Result<()> { + // Either we know what kind of armor we want to produce, or we + // need to detect it using the first packet we see. + let kind = parse_armor_kind(m.value_of("kind")); + let output = m.value_of("output"); + let mut sink = + if m.is_present("binary") { + // No need for any auto-detection. + Some(create_or_stdout_pgp(output, config.force, + true, // Binary. + armor::Kind::File)?) + } else if let Some(kind) = kind { + Some(create_or_stdout_pgp(output, config.force, + false, // Armored. + kind)?) + } else { + None // Defer. + }; + /// Writes a bit-accurate copy of all top-level packets in PPR to /// OUTPUT. - fn copy(mut ppr: PacketParserResult, output: &mut dyn io::Write) + fn copy(mut ppr: PacketParserResult, + output: Option<&str>, force: bool, + sink: &mut Option) -> Result<()> { while let PacketParserResult::Some(pp) = ppr { + if sink.is_none() { + // Autodetect using the first packet. + let kind = match pp.packet { + Packet::Signature(_) => armor::Kind::Signature, + Packet::SecretKey(_) => armor::Kind::SecretKey, + Packet::PublicKey(_) => armor::Kind::PublicKey, + Packet::PKESK(_) | Packet::SKESK(_) => + armor::Kind::Message, + _ => armor::Kind::File, + }; + + *sink = Some(create_or_stdout_pgp(output, force, + false, // Armored. + kind)?); + } + // We (ab)use the mapping feature to create byte-accurate // copies. for field in pp.map().expect("must be mapped").iter() { - output.write_all(field.as_bytes())?; + sink.as_mut().expect("initialized at this point") + .write_all(field.as_bytes())?; } ppr = pp.next()?.1; @@ -456,18 +498,20 @@ pub fn join(inputs: Option, output: &mut dyn io::Write) Ok(()) } - if let Some(inputs) = inputs { + if let Some(inputs) = m.values_of("input") { for name in inputs { let ppr = openpgp::parse::PacketParserBuilder::from_file(name)? .map(true).build()?; - copy(ppr, output)?; + copy(ppr, output, config.force, &mut sink)?; } } else { let ppr = openpgp::parse::PacketParserBuilder::from_reader(io::stdin())? .map(true).build()?; - copy(ppr, output)?; + copy(ppr, output, config.force, &mut sink)?; } + + sink.unwrap().finalize()?; Ok(()) } diff --git a/sq/src/sq-usage.rs b/sq/src/sq-usage.rs index cbf29837..57506231 100644 --- a/sq/src/sq-usage.rs +++ b/sq/src/sq-usage.rs @@ -761,9 +761,9 @@ //! -h, --help Prints help information //! //! OPTIONS: -//! --kind Selects the kind of armor header [default: file] -//! [possible values: message, publickey, secretkey, -//! signature, file] +//! --label