Align output emitted when importing certificates.

- See #503.
This commit is contained in:
Justus Winter 2024-12-14 19:03:37 +01:00
parent 81037e50d4
commit b5ca6e27fe
No known key found for this signature in database
GPG Key ID: 686F55B4AB2B3386
2 changed files with 79 additions and 39 deletions

View File

@ -1,5 +1,3 @@
use std::sync::Arc;
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use buffered_reader::{BufferedReader, Dup}; use buffered_reader::{BufferedReader, Dup};
@ -9,12 +7,12 @@ use openpgp::{
parse::{Cookie, Parse, stream::DecryptorBuilder}, parse::{Cookie, Parse, stream::DecryptorBuilder},
}; };
use sequoia_autocrypt as autocrypt; use sequoia_autocrypt as autocrypt;
use sequoia_cert_store::{LazyCert, StoreUpdate};
use crate::{ use crate::{
Sq, Sq,
commands::network::{ commands::{
certify_downloads, cert::import::import_and_report,
network::certify_downloads,
}, },
output::import::ImportStats, output::import::ImportStats,
}; };
@ -26,6 +24,7 @@ pub fn import_certs(sq: &mut Sq, source: &mut Box<dyn BufferedReader<Cookie>>,
stats: &mut ImportStats) stats: &mut ImportStats)
-> Result<()> -> Result<()>
{ {
let o = &mut std::io::stdout();
let mut acc = Vec::new(); let mut acc = Vec::new();
// First, get the Autocrypt headers from the outside. // First, get the Autocrypt headers from the outside.
@ -39,6 +38,7 @@ pub fn import_certs(sq: &mut Sq, source: &mut Box<dyn BufferedReader<Cookie>>,
use autocrypt::AutocryptHeaderType::*; use autocrypt::AutocryptHeaderType::*;
let mut sender_cert = None; let mut sender_cert = None;
let mut provenance_recorded = false;
for h in ac.headers.into_iter().filter(|h| h.header_type == Sender) { for h in ac.headers.into_iter().filter(|h| h.header_type == Sender) {
if let Some(addr) = h.attributes.iter() if let Some(addr) = h.attributes.iter()
.find_map(|a| (&a.key == "addr" .find_map(|a| (&a.key == "addr"
@ -54,6 +54,7 @@ pub fn import_certs(sq: &mut Sq, source: &mut Box<dyn BufferedReader<Cookie>>,
acc.append(&mut certify_downloads( acc.append(&mut certify_downloads(
sq, false, ca, sq, false, ca,
vec![cert], Some(&addr[..]))); vec![cert], Some(&addr[..])));
provenance_recorded = true;
} else { } else {
acc.push(cert); acc.push(cert);
} }
@ -61,10 +62,14 @@ pub fn import_certs(sq: &mut Sq, source: &mut Box<dyn BufferedReader<Cookie>>,
} }
} }
let cert_store = sq.cert_store_or_else()?; import_and_report(o, sq, acc, None, stats, |o, _| {
for cert in acc.drain(..) { if provenance_recorded {
cert_store.update_by(Arc::new(LazyCert::from(cert)), stats)?; wwriteln!(stream = o, initial_indent = " - ",
} "provenance information recorded");
}
Ok(())
})?;
// If there is no Autocrypt header, don't bother looking for // If there is no Autocrypt header, don't bother looking for
// gossip. // gossip.
@ -108,6 +113,7 @@ pub fn import_certs(sq: &mut Sq, source: &mut Box<dyn BufferedReader<Cookie>>,
return Err(anyhow::anyhow!("Message is not encrypted.")); return Err(anyhow::anyhow!("Message is not encrypted."));
} }
let mut acc = Vec::new();
for h in ac.headers.into_iter().filter(|h| h.header_type == Gossip) { for h in ac.headers.into_iter().filter(|h| h.header_type == Gossip) {
if let Some(_addr) = h.attributes.iter() if let Some(_addr) = h.attributes.iter()
.find_map(|a| (&a.key == "addr").then(|| a.value.clone())) .find_map(|a| (&a.key == "addr").then(|| a.value.clone()))
@ -118,9 +124,7 @@ pub fn import_certs(sq: &mut Sq, source: &mut Box<dyn BufferedReader<Cookie>>,
} }
} }
for cert in acc { import_and_report(o, sq, acc, None, stats, |_, _| Ok(()))?;
cert_store.update_by(Arc::new(LazyCert::from(cert)), stats)?;
}
Ok(()) Ok(())
} }

View File

@ -124,6 +124,67 @@ where 'store: 'rstore
Ok(result?) Ok(result?)
} }
/// Reports on a successfully imported cert.
pub fn emit_cert(o: &mut dyn std::io::Write, sq: &Sq, cert: &openpgp::Cert)
-> Result<()>
{
wwriteln!(stream = o,
initial_indent = " - ┌ ", subsequent_indent = "",
"{}", cert.fingerprint());
wwriteln!(stream = o,
initial_indent = "",
"{}", sq.best_userid(cert, true));
Ok(())
}
/// Imports the certs and reports on the individual certs.
pub fn import_and_report<F>(o: &mut dyn std::io::Write,
sq: &mut Sq,
certs: Vec<openpgp::Cert>,
source_path: Option<&PathBuf>,
stats: &mut ImportStats,
additional: F)
-> Result<()>
where
F: Fn(&mut dyn std::io::Write, &openpgp::Cert)
-> Result<()>,
{
let cert_store = sq.cert_store_or_else()?;
for cert in certs {
emit_cert(o, sq, &cert)?;
let cert = Arc::new(LazyCert::from(cert));
if let Err(err) = cert_store.update_by(cert.clone(), stats) {
wwriteln!(stream = o,
initial_indent = " - ", "failed: {}", err);
wwriteln!(o);
stats.certs.inc_errors();
continue;
} else {
wwriteln!(stream = o,
initial_indent = " - ", "imported");
}
additional(o, cert.to_cert().expect("was a cert"))?;
if cert.is_tsk() {
let mut cmd = sq.hint(format_args!(
"Certificate {} contains secret key material. \
To import keys, do:", cert.fingerprint()))
.sq().arg("key").arg("import");
if let Some(file) = source_path {
cmd = cmd.arg(file.display());
}
cmd.done();
}
}
wwriteln!(o);
Ok(())
}
/// Imports certs encoded as OpenPGP keyring. /// Imports certs encoded as OpenPGP keyring.
fn import_certs(o: &mut dyn std::io::Write, fn import_certs(o: &mut dyn std::io::Write,
sq: &mut Sq, sq: &mut Sq,
@ -134,7 +195,6 @@ fn import_certs(o: &mut dyn std::io::Write,
{ {
let dup = Dup::with_cookie(source, Cookie::default()); let dup = Dup::with_cookie(source, Cookie::default());
let raw_certs = RawCertParser::from_buffered_reader(dup)?; let raw_certs = RawCertParser::from_buffered_reader(dup)?;
let cert_store = sq.cert_store_or_else()?;
let mut one_ok = false; let mut one_ok = false;
let mut errors = Vec::new(); let mut errors = Vec::new();
@ -153,32 +213,8 @@ fn import_certs(o: &mut dyn std::io::Write,
} }
}; };
if cert.is_tsk() { import_and_report(o, sq, vec![cert], source_path, stats,
let mut cmd = sq.hint(format_args!( |_, _| Ok(()))?;
"Certificate {} contains secret key material. \
To import keys, do:", cert.fingerprint()))
.sq().arg("key").arg("import");
if let Some(file) = source_path {
cmd = cmd.arg(file.display());
}
cmd.done();
}
let fingerprint = cert.fingerprint();
let sanitized_userid = sq.best_userid(&cert, true);
if let Err(err) = cert_store.update_by(Arc::new(cert.into()),
stats)
{
wwriteln!(o, "Error importing {}, {}: {}",
fingerprint, sanitized_userid, err);
stats.certs.inc_errors();
continue;
} else {
wwriteln!(o, "Imported {}, {}", fingerprint, sanitized_userid);
}
} }
if ! one_ok { if ! one_ok {