#[cfg(feature = "cipher")]
mod cipher;
#[cfg(feature = "code")]
mod code;
use core::fmt;
use std::{
collections::vec_deque::VecDeque,
env,
};
use crate::{
action::{
CryptographicAction
},
CryptoError,
};
#[cfg(feature = "cipher")]
const OPTION_CIPHER: &str = "cipher";
#[cfg(feature = "code")]
const OPTION_CODE: &str = "code";
const OPTION_HELP: &str = "help";
const OPTION_INTERACTIVE: &str = "interactive";
const OPTION_VERSION: &str = "version";
pub enum ApplicationError {
MalformedKey(String, String, CryptoError),
MissingArgument(String, String),
NoCommandLineArguments(String),
TooManyArguments(String, String),
WrongCommandLineArgument(String, String),
}
impl fmt::Display for ApplicationError {
fn fmt(
&self,
f: &mut fmt::Formatter<'_>,
) -> fmt::Result {
match self {
ApplicationError::MalformedKey(
description, origin, crypto_error
) => {
write![f, ">>> Application Runtime Error:\n\
... Class: Malformed Key\n\
... Origin: {}\n\
... Description: {}\n\
... Crypto Error {}\n",
origin, description, crypto_error]
}
ApplicationError::MissingArgument(description, origin) => {
write![f, ">>> Application Runtime Error:\n\
... Class: Missing Argument\n\
... Origin: {}\n\
... Description: {}\n",
origin, description]
}
ApplicationError::NoCommandLineArguments(origin) => {
write![f, ">>> Application Runtime Error:\n\
... Class: No Command Line Arguments\n\
... Origin: {}\n\
... Description: Use \"--help\" for usage information!",
origin]
}
ApplicationError::TooManyArguments(description, origin) => {
write![f, ">>> Application Runtime Error:\n\
... Class: Too Many Arguments\n\
... Origin: {}\n\
... Description: {}",
origin, description]
}
ApplicationError::WrongCommandLineArgument(description, origin) => {
write![f, ">>> Application Runtime Error:\n\
... Class: Wrong Command Line Argument\n\
... Origin: {}\n\
... Description: {}",
origin, description]
}
}
}
}
pub struct ApplicationContext {
action_queue: VecDeque<(CryptographicAction, Vec<u8>)>,
run_interactively: bool,
}
impl ApplicationContext {
pub fn new() -> Result<ApplicationContext, ApplicationError> {
let mut app_ctx = ApplicationContext {
action_queue: VecDeque::new(),
run_interactively: false,
};
if let Err(error) = app_ctx.parse_cmd_args() {
return Err(error);
} else {
Ok(app_ctx)
}
}
fn parse_cmd_args(
&mut self
) -> Result<(), ApplicationError> {
let mut args = env::args().collect::<Vec<String>>();
let mut args_iterator = args.iter().skip(1);
let mut maybe_arg = args_iterator.next();
if maybe_arg == None {
return Err(ApplicationError::NoCommandLineArguments(
"ApplicationContext::parse_cmd_args()".to_string()
));
}
let mut current_action = None;
while maybe_arg != None {
let arg = maybe_arg.unwrap();
let current_value: &str;
let cmd_line_option = if arg.len() >= 2 && arg.is_ascii() && "--" == &arg[0..2] {
current_value = &arg[2..];
true
} else {
current_value = &arg[..];
false
};
match current_value {
#[cfg(feature = "cipher")]
OPTION_CIPHER if cmd_line_option => {
if let Err(error) = cipher::handle_cipher_option_arguments(
args_iterator.next(), &mut current_action,
) {
return Err(error);
}
}
#[cfg(feature = "code")]
OPTION_CODE if cmd_line_option => {
if let Err(error) = code::handle_code_option_arguments(
args_iterator.next(), &mut current_action,
) {
return Err(error);
}
}
option
if cmd_line_option && (option == OPTION_HELP || option == OPTION_VERSION) => {
if option == OPTION_HELP {
ApplicationContext::print_elp();
} else {
ApplicationContext::print_version();
}
self.action_queue.truncate(0);
self.run_interactively = false;
args.truncate(0);
args_iterator = args.iter().skip(0);
}
OPTION_INTERACTIVE if cmd_line_option => {
self.run_interactively = true;
self.action_queue.truncate(0);
args.truncate(0);
args_iterator = args.iter().skip(0);
}
ever_other_string if cmd_line_option => {
let mut description = "\"".to_string();
description.push_str(ever_other_string);
description.push_str("\" is not a command line option!");
return Err(ApplicationError::WrongCommandLineArgument(
description, "ApplicationContext::parse_cmd_args()".to_string(),
));
}
every_other_string => {
if let Some(action) = ¤t_action {
self.queue_action(action.clone(), Vec::from(every_other_string));
} else {
return Err(ApplicationError::MissingArgument(
"Define an algorithm before an input!".to_string(),
"ApplicationContext::parse_cmd_args()".to_string(),
));
}
}
}
maybe_arg = args_iterator.next();
}
Ok(())
}
fn print_elp() {
let mut elp = String::new();
elp.push_str("Usage: encodex [[--<class> <ALGORITHM>:<arguments>]\n");
elp.push_str(" [<input> | --file <name>]* ]+\n\n");
elp.push_str("<class> can be one of the following:\n\n");
#[cfg(feature = "cipher")]
elp.push_str(" cipher\n");
#[cfg(feature = "code")]
elp.push_str(" code\n");
#[cfg(feature = "digest")]
elp.push_str(" digest\n");
elp.push_str("\nEvery <ALGORITHM> falls into one of the above classes and can be\n");
elp.push_str("one of the following:\n\n");
elp.push_str(" <ALGORITHM> <class>\n");
#[cfg(feature = "base64")]
elp.push_str(" BASE64 code\n");
#[cfg(feature = "base64url")]
elp.push_str(" BASE64URL code\n");
#[cfg(feature = "base32")]
elp.push_str(" BASE32 code\n");
#[cfg(feature = "base32hex")]
elp.push_str(" BASE32HEX code\n");
#[cfg(any(feature = "base16", feature = "hex"))]
elp.push_str(" BASE16 code\n\n");
#[cfg(feature = "sha256")]
elp.push_str(" SHA256 digest (TODO)\n\n");
#[cfg(feature = "vigenere")]
elp.push_str(" VIGENERE cipher\n");
#[cfg(feature = "caesar")]
elp.push_str(" CAESAR cipher \n");
elp.push_str("\nOptions:\n\n");
elp.push_str(" --version Prints version information to the terminal.\n");
elp.push_str(" --help Prints this help to the terminal.\n\n");
println!["{}", elp];
}
fn print_version() {
let program_name = String::from(env!["CARGO_PKG_NAME"]);
let mut version = "v".to_string();
version.push_str(env!["CARGO_PKG_VERSION_MAJOR"]);
version.push_str(".");
version.push_str(env!["CARGO_PKG_VERSION_MINOR"]);
version.push_str(".");
version.push_str(env!["CARGO_PKG_VERSION_PATCH"]);
let description = String::from(env!["CARGO_PKG_DESCRIPTION"]);
println!["{} {} {}\n\
{}\n\
Copyright (C) 2022,2023 Fabian Moos\n\n\
This program is free software: you can redistribute it and/or modify\n\
it under the terms of the GNU General Public License as published by\n\
the Free Software Foundation, either version 3 of the License, or\n\
(at your option) any later version.\n\n\
This program is distributed in the hope that it will be useful,\n\
but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n\
GNU General Public License for more details.\n\n\
You should have received a copy of the GNU General Public License\n\
along with this program. If not, see <https://www.gnu.org/licenses/>.\n",
program_name, version, &description[..51], &description[51..]];
}
fn queue_action(
&mut self,
action: CryptographicAction,
input: Vec<u8>,
) {
self.action_queue.push_back((action, input));
}
pub fn run(
&mut self
) -> Result<(), ApplicationError> {
if self.run_interactively {
todo!["ApplicationContext::run(&mut self) -> Result<(), ApplicationError>\n interactive execution!"];
} else {
let mut maybe_action = self.action_queue.pop_front();
while maybe_action != None {
let (mut action, input) = maybe_action.unwrap();
match action.execute(&input) {
Ok(result) => {
println!["{}", std::str::from_utf8(&result).unwrap()];
}
Err(error) => {
eprintln!["{}", error];
}
}
maybe_action = self.action_queue.pop_front();
}
}
Ok(())
}
}