use std::env::var_os;
use anyhow::{anyhow, Result};
use clap::Parser;
use openpgp_card::state::{Open, Transaction};
use openpgp_card::Card;
use pgp::packet::PublicKey;
use rpgpie::certificate::Certificate;
mod cli;
mod commands;
mod output;
mod util;
mod versioned_output;
use cli::OUTPUT_VERSIONS;
use secrecy::SecretString;
use versioned_output::{OutputBuilder, OutputFormat, OutputVariant, OutputVersion};
use crate::cli::DEFAULT_OUTPUT_VERSION;
const ENTER_USER_PIN: &str = "Enter User PIN:";
const ENTER_ADMIN_PIN: &str = "Enter Admin PIN:";
fn main() -> Result<(), Box<dyn std::error::Error>> {
env_logger::init();
if let Some(mandir) = var_os("OCT_MANPAGE_OUTPUT_DIR") {
match mandir.into_string().as_ref() {
Ok(mandir) => {
clap_allgen::render_manpages::<cli::Cli>(mandir)?;
return Ok(());
}
Err(mandir) => {
return Err(anyhow!("Failed to convert string: {:?}", mandir).into());
}
}
}
if let Some(output_dir) = var_os("OCT_COMPLETION_OUTPUT_DIR") {
match output_dir.into_string().as_ref() {
Ok(output_dir) => {
clap_allgen::render_shell_completions::<cli::Cli>(output_dir)?;
return Ok(());
}
Err(output_dir) => {
return Err(anyhow!("Failed to convert string: {:?}", output_dir).into());
}
}
}
let cli = cli::Cli::parse();
match cli.cmd {
cli::Command::OutputVersions {} => {
output_versions(cli.output_version);
}
cli::Command::List(cmd) => {
commands::list::print_card_list(cli.output_format, cli.output_version, Some(cmd))?;
}
cli::Command::Status(cmd) => {
commands::status::print_status(cli.output_format, cli.output_version, cmd)?;
}
cli::Command::Info(cmd) => {
commands::info::print_info(cli.output_format, cli.output_version, cmd)?;
}
cli::Command::Ssh(cmd) => {
commands::ssh::print_ssh(cli.output_format, cli.output_version, cmd)?;
}
cli::Command::Pubkey(cmd) => {
commands::pubkey::print_pubkey(cli.output_format, cli.output_version, cmd)?;
}
cli::Command::Decrypt(cmd) => {
commands::decrypt::decrypt(cmd)?;
}
cli::Command::Sign(cmd) => {
commands::sign::sign(cmd)?;
}
cli::Command::Attestation(cmd) => {
commands::attestation::attestation(cli.output_format, cli.output_version, cmd)?;
}
cli::Command::System(cmd) => {
commands::system::system(cmd)?;
}
cli::Command::Admin(cmd) => {
commands::admin::admin(cli.output_format, cli.output_version, cmd)?;
}
cli::Command::Pin(cmd) => {
commands::pin::pin(&cmd.ident, cmd.cmd)?;
}
}
Ok(())
}
fn output_versions(chosen: OutputVersion) {
for v in OUTPUT_VERSIONS.iter() {
if v == &chosen {
println!("* {v}");
} else {
println!(" {v}");
}
}
}
fn pick_card_for_reading(ident: Option<String>) -> Result<Card<Open>> {
if let Some(ident) = ident {
Ok(util::open_card(&ident)?)
} else {
let mut cards = util::cards()?;
if cards.len() == 1 {
Ok(cards
.pop()
.ok_or_else(|| anyhow!("There are no more cards"))?)
} else if cards.is_empty() {
Err(anyhow::anyhow!("No cards found"))
} else {
commands::list::print_card_list(OutputFormat::Text, DEFAULT_OUTPUT_VERSION, None)?;
eprintln!("Specify which card to use with '--card <card ident>'\n");
Err(anyhow::anyhow!("Found more than one card"))
}
}
}
fn get_cert(
card: &mut Card<Transaction>,
key_sig: PublicKey,
key_dec: Option<PublicKey>,
key_aut: Option<PublicKey>,
user_pin: Option<SecretString>,
user_ids: &[String],
prompt: &dyn Fn(),
) -> Result<Certificate> {
if user_pin.is_none() && card.feature_pinpad_verify() {
eprintln!(
"The public cert will now be generated.\n\n\
You will need to enter your User PIN multiple times during this process.\n\n"
);
}
Ok(openpgp_card_rpgp::bind_into_certificate(
card,
key_sig,
key_dec,
key_aut,
user_ids,
user_pin,
prompt,
&|| eprintln!("Touch confirmation needed for signing"),
)
.map(Certificate::from)?)
}