/// A command-line frontend for Sequoia.
extern crate clap;
#[macro_use]
extern crate failure;
#[macro_use]
extern crate prettytable;
extern crate rpassword;
extern crate tempfile;
extern crate termsize;
extern crate itertools;
extern crate tokio_core;
use failure::ResultExt;
use prettytable::{Table, Cell, Row};
use std::fs::{File, OpenOptions};
use std::io;
use std::path::{Path, PathBuf};
use std::process::exit;
extern crate sequoia_openpgp as openpgp;
extern crate sequoia_core;
extern crate sequoia_net;
extern crate sequoia_store as store;
use crate::openpgp::{armor, autocrypt, Fingerprint, TPK};
use crate::openpgp::conversions::hex;
use crate::openpgp::constants::KeyFlags;
use crate::openpgp::parse::Parse;
use crate::openpgp::serialize::Serialize;
use crate::openpgp::tpk::TPKParser;
use sequoia_core::{Context, NetworkPolicy};
use sequoia_net::{KeyServer, wkd};
use store::{Mapping, LogIter};
mod sq_cli;
mod commands;
use commands::dump::Convert;
fn open_or_stdin(f: Option<&str>) -> Result<Box<dyn io::Read>, failure::Error> {
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>, failure::Error> {
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(failure::err_msg(
format!("File {:?} exists, use --force to overwrite", p)))
}
}
}
}
fn load_tpks<'a, I>(files: I) -> openpgp::Result<Vec<TPK>>
where I: Iterator<Item=&'a str>
{
let mut tpks = vec![];
for f in files {
tpks.push(TPK::from_file(f)
.context(format!("Failed to load key from file {:?}", f))?);
}
Ok(tpks)
}
/// Serializes a keyring, adding descriptive headers if armored.
fn serialize_keyring(mut output: &mut dyn io::Write, tpks: &[TPK], binary: bool)
-> openpgp::Result<()> {
// Handle the easy options first. No armor no cry:
if binary {
for tpk in tpks {
tpk.serialize(&mut output)?;
}
return Ok(());
}
// Just one TPK? Ez:
if tpks.len() == 1 {
return tpks[0].armored().serialize(&mut output);
}
// Otherwise, collect the headers first:
let mut headers = Vec::new();
for (i, tpk) in tpks.iter().enumerate() {
headers.push(format!("Key #{}", i));
headers.append(&mut tpk.armor_headers());
}
let headers: Vec<_> = headers.iter()
.map(|value| ("Comment", value.as_str()))
.collect();
let mut output = armor::Writer::new(&mut output,
armor::Kind::PublicKey,
&headers)?;
for tpk in tpks {
tpk.serialize(&mut output)?;
}
Ok(())
}
fn parse_armor_kind(kind: Option<&str>) -> armor::Kind {
match kind.expect("has default value") {
"message" => armor::Kind::Message,
"publickey" => ar