use crate::driver::FileDriver; use crate::utils::{IoCursor, ReadUtils}; use crate::zip::encryption::WeakDecoder; use crate::zip::{CompressionMethod, EncryptionMethod, ZipError, ZipFileInfo, ZipResult}; use bzip2::read::BzDecoder; use flate2::read::DeflateDecoder; use liblzma::read::XzDecoder; use liblzma::stream::{Filters, LzmaOptions, Stream}; use std::io::{ BufReader, Error as IoError, ErrorKind as IoErrorKind, Read, Result as IoResult, Seek, SeekFrom, }; use zstd::stream::Decoder as ZstdDecoder; enum Encryption { None(Io), Weak(WeakDecoder), } impl Encryption { pub fn new(io: Io, info: &ZipFileInfo, password: Option<&[u8]>) -> ZipResult { Ok(match info.encryption_method { EncryptionMethod::None => Self::None(io), EncryptionMethod::Weak(check) => Self::Weak(WeakDecoder::new( io, check, password.ok_or(ZipError::PasswordIsNotSpecified)?, )?), EncryptionMethod::Unsupported => { return Err(ZipError::UnsupportedEncryptionMethod.into()) } }) } } impl Read for Encryption { fn read(&mut self, buf: &mut [u8]) -> IoResult { match self { Self::None(io) => io.read(buf), Self::Weak(io) => io.read(buf), } } } impl Seek for Encryption { fn seek(&mut self, pos: SeekFrom) -> IoResult { match self { Self::None(io) => io.seek(pos), _ => Err(IoError::new( IoErrorKind::Unsupported, ZipError::EncryptedDataIsUnseekable, )), } } } enum Compression { Store(Io), Deflate(DeflateDecoder), BZip2(BzDecoder), Zstd(ZstdDecoder<'static, BufReader>), Xz(XzDecoder), } impl Compression { pub fn new(mut io: Io, info: &ZipFileInfo) -> ZipResult { Ok(match info.compression_method { CompressionMethod::Store => Self::Store(io), CompressionMethod::Deflate => Self::Deflate(DeflateDecoder::new(io)), CompressionMethod::BZip2 => Self::BZip2(BzDecoder::new(io)), CompressionMethod::Lzma => { let buf = io.read_arr::<9>()?; Compression::Xz(XzDecoder::new_stream( io, Stream::new_raw_decoder( Filters::new().lzma1( LzmaOptions::new() .literal_context_bits((buf[4] % 9) as u32) .literal_position_bits((buf[4] / 9 % 5) as u32) .position_bits((buf[4] / 45) as u32) .dict_size( u32::from_le_bytes(buf[5..9].try_into().unwrap()).max(4096), ), ), ) .unwrap(), )) } CompressionMethod::Zstd => Self::Zstd(ZstdDecoder::new(io)?), CompressionMethod::Xz => Self::Xz(XzDecoder::new(io)), CompressionMethod::Unsupported(id) => { return Err(ZipError::UnsupportedCompressionMethod(id).into()) } }) } } impl Read for Compression { fn read(&mut self, buf: &mut [u8]) -> IoResult { match self { Compression::Store(io) => io.read(buf), Compression::Deflate(io) => io.read(buf), Compression::BZip2(io) => io.read(buf), Compression::Zstd(io) => io.read(buf), Compression::Xz(io) => io.read(buf), } } } impl Seek for Compression { fn seek(&mut self, pos: SeekFrom) -> IoResult { match self { Compression::Store(io) => io.seek(pos), _ => Err(IoError::new( IoErrorKind::Unsupported, ZipError::CompressedDataIsUnseekable, )), } } } pub struct ZipFileReader<'d, Io: Read> { io: Compression>>, info: &'d ZipFileInfo, } impl<'d, Io: Read> FileDriver for ZipFileReader<'d, Io> { type Io = Io; type FileInfo = ZipFileInfo; } impl<'d, Io: Read + Seek> ZipFileReader<'d, Io> { pub(crate) fn new( io: &'d mut Io, info: &'d ZipFileInfo, password: Option<&[u8]>, ) -> ZipResult { io.seek(SeekFrom::Start(info.header_pointer))?; let buf = io.read_arr::<30>()?; if u32::from_le_bytes(buf[..4].try_into().unwrap()) != 0x04034b50 { return Err(ZipError::InvalidFileHeaderSignature.into()); } let data_pointer = info.header_pointer + 30 + u16::from_le_bytes(buf[26..28].try_into().unwrap()) as u64 + u16::from_le_bytes(buf[28..30].try_into().unwrap()) as u64; Ok(Self { io: Compression::new( Encryption::new( IoCursor::new(io, data_pointer, data_pointer + info.compressed_size)?, info, password, )?, info, )?, info, }) } pub fn info(&self) -> &ZipFileInfo { self.info } pub fn is_seekable(&self) -> bool { match self.io { Compression::Store(Encryption::None(..)) => true, _ => false, } } } impl<'d, Io: Read> Read for ZipFileReader<'d, Io> { fn read(&mut self, buf: &mut [u8]) -> IoResult { self.io.read(buf) } } impl<'d, Io: Read + Seek> Seek for ZipFileReader<'d, Io> { fn seek(&mut self, pos: SeekFrom) -> IoResult { self.io.seek(pos) } }