use std::fs::File;
use std::io::Cursor;
use std::path::PathBuf;
use std::process::ExitCode;
use anyhow::{Context, Result};
use clap::{Parser, ValueEnum};
use malwaredb_client::{decode_from_cart, encode_to_cart};
#[derive(Clone, ValueEnum, Debug, Eq, PartialEq)]
pub enum Action {
Encode,
Decode,
}
#[derive(Parser, Clone, Debug, Eq, PartialEq)]
pub struct CartIO {
#[arg(short, long)]
pub action: Option<Action>,
#[arg(short, long, value_hint = clap::ValueHint::FilePath)]
pub output: Option<PathBuf>,
#[arg(value_name = "FILE", value_hint = clap::ValueHint::FilePath)]
pub file: PathBuf,
}
impl CartIO {
pub fn execute(&self) -> Result<ExitCode> {
if self.output.is_none() != self.action.is_none() {
eprintln!("Action requires an output, and vise-versa");
return Ok(ExitCode::FAILURE);
}
if self.output.is_none() && self.action.is_none() {
let mut input_file = File::open(&self.file)?;
let mut output_buffer = Cursor::new(vec![]);
return if let Ok((header, footer)) =
cart_container::unpack_stream(&mut input_file, &mut output_buffer, None)
{
let data = output_buffer.into_inner();
let magic = hex::encode(&data[0..10]);
println!("First ten bytes: {magic}");
if let Some(meta) = header {
if !meta.is_empty() {
println!("Header:");
}
for (key, value) in meta {
println!("{key}: {value}");
}
}
if let Some(meta) = footer {
if !meta.is_empty() {
println!("Footer:");
}
for (key, value) in meta {
println!("{key}: {value}");
}
}
Ok(ExitCode::SUCCESS)
} else {
eprintln!("{} is not a CaRT file.", self.file.display());
Ok(ExitCode::FAILURE)
};
}
let file_contents = std::fs::read(&self.file).context("failed to read file from disk")?;
match self.action.as_ref().unwrap() {
Action::Encode => {
let encoded = encode_to_cart(&file_contents)?;
std::fs::write(self.output.as_ref().unwrap(), encoded)
.context("failed to write CaRT to disk")?;
}
Action::Decode => {
let (decoded, _, _) = decode_from_cart(&file_contents)?;
std::fs::write(self.output.as_ref().unwrap(), decoded)
.context("failed to write decoded file to disk")?;
}
}
Ok(ExitCode::SUCCESS)
}
}