diff --git a/src/close_operation.rs b/src/close_operation.rs index 3c02697..5acf4cf 100644 --- a/src/close_operation.rs +++ b/src/close_operation.rs @@ -1,4 +1,4 @@ -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use flate2::Compression; use flate2::write::GzEncoder; use clap::Parser; @@ -20,9 +20,6 @@ impl Operation for CloseOperation { validate_ssh_key(&self.key)?; let target = self.target_path.as_ref().cloned() .unwrap_or_else(|| default_target(&self.drawer_file, None)); - if self.target_path.is_none() { - println!("(assuming from {})", target.display()); - } let meta = std::fs::metadata(&target) .map_err(|_| DrawerError::TargetInvalid(target.clone()))?; if !meta.is_dir() && !meta.is_file() { @@ -33,11 +30,7 @@ impl Operation for CloseOperation { } let canon_target = target.canonicalize() .unwrap_or_else(|_| target.clone()); - let drawer_parent = match self.drawer_file.parent() { - None => Path::new("."), - Some(p) if p == Path::new("") => Path::new("."), - Some(p) => p, - }; + let drawer_parent = parent_or_dot(&self.drawer_file); let canon_drawer = drawer_parent.canonicalize() .unwrap_or_else(|_| drawer_parent.to_path_buf()) .join(self.drawer_file.file_name().unwrap_or_default()); @@ -47,11 +40,12 @@ impl Operation for CloseOperation { Ok(()) } - fn perform(&self) -> Result<(), DrawerError> { - self.validate()?; - + fn execute(&self) -> Result<(), DrawerError> { let target = self.target_path.as_ref().cloned() .unwrap_or_else(|| default_target(&self.drawer_file, None)); + if self.target_path.is_none() { + println!("(assuming from {})", target.display()); + } let mut buf: Vec = Vec::new(); { diff --git a/src/info_operation.rs b/src/info_operation.rs index 98ac012..a62b97d 100644 --- a/src/info_operation.rs +++ b/src/info_operation.rs @@ -1,5 +1,4 @@ use std::path::PathBuf; -use flate2::read::GzDecoder; use clap::Parser; use crate::error::DrawerError; use crate::operation::Operation; @@ -18,9 +17,7 @@ enum ArchiveInfo { } fn archive_info(data: &[u8]) -> Result { - let cursor = std::io::Cursor::new(data); - let gz = GzDecoder::new(cursor); - let mut archive = tar::Archive::new(gz); + let mut archive = open_archive(data); let mut entries = archive.entries().map_err(|_| DrawerError::UntarFailed)?; let first = entries.next() @@ -34,9 +31,7 @@ fn archive_info(data: &[u8]) -> Result { } } - let cursor = std::io::Cursor::new(data); - let gz = GzDecoder::new(cursor); - let mut archive = tar::Archive::new(gz); + let mut archive = open_archive(data); let file_count = archive.entries() .map_err(|_| DrawerError::UntarFailed)? .filter_map(|e| e.ok()) @@ -53,9 +48,7 @@ impl Operation for InfoOperation { Ok(()) } - fn perform(&self) -> Result<(), DrawerError> { - self.validate()?; - + fn execute(&self) -> Result<(), DrawerError> { 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)?; diff --git a/src/key_operation.rs b/src/key_operation.rs index 29559cb..603648d 100644 --- a/src/key_operation.rs +++ b/src/key_operation.rs @@ -1,8 +1,9 @@ -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use clap::Parser; use ssh_key::{Algorithm, LineEnding, PrivateKey}; use crate::error::DrawerError; use crate::operation::Operation; +use crate::utils::parent_or_dot; #[derive(Parser, Debug, PartialEq)] pub struct KeyOperation { @@ -14,19 +15,13 @@ impl Operation for KeyOperation { if self.filename.exists() { return Err(DrawerError::KeyOutputFailed(self.filename.clone())); } - let parent = match self.filename.parent() { - None => Path::new("."), - Some(p) if p == Path::new("") => Path::new("."), - Some(p) => p, - }; - if !parent.exists() { + if !parent_or_dot(&self.filename).exists() { return Err(DrawerError::KeyOutputFailed(self.filename.clone())); } Ok(()) } - fn perform(&self) -> Result<(), DrawerError> { - self.validate()?; + fn execute(&self) -> Result<(), DrawerError> { let private_key = PrivateKey::random(&mut rand::rngs::OsRng, Algorithm::Ed25519) .map_err(|_| DrawerError::KeyGenerateFailed)?; let pem = private_key.to_openssh(LineEnding::LF) diff --git a/src/open_operation.rs b/src/open_operation.rs index 6bd1aa7..97a35d1 100644 --- a/src/open_operation.rs +++ b/src/open_operation.rs @@ -1,5 +1,4 @@ -use std::path::PathBuf; -use flate2::read::GzDecoder; +use std::path::{Path, PathBuf}; use clap::Parser; use crate::error::DrawerError; use crate::operation::Operation; @@ -16,9 +15,7 @@ pub struct OpenOperation { } 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 archive = open_archive(data); let mut entries = archive.entries().ok()?; let entry = entries.next()?.ok()?; if !entry.header().entry_type().is_file() { @@ -30,6 +27,23 @@ fn single_file_entry(data: &[u8]) -> Option { Some(entry.path().ok()?.into_owned()) } +fn unpack_single_file(data: &[u8], target: &Path) -> Result<(), DrawerError> { + let mut archive = open_archive(data); + 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)?; + Ok(()) +} + +fn unpack_directory(data: &[u8], target: &Path) -> Result<(), DrawerError> { + let mut archive = open_archive(data); + archive.unpack(target).map_err(|_| DrawerError::UntarFailed)?; + Ok(()) +} + impl Operation for OpenOperation { fn validate(&self) -> Result<(), DrawerError> { validate_drawer_extension(&self.drawer_file)?; @@ -38,9 +52,7 @@ impl Operation for OpenOperation { Ok(()) } - fn perform(&self) -> Result<(), DrawerError> { - self.validate()?; - + fn execute(&self) -> Result<(), DrawerError> { 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)?; @@ -65,21 +77,9 @@ impl Operation for OpenOperation { } 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)?; + unpack_single_file(&decrypted, &target) } 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)?; + unpack_directory(&decrypted, &target) } - Ok(()) } } diff --git a/src/operation.rs b/src/operation.rs index 4865230..e6369c7 100644 --- a/src/operation.rs +++ b/src/operation.rs @@ -1,6 +1,10 @@ use crate::error::DrawerError; pub trait Operation { - fn perform(&self) -> Result<(), DrawerError>; fn validate(&self) -> Result<(), DrawerError>; + fn execute(&self) -> Result<(), DrawerError>; + fn perform(&self) -> Result<(), DrawerError> { + self.validate()?; + self.execute() + } } diff --git a/src/utils.rs b/src/utils.rs index 275aa40..6f78bf9 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,8 +1,21 @@ use std::io::Write; use std::path::{Path, PathBuf}; use age::ssh::Identity; +use flate2::read::GzDecoder; use crate::error::DrawerError; +pub fn parent_or_dot(p: &Path) -> &Path { + match p.parent() { + None => Path::new("."), + Some(p) if p == Path::new("") => Path::new("."), + Some(p) => p, + } +} + +pub fn open_archive(data: &[u8]) -> tar::Archive>> { + tar::Archive::new(GzDecoder::new(std::io::Cursor::new(data))) +} + pub fn key_path(key: &Option) -> Result { if let Some(k) = key { return Ok(k.clone());