/// 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::time::Duration;
use chrono::{DateTime, offset::Utc};
use itertools::Itertools;
use buffered_reader::{BufferedReader, Dup, File, Generic, Limitor};
use sequoia_openpgp as openpgp;
use openpgp::{
Result,
};
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, PacketParser, PacketParserResult};
use crate::openpgp::serialize::{Serialize, stream::{Message, Armorer}};
use crate::openpgp::cert::prelude::*;
use crate::openpgp::policy::StandardPolicy as P;
mod sq_cli;
mod commands;
fn open_or_stdin(f: Option<&str>)
-> Result<Box<dyn BufferedReader<()>>> {
match f {
Some(f) => Ok(Box::new(File::open(f)
.context("Failed to open input file")?)),
None => Ok(Box::new(Generic::new(io::stdin(), None))),
}
}
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)
}
const SECONDS_IN_DAY : u64 = 24 * 60 * 60;
const SECONDS_IN_YEAR : u64 =
// Average number of days in a year.
(365.2422222 * SECONDS_IN_DAY as f64) as u64;
fn parse_duration(expiry: &str) -> Result<Duration> {
let mut expiry = expiry.chars().peekable();
let _ = expiry.by_ref()
.peeking_take_while(|c| c.is_whitespace())
.for_each(|_| ());
let digits = expiry.by_ref()
.peeking_take_while(|c| {
*c == '+' || *c == '-' || c.is_digit(10)
}).collect::<String>();
let _ = expiry.by_ref()
.peeking_take_while(|c| c.is_whitespace())
.for_each(|_| ());
let suffix = expiry.next();
let _ = expiry.by_ref()
.peeking_take_while(