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, pub use_password: bool, pub password: Option, pub output: Option, pub private_only: bool, pub output_public: Option, 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, pub sign: Vec, pub auto_sign: bool, pub aes: Vec, pub translate: Vec<(String, Option)>, 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(input: &mut I) -> Result where I: Iterator, T: AsRef { 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 for Operation { type Error = Error; fn try_from(op: builder::Operation) -> Result { 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), } } }