You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

433 lines
12 KiB

use super::*;
use std::{
convert::{
TryFrom,TryInto,
},
};
mod builder {
use super::*;
#[derive(PartialEq,Eq,Debug)]
pub enum OperationMode
{
Normal,
KeyGen(GenerateKey),
Help,
}
#[derive(PartialEq,Eq,Debug)]
pub struct KeyGenOps
{
pub format: KeyOutputFormat,
pub cycle: Option<String>,
pub use_password: bool,
pub password: Option<String>,
pub output: Option<String>,
pub private_only: bool,
pub output_public: Option<String>,
pub public_only: bool,
}
impl Default for KeyGenOps
{
fn default() -> Self {
Self {
format: KeyOutputFormat::Binary,
cycle: None,
use_password: false,
password: None,
output: None,
private_only: false,
output_public: None,
public_only: false,
}
}
}
#[derive(PartialEq,Eq,Debug)]
pub struct NormalOps
{
pub mode: super::super::OperationMode,
pub rsa: Vec<String>,
pub sign: Vec<String>,
pub auto_sign: bool,
pub aes: Vec<String>,
pub translate: Vec<(String, Option<String>)>,
pub in_place: bool,
pub sync: bool,
}
impl Default for NormalOps
{
fn default() -> Self
{
Self {
mode: super::super::OperationMode::Autodetect,
rsa: Vec::new(),
sign: Vec::new(),
auto_sign: false,
aes: Vec::new(),
translate: Vec::new(),
in_place: false,
sync: false,
}
}
}
#[derive(PartialEq,Eq,Debug)]
pub struct Operation
{
pub mode: OperationMode,
pub verbose: bool,
pub keygen: KeyGenOps,
pub normal: NormalOps,
}
impl Default for Operation
{
fn default() -> Self
{
Self {
mode: OperationMode::Help,
keygen: KeyGenOps::default(),
normal: Default::default(),
verbose: false,
}
}
}
}
pub fn split<'a>(input: &'a str) -> (&'a str, Option<&'a str>)
{
if let Some(index) = input.find('=') {
(&input[..index], Some(&input[(index+1)..]))
} else {
(input, None)
}
}
pub async fn parse<I,T>(input: &mut I) -> Result<Operation, Error>
where I: Iterator<Item=T>,
T: AsRef<str>
{
let mut i=0;
let mut op = builder::Operation::default();
let mut reading = true;
let mut rest = Vec::new();
let mut cfg_files = Vec::new();
macro_rules! take_one
{
($message:expr) => {(i+=1, input.next().ok_or_else(|| $message)?).1.as_ref()}
}
while let Some(arg) = input.next()
{
let arg = arg.as_ref();
'next: {
if i == 0 {
// First arg
match arg {
"--help" => return Ok(Operation::Help),
"--keygen" => {
op.keygen = builder::KeyGenOps::default();
op.mode = builder::OperationMode::KeyGen(match take_one!("Expected a key type").to_uppercase().as_str() {
"AES" => GenerateKey::Aes,
"RSA" => GenerateKey::Rsa,
d => return Err(format!("Unknown key type `{}`. Expected `RSA` or `AES`.",d))?,
});
break 'next;
},
_ => {
op.mode = builder::OperationMode::Normal;
},
}
}
match op.mode {
builder::OperationMode::KeyGen(GenerateKey::Rsa) => {
let (name, value) = split(arg);
'cont: {
match name.to_lowercase().as_str() {
"-c" => {
cfg_files.push(take_one!("Expected a filename").to_owned());
},
"--verbose" if value.is_some() => op.verbose = value.unwrap().to_lowercase().trim() == "true",
"--verbose" => op.verbose = true,
"--cycle" if value.is_some() => op.keygen.cycle = Some(value.unwrap().to_owned()),
"--cycle" => op.keygen.cycle = Some(take_one!("Expected a filename").to_owned()),
"--format" if value.is_some() => op.keygen.format = value.unwrap().parse()?,
"--format" => op.keygen.format = take_one!("Expected an output format").parse()?,
"--password" if value.is_some() => {
op.keygen.use_password = true;
op.keygen.password = Some(value.unwrap().to_owned());
},
"--password" => op.keygen.use_password = true,
"--private-only" => op.keygen.private_only = true,
"--public-only" => op.keygen.public_only = true,
_ => break 'cont,
}
break 'next;
};
},
builder::OperationMode::KeyGen(GenerateKey::Aes) => {
let (name, value) = split(arg);
'cont: {
match name.to_lowercase().as_str() {
"--verbose" if value.is_some() => op.verbose = value.unwrap().to_lowercase().trim() == "true",
"--verbose" => op.verbose = true,
"--cycle" if value.is_some() => op.keygen.cycle = Some(value.unwrap().to_owned()),
"--cycle" => op.keygen.cycle = Some(take_one!("Expected a filename").to_owned()),
"--format" if value.is_some() => op.keygen.format = value.unwrap().parse()?,
"--format" => op.keygen.format = take_one!("Expected an output format").parse()?,
"--password" if value.is_some() => {
op.keygen.use_password = true;
op.keygen.password = Some(value.unwrap().to_owned());
},
"--password" => op.keygen.use_password = true,
_ => break 'cont,
}
break 'next;
}
},
builder::OperationMode::Normal => {
let (name, value) = split(arg);
if reading {
'cont: {
match name.to_lowercase().as_str() {
"--rsa" if value.is_some() => op.normal.rsa.extend(value.unwrap().split(',').map(|x| x.to_owned())),
"--rsa" => op.normal.rsa.push(take_one!("Expected an RSA key filename").to_owned()),
"--sign" if value.is_some() => op.normal.sign.extend(value.unwrap().split(',').map(|x| x.to_owned())),
"--sign" => op.normal.sign.push(take_one!("Expected an RSA signing key filename").to_owned()),
"--auto-sign" => op.normal.auto_sign = true,
"--aes" if value.is_some() => op.normal.aes.extend(value.unwrap().split(',').map(|x| x.to_owned())),
"--aes" => op.normal.aes.push(take_one!("Expected an AES key filename").to_owned()),
"--translate" if value.is_some() => {
let mut value = value.unwrap().split(',');
let input = value.next().ok_or("Invalid --translate singleton")?.to_owned();
let output = value.next().ok_or("Invalid --translate singleton (expected an output)")?.to_owned();
op.normal.translate.push((input, Some(output)));
},
"--translate" => {
let input = take_one!("No --translate input").to_owned();
let output = take_one!("No --translate output").to_owned();
op.normal.translate.push((input.to_owned(), Some(output.to_owned())));
},
"--in-place" => op.normal.in_place = true,
"--verbose" => op.verbose = true,
"--sync" => op.normal.sync = true,
"-e" => {
op.normal.mode = super::OperationMode::Encrypt;
},
"-d" => {
op.normal.mode = super::OperationMode::Decrypt;
},
"-v" => {
op.normal.mode = super::OperationMode::Verify;
},
"--" => reading = false,
_ => {
reading = false;
break 'cont;
},
}
break 'next;
};
}
},
_ => return Err("Try `--help`")?,
}
rest.push(arg.to_owned());
};
i+=1;
}
// Add `rest`
match op.mode {
builder::OperationMode::Normal => {
if rest.len() < 1 {
return Err("Expected at least one input file")?;
}
op.normal.translate.extend(rest.into_iter().map(|x| (x, None)));
},
builder::OperationMode::KeyGen(GenerateKey::Aes) => {
if rest.len() > 1 {
return Err("Expected only one output file")?;
} else if rest.len() < 1 && op.keygen.cycle.is_none() {
return Err("Expected one output file or `--cycle`")?;
} else if rest.len()>0 {
op.keygen.output = Some(rest.into_iter().next().unwrap());
}
},
builder::OperationMode::KeyGen(GenerateKey::Rsa) => {
if rest.len() > 2 {
return Err("Expected one or 2 output files")?;
} else if rest.len() < 1 && op.keygen.cycle.is_none() {
return Err("Expected at least one output or `--cycle`")?;
} else if rest.len() == 2 {
let mut iter = rest.into_iter();
if op.keygen.public_only {
return Err("Expected one public output")?;
} else {
op.keygen.output = Some(iter.next().unwrap());
op.keygen.output_public = Some(iter.next().unwrap());
}
} else {
let mut iter = rest.into_iter();
if op.keygen.public_only {
op.keygen.output_public = Some(iter.next().ok_or("`--public-only` requires one output.")?);
} else {
op.keygen.output = iter.next();
}
}
},
_ => return Err("No params specified. Try `--help`.")?,
};
// Ensure valid
let mut op = op.try_into()?;
// Add spec
for spec in cfg_files.into_iter()
{
spec::Spec::re //TODO: Fuck this whole module. Just rewrite all of this crap shit FUCK
}
Ok(op)
}
impl TryFrom<builder::Operation> for Operation
{
type Error = Error;
fn try_from(op: builder::Operation) -> Result<Self, Self::Error>
{
match op.mode {
builder::OperationMode::Normal => {
if op.keygen != builder::KeyGenOps::default() {
return Err("Invalid mode for params.")?;
}
if op.normal.rsa.len() < 1 && op.normal.auto_sign {
warn!("`--auto-sign` here does nothing.");
}
if op.normal.aes.len() < 1 && op.normal.rsa.len() < 1 {
return Err("No keys specified.")?;
}
if op.normal.translate.len() < 1 {
return Err("No files specified.")?;
}
#[cfg(not(feature="threads"))]
if op.normal.sync {
warn!("`--sync` here does nothing, binary not compiled with `threads` feature.");
}
// Number of inputs without specified outputs
let autodetect_inputs = op.normal.translate.iter().filter(|x| x.1.is_none()).count();
if op.normal.in_place && autodetect_inputs < 1 {
warn!("`--in-place` here does nothing, no autodetected outputs.");
}
Ok(Operation::Normal{
mode: op.normal.mode,
rsa: op.normal.rsa.into_iter().map(|x| x.into()).collect(),
sign: op.normal.sign.into_iter().map(|x| x.into()).collect(),
auto_sign: op.normal.auto_sign,
aes: op.normal.aes.into_iter().map(|x| x.into()).collect(),
translate: op.normal.translate.into_iter().map(|x| (x.0.into(), x.1.and_then(|y| Some(y.into())))).collect(),
in_place: op.normal.in_place,
verbose: op.verbose,
#[cfg(feature="threads")]
sync: op.normal.sync,
})
},
builder::OperationMode::KeyGen(GenerateKey::Rsa) => {
if op.normal != builder::NormalOps::default() {
return Err("Invalid mode for params.")?;
}
if op.keygen.public_only && op.keygen.private_only {
return Err("`--private-only` and `--public-only` will generate nothing.")?;
}
if op.keygen.public_only && op.keygen.cycle.is_none() {
warn!("`--public-only` is only useful with `--cycle`.");
}
if op.keygen.output.is_none()
&& op.keygen.output_public.is_none()
&& op.keygen.cycle.is_none() {
return Err("No output found.")?;
}
if op.keygen.public_only && op.keygen.cycle.is_none() && op.keygen.output_public.is_none()
{
return Err("`--public-only` requires one output or `--cycle`.")?;
}
if op.keygen.private_only && op.keygen.output_public.is_some()
{
warn!("Unused public key output.");
}
Ok(Operation::KeyGen{
key_type: GenerateKey::Rsa,
format: op.keygen.format,
cycle: op.keygen.cycle.and_then(|x| Some(x.into())),
use_password: op.keygen.use_password,
output: op.keygen.output.and_then(|x| Some(x.into())),
private_only: op.keygen.private_only,
public_only: op.keygen.public_only,
output_public: op.keygen.output_public.and_then(|x| Some(x.into())),
password: op.keygen.password,
verbose: op.verbose,
})
},
builder::OperationMode::KeyGen(GenerateKey::Aes) => {
if op.normal != builder::NormalOps::default() {
return Err("Invalid mode for params.")?;
}
if op.keygen.private_only ||
op.keygen.public_only ||
op.keygen.output_public.is_some()
{
return Err("Invalid params for mode.")?;
}
if !op.keygen.format.is_aes_okay()
{
return Err("This output format is not valid for AES key.")?;
}
if op.keygen.cycle.is_none() && op.keygen.output.is_none() {
return Err("No output set.")?;
}
Ok(Operation::KeyGen{
key_type: GenerateKey::Aes,
format: op.keygen.format,
cycle: op.keygen.cycle.and_then(|x| Some(x.into())),
use_password: op.keygen.use_password,
output: op.keygen.output.and_then(|x| Some(x.into())),
password: op.keygen.password,
verbose: op.verbose,
private_only: false,
public_only: false,
output_public: None,
})
},
builder::OperationMode::Help => Ok(Operation::Help),
}
}
}