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.
207 lines
6.2 KiB
207 lines
6.2 KiB
//! Parser for normal mode
|
|
use super::*;
|
|
use tokio::sync::mpsc::Receiver;
|
|
|
|
#[instrument(level="trace", err, skip(rx))]
|
|
pub(super) async fn handle(mut rx: Receiver<Atom>) -> Result<config::Operation, error::ConstructError>
|
|
{
|
|
let mut output = op::Normal::default();
|
|
let mut had = smallmap::Map::new();
|
|
while let Some(atom) = rx.recv().await {
|
|
debug!("[Normal] recv atom: {:?}", atom);
|
|
// Construct `Normal` with atoms
|
|
|
|
if Atom::hash_into(&atom, &mut had) {
|
|
return Err(error::ConstructError::AlreadyExists(format!("TODO: {:?}", atom)));
|
|
}
|
|
|
|
match atom {
|
|
Atom::NonSpecific => output.in_place = true,
|
|
Atom::SetMode(mode) => output.mode = Some(mode),
|
|
Atom::FileAuto(file) => output.files.push((file, None, None)),
|
|
Atom::FileSpecific(finput, foutput, mode) => {
|
|
let mode = match mode {
|
|
TranslationMode::Autodetect => resolve::find_file_mode(&finput).await?,
|
|
TranslationMode::Encrypt => op::Mode::Encrypt,
|
|
TranslationMode::Decrypt => op::Mode::Decrypt,
|
|
};
|
|
output.files.push((finput, Some(foutput), Some(mode)));
|
|
},
|
|
Atom::Key(kind, string, password) => {
|
|
let (kind, sign) = match kind {
|
|
KeyKind::Autodetect(s) => (resolve::find_key_mode(&string).await?, s),
|
|
KeyKind::RsaPrivate(s) => (config::KeyKind::RsaPrivate, s),
|
|
KeyKind::RsaPublic(s) => (config::KeyKind::RsaPublic, s),
|
|
KeyKind::Aes => (config::KeyKind::Aes, Default::default()),
|
|
};
|
|
if sign != IsSigning::No && kind == config::KeyKind::Aes {
|
|
warn!("Key was detected to not be RSA, option to sign is set and ignored.");
|
|
} else if sign != IsSigning::No {
|
|
output.sign.push((string.clone(), password.clone()));
|
|
}
|
|
if sign != IsSigning::Only {
|
|
match kind {
|
|
config::KeyKind::Aes => output.aes.push((string, password)),
|
|
config::KeyKind::RsaPublic => output.rsa.push((string, password)),
|
|
config::KeyKind::RsaPrivate => output.rsa.push((string, password))
|
|
}
|
|
}
|
|
},
|
|
Atom::Generate(_) => unreachable!(),
|
|
}
|
|
}
|
|
Ok(config::Operation::Normal(output))
|
|
}
|
|
|
|
#[instrument(level="trace", skip(args, atoms))]
|
|
pub(super) async fn parse<I: IntoIterator<Item=String>>(args: I, mut atoms: mpsc::Sender<Atom>) -> Result<(), error::Error>
|
|
{
|
|
let mut args = args.into_iter();
|
|
let mut reading=true;
|
|
|
|
let mut mode_set=false;
|
|
while let Some(arg) = args.next()
|
|
{
|
|
trace!("[Normal (parse)] opt string {:?}", arg);
|
|
macro_rules! take_one {
|
|
($msg:literal $($tt:tt)*) => {
|
|
match args.next() {
|
|
Some(v) => v,
|
|
None => return Err((arg, ParseErrorKind::ExpectedArg(format!($msg $($tt)*))).into()),
|
|
}
|
|
}
|
|
}
|
|
if reading && arg.starts_with("-") {
|
|
let mut opt = arg.chars().skip(1);
|
|
|
|
macro_rules! check_mode_set
|
|
{
|
|
() => {
|
|
if mode_set {
|
|
return Err((arg, error::ParseErrorKind::ModeSet).into());
|
|
} else {
|
|
mode_set=true;
|
|
}
|
|
}
|
|
}
|
|
match opt.next() {
|
|
Some('a') => check_mode_set!(),
|
|
Some('e') => {
|
|
check_mode_set!();
|
|
atoms.send(Atom::SetMode(config::op::Mode::Encrypt)).await?;
|
|
},
|
|
Some('d') => {
|
|
check_mode_set!();
|
|
atoms.send(Atom::SetMode(config::op::Mode::Decrypt)).await?;
|
|
},
|
|
Some('i') => match opt.next() {
|
|
None => atoms.send(Atom::NonSpecific).await?,
|
|
Some(r) => return Err((ParseErrorKind::BadTail('i', iter::once(r).chain(opt).collect()), arg).swap().into()),
|
|
},
|
|
Some('t') => {
|
|
let input = take_one!("t: Expected input file");
|
|
let output = take_one!("t: Expected output file");
|
|
let mut trans = TranslationMode::default();
|
|
|
|
let mut had = smallmap::Map::new();
|
|
while let Some(opt) = opt.next() {
|
|
if (had.contains_key(&opt), had.insert(opt, ())).0 {
|
|
// arg has been repeated, not allowed
|
|
return Err((arg, error::ParseErrorKind::NotUnique(opt)).into());
|
|
}
|
|
macro_rules! check_combine {
|
|
($($chars:expr),+) => {
|
|
$(if had.contains_key(&$chars) {
|
|
return Err(error::Error::from((arg, error::ParseErrorKind::CannotCombine(opt, $chars))));
|
|
})*
|
|
};
|
|
}
|
|
|
|
match opt {
|
|
'e' => trans= TranslationMode::Encrypt,
|
|
'd' => trans= TranslationMode::Decrypt,
|
|
_ => return Err((arg, error::ParseErrorKind::UnknownOptionFor('t', opt)).into()),
|
|
}
|
|
}
|
|
atoms.send(Atom::FileSpecific(input, output, trans)).await?;
|
|
},
|
|
Some('k') => {
|
|
let key_file = take_one!("k: Expected a key file");
|
|
enum KeySpec {
|
|
Autodetect,
|
|
RsaPrivate,
|
|
RsaPublic,
|
|
Aes,
|
|
}
|
|
let mut kind = KeySpec::Autodetect;
|
|
let mut password = Password::No;
|
|
let mut signing = IsSigning::No;
|
|
|
|
let mut had = smallmap::Map::new();
|
|
while let Some(opt) = opt.next() {
|
|
if (had.contains_key(&opt), had.insert(opt, ())).0 {
|
|
// arg has been repeated, not allowed
|
|
return Err((arg, error::ParseErrorKind::NotUnique(opt)).into());
|
|
}
|
|
macro_rules! check_combine {
|
|
($($chars:expr),+) => {
|
|
$(if had.contains_key(&$chars) {
|
|
return Err(error::Error::from((arg, error::ParseErrorKind::CannotCombine(opt, $chars))));
|
|
})*
|
|
};
|
|
}
|
|
|
|
match opt {
|
|
'R' => {
|
|
check_combine!['r','a'];
|
|
kind = KeySpec::RsaPrivate;
|
|
},
|
|
'r' => {
|
|
check_combine!['R','a'];
|
|
kind = KeySpec::RsaPublic;
|
|
},
|
|
'a' => {
|
|
check_combine!['r','R'];
|
|
kind = KeySpec::Aes;
|
|
},
|
|
'p' => {
|
|
check_combine!['P'];
|
|
password= Password::Yes;
|
|
},
|
|
'P' => {
|
|
check_combine!['p'];
|
|
password = Password::Specific(take_one!("P: Expected a password"));
|
|
},
|
|
'S' => {
|
|
check_combine!['s', 'a'];
|
|
signing = IsSigning::Only;
|
|
},
|
|
's' => {
|
|
check_combine!['S', 'a'];
|
|
signing = IsSigning::Yes;
|
|
},
|
|
_ => return Err((arg, error::ParseErrorKind::UnknownOptionFor('k', opt)).into()),
|
|
}
|
|
}
|
|
|
|
let kind = match kind {
|
|
KeySpec::Autodetect => KeyKind::Autodetect(signing),
|
|
KeySpec::RsaPublic => KeyKind::RsaPublic(signing),
|
|
KeySpec::RsaPrivate => KeyKind::RsaPrivate(signing),
|
|
KeySpec::Aes => KeyKind::Aes,
|
|
};
|
|
atoms.send(Atom::Key(kind, key_file, password)).await?;
|
|
},
|
|
Some(unknown) => return Err((arg, error::ParseErrorKind::UnknownOption(unknown)).into()),
|
|
None => reading=false,
|
|
}
|
|
} else {
|
|
reading=false;
|
|
atoms.send(Atom::FileAuto(arg)).await?;
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|