#![doc(html_favicon_url = "https://docs.sequoia-pgp.org/favicon.png")]
#![doc(html_logo_url = "https://docs.sequoia-pgp.org/logo.svg")]
#![doc = include_str!("../sq-usage.md")]
use anyhow::Context as _;
use std::fs::OpenOptions;
use std::io;
use std::path::{Path, PathBuf};
use std::str::FromStr;
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 openpgp::{armor, Cert};
use openpgp::KeyHandle;
use openpgp::crypto::Password;
use openpgp::packet::prelude::*;
use openpgp::parse::{Parse, PacketParser, PacketParserResult};
use openpgp::packet::signature::subpacket::NotationData;
use openpgp::packet::signature::subpacket::NotationDataFlags;
use openpgp::serialize::{Serialize, stream::{Message, Armorer}};
use openpgp::cert::prelude::*;
use openpgp::policy::StandardPolicy as P;
use sequoia_keystore as keystore;
use clap::FromArgMatches;
use crate::sq_cli::packet;
use sq_cli::SqSubcommands;
mod sq_cli;
mod man;
mod commands;
pub mod output;
pub use output::{wkd::WkdUrlVariant, Model, OutputFormat, OutputVersion};
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))),
}
}
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(|c| c.is_whitespace())
.for_each(|_| ());
let junk = expiry.collect::<String>();
if digits.is_empty() {
return Err(anyhow::anyhow!(
"--expiry: missing count \
(try: '2y' for 2 years)"));
}
let