diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/archive.rs | 36 | ||||
| -rw-r--r-- | src/driver/driver.rs | 1 | ||||
| -rw-r--r-- | src/utils/cursor.rs | 1 | ||||
| -rw-r--r-- | src/zip/driver.rs | 11 | ||||
| -rw-r--r-- | src/zip/encryption.rs | 62 | ||||
| -rw-r--r-- | src/zip/error.rs | 20 | ||||
| -rw-r--r-- | src/zip/file/info.rs | 47 | ||||
| -rw-r--r-- | src/zip/file/mod.rs | 2 | ||||
| -rw-r--r-- | src/zip/file/read.rs | 159 | ||||
| -rw-r--r-- | src/zip/mod.rs | 5 |
10 files changed, 269 insertions, 75 deletions
diff --git a/src/archive.rs b/src/archive.rs index e17db80..73c515c 100644 --- a/src/archive.rs +++ b/src/archive.rs | |||
| @@ -48,11 +48,31 @@ where | |||
| 48 | self.get_file_info_by_index(self.get_file_index(name)?) | 48 | self.get_file_info_by_index(self.get_file_index(name)?) |
| 49 | } | 49 | } |
| 50 | 50 | ||
| 51 | fn get_file_reader_by_index_with_optional_password<'d>( | ||
| 52 | &'d mut self, | ||
| 53 | index: usize, | ||
| 54 | password: Option<&str>, | ||
| 55 | ) -> ArchiveResult<ArchiveFile<D::FileReader<'d>>, D::Error> { | ||
| 56 | Ok(ArchiveFile::new( | ||
| 57 | self.driver.get_file_reader(index, password)?, | ||
| 58 | )) | ||
| 59 | } | ||
| 60 | |||
| 61 | #[inline] | ||
| 51 | pub fn get_file_reader_by_index<'d>( | 62 | pub fn get_file_reader_by_index<'d>( |
| 52 | &'d mut self, | 63 | &'d mut self, |
| 53 | index: usize, | 64 | index: usize, |
| 54 | ) -> ArchiveResult<ArchiveFile<D::FileReader<'d>>, D::Error> { | 65 | ) -> ArchiveResult<ArchiveFile<D::FileReader<'d>>, D::Error> { |
| 55 | Ok(ArchiveFile::new(self.driver.get_file_reader(index)?)) | 66 | self.get_file_reader_by_index_with_optional_password(index, None) |
| 67 | } | ||
| 68 | |||
| 69 | #[inline] | ||
| 70 | pub fn get_file_reader_by_index_with_password<'d>( | ||
| 71 | &'d mut self, | ||
| 72 | index: usize, | ||
| 73 | password: &str, | ||
| 74 | ) -> ArchiveResult<ArchiveFile<D::FileReader<'d>>, D::Error> { | ||
| 75 | self.get_file_reader_by_index_with_optional_password(index, Some(password)) | ||
| 56 | } | 76 | } |
| 57 | 77 | ||
| 58 | #[inline] | 78 | #[inline] |
| @@ -60,7 +80,19 @@ where | |||
| 60 | &'d mut self, | 80 | &'d mut self, |
| 61 | name: &str, | 81 | name: &str, |
| 62 | ) -> ArchiveResult<ArchiveFile<D::FileReader<'d>>, D::Error> { | 82 | ) -> ArchiveResult<ArchiveFile<D::FileReader<'d>>, D::Error> { |
| 63 | self.get_file_reader_by_index(self.get_file_index(name)?) | 83 | self.get_file_reader_by_index_with_optional_password(self.get_file_index(name)?, None) |
| 84 | } | ||
| 85 | |||
| 86 | #[inline] | ||
| 87 | pub fn get_file_reader_by_name_with_password<'d>( | ||
| 88 | &'d mut self, | ||
| 89 | name: &str, | ||
| 90 | password: &str, | ||
| 91 | ) -> ArchiveResult<ArchiveFile<D::FileReader<'d>>, D::Error> { | ||
| 92 | self.get_file_reader_by_index_with_optional_password( | ||
| 93 | self.get_file_index(name)?, | ||
| 94 | Some(password), | ||
| 95 | ) | ||
| 64 | } | 96 | } |
| 65 | } | 97 | } |
| 66 | 98 | ||
diff --git a/src/driver/driver.rs b/src/driver/driver.rs index 747345c..f0f93a9 100644 --- a/src/driver/driver.rs +++ b/src/driver/driver.rs | |||
| @@ -34,6 +34,7 @@ where | |||
| 34 | fn get_file_reader<'d>( | 34 | fn get_file_reader<'d>( |
| 35 | &'d mut self, | 35 | &'d mut self, |
| 36 | index: usize, | 36 | index: usize, |
| 37 | password: Option<&str>, | ||
| 37 | ) -> ArchiveResult<Self::FileReader<'d>, Self::Error>; | 38 | ) -> ArchiveResult<Self::FileReader<'d>, Self::Error>; |
| 38 | } | 39 | } |
| 39 | 40 | ||
diff --git a/src/utils/cursor.rs b/src/utils/cursor.rs index 0c4b77e..91d44f8 100644 --- a/src/utils/cursor.rs +++ b/src/utils/cursor.rs | |||
| @@ -34,7 +34,6 @@ impl<Io: Write> Write for IoCursor<Io> { | |||
| 34 | Ok(bytes) | 34 | Ok(bytes) |
| 35 | } | 35 | } |
| 36 | 36 | ||
| 37 | #[inline] | ||
| 38 | fn flush(&mut self) -> Result<()> { | 37 | fn flush(&mut self) -> Result<()> { |
| 39 | self.io.flush() | 38 | self.io.flush() |
| 40 | } | 39 | } |
diff --git a/src/zip/driver.rs b/src/zip/driver.rs index 0905d9a..87f9c1a 100644 --- a/src/zip/driver.rs +++ b/src/zip/driver.rs | |||
| @@ -3,7 +3,8 @@ use crate::utils::ReadUtils; | |||
| 3 | use crate::zip::cp437::FromCp437; | 3 | use crate::zip::cp437::FromCp437; |
| 4 | use crate::zip::structs::{deserialize, Cdr, Eocdr, Eocdr64, Eocdr64Locator, ExtraHeader}; | 4 | use crate::zip::structs::{deserialize, Cdr, Eocdr, Eocdr64, Eocdr64Locator, ExtraHeader}; |
| 5 | use crate::zip::{ | 5 | use crate::zip::{ |
| 6 | BitFlag, CompressionMethod, ZipError, ZipFileInfo, ZipFileReader, ZipFileWriter, ZipResult, | 6 | BitFlag, CompressionMethod, EncryptionMethod, ZipError, ZipFileInfo, ZipFileReader, |
| 7 | ZipFileWriter, ZipResult, | ||
| 7 | }; | 8 | }; |
| 8 | use chrono::{DateTime, Local, NaiveDate, NaiveDateTime, NaiveTime}; | 9 | use chrono::{DateTime, Local, NaiveDate, NaiveDateTime, NaiveTime}; |
| 9 | use std::collections::HashMap as Map; | 10 | use std::collections::HashMap as Map; |
| @@ -220,6 +221,7 @@ impl<Io: Read + Seek> ArchiveRead for Zip<Io> { | |||
| 220 | indexes.insert(name.clone(), i); | 221 | indexes.insert(name.clone(), i); |
| 221 | files.push(ZipFileInfo::new( | 222 | files.push(ZipFileInfo::new( |
| 222 | CompressionMethod::from_struct_id(cdr.compression_method)?, | 223 | CompressionMethod::from_struct_id(cdr.compression_method)?, |
| 224 | EncryptionMethod::from_bit_flag(bit_flag), | ||
| 223 | bit_flag, | 225 | bit_flag, |
| 224 | mtime, | 226 | mtime, |
| 225 | atime, | 227 | atime, |
| @@ -256,10 +258,15 @@ impl<Io: Read + Seek> ArchiveRead for Zip<Io> { | |||
| 256 | self.files.get(index).ok_or(ZipError::FileNotFound.into()) | 258 | self.files.get(index).ok_or(ZipError::FileNotFound.into()) |
| 257 | } | 259 | } |
| 258 | 260 | ||
| 259 | fn get_file_reader<'d>(&'d mut self, index: usize) -> ZipResult<Self::FileReader<'d>> { | 261 | fn get_file_reader<'d>( |
| 262 | &'d mut self, | ||
| 263 | index: usize, | ||
| 264 | password: Option<&str>, | ||
| 265 | ) -> ZipResult<Self::FileReader<'d>> { | ||
| 260 | Ok(ZipFileReader::new( | 266 | Ok(ZipFileReader::new( |
| 261 | &mut self.io, | 267 | &mut self.io, |
| 262 | self.files.get(index).ok_or(ZipError::FileNotFound)?, | 268 | self.files.get(index).ok_or(ZipError::FileNotFound)?, |
| 269 | password, | ||
| 263 | )?) | 270 | )?) |
| 264 | } | 271 | } |
| 265 | } | 272 | } |
diff --git a/src/zip/encryption.rs b/src/zip/encryption.rs new file mode 100644 index 0000000..84e30d5 --- /dev/null +++ b/src/zip/encryption.rs | |||
| @@ -0,0 +1,62 @@ | |||
| 1 | use crate::utils::ReadUtils; | ||
| 2 | use crate::zip::ZipResult; | ||
| 3 | use crc32fast::Hasher; | ||
| 4 | use std::io::{Read, Result as IoResult}; | ||
| 5 | |||
| 6 | fn crc32(byte: u8, crc32: u32) -> u32 { | ||
| 7 | let mut hasher = Hasher::new_with_initial(crc32 ^ 0xFFFFFFFF); | ||
| 8 | hasher.update(&[byte]); | ||
| 9 | hasher.finalize() ^ 0xFFFFFFFF | ||
| 10 | } | ||
| 11 | |||
| 12 | pub struct WeakDecoder<Io: Read> { | ||
| 13 | key0: u32, | ||
| 14 | key1: u32, | ||
| 15 | key2: u32, | ||
| 16 | io: Io, | ||
| 17 | } | ||
| 18 | |||
| 19 | impl<Io: Read> WeakDecoder<Io> { | ||
| 20 | pub fn new(io: Io, password: &str) -> ZipResult<Self> { | ||
| 21 | let mut decoder = Self { | ||
| 22 | key0: 305419896, | ||
| 23 | key1: 591751049, | ||
| 24 | key2: 878082192, | ||
| 25 | io, | ||
| 26 | }; | ||
| 27 | |||
| 28 | for c in password.chars() { | ||
| 29 | decoder.update_keys(c as u8) | ||
| 30 | } | ||
| 31 | |||
| 32 | let buf = decoder.read_arr::<12>()?; | ||
| 33 | |||
| 34 | Ok(decoder) | ||
| 35 | } | ||
| 36 | |||
| 37 | fn update_keys(&mut self, byte: u8) { | ||
| 38 | self.key0 = crc32(byte, self.key0); | ||
| 39 | self.key1 = self | ||
| 40 | .key1 | ||
| 41 | .wrapping_add(self.key0 & 0xFF) | ||
| 42 | .wrapping_mul(134775813) | ||
| 43 | .wrapping_add(1); | ||
| 44 | self.key2 = crc32((self.key1 >> 24) as u8, self.key2); | ||
| 45 | } | ||
| 46 | |||
| 47 | fn decode_byte(&mut self, byte: u8) -> u8 { | ||
| 48 | let key = self.key2 | 2; | ||
| 49 | byte ^ (((key * (key ^ 1)) >> 8) as u8) | ||
| 50 | } | ||
| 51 | } | ||
| 52 | |||
| 53 | impl<Io: Read> Read for WeakDecoder<Io> { | ||
| 54 | fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> { | ||
| 55 | let bytes = self.io.read(buf)?; | ||
| 56 | for i in 0..bytes { | ||
| 57 | buf[i] = self.decode_byte(buf[i]); | ||
| 58 | self.update_keys(buf[i]) | ||
| 59 | } | ||
| 60 | Ok(bytes) | ||
| 61 | } | ||
| 62 | } | ||
diff --git a/src/zip/error.rs b/src/zip/error.rs index 3eb68b8..9ec0956 100644 --- a/src/zip/error.rs +++ b/src/zip/error.rs | |||
| @@ -11,15 +11,18 @@ pub enum ZipError { | |||
| 11 | InvalidFileHeaderSignature, | 11 | InvalidFileHeaderSignature, |
| 12 | InvalidCDRSignature, | 12 | InvalidCDRSignature, |
| 13 | 13 | ||
| 14 | InvalidCompressionMethod, | 14 | InvalidCompressionMethod(u16), |
| 15 | UnsupportedCompressionMethod, | 15 | UnsupportedCompressionMethod(u16), |
| 16 | UnsupportedEncryptionMethod, | ||
| 16 | InvalidDate, | 17 | InvalidDate, |
| 17 | InvalidTime, | 18 | InvalidTime, |
| 18 | InvalidFileName, | 19 | InvalidFileName, |
| 19 | InvalidFileComment, | 20 | InvalidFileComment, |
| 20 | 21 | ||
| 21 | FileNotFound, | 22 | FileNotFound, |
| 23 | PasswordIsNotSpecified, | ||
| 22 | CompressedDataIsUnseekable, | 24 | CompressedDataIsUnseekable, |
| 25 | EncryptedDataIsUnseekable, | ||
| 23 | } | 26 | } |
| 24 | 27 | ||
| 25 | impl From<ZipError> for ArchiveError<ZipError> { | 28 | impl From<ZipError> for ArchiveError<ZipError> { |
| @@ -48,15 +51,24 @@ impl Display for ZipError { | |||
| 48 | write!(f, "Invalid signature of central directory record") | 51 | write!(f, "Invalid signature of central directory record") |
| 49 | } | 52 | } |
| 50 | 53 | ||
| 51 | Self::InvalidCompressionMethod => writeln!(f, "Invalid compression method"), | 54 | Self::InvalidCompressionMethod(id) => { |
| 52 | Self::UnsupportedCompressionMethod => writeln!(f, "Unsupported compression method"), | 55 | writeln!(f, "Invalid compression method {}", id) |
| 56 | } | ||
| 57 | Self::UnsupportedCompressionMethod(id) => { | ||
| 58 | writeln!(f, "Unsupported compression method {}", id) | ||
| 59 | } | ||
| 60 | Self::UnsupportedEncryptionMethod => { | ||
| 61 | writeln!(f, "Unsupported encryption method") | ||
| 62 | } | ||
| 53 | Self::InvalidDate => write!(f, "Invalid date"), | 63 | Self::InvalidDate => write!(f, "Invalid date"), |
| 54 | Self::InvalidTime => write!(f, "Invalid time"), | 64 | Self::InvalidTime => write!(f, "Invalid time"), |
| 55 | Self::InvalidFileName => write!(f, "Invalid file name"), | 65 | Self::InvalidFileName => write!(f, "Invalid file name"), |
| 56 | Self::InvalidFileComment => write!(f, "Invalid file comment"), | 66 | Self::InvalidFileComment => write!(f, "Invalid file comment"), |
| 57 | 67 | ||
| 58 | Self::FileNotFound => write!(f, "File not found"), | 68 | Self::FileNotFound => write!(f, "File not found"), |
| 69 | Self::PasswordIsNotSpecified => write!(f, "Password is not specified"), | ||
| 59 | Self::CompressedDataIsUnseekable => write!(f, "Compressed data is unseekable"), | 70 | Self::CompressedDataIsUnseekable => write!(f, "Compressed data is unseekable"), |
| 71 | Self::EncryptedDataIsUnseekable => write!(f, "Encrypted data is unseekable"), | ||
| 60 | } | 72 | } |
| 61 | } | 73 | } |
| 62 | } | 74 | } |
diff --git a/src/zip/file/info.rs b/src/zip/file/info.rs index 4e1b293..f5d4d8a 100644 --- a/src/zip/file/info.rs +++ b/src/zip/file/info.rs | |||
| @@ -2,7 +2,7 @@ use crate::driver::ArchiveFileInfo; | |||
| 2 | use crate::zip::{ZipError, ZipResult}; | 2 | use crate::zip::{ZipError, ZipResult}; |
| 3 | use chrono::{DateTime, Local}; | 3 | use chrono::{DateTime, Local}; |
| 4 | 4 | ||
| 5 | #[derive(Debug, Clone)] | 5 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] |
| 6 | pub enum CompressionMethod { | 6 | pub enum CompressionMethod { |
| 7 | Store, | 7 | Store, |
| 8 | Deflate, | 8 | Deflate, |
| @@ -10,31 +10,49 @@ pub enum CompressionMethod { | |||
| 10 | Lzma, | 10 | Lzma, |
| 11 | Zstd, | 11 | Zstd, |
| 12 | Xz, | 12 | Xz, |
| 13 | Unsupported, | 13 | Unsupported(u16), |
| 14 | } | 14 | } |
| 15 | 15 | ||
| 16 | impl CompressionMethod { | 16 | impl CompressionMethod { |
| 17 | pub(crate) fn from_struct_id(id: u16) -> ZipResult<Self> { | 17 | pub(crate) fn from_struct_id(id: u16) -> ZipResult<Self> { |
| 18 | match id { | 18 | Ok(match id { |
| 19 | 0 => Ok(Self::Store), | 19 | 0 => Self::Store, |
| 20 | 8 => Ok(Self::Deflate), | 20 | 8 => Self::Deflate, |
| 21 | 12 => Ok(Self::BZip2), | 21 | 12 => Self::BZip2, |
| 22 | 14 => Ok(Self::Lzma), | 22 | 14 => Self::Lzma, |
| 23 | 93 => Ok(Self::Zstd), | 23 | 93 => Self::Zstd, |
| 24 | 95 => Ok(Self::Xz), | 24 | 95 => Self::Xz, |
| 25 | 1..=7 | 9..=11 | 13 | 15..=20 | 94 | 96..=99 => Ok(Self::Unsupported), | 25 | 1..=7 | 9..=11 | 13 | 15..=20 | 94 | 96..=99 => Self::Unsupported(id), |
| 26 | 21..=92 | 100.. => Err(ZipError::InvalidCompressionMethod.into()), | 26 | 21..=92 | 100.. => return Err(ZipError::InvalidCompressionMethod(id).into()), |
| 27 | }) | ||
| 28 | } | ||
| 29 | } | ||
| 30 | |||
| 31 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] | ||
| 32 | pub enum EncryptionMethod { | ||
| 33 | None, | ||
| 34 | Weak, | ||
| 35 | Unsupported, | ||
| 36 | } | ||
| 37 | |||
| 38 | impl EncryptionMethod { | ||
| 39 | pub(crate) fn from_bit_flag(bit_flag: BitFlag) -> EncryptionMethod { | ||
| 40 | match (bit_flag.is_encrypted(), bit_flag.is_strong_encryption()) { | ||
| 41 | (false, false) => EncryptionMethod::None, | ||
| 42 | (true, false) => EncryptionMethod::Weak, | ||
| 43 | (true, true) => EncryptionMethod::Unsupported, | ||
| 44 | _ => panic!("impossible"), | ||
| 27 | } | 45 | } |
| 28 | } | 46 | } |
| 29 | } | 47 | } |
| 30 | 48 | ||
| 31 | #[derive(Debug, Clone)] | 49 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] |
| 32 | pub struct BitFlag { | 50 | pub struct BitFlag { |
| 33 | flag: u16, | 51 | flag: u16, |
| 34 | } | 52 | } |
| 35 | 53 | ||
| 36 | pub mod bit { | 54 | pub mod bit { |
| 37 | #[derive(Debug, PartialEq, Eq)] | 55 | #[derive(Debug, PartialEq, Eq, Clone, Copy)] |
| 38 | pub enum DeflateMode { | 56 | pub enum DeflateMode { |
| 39 | Normal, | 57 | Normal, |
| 40 | Maximum, | 58 | Maximum, |
| @@ -123,6 +141,7 @@ impl BitFlag { | |||
| 123 | #[derive(Debug, Clone)] | 141 | #[derive(Debug, Clone)] |
| 124 | pub struct ZipFileInfo { | 142 | pub struct ZipFileInfo { |
| 125 | pub compression_method: CompressionMethod, | 143 | pub compression_method: CompressionMethod, |
| 144 | pub encryption_method: EncryptionMethod, | ||
| 126 | pub bit_flag: BitFlag, | 145 | pub bit_flag: BitFlag, |
| 127 | pub mtime: DateTime<Local>, | 146 | pub mtime: DateTime<Local>, |
| 128 | pub atime: Option<DateTime<Local>>, | 147 | pub atime: Option<DateTime<Local>>, |
| @@ -138,6 +157,7 @@ pub struct ZipFileInfo { | |||
| 138 | impl ZipFileInfo { | 157 | impl ZipFileInfo { |
| 139 | pub fn new( | 158 | pub fn new( |
| 140 | compression_method: CompressionMethod, | 159 | compression_method: CompressionMethod, |
| 160 | encryption_method: EncryptionMethod, | ||
| 141 | bit_flag: BitFlag, | 161 | bit_flag: BitFlag, |
| 142 | mtime: DateTime<Local>, | 162 | mtime: DateTime<Local>, |
| 143 | atime: Option<DateTime<Local>>, | 163 | atime: Option<DateTime<Local>>, |
| @@ -151,6 +171,7 @@ impl ZipFileInfo { | |||
| 151 | ) -> Self { | 171 | ) -> Self { |
| 152 | Self { | 172 | Self { |
| 153 | compression_method, | 173 | compression_method, |
| 174 | encryption_method, | ||
| 154 | bit_flag, | 175 | bit_flag, |
| 155 | mtime, | 176 | mtime, |
| 156 | atime, | 177 | atime, |
diff --git a/src/zip/file/mod.rs b/src/zip/file/mod.rs index 43ccc04..ce3c21d 100644 --- a/src/zip/file/mod.rs +++ b/src/zip/file/mod.rs | |||
| @@ -2,6 +2,6 @@ mod info; | |||
| 2 | mod read; | 2 | mod read; |
| 3 | mod write; | 3 | mod write; |
| 4 | 4 | ||
| 5 | pub use info::{bit, BitFlag, CompressionMethod, ZipFileInfo}; | 5 | pub use info::{bit, BitFlag, CompressionMethod, EncryptionMethod, ZipFileInfo}; |
| 6 | pub use read::ZipFileReader; | 6 | pub use read::ZipFileReader; |
| 7 | pub use write::ZipFileWriter; | 7 | pub use write::ZipFileWriter; |
diff --git a/src/zip/file/read.rs b/src/zip/file/read.rs index f5a54f3..aa665c3 100644 --- a/src/zip/file/read.rs +++ b/src/zip/file/read.rs | |||
| @@ -1,6 +1,7 @@ | |||
| 1 | use crate::driver::FileDriver; | 1 | use crate::driver::FileDriver; |
| 2 | use crate::utils::{IoCursor, ReadUtils}; | 2 | use crate::utils::{IoCursor, ReadUtils}; |
| 3 | use crate::zip::{CompressionMethod, ZipError, ZipFileInfo, ZipResult}; | 3 | use crate::zip::encryption::WeakDecoder; |
| 4 | use crate::zip::{CompressionMethod, EncryptionMethod, ZipError, ZipFileInfo, ZipResult}; | ||
| 4 | use bzip2::read::BzDecoder; | 5 | use bzip2::read::BzDecoder; |
| 5 | use flate2::read::DeflateDecoder; | 6 | use flate2::read::DeflateDecoder; |
| 6 | use liblzma::read::XzDecoder; | 7 | use liblzma::read::XzDecoder; |
| @@ -10,6 +11,47 @@ use std::io::{ | |||
| 10 | }; | 11 | }; |
| 11 | use zstd::stream::Decoder as ZstdDecoder; | 12 | use zstd::stream::Decoder as ZstdDecoder; |
| 12 | 13 | ||
| 14 | enum Encryption<Io: Read> { | ||
| 15 | None(Io), | ||
| 16 | Weak(WeakDecoder<Io>), | ||
| 17 | } | ||
| 18 | |||
| 19 | impl<Io: Read> Encryption<Io> { | ||
| 20 | pub fn new(io: Io, method: EncryptionMethod, password: Option<&str>) -> ZipResult<Self> { | ||
| 21 | Ok(match method { | ||
| 22 | EncryptionMethod::None => Self::None(io), | ||
| 23 | EncryptionMethod::Weak => Self::Weak(WeakDecoder::new( | ||
| 24 | io, | ||
| 25 | password.ok_or(ZipError::PasswordIsNotSpecified)?, | ||
| 26 | )?), | ||
| 27 | EncryptionMethod::Unsupported => { | ||
| 28 | return Err(ZipError::UnsupportedEncryptionMethod.into()) | ||
| 29 | } | ||
| 30 | }) | ||
| 31 | } | ||
| 32 | } | ||
| 33 | |||
| 34 | impl<Io: Read> Read for Encryption<Io> { | ||
| 35 | fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> { | ||
| 36 | match self { | ||
| 37 | Self::None(io) => io.read(buf), | ||
| 38 | Self::Weak(io) => io.read(buf), | ||
| 39 | } | ||
| 40 | } | ||
| 41 | } | ||
| 42 | |||
| 43 | impl<Io: Read + Seek> Seek for Encryption<Io> { | ||
| 44 | fn seek(&mut self, pos: SeekFrom) -> IoResult<u64> { | ||
| 45 | match self { | ||
| 46 | Self::None(io) => io.seek(pos), | ||
| 47 | _ => Err(IoError::new( | ||
| 48 | IoErrorKind::Unsupported, | ||
| 49 | ZipError::EncryptedDataIsUnseekable, | ||
| 50 | )), | ||
| 51 | } | ||
| 52 | } | ||
| 53 | } | ||
| 54 | |||
| 13 | enum Compression<Io: Read> { | 55 | enum Compression<Io: Read> { |
| 14 | Store(Io), | 56 | Store(Io), |
| 15 | Deflate(DeflateDecoder<Io>), | 57 | Deflate(DeflateDecoder<Io>), |
| @@ -18,8 +60,48 @@ enum Compression<Io: Read> { | |||
| 18 | Xz(XzDecoder<Io>), | 60 | Xz(XzDecoder<Io>), |
| 19 | } | 61 | } |
| 20 | 62 | ||
| 63 | impl<Io: Read + Seek> Compression<Io> { | ||
| 64 | pub fn new(io: Io, method: CompressionMethod) -> ZipResult<Self> { | ||
| 65 | Ok(match method { | ||
| 66 | CompressionMethod::Store => Self::Store(io), | ||
| 67 | CompressionMethod::Deflate => Self::Deflate(DeflateDecoder::new(io)), | ||
| 68 | CompressionMethod::BZip2 => Self::BZip2(BzDecoder::new(io)), | ||
| 69 | CompressionMethod::Lzma => panic!(), | ||
| 70 | CompressionMethod::Zstd => Self::Zstd(ZstdDecoder::new(io)?), | ||
| 71 | CompressionMethod::Xz => Self::Xz(XzDecoder::new(io)), | ||
| 72 | CompressionMethod::Unsupported(id) => { | ||
| 73 | return Err(ZipError::UnsupportedCompressionMethod(id).into()) | ||
| 74 | } | ||
| 75 | }) | ||
| 76 | } | ||
| 77 | } | ||
| 78 | |||
| 79 | impl<Io: Read> Read for Compression<Io> { | ||
| 80 | fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> { | ||
| 81 | match self { | ||
| 82 | Compression::Store(io) => io.read(buf), | ||
| 83 | Compression::Deflate(io) => io.read(buf), | ||
| 84 | Compression::BZip2(io) => io.read(buf), | ||
| 85 | Compression::Zstd(io) => io.read(buf), | ||
| 86 | Compression::Xz(io) => io.read(buf), | ||
| 87 | } | ||
| 88 | } | ||
| 89 | } | ||
| 90 | |||
| 91 | impl<Io: Read + Seek> Seek for Compression<Io> { | ||
| 92 | fn seek(&mut self, pos: SeekFrom) -> IoResult<u64> { | ||
| 93 | match self { | ||
| 94 | Compression::Store(io) => io.seek(pos), | ||
| 95 | _ => Err(IoError::new( | ||
| 96 | IoErrorKind::Unsupported, | ||
| 97 | ZipError::CompressedDataIsUnseekable, | ||
| 98 | )), | ||
| 99 | } | ||
| 100 | } | ||
| 101 | } | ||
| 102 | |||
| 21 | pub struct ZipFileReader<'d, Io: Read> { | 103 | pub struct ZipFileReader<'d, Io: Read> { |
| 22 | io: Compression<IoCursor<&'d mut Io>>, | 104 | io: Compression<Encryption<IoCursor<&'d mut Io>>>, |
| 23 | info: &'d ZipFileInfo, | 105 | info: &'d ZipFileInfo, |
| 24 | } | 106 | } |
| 25 | 107 | ||
| @@ -33,7 +115,7 @@ impl<'d, Io: Read> FileDriver for ZipFileReader<'d, Io> { | |||
| 33 | } | 115 | } |
| 34 | 116 | ||
| 35 | impl<'d, Io: Read + Seek> ZipFileReader<'d, Io> { | 117 | impl<'d, Io: Read + Seek> ZipFileReader<'d, Io> { |
| 36 | pub fn new(io: &'d mut Io, info: &'d ZipFileInfo) -> ZipResult<Self> { | 118 | pub fn new(io: &'d mut Io, info: &'d ZipFileInfo, password: Option<&str>) -> ZipResult<Self> { |
| 37 | io.seek(SeekFrom::Start(info.header_pointer))?; | 119 | io.seek(SeekFrom::Start(info.header_pointer))?; |
| 38 | 120 | ||
| 39 | let buf = io.read_arr::<30>()?; | 121 | let buf = io.read_arr::<30>()?; |
| @@ -45,27 +127,22 @@ impl<'d, Io: Read + Seek> ZipFileReader<'d, Io> { | |||
| 45 | + u16::from_le_bytes(buf[26..28].try_into().unwrap()) as u64 | 127 | + u16::from_le_bytes(buf[26..28].try_into().unwrap()) as u64 |
| 46 | + u16::from_le_bytes(buf[28..30].try_into().unwrap()) as u64; | 128 | + u16::from_le_bytes(buf[28..30].try_into().unwrap()) as u64; |
| 47 | 129 | ||
| 130 | io.seek(SeekFrom::Start(data_pointer))?; | ||
| 131 | |||
| 48 | Ok(Self { | 132 | Ok(Self { |
| 49 | io: match info.compression_method { | 133 | io: match info.compression_method { |
| 50 | CompressionMethod::Store => Compression::Store(IoCursor::new( | ||
| 51 | io, | ||
| 52 | data_pointer, | ||
| 53 | data_pointer + info.compressed_size, | ||
| 54 | )?), | ||
| 55 | CompressionMethod::Deflate => Compression::Deflate(DeflateDecoder::new( | ||
| 56 | IoCursor::new(io, data_pointer, data_pointer + info.compressed_size)?, | ||
| 57 | )), | ||
| 58 | CompressionMethod::BZip2 => Compression::BZip2(BzDecoder::new(IoCursor::new( | ||
| 59 | io, | ||
| 60 | data_pointer, | ||
| 61 | data_pointer + info.compressed_size, | ||
| 62 | )?)), | ||
| 63 | CompressionMethod::Lzma => { | 134 | CompressionMethod::Lzma => { |
| 64 | io.seek(SeekFrom::Start(data_pointer))?; | ||
| 65 | let buf = io.read_arr::<9>()?; | 135 | let buf = io.read_arr::<9>()?; |
| 66 | |||
| 67 | Compression::Xz(XzDecoder::new_stream( | 136 | Compression::Xz(XzDecoder::new_stream( |
| 68 | IoCursor::new(io, data_pointer + 9, data_pointer + info.compressed_size)?, | 137 | Encryption::new( |
| 138 | IoCursor::new( | ||
| 139 | io, | ||
| 140 | data_pointer + 9, | ||
| 141 | data_pointer + info.compressed_size, | ||
| 142 | )?, | ||
| 143 | info.encryption_method, | ||
| 144 | password, | ||
| 145 | )?, | ||
| 69 | Stream::new_raw_decoder( | 146 | Stream::new_raw_decoder( |
| 70 | Filters::new().lzma1( | 147 | Filters::new().lzma1( |
| 71 | LzmaOptions::new() | 148 | LzmaOptions::new() |
| @@ -80,30 +157,22 @@ impl<'d, Io: Read + Seek> ZipFileReader<'d, Io> { | |||
| 80 | .unwrap(), | 157 | .unwrap(), |
| 81 | )) | 158 | )) |
| 82 | } | 159 | } |
| 83 | CompressionMethod::Zstd => Compression::Zstd( | 160 | _ => Compression::new( |
| 84 | ZstdDecoder::new(IoCursor::new( | 161 | Encryption::new( |
| 85 | io, | 162 | IoCursor::new(io, data_pointer, data_pointer + info.compressed_size)?, |
| 86 | data_pointer, | 163 | info.encryption_method, |
| 87 | data_pointer + info.compressed_size, | 164 | password, |
| 88 | )?) | 165 | )?, |
| 89 | .unwrap(), | 166 | info.compression_method, |
| 90 | ), | 167 | )?, |
| 91 | CompressionMethod::Xz => Compression::Xz(XzDecoder::new(IoCursor::new( | ||
| 92 | io, | ||
| 93 | data_pointer, | ||
| 94 | data_pointer + info.compressed_size, | ||
| 95 | )?)), | ||
| 96 | CompressionMethod::Unsupported => { | ||
| 97 | return Err(ZipError::UnsupportedCompressionMethod.into()) | ||
| 98 | } | ||
| 99 | }, | 168 | }, |
| 100 | info, | 169 | info, |
| 101 | }) | 170 | }) |
| 102 | } | 171 | } |
| 103 | 172 | ||
| 104 | pub fn seekable(&self) -> bool { | 173 | pub fn is_seekable(&self) -> bool { |
| 105 | match self.io { | 174 | match self.io { |
| 106 | Compression::Store(..) => true, | 175 | Compression::Store(Encryption::None(..)) => true, |
| 107 | _ => false, | 176 | _ => false, |
| 108 | } | 177 | } |
| 109 | } | 178 | } |
| @@ -111,24 +180,12 @@ impl<'d, Io: Read + Seek> ZipFileReader<'d, Io> { | |||
| 111 | 180 | ||
| 112 | impl<'d, Io: Read> Read for ZipFileReader<'d, Io> { | 181 | impl<'d, Io: Read> Read for ZipFileReader<'d, Io> { |
| 113 | fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> { | 182 | fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> { |
| 114 | match &mut self.io { | 183 | self.io.read(buf) |
| 115 | Compression::Store(io) => io.read(buf), | ||
| 116 | Compression::Deflate(io) => io.read(buf), | ||
| 117 | Compression::BZip2(io) => io.read(buf), | ||
| 118 | Compression::Zstd(io) => io.read(buf), | ||
| 119 | Compression::Xz(io) => io.read(buf), | ||
| 120 | } | ||
| 121 | } | 184 | } |
| 122 | } | 185 | } |
| 123 | 186 | ||
| 124 | impl<'d, Io: Read + Seek> Seek for ZipFileReader<'d, Io> { | 187 | impl<'d, Io: Read + Seek> Seek for ZipFileReader<'d, Io> { |
| 125 | fn seek(&mut self, pos: SeekFrom) -> IoResult<u64> { | 188 | fn seek(&mut self, pos: SeekFrom) -> IoResult<u64> { |
| 126 | match &mut self.io { | 189 | self.io.seek(pos) |
| 127 | Compression::Store(io) => io.seek(pos), | ||
| 128 | _ => Err(IoError::new( | ||
| 129 | IoErrorKind::Unsupported, | ||
| 130 | ZipError::CompressedDataIsUnseekable, | ||
| 131 | )), | ||
| 132 | } | ||
| 133 | } | 190 | } |
| 134 | } | 191 | } |
diff --git a/src/zip/mod.rs b/src/zip/mod.rs index bcc34ed..fcc6161 100644 --- a/src/zip/mod.rs +++ b/src/zip/mod.rs | |||
| @@ -1,13 +1,16 @@ | |||
| 1 | mod archive; | 1 | mod archive; |
| 2 | mod cp437; | 2 | mod cp437; |
| 3 | mod driver; | 3 | mod driver; |
| 4 | mod encryption; | ||
| 4 | mod error; | 5 | mod error; |
| 5 | mod file; | 6 | mod file; |
| 6 | mod structs; | 7 | mod structs; |
| 7 | 8 | ||
| 8 | pub use driver::Zip; | 9 | pub use driver::Zip; |
| 9 | pub use error::{ZipError, ZipResult}; | 10 | pub use error::{ZipError, ZipResult}; |
| 10 | pub use file::{bit, BitFlag, CompressionMethod, ZipFileInfo, ZipFileReader, ZipFileWriter}; | 11 | pub use file::{ |
| 12 | bit, BitFlag, CompressionMethod, EncryptionMethod, ZipFileInfo, ZipFileReader, ZipFileWriter, | ||
| 13 | }; | ||
| 11 | 14 | ||
| 12 | #[cfg(test)] | 15 | #[cfg(test)] |
| 13 | mod tests; | 16 | mod tests; |
