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() { | 
