use std::{
fs::{File, OpenOptions},
io::{stdin, stdout, BufReader, Write},
};
use clap::{Parser, Subcommand};
use fritzdecode::{markers::Unverified, ConfigBackup};
#[derive(Parser)]
struct Args {
#[command(subcommand)]
operation: Operation,
}
#[derive(Subcommand)]
enum Operation {
Decrypt {
file: String,
#[arg(long)]
ignore_invalid_crc: bool,
#[arg(short, long)]
output: Option<String>,
},
Verify {
file: Option<String>,
},
#[command(name = "gencrc")]
GenCrc {
file: Option<String>,
},
}
fn load_file(file: Option<&str>) -> color_eyre::Result<ConfigBackup<Unverified>> {
match file {
None | Some("-") => {
let config = ConfigBackup::load(BufReader::new(stdin()))?;
Ok(config)
}
Some(file) => {
let config = ConfigBackup::load(BufReader::new(File::open(file)?))?;
Ok(config)
}
}
}
fn main() -> color_eyre::Result<()> {
color_eyre::install()?;
let args = Args::parse();
match args.operation {
Operation::Decrypt {
file,
ignore_invalid_crc,
output,
} => {
let config = load_file(Some(&file))?;
print!("Password: ");
stdout().flush()?;
let mut password = String::new();
stdin().read_line(&mut password)?;
let password = password.trim_end_matches('\n');
let decrypted = match config.verify_crc() {
Ok(config) => config.decrypt(password)?,
Err((unverified, _e)) if ignore_invalid_crc => unverified.decrypt(password)?,
Err((_unverified, e)) => return Err(e.into()),
};
let decrypted = decrypted.update_crc();
if let Some(output) = output {
decrypted.serialize(
OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(output)?,
)?;
} else {
decrypted.serialize(stdout())?;
}
}
Operation::Verify { file } => {
let config = load_file(file.as_deref())?;
config.verify_crc().map_err(|(_unverified, e)| e)?;
}
Operation::GenCrc { file } => {
let config = load_file(file.as_deref())?.update_crc();
println!("{:08X}", config.crc());
}
}
Ok(())
}