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 buffered_reader::{BufferedReader, Dup};
@ -9,12 +7,12 @@ use openpgp::{
parse::{Cookie, Parse, stream::DecryptorBuilder},
};
use sequoia_autocrypt as autocrypt;
use sequoia_cert_store::{LazyCert, StoreUpdate};
use crate::{
Sq,
commands::network::{
certify_downloads,
commands::{
cert::import::import_and_report,
network::certify_downloads,
},
output::import::ImportStats,
};
@ -26,6 +24,7 @@ pub fn import_certs(sq: &mut Sq, source: &mut Box<dyn BufferedReader<Cookie>>,
stats: &mut ImportStats)
-> Result<()>
{
let o = &mut std::io::stdout();
let mut acc = Vec::new();
// 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::*;
let mut sender_cert = None;
let mut provenance_recorded = false;
for h in ac.headers.into_iter().filter(|h| h.header_type == Sender) {
if let Some(addr) = h.attributes.iter()
.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(
sq, false, ca,
vec![cert], Some(&addr[..])));
provenance_recorded = true;
} else {
acc.push(cert);
}
@ -61,11 +62,15 @@ pub fn import_certs(sq: &mut Sq, source: &mut Box<dyn BufferedReader<Cookie>>,
}
}
let cert_store = sq.cert_store_or_else()?;
for cert in acc.drain(..) {
cert_store.update_by(Arc::new(LazyCert::from(cert)), stats)?;
import_and_report(o, sq, acc, None, stats, |o, _| {
if provenance_recorded {
wwriteln!(stream = o, initial_indent = " - ",
"provenance information recorded");
}
Ok(())
})?;
// If there is no Autocrypt header, don't bother looking for
// gossip.
let sender_cert = match sender_cert {
@ -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."));
}
let mut acc = Vec::new();
for h in ac.headers.into_iter().filter(|h| h.header_type == Gossip) {
if let Some(_addr) = h.attributes.iter()
.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 {
cert_store.update_by(Arc::new(LazyCert::from(cert)), stats)?;
}
import_and_report(o, sq, acc, None, stats, |_, _| Ok(()))?;
Ok(())
}

View File

@ -124,6 +124,67 @@ where 'store: 'rstore
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.
fn import_certs(o: &mut dyn std::io::Write,
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 raw_certs = RawCertParser::from_buffered_reader(dup)?;
let cert_store = sq.cert_store_or_else()?;
let mut one_ok = false;
let mut errors = Vec::new();
@ -153,32 +213,8 @@ fn import_certs(o: &mut dyn std::io::Write,
}
};
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();
}
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);
}
import_and_report(o, sq, vec![cert], source_path, stats,
|_, _| Ok(()))?;
}
if ! one_ok {