use std::sync::Arc;
use delharc::decode::{Decoder, DecoderAny};
use super::crypto::decrypt_arj_data;
use super::decode_fastest::decode_fastest;
use super::local_file_header::CompressionMethod;
use crate::encryption::ArjEncryption;
use crate::error::Result;
#[derive(Clone)]
pub struct ArjPasswordVerifier {
compressed_data: Arc<[u8]>,
compression_method: CompressionMethod,
expected_crc: u32,
original_size: u32,
entry_name: String,
encryption_type: Option<ArjEncryption>,
password_modifier: u8,
file_time: u32,
}
unsafe impl Send for ArjPasswordVerifier {}
unsafe impl Sync for ArjPasswordVerifier {}
impl ArjPasswordVerifier {
#[allow(clippy::too_many_arguments)]
pub fn new(
compressed_data: Vec<u8>,
compression_method: CompressionMethod,
expected_crc: u32,
original_size: u32,
entry_name: String,
encryption_type: Option<ArjEncryption>,
password_modifier: u8,
file_time: u32,
) -> Self {
Self {
compressed_data: Arc::from(compressed_data.into_boxed_slice()),
compression_method,
expected_crc,
original_size,
entry_name,
encryption_type,
password_modifier,
file_time,
}
}
pub fn entry_name(&self) -> &str {
&self.entry_name
}
pub fn compressed_size(&self) -> usize {
self.compressed_data.len()
}
pub fn original_size(&self) -> u32 {
self.original_size
}
pub fn encryption_type(&self) -> Option<ArjEncryption> {
self.encryption_type
}
pub fn verify(&self, password: &str) -> bool {
thread_local! {
static DECRYPT_BUF: std::cell::RefCell<Vec<u8>> = std::cell::RefCell::new(Vec::with_capacity(64 * 1024));
}
DECRYPT_BUF.with(|buf| {
let mut decrypt_buf = buf.borrow_mut();
decrypt_buf.clear();
decrypt_buf.extend_from_slice(&self.compressed_data);
decrypt_arj_data(&mut decrypt_buf, self.encryption_type, password, self.password_modifier, self.file_time);
self.decompress_and_verify(&decrypt_buf).unwrap_or(false)
})
}
fn decompress_and_verify(&self, decrypted: &[u8]) -> Result<bool> {
let uncompressed = match self.compression_method {
CompressionMethod::Stored => decrypted.to_vec(),
CompressionMethod::CompressedMost | CompressionMethod::Compressed | CompressionMethod::CompressedFaster => {
let mut decoder = DecoderAny::new_from_compression(delharc::CompressionMethod::Lh6, decrypted);
let mut decompressed_buffer = vec![0; self.original_size as usize];
if decoder.fill_buffer(&mut decompressed_buffer).is_err() {
return Ok(false);
}
decompressed_buffer
}
CompressionMethod::CompressedFastest => match decode_fastest(decrypted, self.original_size as usize) {
Ok(data) => data,
Err(_) => return Ok(false),
},
CompressionMethod::NoDataNoCrc | CompressionMethod::NoData | CompressionMethod::Unknown(_) => {
return Ok(false);
}
};
if uncompressed.len() != self.original_size as usize {
return Ok(false);
}
let checksum = crc32fast::hash(&uncompressed);
Ok(checksum == self.expected_crc)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_verifier_is_send_sync() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<ArjPasswordVerifier>();
}
}