use std::path::PathBuf; use flate2::read::GzDecoder; use clap::Parser; use crate::error::DrawerError; use crate::operation::Operation; use crate::utils::*; #[derive(Parser, Debug, PartialEq)] pub struct OpenOperation { pub drawer_file: PathBuf, pub target_path: Option, #[arg(short = 'i')] pub key: Option, #[arg(short = 'f')] pub force: bool, } fn single_file_entry(data: &[u8]) -> Option { let cursor = std::io::Cursor::new(data); let gz = GzDecoder::new(cursor); let mut archive = tar::Archive::new(gz); let mut entries = archive.entries().ok()?; let entry = entries.next()?.ok()?; if !entry.header().entry_type().is_file() { return None; } if entries.next().is_some() { return None; } Some(entry.path().ok()?.into_owned()) } impl Operation for OpenOperation { fn validate(&self) -> Result<(), DrawerError> { validate_drawer_extension(&self.drawer_file)?; validate_ssh_key(&self.key)?; validate_drawer_file_exists(&self.drawer_file)?; Ok(()) } fn perform(&self) -> Result<(), DrawerError> { self.validate()?; let encrypted = std::fs::read(&self.drawer_file) .map_err(|_| DrawerError::DrawerFileNotFound(self.drawer_file.clone()))?; let decrypted = decrypt(encrypted, &self.key, &self.drawer_file)?; let sf = single_file_entry(&decrypted); let target = self.target_path.as_ref().cloned() .unwrap_or_else(|| default_target(&self.drawer_file, sf.as_deref())); if self.target_path.is_none() { println!("(assuming into {})", target.display()); } if !self.force && target.exists() { if target.is_file() { return Err(DrawerError::TargetNotEmpty(target.clone())); } let mut entries = std::fs::read_dir(&target) .map_err(|_| DrawerError::TargetInvalid(target.clone()))?; if entries.next().is_some() { return Err(DrawerError::TargetNotEmpty(target.clone())); } } if sf.is_some() { let cursor = std::io::Cursor::new(&decrypted); let gz = GzDecoder::new(cursor); let mut archive = tar::Archive::new(gz); let mut entry = archive.entries() .map_err(|_| DrawerError::UntarFailed)? .next() .ok_or(DrawerError::UntarFailed)? .map_err(|_| DrawerError::UntarFailed)?; entry.unpack(&target).map_err(|_| DrawerError::UntarFailed)?; } else { let cursor = std::io::Cursor::new(decrypted); let gz = GzDecoder::new(cursor); let mut archive = tar::Archive::new(gz); archive.unpack(&target).map_err(|_| DrawerError::UntarFailed)?; } Ok(()) } }