diff --git a/Readme.md b/Readme.md index cfe1f74..660857f 100644 --- a/Readme.md +++ b/Readme.md @@ -16,7 +16,7 @@ To install Zap, run the following command from the project root: Where the `[OUTPUT]` is the path to which you want to store the `.zap` file. -- The zap file can optionally be encrypted with by providing the `-e` flag and choosing `password` or `key`. Note that `key` method is not supported yet. +Using `zap archive --help` will list the available options for encryption and compression. ### In order to **decompress** a Zap archive @@ -24,7 +24,9 @@ Where the `[OUTPUT]` is the path to which you want to store the `.zap` file. Where the `[ARCHIVE]` is the path to the file which you want to extract and the `[OUTPUT]` is the folder in which you want the contents to be placed inside. -- If the Zap file was encrypted, the `-e` flag needs to be provided along with the correct encryption method. *This will hopefully be resolved in future versions* +Using `zap archive --help` will list the available options for encryption and compression. + +Unfortunately, in it's current state, that compression and encryption methods aren't stored in metadata and must be given when extracting. this will be fixed in coming releases. ### In order to **list** the contents of a Zap archive diff --git a/src/bin/cli_util/compression.rs b/src/bin/cli_util/compression.rs new file mode 100644 index 0000000..588cb22 --- /dev/null +++ b/src/bin/cli_util/compression.rs @@ -0,0 +1,75 @@ +use clap::ValueEnum; +use zap::compression::CompressionType; + + + +#[derive(Debug, Clone, ValueEnum)] +pub enum CompressionLevel{ + Fastest, + Best, + Default, +} + +impl From for CompressionLevel { + fn from(s: String) -> Self { + match s.as_str() { + "fastest" => CompressionLevel::Fastest, + "best" => CompressionLevel::Best, + _ => CompressionLevel::Default, + } + } +} + +impl Into for CompressionLevel { + fn into(self) -> flate2::Compression { + match self { + CompressionLevel::Fastest => flate2::Compression::fast(), + CompressionLevel::Best => flate2::Compression::best(), + CompressionLevel::Default => flate2::Compression::default(), + } + } +} + +#[derive(Default, Debug, Clone, ValueEnum)] +pub enum BinCompressionType { + Passthrough, + #[default] + Lz4, + Gzip, + Snappy, +} + +impl From for BinCompressionType { + fn from(s: String) -> Self { + match s.as_str() { + "passthrough" => Self::Passthrough, + "lz4" => Self::Lz4, + "gzip" => Self::Gzip, + "snappy" => Self::Snappy, + "" => Self::default(), + _ => Self::Passthrough, + } + } +} + +impl From for BinCompressionType { + fn from(e: CompressionType) -> Self { + match e { + CompressionType::Passthrough => Self::Passthrough, + CompressionType::Lz4 => Self::Lz4, + CompressionType::Gzip => Self::Gzip, + CompressionType::Snappy => Self::Snappy, + } + } +} + +impl Into for BinCompressionType { + fn into(self) -> CompressionType { + match self { + BinCompressionType::Passthrough => CompressionType::Passthrough, + BinCompressionType::Lz4 => CompressionType::Lz4, + BinCompressionType::Gzip => CompressionType::Gzip, + BinCompressionType::Snappy => CompressionType::Snappy, + } + } +} \ No newline at end of file diff --git a/src/bin/cli_util/encryption.rs b/src/bin/cli_util/encryption.rs index 8cfcb5d..4e6e8de 100644 --- a/src/bin/cli_util/encryption.rs +++ b/src/bin/cli_util/encryption.rs @@ -1,8 +1,47 @@ use clap::ValueEnum; +use zap::encryption::EncryptionType; // Consider moving -#[derive(Debug, Clone, ValueEnum)] -pub enum SecretType { - Password, - Key +#[derive(Debug, Default, Clone, ValueEnum)] +pub enum BinEncryptionType { + Passthrough, + #[default] + XChaCha, + AesGcm, + ChaCha, } + +impl From for BinEncryptionType { + fn from(s: String) -> Self { + match s.as_str() { + "passthrough" => Self::Passthrough, + "xchacha" => Self::XChaCha, + "aesgcm" => Self::AesGcm, + "chacha" => Self::ChaCha, + "" => Self::default(), + _ => Self::Passthrough, + } + } +} + +impl From for BinEncryptionType { + fn from(e: EncryptionType) -> Self { + match e { + EncryptionType::Passthrough => Self::Passthrough, + EncryptionType::XChaCha => Self::XChaCha, + EncryptionType::AesGcm => Self::AesGcm, + EncryptionType::ChaCha => Self::ChaCha, + } + } +} + +impl Into for BinEncryptionType { + fn into(self) -> EncryptionType { + match self { + BinEncryptionType::Passthrough => EncryptionType::Passthrough, + BinEncryptionType::XChaCha => EncryptionType::XChaCha, + BinEncryptionType::AesGcm => EncryptionType::AesGcm, + BinEncryptionType::ChaCha => EncryptionType::ChaCha, + } + } +} \ No newline at end of file diff --git a/src/bin/cli_util/mod.rs b/src/bin/cli_util/mod.rs index 9205206..0447ebe 100644 --- a/src/bin/cli_util/mod.rs +++ b/src/bin/cli_util/mod.rs @@ -1,3 +1,4 @@ +mod compression; mod encryption; mod logging; mod password; @@ -10,19 +11,18 @@ use std::{ use clap::{Parser, Subcommand}; use log::info; -use zap::{ - encryption::EncryptionSecret, - error::{EncryptionKeyError, EncryptionSecretError, ZapError}, -}; +use zap::{encryption::EncryptionSecret, error::ZapError}; use zapf::{pack_files, unpack_files}; -use crate::cli_util::{ - logging::init_logger, - password::{get_password_confirm, get_password_noconf}, -}; +use crate::cli_util::{logging::init_logger, password::get_password_confirm}; -use self::{encryption::SecretType, logging::Verbosity}; +use self::{ + compression::{BinCompressionType, CompressionLevel}, + encryption::BinEncryptionType, + logging::Verbosity, + password::get_password_noconf, +}; #[derive(Debug, Parser)] #[command( @@ -49,31 +49,32 @@ enum Command { input: String, /// Output file output: String, - - /// Whether to encrypt the data - #[arg(short, long)] - encryption: Option, /// If SecretType is key then keypath must be provided #[arg(short, long)] keypath: Option, #[arg(short, long, default_value = "normal")] verbosity: Verbosity, + #[arg(long, short, default_value = "passthrough")] + encryption_algorithm: BinEncryptionType, + #[arg(long, short, default_value = "passthrough")] + compression_algorithm: BinCompressionType, + #[arg(long, default_value = "fastest")] + compression_level: CompressionLevel, }, Extract { /// Input file input: String, - /// Output folder output: String, - - /// Whether to encrypt the data - #[arg(short, long)] - encryption: Option, /// If SecretType is key then keypath must be provided #[arg(short, long)] keypath: Option, #[arg(short, long, default_value = "normal")] verbosity: Verbosity, + #[arg(long, short, default_value = "passthrough")] + encryption_algorithm: BinEncryptionType, + #[arg(long, short, default_value = "passthrough")] + compression_algorithm: BinCompressionType, }, List { archive: String, @@ -88,17 +89,35 @@ impl Command { Command::Archive { input, output, - encryption, keypath, verbosity, - } => Self::archive(input, output, encryption, keypath, verbosity), + encryption_algorithm, + compression_algorithm, + compression_level, + } => Self::archive( + input, + output, + keypath, + verbosity, + encryption_algorithm, + compression_algorithm, + compression_level, + ), Command::Extract { input, output, - encryption, keypath, verbosity, - } => Self::extract(input, output, encryption, keypath, verbosity), + encryption_algorithm, + compression_algorithm, + } => Self::extract( + input, + output, + keypath, + verbosity, + encryption_algorithm, + compression_algorithm, + ), Command::List { archive, verbosity } => Self::list(archive, verbosity), } } @@ -106,34 +125,35 @@ impl Command { fn archive( input: String, output: String, - encryption: Option, keypath: Option, verbosity: Verbosity, + encryption_algorithm: BinEncryptionType, + compression_algorithm: BinCompressionType, + compression_level: CompressionLevel, ) -> Result<(), ZapError> { preamble(verbosity)?; - let enc: Option = match encryption { - Some(inner) => match inner { - SecretType::Password => Some(EncryptionSecret::Password( - match get_password_confirm(256) { - Ok(pass) => pass, - Err(e) => return Err(e.into()), - }, - )), - SecretType::Key => match keypath { - Some(path) => Some(EncryptionSecret::Key(path)), - None => { - return Err(EncryptionSecretError::Key( - EncryptionKeyError::KeyfileNotProvided, - ) - .into()) - } - }, - }, - None => None, + let encryption_secret: EncryptionSecret = match (&encryption_algorithm, keypath) { + (BinEncryptionType::Passthrough, _) => EncryptionSecret::None, + (_, Some(path)) => EncryptionSecret::Key(path), + (_, None) => EncryptionSecret::Password(match get_password_confirm(256) { + Ok(pass) => pass, + Err(e) => return Err(e.into()), + }), }; - zap::compress_directory(&input, "/tmp/unpacked", enc)?; + info!("Encryption: {:?}", encryption_algorithm); + info!("Compression: {:?}", compression_algorithm); + + zap::compress_directory( + &input, + "/tmp/unpacked", + encryption_algorithm.into(), + encryption_secret, + compression_algorithm.into(), + compression_level.into(), + zap::signing::SigningType::default(), + )?; let out_file = File::create(output).expect("Could not create file"); @@ -147,38 +167,34 @@ impl Command { fn extract( input: String, output: String, - decryption: Option, keypath: Option, verbosity: Verbosity, + encryption_algorithm: BinEncryptionType, + compression_algorithm: BinCompressionType, ) -> Result<(), ZapError> { preamble(verbosity)?; - let enc: Option = match decryption { - Some(inner) => match inner { - SecretType::Password => { - Some(EncryptionSecret::Password(match get_password_noconf(256) { - Ok(pass) => pass, - Err(e) => return Err(e.into()), - })) - } - SecretType::Key => match keypath { - Some(path) => Some(EncryptionSecret::Key(path)), - None => { - return Err(EncryptionSecretError::Key( - EncryptionKeyError::KeyfileNotProvided, - ) - .into()) - } - }, - }, - None => None, + let encryption_secret: EncryptionSecret = match (&encryption_algorithm, keypath) { + (BinEncryptionType::Passthrough, _) => EncryptionSecret::None, + (_, None) => EncryptionSecret::Password(match get_password_noconf(256) { + Ok(pass) => pass, + Err(e) => return Err(e.into()), + }), + (_, Some(path)) => EncryptionSecret::Key(path), }; // Need to check if this function validates path names // to prevent directory traversal. unpack_files(input, "/tmp/unpacked")?; - zap::decompress_directory("/tmp/unpacked", &output, enc)?; + zap::decompress_directory( + "/tmp/unpacked", + &output, + encryption_algorithm.into(), + encryption_secret, + compression_algorithm.into(), + zap::signing::SigningType::default(), + )?; Ok(fs::remove_dir_all("/tmp/unpacked")?) //Ok(()) diff --git a/src/compression/mod.rs b/src/compression/mod.rs index c1bd27e..1d43cca 100644 --- a/src/compression/mod.rs +++ b/src/compression/mod.rs @@ -1,22 +1,12 @@ -pub mod passthrough; -pub mod lz4; pub mod gzip; +pub mod lz4; +pub mod passthrough; pub mod snappy; -use crate::{signing::{Signer, Verifier}, error::CompressorInitError}; +use crate::error::CompressorInitError; // External -use std::io::{ - Error, - Read, - Write, - copy, - ErrorKind, -}; -use lz4_flex::frame::{ - FrameEncoder, - FrameDecoder -}; +use std::io::{Error, Read, Write}; pub struct CompressionMode; pub struct DecompressionMode; @@ -29,7 +19,8 @@ pub trait Decompress: Read { } pub trait CompressionAlgorithm -where T: Write +where + T: Write, { type Compressor: Compress; @@ -37,18 +28,31 @@ where T: Write } pub trait DecompressionAlgorithm -where T: Read +where + T: Read, { type Decompressor: Decompress; fn decompressor(&self, reader: T) -> Result; } -#[derive(Default)] +#[derive(Default, Debug, Clone)] pub enum CompressionType { #[default] Passthrough, Lz4, Gzip, Snappy, -} \ No newline at end of file +} + +impl From for CompressionType { + fn from(s: String) -> Self { + match s.as_str() { + "passthrough" => Self::Passthrough, + "lz4" => Self::Lz4, + "gzip" => Self::Gzip, + "snappy" => Self::Snappy, + _ => Self::Passthrough, + } + } +} diff --git a/src/encryption/mod.rs b/src/encryption/mod.rs index b96d07a..e1158e0 100644 --- a/src/encryption/mod.rs +++ b/src/encryption/mod.rs @@ -46,11 +46,23 @@ pub enum EncryptionSecret { Key(String), } -#[derive(Default)] +#[derive(Default, Clone, Debug)] pub enum EncryptionType { #[default] Passthrough, XChaCha, AesGcm, ChaCha, +} + +impl From for EncryptionType { + fn from(s: String) -> Self { + match s.as_str() { + "passthrough" => Self::Passthrough, + "xchacha" => Self::XChaCha, + "aesgcm" => Self::AesGcm, + "chacha" => Self::ChaCha, + _ => Self::Passthrough, + } + } } \ No newline at end of file diff --git a/src/error.rs b/src/error.rs index a0c420f..1e96d95 100644 --- a/src/error.rs +++ b/src/error.rs @@ -309,6 +309,22 @@ pub enum PipelineDecompressionError { EncryptionError(EncryptionError), #[error(transparent)] EncryptionSecretError(EncryptionSecretError), + #[error(transparent)] + DecryptorInitError(EncryptorInitError), + #[error(transparent)] + CompressionInitError(CompressorInitError), +} + +impl From for PipelineDecompressionError { + fn from(value: CompressorInitError) -> Self { + PipelineDecompressionError::CompressionInitError(value) + } +} + +impl From for PipelineDecompressionError { + fn from(value: EncryptorInitError) -> Self { + PipelineDecompressionError::DecryptorInitError(value) + } } impl From for PipelineDecompressionError { diff --git a/src/lib.rs b/src/lib.rs index 521e4d8..f6ef66f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,43 +8,44 @@ pub mod signing; use std::{ backtrace, - fs::{self}, path::{self, Path, PathBuf}, sync::Arc, }; -use crate::encryption::DecryptionAlgorithm; -use compression::{lz4::Lz4Algorithm, CompressionAlgorithm, DecompressionAlgorithm, CompressionType}; +use crate::pipeline::ProcessingPipeline; +use compression::CompressionType; use crossbeam::sync::WaitGroup; -use encryption::{ - passthrough::{DecryptorPassthrough, EncryptorPassthrough}, - xchachapoly::XChaChaPolyAlgorithm, - EncryptionAlgorithm, EncryptionSecret, EncryptionType, -}; +use encryption::{EncryptionSecret, EncryptionType}; use error::{CompressionError, DecompressionError}; -use log::{error, debug}; -use pipeline::{CompressionPipeline, DecompressionPipeline}; +use log::{debug, error}; use rayon::ThreadPoolBuilder; -use signing::{passthrough::{SignerPassthrough, VerifierPassthrough}, SigningType}; +use signing::SigningType; use walkdir::WalkDir; +pub struct Processor {} + pub fn compress_directory( input_folder_path: &str, output_folder_path: &str, - secret: Option, + encryption: EncryptionType, + encryption_secret: EncryptionSecret, + compression: CompressionType, + compression_level: flate2::Compression, + signing: SigningType, ) -> Result<(), CompressionError> { - let avail_thread: usize = std::thread::available_parallelism()?.into(); debug!("Building thread pool with {} threads", avail_thread); - let thread_pool = ThreadPoolBuilder::new() - .num_threads(avail_thread) - .build()?; + let thread_pool = ThreadPoolBuilder::new().num_threads(avail_thread).build()?; let wg = WaitGroup::new(); - let secret_ref = Arc::new(secret); + let encryption_ref = Arc::new(encryption); + let compression_ref = Arc::new(compression); + let compression_level_ref = Arc::new(compression_level); + let signing_ref = Arc::new(signing); + let encryption_secret_ref = Arc::new(encryption_secret); for entry in WalkDir::new(input_folder_path) { let entry = entry?; @@ -79,105 +80,48 @@ pub fn compress_directory( std::fs::create_dir_all(current_dir) .expect("Failed to create all the required directories/subdirectories"); - let secret_ref = secret_ref.clone(); + let encryption_secret_ref = encryption_secret_ref.clone(); + let encryption_ref = encryption_ref.clone(); + let compression_ref = compression_ref.clone(); + let compression_level_ref = compression_level_ref.clone(); + let signing_ref = signing_ref.clone(); + let wg_ref = wg.clone(); thread_pool.spawn(move || { - let result = - std::panic::catch_unwind(|| { - let mut source = match fs::File::open(&entry_path) { - Ok(f) => f, - Err(e) => panic!("Error: {:?}", e), // TODO: Graceful cleanup - }; - - let destination = match fs::File::create(&output_path) { - Ok(f) => f, - Err(e) => panic!("Error: {:?}", e), // TODO: Graceful cleanup - }; - - match secret_ref.as_ref() { - Some(p) => { - match p { - EncryptionSecret::Password(p) => { - let enc = XChaChaPolyAlgorithm::new() - .with_key(p.clone()) - .encryptor(destination) - .unwrap(); - - let comp = match Lz4Algorithm::new().compressor(enc) { - Ok(c) => c, - Err(e) => panic!("Failed to build compress while compressing '{:?}': {:?}", output_path.display(), e), // TODO: Graceful cleanup - }; - - let sign = SignerPassthrough::from(comp); - - let pipeline = pipeline::TaskPipeline::from_writer(sign); - - match pipeline.compress(&mut source) { - Ok(_) => debug!( - "Finished compressing '{:?}' successfully", - entry_path.display() - ), - Err(e) => { - let bt = backtrace::Backtrace::capture(); - error!( - "Error while compressing '{}': {:?}", - entry_path.display(), - e - ); - log::trace!( - "Error while compressing '{}': {:?}", - entry_path.display(), - bt - ); - panic!(); - } - }; - } - EncryptionSecret::Key(_p) => { - unimplemented!("Keyfile encryption not implemented yet") - } - } - } - None => { - let enc = EncryptorPassthrough::from(destination); - let comp = match Lz4Algorithm::new().compressor(enc) { - Ok(c) => c, - Err(e) => panic!( - "Failed to build compress while compressing '{:?}': {:?}", - output_path.display(), - e - ), // TODO: Graceful cleanup - }; - let sign = SignerPassthrough::from(comp); - - let pipeline = pipeline::TaskPipeline::from_writer(sign); - - match pipeline.compress(&mut source) { - Ok(_) => debug!( - "Finished compressing '{:?}' successfully", - entry_path.display() - ), - Err(e) => { - let bt = backtrace::Backtrace::capture(); - error!( - "Error while compressing '{}': {:?}", - entry_path.display(), - e - ); - log::trace!( - "Error while compressing '{}': {:?}", - entry_path.display(), - bt - ); - panic!(); - } - }; - } - }; + let result = std::panic::catch_unwind(|| { + let pipeline = ProcessingPipeline::new() + .with_source(entry_path.clone()) + .with_destination(output_path.clone()) + .with_compression(compression_ref) + .with_compression_level(compression_level_ref) + .with_encryption(encryption_ref) + .with_encryption_secret(encryption_secret_ref) + .with_signing(signing_ref); + + match pipeline.compress_dir() { + Ok(_) => debug!( + "Finished compressing '{:?}' successfully", + entry_path.display() + ), + Err(e) => { + let bt = backtrace::Backtrace::capture(); + error!( + "Error while compressing '{}': {:?}", + entry_path.display(), + e + ); + log::trace!( + "Error while compressing '{}': {:?}", + entry_path.display(), + bt + ); + panic!(); + } + }; - drop(wg_ref); - }); + drop(wg_ref); + }); match result { Ok(_) => {} @@ -202,19 +146,23 @@ pub fn compress_directory( pub fn decompress_directory( input_folder_path: &str, output_folder_path: &str, - secret: Option, + encryption: EncryptionType, + encryption_secret: EncryptionSecret, + compression: CompressionType, + signing: SigningType, ) -> Result<(), DecompressionError> { let avail_thread: usize = std::thread::available_parallelism()?.into(); debug!("Building thread pool with {} threads", avail_thread); - let thread_pool = ThreadPoolBuilder::new() - .num_threads(avail_thread) - .build()?; + let thread_pool = ThreadPoolBuilder::new().num_threads(avail_thread).build()?; let wg = WaitGroup::new(); - let secret_ref = Arc::new(secret); + let encryption_ref = Arc::new(encryption); + let compression_ref = Arc::new(compression); + let signing_ref = Arc::new(signing); + let encryption_secret_ref = Arc::new(encryption_secret); for entry in WalkDir::new(input_folder_path) { let entry = entry.unwrap(); @@ -245,108 +193,41 @@ pub fn decompress_directory( std::fs::create_dir_all(current_dir) .expect("Failed to create all the required directories/subdirectories"); - let secret_ref = secret_ref.clone(); + let encryption_secret_ref = encryption_secret_ref.clone(); + let encryption_ref = encryption_ref.clone(); + let compression_ref = compression_ref.clone(); + let signing_ref = signing_ref.clone(); + let wg_ref = wg.clone(); thread_pool.spawn(move || { let result = std::panic::catch_unwind(|| { - let source = match fs::File::open(&entry_path) { - Ok(f) => f, - Err(e) => panic!("Error: {:?}", e), // TODO: Graceful cleanup - }; - - let mut destination = match fs::File::create(&output_path) { - Ok(f) => f, - Err(e) => panic!("Error: {:?}", e), // TODO: Graceful cleanup - }; - - match secret_ref.as_ref() { - Some(p) => match p { - EncryptionSecret::Password(p) => { - let enc = match XChaChaPolyAlgorithm::new() - .with_key(p.clone()) - .decryptor(source) - { - Ok(e) => e, - Err(e) => panic!("Error: {:?}", e), // TODO: Graceful cleanup - }; - - let comp = match Lz4Algorithm::new().decompressor(enc) { - Ok(c) => c, - Err(e) => panic!("Error: {:?}", e), // TODO: Graceful cleanup - }; - - let sign = VerifierPassthrough::from(comp); - - let pipeline = pipeline::TaskPipeline::from_reader(sign); - - match pipeline.decompress(&mut destination) { - Ok(_) => debug!( - "Finished decompressing '{:?}' successfully", - entry_path.display() - ), - Err(e) => { - let bt = backtrace::Backtrace::capture(); - error!( - "Error while decompressing '{}': {:?}", - entry_path.display(), - e - ); - log::trace!( - "Error while decompressing '{}': {:?}", - entry_path.display(), - bt - ); - panic!(); - } - }; - } - EncryptionSecret::Key(_p) => { - unimplemented!("Keyfile encryption not implemented yet") - } - }, - None => { - debug!( - "Building decompressor: {:?} -> {:?}", + let pipeline = ProcessingPipeline::new() + .with_source(entry_path.clone()) + .with_destination(output_path.clone()) + .with_compression(compression_ref) + .with_encryption(encryption_ref) + .with_encryption_secret(encryption_secret_ref) + .with_signing(signing_ref); + + match pipeline.decompress_dir() { + Ok(_) => debug!( + "Finished decompressing '{:?}' successfully", + entry_path.display() + ), + Err(e) => { + let bt = backtrace::Backtrace::capture(); + error!( + "Error while decompressing '{}': {:?}", entry_path.display(), - output_path.display() + e ); - - let enc = DecryptorPassthrough::from(source); - let comp = match Lz4Algorithm::new().decompressor(enc) { - Ok(c) => c, - Err(e) => panic!("Error: {:?}", e), // TODO: Graceful cleanup - }; - let sign = VerifierPassthrough::from(comp); - - let pipeline = pipeline::TaskPipeline::from_reader(sign); - - debug!( - "Starting decompression: {:?} -> {:?}", + log::trace!( + "Error while decompressing '{}': {:?}", entry_path.display(), - output_path.display() + bt ); - - match pipeline.decompress(&mut destination) { - Ok(_) => debug!( - "Finished decompressing '{:?}' successfully", - entry_path.display() - ), - Err(e) => { - let bt = backtrace::Backtrace::capture(); - error!( - "Error while decompressing '{}': {:?}", - entry_path.display(), - e - ); - log::trace!( - "Error while decompressing '{}': {:?}", - entry_path.display(), - bt - ); - panic!(); - } - }; + panic!(); } }; diff --git a/src/pipeline/mod.rs b/src/pipeline/mod.rs index beed640..87baf62 100644 --- a/src/pipeline/mod.rs +++ b/src/pipeline/mod.rs @@ -1,7 +1,7 @@ use std::{ fs::File, io::{copy, Read, Write}, - path::PathBuf, backtrace, + path::PathBuf, sync::Arc, }; use crate::{ @@ -12,7 +12,7 @@ use crate::{ encryption::{ aes_gcm_256::AesGcmAlgorithm, chachapoly::ChaChaPolyAlgorithm, passthrough::{EncryptorPassthrough, DecryptorPassthrough}, xchachapoly::XChaChaPolyAlgorithm, DecryptionAlgorithm, - EncryptionAlgorithm, EncryptionModule, EncryptionSecret, EncryptionType, self, DecryptionModule, + EncryptionAlgorithm, EncryptionModule, EncryptionSecret, EncryptionType, DecryptionModule, }, error::{PipelineBuildError, PipelineCompressionError, PipelineDecompressionError}, signing::{ @@ -20,41 +20,83 @@ use crate::{ }, }; -pub struct PipeLineConfig { - encryption: EncryptionType, - encryption_secret: EncryptionSecret, - compression: CompressionType, - compression_level: flate2::Compression, - signing: SigningType, +#[derive(Default)] +pub struct ProcessingPipeline { + encryption: Arc, + encryption_secret: Arc, + compression: Arc, + compression_level: Arc, + signing: Arc, source: PathBuf, destination: PathBuf, } -impl PipeLineConfig { +impl ProcessingPipeline { + pub fn new() -> ProcessingPipeline { + ProcessingPipeline::default() + } + + pub fn with_encryption(mut self, encryption: Arc) -> Self { + self.encryption = encryption; + self + } + + pub fn with_encryption_secret(mut self, encryption_secret: Arc) -> Self { + self.encryption_secret = encryption_secret; + self + } + + pub fn with_compression(mut self, compression: Arc) -> Self { + self.compression = compression; + self + } + + pub fn with_compression_level(mut self, compression_level: Arc) -> Self { + self.compression_level = compression_level; + self + } + + pub fn with_signing(mut self, signing: Arc) -> Self { + self.signing = signing; + self + } + + pub fn with_source(mut self, source: PathBuf) -> Self { + self.source = source; + self + } + + pub fn with_destination(mut self, destination: PathBuf) -> Self { + self.destination = destination; + self + } + pub fn compress_dir(self) -> Result<(), PipelineCompressionError> { - let mut io = File::open(&self.source)?; + let io = File::create(&self.destination)?; self.build_encryptor(io) } pub fn decompress_dir(self) -> Result<(), PipelineDecompressionError> { - Ok(()) + let io = File::open(&self.source)?; + + self.build_dencryptor(io) } - pub fn build_encryptor(mut self, io: T) -> Result<(), PipelineCompressionError> + pub fn build_encryptor(self, io: T) -> Result<(), PipelineCompressionError> where T: Write, { - let encryption_secret = self.encryption_secret.clone(); // TODO: Try to get rid of this clone... + let encryption_secret = (*self.encryption_secret).clone(); // TODO: Try to get rid of this clone... match encryption_secret { - EncryptionSecret::Password(p) => match self.encryption { + EncryptionSecret::Password(p) => match *self.encryption { EncryptionType::XChaCha => self.build_compressor(XChaChaPolyAlgorithm::new().with_key(p).encryptor(io)?), EncryptionType::ChaCha => self.build_compressor(ChaChaPolyAlgorithm::new().with_key(p).encryptor(io)?), EncryptionType::AesGcm => self.build_compressor(AesGcmAlgorithm::new().with_key(p).encryptor(io)?), EncryptionType::Passthrough => self.build_compressor(EncryptorPassthrough::from(io)), }, - EncryptionSecret::Key(k) => { + EncryptionSecret::Key(_) => { unimplemented!("Key encryption not yet implemented") } EncryptionSecret::None => self.build_compressor(EncryptorPassthrough::from(io)), @@ -65,27 +107,25 @@ impl PipeLineConfig { where T: EncryptionModule, { - let compression_level = self.compression_level.clone(); // TODO: Try to get rid of this clone... + let compression_level = *self.compression_level; // TODO: Try to get rid of this copy... - match self.compression { + match *self.compression { CompressionType::Lz4 => self.build_signer(Lz4Algorithm::new().compressor(io)?), CompressionType::Gzip => self.build_signer( GzipAlgorithm::with_compression_level(compression_level).compressor(io)?, ), CompressionType::Snappy => self.build_signer(SnappyAlgorithm::new().compressor(io)?), CompressionType::Passthrough => self.build_signer(PassthroughAlgorithm::new().compressor(io)?), - }; - - Ok(()) + } } pub fn build_signer(&self, io: T) -> Result<(), PipelineCompressionError> where T: Compress, { - match self.signing { + match *self.signing { SigningType::Passthrough => { - let pipeline = TaskPipeline::from_writer(SignerPassthrough::from(io)); + let pipeline = PipelineTask::from_writer(SignerPassthrough::from(io)); self.execute_compression_pipeline(pipeline) } } @@ -98,130 +138,69 @@ impl PipeLineConfig { let mut source = File::open(&self.source)?; match pipeline.compress(&mut source) { - // TODO: Return ok - Ok(_) => log::debug!( - "Finished compressing '{:?}' successfully", - self.source.display() - ), - // TODO: Return error instead of panicking - Err(e) => { - - - let bt = backtrace::Backtrace::capture(); - log::error!( - "Error while compressing '{}': {:?}", - self.source.display(), - e - ); - log::trace!( - "Error while compressing '{}': {:?}", - self.source.display(), - bt - ); - panic!(); - } + Ok(_) => Ok(()), + Err(e) => Err(e) } - - Ok(()) } - pub fn build_dencryptor(mut self, io: T) -> Result<(), PipelineCompressionError> + pub fn build_dencryptor(self, io: T) -> Result<(), PipelineDecompressionError> where T: Read, { - let encryption_secret = self.encryption_secret.clone(); // TODO: Try to get rid of this clone... + let encryption_secret = (*self.encryption_secret).clone(); // TODO: Try to get rid of this clone... match encryption_secret { - EncryptionSecret::Password(p) => match self.encryption { + EncryptionSecret::Password(p) => match *self.encryption { EncryptionType::XChaCha => self.build_decompressor(XChaChaPolyAlgorithm::new().with_key(p).decryptor(io)?), EncryptionType::ChaCha => self.build_decompressor(ChaChaPolyAlgorithm::new().with_key(p).decryptor(io)?), EncryptionType::AesGcm => self.build_decompressor(AesGcmAlgorithm::new().with_key(p).decryptor(io)?), EncryptionType::Passthrough => self.build_decompressor(DecryptorPassthrough::from(io)), }, - EncryptionSecret::Key(k) => { + EncryptionSecret::Key(_) => { unimplemented!("Key encryption not yet implemented") } EncryptionSecret::None => self.build_decompressor(DecryptorPassthrough::from(io)), } } - pub fn build_decompressor(&self, io: T) -> Result<(), PipelineCompressionError> + pub fn build_decompressor(&self, io: T) -> Result<(), PipelineDecompressionError> where T: DecryptionModule, { - let compression_level = self.compression_level.clone(); // TODO: Try to get rid of this clone... + let compression_level = *self.compression_level; // TODO: Try to get rid of this copy... - match self.compression { + match *self.compression { CompressionType::Lz4 => self.build_verifier(Lz4Algorithm::new().decompressor(io)?), CompressionType::Gzip => self.build_verifier( GzipAlgorithm::with_compression_level(compression_level).decompressor(io)?, ), CompressionType::Snappy => self.build_verifier(SnappyAlgorithm::new().decompressor(io)?), CompressionType::Passthrough => self.build_verifier(PassthroughAlgorithm::new().decompressor(io)?), - }; - - Ok(()) + } } - pub fn build_verifier(&self, io: T) -> Result<(), PipelineCompressionError> + pub fn build_verifier(&self, io: T) -> Result<(), PipelineDecompressionError> where T: Decompress, { - match self.signing { + match *self.signing { SigningType::Passthrough => { - let pipeline = TaskPipeline::from_reader(VerifierPassthrough::from(io)); + let pipeline = PipelineTask::from_reader(VerifierPassthrough::from(io)); self.execute_decompression_pipeline(pipeline) } } } - fn execute_decompression_pipeline(&self, pipeline: T) -> Result<(), PipelineCompressionError> + fn execute_decompression_pipeline(&self, pipeline: T) -> Result<(), PipelineDecompressionError> where T: DecompressionPipeline, { - let mut source = File::open(&self.source)?; + let mut destination = File::create(&self.destination)?; - match pipeline.decompress(&mut source) { - // TODO: Return ok - Ok(_) => log::debug!( - "Finished compressing '{:?}' successfully", - self.source.display() - ), - // TODO: Return error instead of panicking - Err(e) => { - - - let bt = backtrace::Backtrace::capture(); - log::error!( - "Error while compressing '{}': {:?}", - self.source.display(), - e - ); - log::trace!( - "Error while compressing '{}': {:?}", - self.source.display(), - bt - ); - panic!(); - } - } - - Ok(()) - } -} - -impl Default for PipeLineConfig { - fn default() -> Self { - PipeLineConfig { - encryption: EncryptionType::default(), - encryption_secret: EncryptionSecret::default(), - compression: CompressionType::default(), - compression_level: flate2::Compression::default(), - signing: SigningType::default(), - source: PathBuf::default(), - destination: PathBuf::default(), + match pipeline.decompress(&mut destination) { + Ok(_) => Ok(()), + Err(e) => Err(e) } - } } @@ -237,31 +216,31 @@ pub trait DecompressionPipeline { F: Write; } -pub struct TaskPipeline { +pub struct PipelineTask { inner: T, } -impl TaskPipeline<()> { +impl PipelineTask<()> { pub fn builder() -> TaskPipelineBuilder<(), (), (), ()> { TaskPipelineBuilder::new() } - pub fn from_writer(io: U) -> TaskPipeline + pub fn from_writer(io: U) -> PipelineTask where U: Write, { - TaskPipeline { inner: io } + PipelineTask { inner: io } } - pub fn from_reader(io: U) -> TaskPipeline + pub fn from_reader(io: U) -> PipelineTask where U: Read, { - TaskPipeline { inner: io } + PipelineTask { inner: io } } } -impl CompressionPipeline for TaskPipeline +impl CompressionPipeline for PipelineTask where T: Sign, { @@ -274,7 +253,7 @@ where } } -impl DecompressionPipeline for TaskPipeline +impl DecompressionPipeline for PipelineTask where T: Verify, { @@ -361,8 +340,8 @@ where C: CompressionAlgorithm, S: SignerMethod, { - pub fn compression_pipeline(self) -> Result, PipelineBuildError> { - Ok(TaskPipeline { + pub fn compression_pipeline(self) -> Result, PipelineBuildError> { + Ok(PipelineTask { inner: self.signing.signer( self.compression .compressor(self.encryption.encryptor(self.io)?)?, @@ -378,8 +357,8 @@ where C: DecompressionAlgorithm, S: VerifierMethod, { - pub fn decompression_pipeline(self) -> Result, PipelineBuildError> { - Ok(TaskPipeline { + pub fn decompression_pipeline(self) -> Result, PipelineBuildError> { + Ok(PipelineTask { inner: self.signing.verifier( self.compression .decompressor(self.encryption.decryptor(self.io)?)?,