/// A command-line frontend for Sequoia.
use anyhow::Context as _;
use std::fs::OpenOptions;
use std::io::{self, Write};
use std::path::{Path, PathBuf};
use std::process::exit;
use chrono::{DateTime, offset::Utc};
use buffered_reader::File;
use sequoia_openpgp as openpgp;
use sequoia_net;
use openpgp::{
Result,
Fingerprint,
KeyID,
KeyHandle,
packet::UserID,
};
use crate::openpgp::{armor, Cert};
use sequoia_autocrypt as autocrypt;
use crate::openpgp::crypto::Password;
use crate::openpgp::fmt::hex;
use crate::openpgp::types::KeyFlags;
use crate::openpgp::packet::prelude::*;
use crate::openpgp::parse::Parse;
use crate::openpgp::serialize::{Serialize, stream::{Message, Armorer}};
use crate::openpgp::cert::prelude::*;
use crate::openpgp::policy::StandardPolicy as P;
use sequoia_net as net;
use sequoia_net::{KeyServer, wkd};
mod sq_cli;
mod commands;
fn open_or_stdin(f: Option<&str>) -> Result<Box<dyn io::Read + Send + Sync>> {
match f {
Some(f) => Ok(Box::new(File::open(f)
.context("Failed to open input file")?)),
None => Ok(Box::new(io::stdin())),
}
}
fn create_or_stdout(f: Option<&str>, force: bool)
-> Result<Box<dyn io::Write + Sync + Send>> {
match f {
None => Ok(Box::new(io::stdout())),
Some(p) if p == "-" => Ok(Box::new(io::stdout())),
Some(f) => {
let p = Path::new(f);
if !p.exists() || force {
Ok(Box::new(OpenOptions::new()
.write(true)
.truncate(true)
.create(true)
.open(f)
.context("Failed to create output file")?))
} else {
Err(anyhow::anyhow!(
format!("File {:?} exists, use --force to overwrite", p)))
}
}
}
}
fn create_or_stdout_pgp<'a>(f: Option<&str>, force: bool,
binary: bool, kind: armor::Kind)
-> Result<Message<'a>>
{
let sink = create_or_stdout(f, force)?;
let mut message = Message::new(sink);
if ! binary {
message = Armorer::new(message).kind(kind).build()?;
}
Ok(message)
}
/// Loads one TSK from every given file.
fn load_keys<'a, I>(files: I) -> openpgp::Result<Vec<Cert>>
where I: Iterator<Item=&'a str>
{
let mut certs = vec![];
for f in files {
let cert = Cert::from_file(f)
.context(format!("Failed to load key from file {:?}", f))?;
if ! cert.is_tsk() {
Err(anyhow::anyhow!(
"Cert in file {:?} does not contain secret keys", f))?;
}
certs.push(cert);
}
Ok(certs)
}
/// Loads one or more certs from every given file.
fn load_certs<'a, I>(files: I) -> openpgp::Result<Vec<Cert>>
where I: Iterator<Item=&'a str>
{
let mut certs = vec![];
for f in files {
for maybe_cert in CertParser::from_file(f)
.context(format!("Failed to load certs from file {:?}", f))?
{
certs.push(maybe_cert.context(
format!