fix(password): update password handling for archives

Refactor password handling in archive functions to use &[u8] instead of impl AsRef<[u8]>. Include better error reporting for invalid UTF-8 passwords in 7z archives.
This commit is contained in:
ttyS3 2024-09-06 13:50:00 +00:00 committed by João Marcos
parent d4e252a732
commit 2dad11d0ba
8 changed files with 31 additions and 32 deletions

View File

@ -11,13 +11,11 @@ use crate::{error::Error, list::FileInArchive, utils::logger::info};
pub fn unpack_archive( pub fn unpack_archive(
archive_path: &Path, archive_path: &Path,
output_folder: &Path, output_folder: &Path,
password: Option<impl AsRef<[u8]>>, password: Option<&[u8]>,
quiet: bool, quiet: bool,
) -> crate::Result<usize> { ) -> crate::Result<usize> {
assert!(output_folder.read_dir().expect("dir exists").count() == 0); assert!(output_folder.read_dir().expect("dir exists").count() == 0);
let password = password.as_ref().map(|p| p.as_ref());
let archive = match password { let archive = match password {
Some(password) => Archive::with_password(archive_path, password), Some(password) => Archive::with_password(archive_path, password),
None => Archive::new(archive_path), None => Archive::new(archive_path),
@ -49,9 +47,8 @@ pub fn unpack_archive(
/// List contents of `archive_path`, returning a vector of archive entries /// List contents of `archive_path`, returning a vector of archive entries
pub fn list_archive( pub fn list_archive(
archive_path: &Path, archive_path: &Path,
password: Option<impl AsRef<[u8]>>, password: Option<&[u8]>,
) -> impl Iterator<Item = crate::Result<FileInArchive>> { ) -> impl Iterator<Item = crate::Result<FileInArchive>> {
let password = password.as_ref().map(|p| p.as_ref());
let archive = match password { let archive = match password {
Some(password) => Archive::with_password(archive_path, password), Some(password) => Archive::with_password(archive_path, password),
None => Archive::new(archive_path), None => Archive::new(archive_path),

View File

@ -20,6 +20,7 @@ use crate::{
Bytes, EscapedPathDisplay, FileVisibilityPolicy, Bytes, EscapedPathDisplay, FileVisibilityPolicy,
}, },
}; };
use crate::error::Error;
pub fn compress_sevenz<W>( pub fn compress_sevenz<W>(
files: &[PathBuf], files: &[PathBuf],
@ -102,7 +103,7 @@ where
pub fn decompress_sevenz<R>( pub fn decompress_sevenz<R>(
reader: R, reader: R,
output_path: &Path, output_path: &Path,
password: Option<impl AsRef<[u8]>>, password: Option<&[u8]>,
quiet: bool, quiet: bool,
) -> crate::Result<usize> ) -> crate::Result<usize>
where where
@ -161,17 +162,17 @@ where
Ok(true) Ok(true)
}; };
let password = password.as_ref().map(|p| p.as_ref());
match password { match password {
Some(password) => sevenz_rust::decompress_with_extract_fn_and_password( Some(password) => sevenz_rust::decompress_with_extract_fn_and_password(
reader, reader,
output_path, output_path,
sevenz_rust::Password::from(password.to_str().unwrap()), sevenz_rust::Password::from(password.to_str().map_err(|_| {
Error::InvalidPassword("7z requires that all passwords are valid UTF-8")
})?),
entry_extract_fn, entry_extract_fn,
)?, )?,
None => sevenz_rust::decompress_with_extract_fn(reader, output_path, entry_extract_fn)?, None => sevenz_rust::decompress_with_extract_fn(reader, output_path, entry_extract_fn)?,
}; }
Ok(count) Ok(count)
} }
@ -179,10 +180,9 @@ where
/// List contents of `archive_path`, returning a vector of archive entries /// List contents of `archive_path`, returning a vector of archive entries
pub fn list_archive( pub fn list_archive(
archive_path: &Path, archive_path: &Path,
password: Option<impl AsRef<[u8]>>, password: Option<&[u8]>,
) -> impl Iterator<Item = crate::Result<FileInArchive>> { ) -> impl Iterator<Item = crate::Result<FileInArchive>> {
let reader = fs::File::open(archive_path).unwrap(); let reader = fs::File::open(archive_path).unwrap();
let password = password.as_ref().map(|p| p.as_ref());
let mut files = Vec::new(); let mut files = Vec::new();
@ -195,15 +195,16 @@ pub fn list_archive(
}; };
match password { match password {
Some(password) => sevenz_rust::decompress_with_extract_fn_and_password( Some(password) => {
sevenz_rust::decompress_with_extract_fn_and_password(
reader, reader,
".", ".",
sevenz_rust::Password::from(password.to_str().unwrap()), sevenz_rust::Password::from(password.to_str().unwrap()),
entry_extract_fn, entry_extract_fn,
) ).unwrap()
.unwrap(), },
None => sevenz_rust::decompress_with_extract_fn(reader, ".", entry_extract_fn).unwrap(), None => sevenz_rust::decompress_with_extract_fn(reader, ".", entry_extract_fn).unwrap(),
}; }
files.into_iter() files.into_iter()
} }

View File

@ -32,7 +32,7 @@ use crate::{
pub fn unpack_archive<R>( pub fn unpack_archive<R>(
mut archive: ZipArchive<R>, mut archive: ZipArchive<R>,
output_folder: &Path, output_folder: &Path,
password: Option<impl AsRef<[u8]>>, password: Option<&[u8]>,
quiet: bool, quiet: bool,
) -> crate::Result<usize> ) -> crate::Result<usize>
where where
@ -42,10 +42,8 @@ where
let mut unpacked_files = 0; let mut unpacked_files = 0;
let password = password.as_ref().map(|p| p.as_ref().to_owned());
for idx in 0..archive.len() { for idx in 0..archive.len() {
let mut file = match password.clone() { let mut file = match password {
Some(password) => archive Some(password) => archive
.by_index_decrypt(idx, password.to_owned().as_bytes()) .by_index_decrypt(idx, password.to_owned().as_bytes())
.unwrap() .unwrap()
@ -108,7 +106,7 @@ where
/// List contents of `archive`, returning a vector of archive entries /// List contents of `archive`, returning a vector of archive entries
pub fn list_archive<R>( pub fn list_archive<R>(
mut archive: ZipArchive<R>, mut archive: ZipArchive<R>,
password: Option<impl AsRef<[u8]>>, password: Option<&[u8]>,
) -> impl Iterator<Item = crate::Result<FileInArchive>> ) -> impl Iterator<Item = crate::Result<FileInArchive>>
where where
R: Read + Seek + Send + 'static, R: Read + Seek + Send + 'static,
@ -122,7 +120,7 @@ where
} }
} }
let password = password.as_ref().map(|p| p.as_ref().to_owned()); let password = password.map(|p| p.to_owned());
let (tx, rx) = mpsc::channel(); let (tx, rx) = mpsc::channel();
thread::spawn(move || { thread::spawn(move || {

View File

@ -43,7 +43,7 @@ pub struct CliArgs {
/// decompress or list with password /// decompress or list with password
#[arg(short = 'p', long = "password", global = true)] #[arg(short = 'p', long = "password", global = true)]
pub password: Option<String>, pub password: Option<OsString>,
// Ouch and claps subcommands // Ouch and claps subcommands
#[command(subcommand)] #[command(subcommand)]

View File

@ -36,7 +36,7 @@ pub fn decompress_file(
output_file_path: PathBuf, output_file_path: PathBuf,
question_policy: QuestionPolicy, question_policy: QuestionPolicy,
quiet: bool, quiet: bool,
password: Option<&str>, password: Option<&[u8]>,
) -> crate::Result<()> { ) -> crate::Result<()> {
assert!(output_dir.exists()); assert!(output_dir.exists());
let input_is_stdin = is_path_stdin(input_file_path); let input_is_stdin = is_path_stdin(input_file_path);

View File

@ -21,7 +21,7 @@ pub fn list_archive_contents(
formats: Vec<CompressionFormat>, formats: Vec<CompressionFormat>,
list_options: ListOptions, list_options: ListOptions,
question_policy: QuestionPolicy, question_policy: QuestionPolicy,
password: Option<&str>, password: Option<&[u8]>,
) -> crate::Result<()> { ) -> crate::Result<()> {
let reader = fs::File::open(archive_path)?; let reader = fs::File::open(archive_path)?;

View File

@ -5,7 +5,7 @@ mod decompress;
mod list; mod list;
use std::{ops::ControlFlow, path::PathBuf}; use std::{ops::ControlFlow, path::PathBuf};
use std::os::unix::prelude::OsStrExt;
use rayon::prelude::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator}; use rayon::prelude::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator};
use utils::colors; use utils::colors;
@ -188,7 +188,7 @@ pub fn run(
output_file_path, output_file_path,
question_policy, question_policy,
args.quiet, args.quiet,
args.password.as_deref(), args.password.as_deref().map(|str|str.as_bytes()),
) )
}) })
} }
@ -227,7 +227,7 @@ pub fn run(
formats, formats,
list_options, list_options,
question_policy, question_policy,
args.password.as_deref(), args.password.as_deref().map(|str|str.as_bytes()),
)?; )?;
} }

View File

@ -39,6 +39,8 @@ pub enum Error {
/// Recognised but unsupported format /// Recognised but unsupported format
// currently only RAR when built without the `unrar` feature // currently only RAR when built without the `unrar` feature
UnsupportedFormat { reason: String }, UnsupportedFormat { reason: String },
/// Invalid password provided
InvalidPassword(&'static str),
} }
/// Alias to std's Result with ouch's Error /// Alias to std's Result with ouch's Error
@ -148,6 +150,7 @@ impl fmt::Display for Error {
Error::UnsupportedFormat { reason } => { Error::UnsupportedFormat { reason } => {
FinalError::with_title("Recognised but unsupported format").detail(reason.clone()) FinalError::with_title("Recognised but unsupported format").detail(reason.clone())
} }
Error::InvalidPassword(reason) => FinalError::with_title("Invalid password").detail(*reason),
}; };
write!(f, "{err}") write!(f, "{err}")