diff options
-rw-r--r-- | nitrocli/CHANGELOG.md | 3 | ||||
-rw-r--r-- | nitrocli/src/args.rs | 153 | ||||
-rw-r--r-- | nitrocli/src/error.rs | 2 | ||||
-rw-r--r-- | nitrocli/src/main.rs | 47 |
4 files changed, 173 insertions, 32 deletions
diff --git a/nitrocli/CHANGELOG.md b/nitrocli/CHANGELOG.md index 1beabf2..2d43788 100644 --- a/nitrocli/CHANGELOG.md +++ b/nitrocli/CHANGELOG.md @@ -12,7 +12,8 @@ Unreleased version of the crate, and minimum version of `rustc` required - Fixed wrong messages in the pinentry dialog that were caused by unescaped spaces in a string -- Added `argparse` dependency in version `0.2.2` +- Use the `argparse` crate to parse the command-line arguments + - Added `argparse` dependency in version `0.2.2` 0.1.3 diff --git a/nitrocli/src/args.rs b/nitrocli/src/args.rs new file mode 100644 index 0000000..07a3e6a --- /dev/null +++ b/nitrocli/src/args.rs @@ -0,0 +1,153 @@ +// args.rs + +// ************************************************************************* +// * Copyright (C) 2018 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::fmt; +use std::io; +use std::result; +use std::str; + +use crate::commands; +use crate::error::Error; + +type Result<T> = result::Result<T, Error>; + +/// A top-level command for nitrocli. +#[derive(Debug)] +pub enum Command { + Clear, + Close, + Open, + Status, +} + +impl Command { + /// Execute this command with the given arguments. + pub fn execute(&self, args: Vec<String>) -> Result<()> { + match *self { + Command::Clear => clear(args), + Command::Close => close(args), + Command::Open => open(args), + Command::Status => status(args), + } + } +} + +impl fmt::Display for Command { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}", + match *self { + Command::Clear => "clear", + Command::Close => "close", + Command::Open => "open", + Command::Status => "status", + } + ) + } +} + +impl str::FromStr for Command { + type Err = (); + + fn from_str(s: &str) -> result::Result<Self, Self::Err> { + match s { + "clear" => Ok(Command::Clear), + "close" => Ok(Command::Close), + "open" => Ok(Command::Open), + "status" => Ok(Command::Status), + _ => Err(()), + } + } +} + +fn parse(parser: &argparse::ArgumentParser<'_>, args: Vec<String>) -> Result<()> { + if let Err(err) = parser.parse(args, &mut io::stdout(), &mut io::stderr()) { + Err(Error::ArgparseError(err)) + } else { + Ok(()) + } +} + +/// Inquire the status of the nitrokey. +fn status(args: Vec<String>) -> Result<()> { + let mut parser = argparse::ArgumentParser::new(); + parser.set_description("Print the status of the connected Nitrokey Storage"); + parse(&parser, args)?; + + commands::status() +} + +/// Open the encrypted volume on the nitrokey. +fn open(args: Vec<String>) -> Result<()> { + let mut parser = argparse::ArgumentParser::new(); + parser.set_description("Opens the encrypted volume on a Nitrokey Storage"); + parse(&parser, args)?; + + commands::open() +} + +/// Close the previously opened encrypted volume. +fn close(args: Vec<String>) -> Result<()> { + let mut parser = argparse::ArgumentParser::new(); + parser.set_description("Closes the encrypted volume on a Nitrokey Storage"); + parse(&parser, args)?; + + commands::close() +} + +/// Clear the PIN stored when opening the nitrokey's encrypted volume. +fn clear(args: Vec<String>) -> Result<()> { + let mut parser = argparse::ArgumentParser::new(); + parser.set_description("Clears the cached passphrase"); + parse(&parser, args)?; + + commands::clear() +} + +/// Parse the command-line arguments and return the selected command and +/// the remaining arguments for the command. +fn parse_arguments(args: Vec<String>) -> Result<(Command, Vec<String>)> { + let mut command = Command::Status; + let mut subargs = vec![]; + let mut parser = argparse::ArgumentParser::new(); + parser.set_description("Provides access to a Nitrokey device"); + let _ = parser.refer(&mut command).required().add_argument( + "command", + argparse::Store, + "The command to execute (clear|close|open|status)", + ); + let _ = parser.refer(&mut subargs).add_argument( + "arguments", + argparse::List, + "The arguments for the command", + ); + parser.stop_on_first_argument(true); + parse(&parser, args)?; + drop(parser); + + subargs.insert(0, format!("nitrocli {}", command)); + Ok((command, subargs)) +} + +/// Parse the command-line arguments and execute the selected command. +pub fn handle_arguments(args: Vec<String>) -> Result<()> { + let (command, args) = parse_arguments(args)?; + command.execute(args) +} diff --git a/nitrocli/src/error.rs b/nitrocli/src/error.rs index d755cac..d86a635 100644 --- a/nitrocli/src/error.rs +++ b/nitrocli/src/error.rs @@ -23,6 +23,7 @@ use std::string; #[derive(Debug)] pub enum Error { + ArgparseError(i32), IoError(io::Error), Utf8Error(string::FromUtf8Error), Error(String), @@ -43,6 +44,7 @@ impl From<string::FromUtf8Error> for Error { impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { + Error::ArgparseError(_) => write!(f, "Could not parse arguments"), Error::Utf8Error(_) => write!(f, "Encountered UTF-8 conversion error"), Error::IoError(ref e) => write!(f, "IO error: {}", e.get_ref().unwrap()), Error::Error(ref e) => write!(f, "{}", e), diff --git a/nitrocli/src/main.rs b/nitrocli/src/main.rs index 86e9188..8a20494 100644 --- a/nitrocli/src/main.rs +++ b/nitrocli/src/main.rs @@ -68,6 +68,7 @@ //! Nitrocli is a program providing a command line interface to certain //! commands of the Nitrokey Storage device. +mod args; mod commands; mod error; mod pinentry; @@ -75,43 +76,27 @@ mod pinentry; use std::process; use std::result; -use nitrokey; - use crate::error::Error; type Result<T> = result::Result<T, Error>; -// A macro for generating a match of the different supported commands. -// Each supplied command is converted into a string and matched against. -macro_rules! commands { - ( $str:expr, [ $( $command:expr), *] ) => { - match &*$str.to_string() { - $( - stringify!($command) => { - if let Err(err) = $command() { - println!("{}", err); - return 1 - } - return 0 - }, - )* - x => { - println!("Invalid command: {}", x); - println!("Available commands: {}", stringify!( $($command)* )); - return 1 - }, - } - } -} - fn run() -> i32 { - let argv: Vec<String> = std::env::args().collect(); - if argv.len() != 2 { - println!("Usage: {} <command>", argv[0]); - return 1; + let args = std::env::args().collect(); + match args::handle_arguments(args) { + Ok(()) => 0, + Err(err) => match err { + Error::ArgparseError(err) => match err { + // argparse printed the help message + 0 => 0, + // argparse printed an error message + _ => 1, + }, + _ => { + println!("{}", err); + 1 + } + }, } - - commands!(&argv[1], [commands::status, commands::open, commands::close, commands::clear]); } fn main() { |