diff options
-rw-r--r-- | src/arg_util.rs | 2 | ||||
-rw-r--r-- | src/args.rs | 99 | ||||
-rw-r--r-- | src/commands.rs | 112 | ||||
-rw-r--r-- | src/main.rs | 85 | ||||
-rw-r--r-- | src/pinentry.rs | 6 | ||||
-rw-r--r-- | src/tests/mod.rs | 2 |
6 files changed, 134 insertions, 172 deletions
diff --git a/src/arg_util.rs b/src/arg_util.rs index c16c326..439a594 100644 --- a/src/arg_util.rs +++ b/src/arg_util.rs @@ -46,7 +46,7 @@ macro_rules! Command { impl $name { pub fn execute( self, - ctx: &mut crate::args::ExecCtx<'_>, + ctx: &mut crate::ExecCtx<'_>, ) -> crate::Result<()> { match self { $( diff --git a/src/args.rs b/src/args.rs deleted file mode 100644 index d0b2a88..0000000 --- a/src/args.rs +++ /dev/null @@ -1,99 +0,0 @@ -// args.rs - -// ************************************************************************* -// * Copyright (C) 2018-2020 Daniel Mueller (deso@posteo.net) * -// * * -// * This program is free software: you can redistribute it and/or modify * -// * it under the terms of the GNU General Public License as published by * -// * the Free Software Foundation, either version 3 of the License, or * -// * (at your option) any later version. * -// * * -// * This program is distributed in the hope that it will be useful, * -// * but WITHOUT ANY WARRANTY; without even the implied warranty of * -// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * -// * GNU General Public License for more details. * -// * * -// * You should have received a copy of the GNU General Public License * -// * along with this program. If not, see <http://www.gnu.org/licenses/>. * -// ************************************************************************* - -use std::ffi; -use std::io; -use std::result; - -use crate::arg_defs; -use crate::error::Error; -use crate::RunCtx; - -type Result<T> = result::Result<T, Error>; - -trait Stdio { - fn stdio(&mut self) -> (&mut dyn io::Write, &mut dyn io::Write); -} - -impl<'io> Stdio for RunCtx<'io> { - fn stdio(&mut self) -> (&mut dyn io::Write, &mut dyn io::Write) { - (self.stdout, self.stderr) - } -} - -impl<W> Stdio for (&mut W, &mut W) -where - W: io::Write, -{ - fn stdio(&mut self) -> (&mut dyn io::Write, &mut dyn io::Write) { - (self.0, self.1) - } -} - -/// A command execution context that captures additional data pertaining -/// the command execution. -pub struct ExecCtx<'io> { - pub model: Option<arg_defs::DeviceModel>, - pub stdout: &'io mut dyn io::Write, - pub stderr: &'io mut dyn io::Write, - pub admin_pin: Option<ffi::OsString>, - pub user_pin: Option<ffi::OsString>, - pub new_admin_pin: Option<ffi::OsString>, - pub new_user_pin: Option<ffi::OsString>, - pub password: Option<ffi::OsString>, - pub no_cache: bool, - pub verbosity: u64, -} - -impl<'io> Stdio for ExecCtx<'io> { - fn stdio(&mut self) -> (&mut dyn io::Write, &mut dyn io::Write) { - (self.stdout, self.stderr) - } -} - -/// Parse the command-line arguments and execute the selected command. -pub(crate) fn handle_arguments(ctx: &mut RunCtx<'_>, args: Vec<String>) -> Result<()> { - use structopt::StructOpt; - - match arg_defs::Args::from_iter_safe(args.iter()) { - Ok(args) => { - let mut ctx = ExecCtx { - model: args.model, - stdout: ctx.stdout, - stderr: ctx.stderr, - admin_pin: ctx.admin_pin.take(), - user_pin: ctx.user_pin.take(), - new_admin_pin: ctx.new_admin_pin.take(), - new_user_pin: ctx.new_user_pin.take(), - password: ctx.password.take(), - no_cache: ctx.no_cache, - verbosity: args.verbose.into(), - }; - args.cmd.execute(&mut ctx) - } - Err(err) => { - if err.use_stderr() { - Err(err.into()) - } else { - println!(ctx, "{}", err.message)?; - Ok(()) - } - } - } -} diff --git a/src/commands.rs b/src/commands.rs index b40d552..208c039 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -32,10 +32,10 @@ use nitrokey::GenerateOtp; use nitrokey::GetPasswordSafe; use crate::arg_defs; -use crate::args; use crate::error; use crate::error::Error; use crate::pinentry; +use crate::ExecCtx; use crate::Result; /// Create an `error::Error` with an error message of the format `msg: err`. @@ -44,7 +44,7 @@ fn get_error(msg: &'static str, err: nitrokey::Error) -> Error { } /// Set `libnitrokey`'s log level based on the execution context's verbosity. -fn set_log_level(ctx: &mut args::ExecCtx<'_>) { +fn set_log_level(ctx: &mut ExecCtx<'_>) { let log_lvl = match ctx.verbosity { // The error log level is what libnitrokey uses by default. As such, // there is no harm in us setting that as well when the user did not @@ -60,9 +60,9 @@ fn set_log_level(ctx: &mut args::ExecCtx<'_>) { } /// Connect to any Nitrokey device and do something with it. -fn with_device<F>(ctx: &mut args::ExecCtx<'_>, op: F) -> Result<()> +fn with_device<F>(ctx: &mut ExecCtx<'_>, op: F) -> Result<()> where - F: FnOnce(&mut args::ExecCtx<'_>, nitrokey::DeviceWrapper<'_>) -> Result<()>, + F: FnOnce(&mut ExecCtx<'_>, nitrokey::DeviceWrapper<'_>) -> Result<()>, { let mut manager = nitrokey::take()?; set_log_level(ctx); @@ -81,9 +81,9 @@ where } /// Connect to a Nitrokey Storage device and do something with it. -fn with_storage_device<F>(ctx: &mut args::ExecCtx<'_>, op: F) -> Result<()> +fn with_storage_device<F>(ctx: &mut ExecCtx<'_>, op: F) -> Result<()> where - F: FnOnce(&mut args::ExecCtx<'_>, nitrokey::Storage<'_>) -> Result<()>, + F: FnOnce(&mut ExecCtx<'_>, nitrokey::Storage<'_>) -> Result<()>, { let mut manager = nitrokey::take()?; set_log_level(ctx); @@ -104,9 +104,9 @@ where /// Connect to any Nitrokey device, retrieve a password safe handle, and /// do something with it. -fn with_password_safe<F>(ctx: &mut args::ExecCtx<'_>, mut op: F) -> Result<()> +fn with_password_safe<F>(ctx: &mut ExecCtx<'_>, mut op: F) -> Result<()> where - F: FnMut(&mut args::ExecCtx<'_>, nitrokey::PasswordSafe<'_, '_>) -> Result<()>, + F: FnMut(&mut ExecCtx<'_>, nitrokey::PasswordSafe<'_, '_>) -> Result<()>, { with_device(ctx, |ctx, mut device| { let pin_entry = pinentry::PinEntry::from(arg_defs::PinType::User, &device)?; @@ -131,7 +131,7 @@ where /// /// If an error occurs, the error message `msg` is used. fn authenticate<'mgr, D, A, F>( - ctx: &mut args::ExecCtx<'_>, + ctx: &mut ExecCtx<'_>, device: D, pin_type: arg_defs::PinType, msg: &'static str, @@ -139,7 +139,7 @@ fn authenticate<'mgr, D, A, F>( ) -> Result<A> where D: Device<'mgr>, - F: FnMut(&mut args::ExecCtx<'_>, D, &str) -> result::Result<A, (D, nitrokey::Error)>, + F: FnMut(&mut ExecCtx<'_>, D, &str) -> result::Result<A, (D, nitrokey::Error)>, { let pin_entry = pinentry::PinEntry::from(pin_type, &device)?; @@ -147,10 +147,7 @@ where } /// Authenticate the given device with the user PIN. -fn authenticate_user<'mgr, T>( - ctx: &mut args::ExecCtx<'_>, - device: T, -) -> Result<nitrokey::User<'mgr, T>> +fn authenticate_user<'mgr, T>(ctx: &mut ExecCtx<'_>, device: T) -> Result<nitrokey::User<'mgr, T>> where T: Device<'mgr>, { @@ -164,10 +161,7 @@ where } /// Authenticate the given device with the admin PIN. -fn authenticate_admin<'mgr, T>( - ctx: &mut args::ExecCtx<'_>, - device: T, -) -> Result<nitrokey::Admin<'mgr, T>> +fn authenticate_admin<'mgr, T>(ctx: &mut ExecCtx<'_>, device: T) -> Result<nitrokey::Admin<'mgr, T>> where T: Device<'mgr>, { @@ -208,14 +202,14 @@ fn get_volume_status(status: &nitrokey::VolumeStatus) -> &'static str { /// second or third try, it will call `op` with the data returned by the /// previous call to `op`. fn try_with_pin_and_data_with_pinentry<D, F, R, E>( - ctx: &mut args::ExecCtx<'_>, + ctx: &mut ExecCtx<'_>, pin_entry: &pinentry::PinEntry, msg: &'static str, data: D, mut op: F, ) -> Result<R> where - F: FnMut(&mut args::ExecCtx<'_>, D, &str) -> result::Result<R, (D, E)>, + F: FnMut(&mut ExecCtx<'_>, D, &str) -> result::Result<R, (D, E)>, E: error::TryInto<nitrokey::Error>, { let mut data = data; @@ -248,14 +242,14 @@ where /// Try to execute the given function with a PIN. fn try_with_pin_and_data<D, F, R, E>( - ctx: &mut args::ExecCtx<'_>, + ctx: &mut ExecCtx<'_>, pin_entry: &pinentry::PinEntry, msg: &'static str, data: D, mut op: F, ) -> Result<R> where - F: FnMut(&mut args::ExecCtx<'_>, D, &str) -> result::Result<R, (D, E)>, + F: FnMut(&mut ExecCtx<'_>, D, &str) -> result::Result<R, (D, E)>, E: Into<Error> + error::TryInto<nitrokey::Error>, { let pin = match pin_entry.pin_type() { @@ -284,7 +278,7 @@ where /// This function behaves exactly as `try_with_pin_and_data`, but /// it refrains from passing any data to it. fn try_with_pin<F, E>( - ctx: &mut args::ExecCtx<'_>, + ctx: &mut ExecCtx<'_>, pin_entry: &pinentry::PinEntry, msg: &'static str, mut op: F, @@ -299,10 +293,7 @@ where } /// Pretty print the status of a Nitrokey Storage. -fn print_storage_status( - ctx: &mut args::ExecCtx<'_>, - status: &nitrokey::StorageStatus, -) -> Result<()> { +fn print_storage_status(ctx: &mut ExecCtx<'_>, status: &nitrokey::StorageStatus) -> Result<()> { println!( ctx, r#" Storage: @@ -333,7 +324,7 @@ fn print_storage_status( /// Query and pretty print the status that is common to all Nitrokey devices. fn print_status( - ctx: &mut args::ExecCtx<'_>, + ctx: &mut ExecCtx<'_>, model: &'static str, device: &nitrokey::DeviceWrapper<'_>, ) -> Result<()> { @@ -368,7 +359,7 @@ fn print_status( } /// Inquire the status of the nitrokey. -pub fn status(ctx: &mut args::ExecCtx<'_>) -> Result<()> { +pub fn status(ctx: &mut ExecCtx<'_>) -> Result<()> { with_device(ctx, |ctx, device| { let model = match device { nitrokey::DeviceWrapper::Pro(_) => "Pro", @@ -379,7 +370,7 @@ pub fn status(ctx: &mut args::ExecCtx<'_>) -> Result<()> { } /// List the attached Nitrokey devices. -pub fn list(ctx: &mut args::ExecCtx<'_>, no_connect: bool) -> Result<()> { +pub fn list(ctx: &mut ExecCtx<'_>, no_connect: bool) -> Result<()> { set_log_level(ctx); let device_infos = nitrokey::list_devices()?; @@ -417,7 +408,7 @@ pub fn list(ctx: &mut args::ExecCtx<'_>, no_connect: bool) -> Result<()> { } /// Perform a factory reset. -pub fn reset(ctx: &mut args::ExecCtx<'_>) -> Result<()> { +pub fn reset(ctx: &mut ExecCtx<'_>) -> Result<()> { with_device(ctx, |ctx, mut device| { let pin_entry = pinentry::PinEntry::from(arg_defs::PinType::Admin, &device)?; @@ -441,10 +432,7 @@ pub fn reset(ctx: &mut args::ExecCtx<'_>) -> Result<()> { } /// Change the configuration of the unencrypted volume. -pub fn unencrypted_set( - ctx: &mut args::ExecCtx<'_>, - mode: arg_defs::UnencryptedVolumeMode, -) -> Result<()> { +pub fn unencrypted_set(ctx: &mut ExecCtx<'_>, mode: arg_defs::UnencryptedVolumeMode) -> Result<()> { with_storage_device(ctx, |ctx, mut device| { let pin_entry = pinentry::PinEntry::from(arg_defs::PinType::Admin, &device)?; let mode = match mode { @@ -466,7 +454,7 @@ pub fn unencrypted_set( } /// Open the encrypted volume on the Nitrokey. -pub fn encrypted_open(ctx: &mut args::ExecCtx<'_>) -> Result<()> { +pub fn encrypted_open(ctx: &mut ExecCtx<'_>) -> Result<()> { with_storage_device(ctx, |ctx, mut device| { let pin_entry = pinentry::PinEntry::from(arg_defs::PinType::User, &device)?; @@ -481,7 +469,7 @@ pub fn encrypted_open(ctx: &mut args::ExecCtx<'_>) -> Result<()> { } /// Close the previously opened encrypted volume. -pub fn encrypted_close(ctx: &mut args::ExecCtx<'_>) -> Result<()> { +pub fn encrypted_close(ctx: &mut ExecCtx<'_>) -> Result<()> { with_storage_device(ctx, |_ctx, mut device| { // Flush all filesystem caches to disk. We are mostly interested in // making sure that the encrypted volume on the Nitrokey we are @@ -496,7 +484,7 @@ pub fn encrypted_close(ctx: &mut args::ExecCtx<'_>) -> Result<()> { } /// Create a hidden volume. -pub fn hidden_create(ctx: &mut args::ExecCtx<'_>, slot: u8, start: u8, end: u8) -> Result<()> { +pub fn hidden_create(ctx: &mut ExecCtx<'_>, slot: u8, start: u8, end: u8) -> Result<()> { with_storage_device(ctx, |ctx, mut device| { let pwd_entry = pinentry::PwdEntry::from(&device)?; let pwd = if let Some(pwd) = &ctx.password { @@ -515,7 +503,7 @@ pub fn hidden_create(ctx: &mut args::ExecCtx<'_>, slot: u8, start: u8, end: u8) } /// Open a hidden volume. -pub fn hidden_open(ctx: &mut args::ExecCtx<'_>) -> Result<()> { +pub fn hidden_open(ctx: &mut ExecCtx<'_>) -> Result<()> { with_storage_device(ctx, |ctx, mut device| { let pwd_entry = pinentry::PwdEntry::from(&device)?; let pwd = if let Some(pwd) = &ctx.password { @@ -538,7 +526,7 @@ pub fn hidden_open(ctx: &mut args::ExecCtx<'_>) -> Result<()> { } /// Close a previously opened hidden volume. -pub fn hidden_close(ctx: &mut args::ExecCtx<'_>) -> Result<()> { +pub fn hidden_close(ctx: &mut ExecCtx<'_>) -> Result<()> { with_storage_device(ctx, |_ctx, mut device| { unsafe { sync() }; @@ -557,7 +545,7 @@ fn format_option<T: fmt::Display>(option: Option<T>) -> String { } /// Read the Nitrokey configuration. -pub fn config_get(ctx: &mut args::ExecCtx<'_>) -> Result<()> { +pub fn config_get(ctx: &mut ExecCtx<'_>) -> Result<()> { with_device(ctx, |ctx, device| { let config = device .get_config() @@ -579,7 +567,7 @@ pub fn config_get(ctx: &mut args::ExecCtx<'_>) -> Result<()> { } /// Write the Nitrokey configuration. -pub fn config_set(ctx: &mut args::ExecCtx<'_>, args: arg_defs::ConfigSetArgs) -> Result<()> { +pub fn config_set(ctx: &mut ExecCtx<'_>, args: arg_defs::ConfigSetArgs) -> Result<()> { let numlock = arg_defs::ConfigOption::try_from(args.no_numlock, args.numlock, "numlock")?; let capslock = arg_defs::ConfigOption::try_from(args.no_capslock, args.capslock, "capslock")?; let scrollock = arg_defs::ConfigOption::try_from(args.no_scrollock, args.scrollock, "scrollock")?; @@ -609,7 +597,7 @@ pub fn config_set(ctx: &mut args::ExecCtx<'_>, args: arg_defs::ConfigSetArgs) -> } /// Lock the Nitrokey device. -pub fn lock(ctx: &mut args::ExecCtx<'_>) -> Result<()> { +pub fn lock(ctx: &mut ExecCtx<'_>) -> Result<()> { with_device(ctx, |_ctx, mut device| { device .lock() @@ -637,7 +625,7 @@ fn get_unix_timestamp() -> Result<u64> { /// Generate a one-time password on the Nitrokey device. pub fn otp_get( - ctx: &mut args::ExecCtx<'_>, + ctx: &mut ExecCtx<'_>, slot: u8, algorithm: arg_defs::OtpAlgorithm, time: Option<u64>, @@ -700,7 +688,7 @@ fn prepare_base32_secret(secret: &str) -> Result<String> { } /// Configure a one-time password slot on the Nitrokey device. -pub fn otp_set(ctx: &mut args::ExecCtx<'_>, mut args: arg_defs::OtpSetArgs) -> Result<()> { +pub fn otp_set(ctx: &mut ExecCtx<'_>, mut args: arg_defs::OtpSetArgs) -> Result<()> { let mut data = nitrokey::OtpSlotData { number: args.slot, name: mem::take(&mut args.name), @@ -739,11 +727,7 @@ pub fn otp_set(ctx: &mut args::ExecCtx<'_>, mut args: arg_defs::OtpSetArgs) -> R } /// Clear an OTP slot. -pub fn otp_clear( - ctx: &mut args::ExecCtx<'_>, - slot: u8, - algorithm: arg_defs::OtpAlgorithm, -) -> Result<()> { +pub fn otp_clear(ctx: &mut ExecCtx<'_>, slot: u8, algorithm: arg_defs::OtpAlgorithm) -> Result<()> { with_device(ctx, |ctx, device| { let mut device = authenticate_admin(ctx, device)?; match algorithm { @@ -756,7 +740,7 @@ pub fn otp_clear( } fn print_otp_status( - ctx: &mut args::ExecCtx<'_>, + ctx: &mut ExecCtx<'_>, algorithm: arg_defs::OtpAlgorithm, device: &nitrokey::DeviceWrapper<'_>, all: bool, @@ -790,7 +774,7 @@ fn print_otp_status( } /// Print the status of the OTP slots. -pub fn otp_status(ctx: &mut args::ExecCtx<'_>, all: bool) -> Result<()> { +pub fn otp_status(ctx: &mut ExecCtx<'_>, all: bool) -> Result<()> { with_device(ctx, |ctx, device| { println!(ctx, "alg\tslot\tname")?; print_otp_status(ctx, arg_defs::OtpAlgorithm::Hotp, &device, all)?; @@ -800,7 +784,7 @@ pub fn otp_status(ctx: &mut args::ExecCtx<'_>, all: bool) -> Result<()> { } /// Clear the PIN stored by various operations. -pub fn pin_clear(ctx: &mut args::ExecCtx<'_>) -> Result<()> { +pub fn pin_clear(ctx: &mut ExecCtx<'_>) -> Result<()> { with_device(ctx, |_ctx, device| { pinentry::clear(&pinentry::PinEntry::from( arg_defs::PinType::Admin, @@ -815,11 +799,7 @@ pub fn pin_clear(ctx: &mut args::ExecCtx<'_>) -> Result<()> { /// /// If the user has set the respective environment variable for the /// given PIN type, it will be used. -fn choose_pin( - ctx: &mut args::ExecCtx<'_>, - pin_entry: &pinentry::PinEntry, - new: bool, -) -> Result<String> { +fn choose_pin(ctx: &mut ExecCtx<'_>, pin_entry: &pinentry::PinEntry, new: bool) -> Result<String> { let new_pin = match pin_entry.pin_type() { arg_defs::PinType::Admin => { if new { @@ -848,7 +828,7 @@ fn choose_pin( } /// Change a PIN. -pub fn pin_set(ctx: &mut args::ExecCtx<'_>, pin_type: arg_defs::PinType) -> Result<()> { +pub fn pin_set(ctx: &mut ExecCtx<'_>, pin_type: arg_defs::PinType) -> Result<()> { with_device(ctx, |ctx, mut device| { let pin_entry = pinentry::PinEntry::from(pin_type, &device)?; let new_pin = choose_pin(ctx, &pin_entry, true)?; @@ -871,7 +851,7 @@ pub fn pin_set(ctx: &mut args::ExecCtx<'_>, pin_type: arg_defs::PinType) -> Resu } /// Unblock and reset the user PIN. -pub fn pin_unblock(ctx: &mut args::ExecCtx<'_>) -> Result<()> { +pub fn pin_unblock(ctx: &mut ExecCtx<'_>) -> Result<()> { with_device(ctx, |ctx, mut device| { let pin_entry = pinentry::PinEntry::from(arg_defs::PinType::User, &device)?; let user_pin = choose_pin(ctx, &pin_entry, false)?; @@ -887,7 +867,7 @@ pub fn pin_unblock(ctx: &mut args::ExecCtx<'_>) -> Result<()> { } fn print_pws_data( - ctx: &mut args::ExecCtx<'_>, + ctx: &mut ExecCtx<'_>, description: &'static str, result: result::Result<String, nitrokey::Error>, quiet: bool, @@ -920,7 +900,7 @@ fn check_slot(pws: &nitrokey::PasswordSafe<'_, '_>, slot: u8) -> Result<()> { /// Read a PWS slot. pub fn pws_get( - ctx: &mut args::ExecCtx<'_>, + ctx: &mut ExecCtx<'_>, slot: u8, show_name: bool, show_login: bool, @@ -946,7 +926,7 @@ pub fn pws_get( /// Write a PWS slot. pub fn pws_set( - ctx: &mut args::ExecCtx<'_>, + ctx: &mut ExecCtx<'_>, slot: u8, name: &str, login: &str, @@ -960,7 +940,7 @@ pub fn pws_set( } /// Clear a PWS slot. -pub fn pws_clear(ctx: &mut args::ExecCtx<'_>, slot: u8) -> Result<()> { +pub fn pws_clear(ctx: &mut ExecCtx<'_>, slot: u8) -> Result<()> { with_password_safe(ctx, |_ctx, mut pws| { pws .erase_slot(slot) @@ -969,7 +949,7 @@ pub fn pws_clear(ctx: &mut args::ExecCtx<'_>, slot: u8) -> Result<()> { } fn print_pws_slot( - ctx: &mut args::ExecCtx<'_>, + ctx: &mut ExecCtx<'_>, pws: &nitrokey::PasswordSafe<'_, '_>, slot: usize, programmed: bool, @@ -990,7 +970,7 @@ fn print_pws_slot( } /// Print the status of all PWS slots. -pub fn pws_status(ctx: &mut args::ExecCtx<'_>, all: bool) -> Result<()> { +pub fn pws_status(ctx: &mut ExecCtx<'_>, all: bool) -> Result<()> { with_password_safe(ctx, |ctx, pws| { let slots = pws .get_slot_status() diff --git a/src/main.rs b/src/main.rs index 89c9d67..a2c4f48 100644 --- a/src/main.rs +++ b/src/main.rs @@ -69,7 +69,6 @@ mod redefine; mod arg_util; mod arg_defs; -mod args; mod commands; mod error; mod pinentry; @@ -93,6 +92,88 @@ const NITROCLI_NEW_USER_PIN: &str = "NITROCLI_NEW_USER_PIN"; const NITROCLI_PASSWORD: &str = "NITROCLI_PASSWORD"; const NITROCLI_NO_CACHE: &str = "NITROCLI_NO_CACHE"; +trait Stdio { + fn stdio(&mut self) -> (&mut dyn io::Write, &mut dyn io::Write); +} + +impl<'io> Stdio for RunCtx<'io> { + fn stdio(&mut self) -> (&mut dyn io::Write, &mut dyn io::Write) { + (self.stdout, self.stderr) + } +} + +impl<W> Stdio for (&mut W, &mut W) +where + W: io::Write, +{ + fn stdio(&mut self) -> (&mut dyn io::Write, &mut dyn io::Write) { + (self.0, self.1) + } +} + +/// A command execution context that captures additional data pertaining +/// the command execution. +#[allow(missing_debug_implementations)] +pub struct ExecCtx<'io> { + /// The Nitrokey model to use. + pub model: Option<arg_defs::DeviceModel>, + /// See `RunCtx::stdout`. + pub stdout: &'io mut dyn io::Write, + /// See `RunCtx::stderr`. + pub stderr: &'io mut dyn io::Write, + /// See `RunCtx::admin_pin`. + pub admin_pin: Option<ffi::OsString>, + /// See `RunCtx::user_pin`. + pub user_pin: Option<ffi::OsString>, + /// See `RunCtx::new_admin_pin`. + pub new_admin_pin: Option<ffi::OsString>, + /// See `RunCtx::new_user_pin`. + pub new_user_pin: Option<ffi::OsString>, + /// See `RunCtx::password`. + pub password: Option<ffi::OsString>, + /// See `RunCtx::no_cache`. + pub no_cache: bool, + /// The verbosity level to use for logging. + pub verbosity: u64, +} + +impl<'io> Stdio for ExecCtx<'io> { + fn stdio(&mut self) -> (&mut dyn io::Write, &mut dyn io::Write) { + (self.stdout, self.stderr) + } +} + +/// Parse the command-line arguments and execute the selected command. +fn handle_arguments(ctx: &mut RunCtx<'_>, args: Vec<String>) -> Result<()> { + use structopt::StructOpt; + + match arg_defs::Args::from_iter_safe(args.iter()) { + Ok(args) => { + let mut ctx = ExecCtx { + model: args.model, + stdout: ctx.stdout, + stderr: ctx.stderr, + admin_pin: ctx.admin_pin.take(), + user_pin: ctx.user_pin.take(), + new_admin_pin: ctx.new_admin_pin.take(), + new_user_pin: ctx.new_user_pin.take(), + password: ctx.password.take(), + no_cache: ctx.no_cache, + verbosity: args.verbose.into(), + }; + args.cmd.execute(&mut ctx) + } + Err(err) => { + if err.use_stderr() { + Err(err.into()) + } else { + println!(ctx, "{}", err.message)?; + Ok(()) + } + } + } +} + /// The context used when running the program. pub(crate) struct RunCtx<'io> { /// The `Write` object used as standard output throughout the program. @@ -118,7 +199,7 @@ pub(crate) struct RunCtx<'io> { } fn run<'ctx, 'io: 'ctx>(ctx: &'ctx mut RunCtx<'io>, args: Vec<String>) -> i32 { - match args::handle_arguments(ctx, args) { + match handle_arguments(ctx, args) { Ok(()) => 0, Err(err) => { let _ = eprintln!(ctx, "{}", err); diff --git a/src/pinentry.rs b/src/pinentry.rs index 878ed9e..47c8844 100644 --- a/src/pinentry.rs +++ b/src/pinentry.rs @@ -24,8 +24,8 @@ use std::process; use std::str; use crate::arg_defs; -use crate::args; use crate::error::Error; +use crate::ExecCtx; type CowStr = borrow::Cow<'static, str>; @@ -222,7 +222,7 @@ where /// dialog. It is used to choose an appropriate description and to /// decide whether a quality bar is shown in the dialog. pub fn inquire<E>( - ctx: &mut args::ExecCtx<'_>, + ctx: &mut ExecCtx<'_>, entry: &E, mode: Mode, error_msg: Option<&str>, @@ -279,7 +279,7 @@ where } } -pub fn choose<E>(ctx: &mut args::ExecCtx<'_>, entry: &E) -> crate::Result<String> +pub fn choose<E>(ctx: &mut ExecCtx<'_>, entry: &E) -> crate::Result<String> where E: SecretEntry, { diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 1edf8f2..abf63e3 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -178,7 +178,7 @@ impl Nitrocli { /// Run `nitrocli`'s `handle_arguments` function. pub fn handle(&mut self, args: &[&str]) -> crate::Result<String> { - let (res, out, _) = self.do_run(args, |c, a| crate::args::handle_arguments(c, a)); + let (res, out, _) = self.do_run(args, |c, a| crate::handle_arguments(c, a)); res.map(|_| String::from_utf8_lossy(&out).into_owned()) } |