use crate::error::{MyError, MyResult};
use crate::finder::count_components;
use crate::fs::entry::{Entry, EntryResult};
use crate::fs::file::Signature;
use crate::fs::flags::FileFlags;
#[cfg(windows)]
use crate::util::version;
use crate::zip::manager::PasswordManager;
use crate::zip::parent::ZipParent;
use path_clean::PathClean;
use sevenz_rust2::{ArchiveEntry, ArchiveReader, Error, Password};
use std::cell::RefCell;
use std::ffi::OsStr;
use std::io::Read;
use std::path::{Path, PathBuf};
use std::time::SystemTime;
#[cfg(unix)]
use uzers::{gid_t, uid_t};
struct SevenZEntry<'a> {
zip_entry: &'a ArchiveEntry,
zip_reader: RefCell<&'a mut dyn Read>,
#[cfg(windows)]
zip_buffer: RefCell<Vec<u8>>,
file_path: PathBuf,
file_depth: usize,
inner_path: PathBuf,
inner_depth: usize,
#[cfg(unix)]
owner_uid: uid_t,
#[cfg(unix)]
owner_gid: gid_t,
}
impl<'a> SevenZEntry<'a> {
fn new(
zip_parent: &ZipParent,
zip_entry: &'a ArchiveEntry,
zip_reader: &'a mut dyn Read,
) -> Self {
let (file_path, file_depth, inner_path, inner_depth) = Self::prepare_path(zip_parent, zip_entry);
let zip_reader = RefCell::new(zip_reader);
#[cfg(windows)]
let zip_buffer = RefCell::new(Vec::new());
#[cfg(unix)]
let owner_uid = zip_parent.uid();
#[cfg(unix)]
let owner_gid = zip_parent.gid();
Self {
zip_entry,
zip_reader,
#[cfg(windows)]
zip_buffer,
file_path,
file_depth,
inner_path,
inner_depth,
#[cfg(unix)]
owner_uid,
#[cfg(unix)]
owner_gid,
}
}
fn prepare_path(
zip_parent: &ZipParent,
zip_entry: &ArchiveEntry,
) -> (PathBuf, usize, PathBuf, usize) {
let inner_path = PathBuf::from(&zip_entry.name).clean();
let inner_depth = count_components(&inner_path);
let file_depth = zip_parent.depth() + inner_depth;
let file_path = zip_parent.path().join(&inner_path);
(file_path, file_depth, inner_path, inner_depth)
}
#[cfg(windows)]
fn cache_sig(&self, data: Signature, count: usize) {
let mut buffer = self.zip_buffer.borrow_mut();
buffer.resize(count, 0);
buffer.copy_from_slice(&data[0..count]);
}
}
impl<'a> Entry for SevenZEntry<'a> {
fn file_path(&self) -> &Path {
&self.file_path
}
fn file_name(&self) -> &OsStr {
self.file_path.file_name().unwrap_or_default()
}
fn file_depth(&self) -> usize {
self.file_depth
}
fn inner_path(&self) -> Option<&Path> {
Some(&self.inner_path)
}
fn inner_depth(&self) -> Option<usize> {
Some(self.inner_depth)
}
fn file_flags(&self) -> FileFlags {
if self.zip_entry.is_directory {
FileFlags::Dir
} else {
FileFlags::File
}
}
#[allow(unused)]
fn read_sig(&self) -> Option<Signature> {
let mut data = [0; 4];
let mut reader = self.zip_reader.borrow_mut();
if let Ok(count) = reader.read(&mut data) {
#[cfg(windows)]
self.cache_sig(data, count);
return Some(data);
}
None
}
#[cfg(windows)]
fn read_version(&self) -> Option<String> {
if version::inner::test_extension(&self.file_path) {
let mut reader = self.zip_reader.borrow_mut();
let mut buffer = self.zip_buffer.borrow_mut();
if reader.read_to_end(&mut buffer).is_ok() {
return version::inner::query_buffer(&mut buffer);
}
}
None
}
fn read_link(&self) -> MyResult<Option<PathBuf>> {
Ok(None)
}
fn copy_metadata(&self, _other: &dyn Entry) {
}
fn reset_metadata(&self) {
}
fn file_mode(&self) -> u32 {
0
}
#[cfg(unix)]
fn owner_uid(&self) -> uid_t {
self.owner_uid
}
#[cfg(unix)]
fn owner_gid(&self) -> gid_t {
self.owner_gid
}
fn file_size(&self) -> u64 {
self.zip_entry.size
}
fn file_time(&self) -> SystemTime {
SystemTime::from(self.zip_entry.last_modified_date)
}
}
pub fn walk_entries<F: Fn(EntryResult)>(
zip_parent: &ZipParent,
zip_manager: &mut PasswordManager,
want_decrypt: bool,
function: &F,
) -> MyResult<()> {
let zip_path = zip_parent.path();
let zip_password = default_password(want_decrypt);
match walk_reader(zip_parent, zip_password, function) {
Ok(()) => {
return Ok(());
}
Err(zip_error) => {
if !password_error(&zip_error, want_decrypt) {
function(Err(MyError::from((zip_error, zip_path))));
return Ok(());
}
}
}
if let Some(zip_password) = zip_manager.config() {
let zip_password = Password::from(zip_password.as_str());
let _ = walk_reader(zip_parent, zip_password, function);
return Ok(());
}
for zip_password in zip_manager.passwords() {
let zip_password = Password::from(zip_password.as_str());
if walk_reader(zip_parent, zip_password, function).is_ok() {
return Ok(());
}
}
loop {
let zip_password = zip_manager.prompt(zip_path)?;
let zip_password = Password::from(zip_password.as_str());
if walk_reader(zip_parent, zip_password, function).is_ok() {
return Ok(());
}
}
}
fn walk_reader<F: Fn(EntryResult)>(
zip_parent: &ZipParent,
zip_password: Password,
function: &F,
) -> Result<(), Error> {
let zip_path = zip_parent.path();
let mut zip_reader = ArchiveReader::open(zip_path, zip_password)?;
zip_reader.for_each_entries(|zip_entry, zip_reader| {
let zip_entry = SevenZEntry::new(zip_parent, zip_entry, zip_reader);
function(Ok(&zip_entry));
Ok(true)
})
}
fn default_password(want_decrypt: bool) -> Password {
if want_decrypt {
Password::empty()
} else {
Password::from("x")
}
}
fn password_error(zip_error: &Error, want_decrypt: bool) -> bool {
match zip_error {
Error::PasswordRequired => want_decrypt,
Error::MaybeBadPassword(_) => want_decrypt,
_ => false,
}
}