aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.vscode/settings.json3
-rw-r--r--Cargo.toml3
-rw-r--r--src/archive.rs18
-rw-r--r--src/driver/driver.rs13
-rw-r--r--src/error.rs55
-rw-r--r--src/lib.rs2
-rw-r--r--src/structs/de.rs33
-rw-r--r--src/structs/error.rs41
-rw-r--r--src/structs/ser.rs47
-rw-r--r--src/utils/read.rs10
-rw-r--r--src/zip/datetime.rs45
-rw-r--r--src/zip/driver.rs179
-rw-r--r--src/zip/encryption/aes.rs46
-rw-r--r--src/zip/encryption/mod.rs5
-rw-r--r--src/zip/encryption/weak.rs (renamed from src/zip/encryption.rs)62
-rw-r--r--src/zip/error.rs38
-rw-r--r--src/zip/file/info.rs43
-rw-r--r--src/zip/file/read.rs99
-rw-r--r--src/zip/mod.rs1
-rw-r--r--src/zip/structs.rs14
-rw-r--r--src/zip/tests.rs19
21 files changed, 472 insertions, 304 deletions
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 8ab0a05..4a76d39 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -4,11 +4,13 @@
4 "bincode", 4 "bincode",
5 "chrono", 5 "chrono",
6 "datatypes", 6 "datatypes",
7 "datelike",
7 "datetime", 8 "datetime",
8 "decompressor", 9 "decompressor",
9 "eocdr", 10 "eocdr",
10 "flate", 11 "flate",
11 "hasher", 12 "hasher",
13 "hmac",
12 "lzma", 14 "lzma",
13 "newtype", 15 "newtype",
14 "ntfs", 16 "ntfs",
@@ -17,6 +19,7 @@
17 "repr", 19 "repr",
18 "rposition", 20 "rposition",
19 "seekable", 21 "seekable",
22 "timelike",
20 "unseekable", 23 "unseekable",
21 "xorout" 24 "xorout"
22 ], 25 ],
diff --git a/Cargo.toml b/Cargo.toml
index 96900a5..ea16168 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -12,10 +12,13 @@ categories = ["compression", "filesystem"]
12exclude = [".vscode/"] 12exclude = [".vscode/"]
13 13
14[dependencies] 14[dependencies]
15aes = "0.8.4"
15bzip2 = "0.4.4" 16bzip2 = "0.4.4"
16chrono = "0.4.29" 17chrono = "0.4.29"
17crc32fast = "1.4.2" 18crc32fast = "1.4.2"
18flate2 = "1.0.30" 19flate2 = "1.0.30"
19liblzma = "0.3.2" 20liblzma = "0.3.2"
21pbkdf2 = "0.12.2"
20serde = { version = "1.0.203", features = ["derive"] } 22serde = { version = "1.0.203", features = ["derive"] }
23sha1 = "0.10.6"
21zstd = "0.13.2" 24zstd = "0.13.2"
diff --git a/src/archive.rs b/src/archive.rs
index d49689d..d03f143 100644
--- a/src/archive.rs
+++ b/src/archive.rs
@@ -1,7 +1,6 @@
1use crate::driver::{ArchiveRead, ArchiveWrite, Driver}; 1use crate::driver::{ArchiveRead, ArchiveWrite, Driver};
2use crate::ArchiveResult;
3use std::fs::File; 2use std::fs::File;
4use std::io::{Read, Write}; 3use std::io::{Error as IoError, Read, Write};
5use std::path::Path; 4use std::path::Path;
6 5
7pub struct Archive<D: Driver> { 6pub struct Archive<D: Driver> {
@@ -12,16 +11,17 @@ impl<D: ArchiveRead> Archive<D>
12where 11where
13 D::Io: std::io::Read, 12 D::Io: std::io::Read,
14{ 13{
15 pub fn read(io: D::Io) -> ArchiveResult<Self, D::Error> { 14 pub fn read(io: D::Io) -> Result<Self, D::Error> {
16 Ok(Self { 15 Ok(Self {
17 driver: D::read(io)?, 16 driver: D::read(io)?,
18 }) 17 })
19 } 18 }
20 19
21 #[inline] 20 #[inline]
22 pub fn read_from_file(path: impl AsRef<Path>) -> ArchiveResult<Self, D::Error> 21 pub fn read_from_file(path: impl AsRef<Path>) -> Result<Self, D::Error>
23 where 22 where
24 D: ArchiveRead<Io = File>, 23 D: ArchiveRead<Io = File>,
24 D::Error: From<IoError>,
25 { 25 {
26 Self::read(File::open(path)?) 26 Self::read(File::open(path)?)
27 } 27 }
@@ -35,23 +35,23 @@ where
35 self.files().len() 35 self.files().len()
36 } 36 }
37 37
38 pub fn get_file_index(&self, name: &str) -> ArchiveResult<usize, D::Error> { 38 pub fn get_file_index(&self, name: &str) -> Result<usize, D::Error> {
39 self.driver.get_file_index(name) 39 self.driver.get_file_index(name)
40 } 40 }
41 41
42 pub fn get_file_info_by_index(&self, index: usize) -> ArchiveResult<&D::FileInfo, D::Error> { 42 pub fn get_file_info_by_index(&self, index: usize) -> Result<&D::FileInfo, D::Error> {
43 self.driver.get_file_info(index) 43 self.driver.get_file_info(index)
44 } 44 }
45 45
46 #[inline] 46 #[inline]
47 pub fn get_file_info_by_name(&self, name: &str) -> ArchiveResult<&D::FileInfo, D::Error> { 47 pub fn get_file_info_by_name(&self, name: &str) -> Result<&D::FileInfo, D::Error> {
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 pub fn get_file_reader_by_index<'d>( 51 pub fn get_file_reader_by_index<'d>(
52 &'d mut self, 52 &'d mut self,
53 index: usize, 53 index: usize,
54 ) -> ArchiveResult<D::FileReader<'d>, D::Error> { 54 ) -> Result<D::FileReader<'d>, D::Error> {
55 self.driver.get_file_reader(index) 55 self.driver.get_file_reader(index)
56 } 56 }
57 57
@@ -59,7 +59,7 @@ where
59 pub fn get_file_reader_by_name<'d>( 59 pub fn get_file_reader_by_name<'d>(
60 &'d mut self, 60 &'d mut self,
61 name: &str, 61 name: &str,
62 ) -> ArchiveResult<D::FileReader<'d>, D::Error> { 62 ) -> Result<D::FileReader<'d>, D::Error> {
63 self.get_file_reader_by_index(self.get_file_index(name)?) 63 self.get_file_reader_by_index(self.get_file_index(name)?)
64 } 64 }
65} 65}
diff --git a/src/driver/driver.rs b/src/driver/driver.rs
index 359793d..755380f 100644
--- a/src/driver/driver.rs
+++ b/src/driver/driver.rs
@@ -1,5 +1,4 @@
1use crate::driver::{ArchiveFileInfo, FileDriver}; 1use crate::driver::{ArchiveFileInfo, FileDriver};
2use crate::ArchiveResult;
3use std::error::Error; 2use std::error::Error;
4use std::io::{Read, Write}; 3use std::io::{Read, Write};
5 4
@@ -19,22 +18,20 @@ where
19 Self: 'd; 18 Self: 'd;
20 19
21 // Create driver instance 20 // Create driver instance
22 fn read(io: Self::Io) -> ArchiveResult<Self, Self::Error>; 21 fn read(io: Self::Io) -> Result<Self, Self::Error>;
23 22
24 // Return vec of file infos 23 // Return vec of file infos
25 fn files(&self) -> &Vec<Self::FileInfo>; 24 fn files(&self) -> &Vec<Self::FileInfo>;
26 25
27 // Return file index by name 26 // Return file index by name
28 fn get_file_index(&self, name: &str) -> ArchiveResult<usize, Self::Error>; 27 fn get_file_index(&self, name: &str) -> Result<usize, Self::Error>;
29 28
30 // Return file info by index 29 // Return file info by index
31 fn get_file_info(&self, index: usize) -> ArchiveResult<&Self::FileInfo, Self::Error>; 30 fn get_file_info(&self, index: usize) -> Result<&Self::FileInfo, Self::Error>;
32 31
33 // Return file reader by index 32 // Return file reader by index
34 fn get_file_reader<'d>( 33 fn get_file_reader<'d>(&'d mut self, index: usize)
35 &'d mut self, 34 -> Result<Self::FileReader<'d>, Self::Error>;
36 index: usize,
37 ) -> ArchiveResult<Self::FileReader<'d>, Self::Error>;
38} 35}
39 36
40pub trait ArchiveWrite: Driver 37pub trait ArchiveWrite: Driver
diff --git a/src/error.rs b/src/error.rs
deleted file mode 100644
index b7866d7..0000000
--- a/src/error.rs
+++ /dev/null
@@ -1,55 +0,0 @@
1use std::error::Error;
2use std::fmt::Display;
3use std::io;
4
5pub type ArchiveResult<T, E> = Result<T, ArchiveError<E>>;
6
7#[derive(Debug)]
8pub enum ArchiveError<E: Error> {
9 Io { error: io::Error },
10 Serde { message: String },
11 Archivator { module: &'static str, error: E },
12}
13
14impl<E: Error> From<io::Error> for ArchiveError<E> {
15 fn from(value: io::Error) -> Self {
16 Self::Io { error: value }
17 }
18}
19
20impl<E: Error + PartialEq> PartialEq<E> for ArchiveError<E> {
21 fn eq(&self, other: &E) -> bool {
22 match self {
23 Self::Archivator { error, .. } => error == other,
24 _ => false,
25 }
26 }
27}
28
29impl<E: Error> Display for ArchiveError<E> {
30 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31 match self {
32 Self::Io { error } => writeln!(f, "IO: {error}"),
33 Self::Serde { message } => writeln!(f, "Serde: {message}"),
34 Self::Archivator { module, error } => writeln!(f, "{module}: {error}"),
35 }
36 }
37}
38
39impl<E: Error> Error for ArchiveError<E> {}
40
41impl<E: Error> serde::ser::Error for ArchiveError<E> {
42 fn custom<T: Display>(msg: T) -> Self {
43 Self::Serde {
44 message: msg.to_string(),
45 }
46 }
47}
48
49impl<E: Error> serde::de::Error for ArchiveError<E> {
50 fn custom<T: Display>(msg: T) -> Self {
51 Self::Serde {
52 message: msg.to_string(),
53 }
54 }
55}
diff --git a/src/lib.rs b/src/lib.rs
index 7490cc8..5dc36b3 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,12 +1,10 @@
1mod archive; 1mod archive;
2mod driver; 2mod driver;
3mod error;
4mod structs; 3mod structs;
5mod utils; 4mod utils;
6 5
7pub mod zip; 6pub mod zip;
8 7
9pub use archive::Archive; 8pub use archive::Archive;
10pub use error::{ArchiveError, ArchiveResult};
11 9
12pub use zip::Zip; 10pub use zip::Zip;
diff --git a/src/structs/de.rs b/src/structs/de.rs
index edd8011..8b2130e 100644
--- a/src/structs/de.rs
+++ b/src/structs/de.rs
@@ -1,5 +1,4 @@
1use crate::structs::{Settings, StructError, StructResult}; 1use crate::structs::{Settings, StructError, StructResult};
2use crate::ArchiveError;
3use serde::de; 2use serde::de;
4 3
5pub struct ArchiveDeserializer<'de> { 4pub struct ArchiveDeserializer<'de> {
@@ -60,14 +59,14 @@ impl<'a, 'de> EnumDeserializer<'a, 'de> {
60} 59}
61 60
62impl<'de> de::Deserializer<'de> for &mut ArchiveDeserializer<'de> { 61impl<'de> de::Deserializer<'de> for &mut ArchiveDeserializer<'de> {
63 type Error = ArchiveError<StructError>; 62 type Error = StructError;
64 63
65 #[inline] 64 #[inline]
66 fn deserialize_any<V>(self, _visitor: V) -> StructResult<V::Value> 65 fn deserialize_any<V>(self, _visitor: V) -> StructResult<V::Value>
67 where 66 where
68 V: de::Visitor<'de>, 67 V: de::Visitor<'de>,
69 { 68 {
70 Err(StructError::DeserializationNotSupported { type_name: "any" }.into()) 69 Err(StructError::DeserializationNotSupported("any"))
71 } 70 }
72 71
73 fn deserialize_bool<V>(self, visitor: V) -> StructResult<V::Value> 72 fn deserialize_bool<V>(self, visitor: V) -> StructResult<V::Value>
@@ -160,7 +159,7 @@ impl<'de> de::Deserializer<'de> for &mut ArchiveDeserializer<'de> {
160 where 159 where
161 V: de::Visitor<'de>, 160 V: de::Visitor<'de>,
162 { 161 {
163 Err(StructError::DeserializationNotSupported { type_name: "char" }.into()) 162 Err(StructError::DeserializationNotSupported("char"))
164 } 163 }
165 164
166 #[inline] 165 #[inline]
@@ -168,7 +167,7 @@ impl<'de> de::Deserializer<'de> for &mut ArchiveDeserializer<'de> {
168 where 167 where
169 V: de::Visitor<'de>, 168 V: de::Visitor<'de>,
170 { 169 {
171 Err(StructError::DeserializationNotSupported { type_name: "str" }.into()) 170 Err(StructError::DeserializationNotSupported("str"))
172 } 171 }
173 172
174 #[inline] 173 #[inline]
@@ -176,10 +175,7 @@ impl<'de> de::Deserializer<'de> for &mut ArchiveDeserializer<'de> {
176 where 175 where
177 V: de::Visitor<'de>, 176 V: de::Visitor<'de>,
178 { 177 {
179 Err(StructError::DeserializationNotSupported { 178 Err(StructError::DeserializationNotSupported("string"))
180 type_name: "string",
181 }
182 .into())
183 } 179 }
184 180
185 #[inline] 181 #[inline]
@@ -187,7 +183,7 @@ impl<'de> de::Deserializer<'de> for &mut ArchiveDeserializer<'de> {
187 where 183 where
188 V: de::Visitor<'de>, 184 V: de::Visitor<'de>,
189 { 185 {
190 Err(StructError::DeserializationNotSupported { type_name: "bytes" }.into()) 186 Err(StructError::DeserializationNotSupported("bytes"))
191 } 187 }
192 188
193 #[inline] 189 #[inline]
@@ -195,7 +191,7 @@ impl<'de> de::Deserializer<'de> for &mut ArchiveDeserializer<'de> {
195 where 191 where
196 V: de::Visitor<'de>, 192 V: de::Visitor<'de>,
197 { 193 {
198 Err(StructError::DeserializationNotSupported { type_name: "bytes" }.into()) 194 Err(StructError::DeserializationNotSupported("bytes"))
199 } 195 }
200 196
201 #[inline] 197 #[inline]
@@ -203,10 +199,7 @@ impl<'de> de::Deserializer<'de> for &mut ArchiveDeserializer<'de> {
203 where 199 where
204 V: de::Visitor<'de>, 200 V: de::Visitor<'de>,
205 { 201 {
206 Err(StructError::DeserializationNotSupported { 202 Err(StructError::DeserializationNotSupported("optional"))
207 type_name: "optional",
208 }
209 .into())
210 } 203 }
211 204
212 #[inline] 205 #[inline]
@@ -242,7 +235,7 @@ impl<'de> de::Deserializer<'de> for &mut ArchiveDeserializer<'de> {
242 where 235 where
243 V: de::Visitor<'de>, 236 V: de::Visitor<'de>,
244 { 237 {
245 Err(StructError::DeserializationNotSupported { type_name: "seq" }.into()) 238 Err(StructError::DeserializationNotSupported("seq"))
246 } 239 }
247 240
248 #[inline] 241 #[inline]
@@ -271,7 +264,7 @@ impl<'de> de::Deserializer<'de> for &mut ArchiveDeserializer<'de> {
271 where 264 where
272 V: de::Visitor<'de>, 265 V: de::Visitor<'de>,
273 { 266 {
274 Err(StructError::DeserializationNotSupported { type_name: "map" }.into()) 267 Err(StructError::DeserializationNotSupported("map"))
275 } 268 }
276 269
277 #[inline] 270 #[inline]
@@ -322,7 +315,7 @@ impl<'de> de::Deserializer<'de> for &mut ArchiveDeserializer<'de> {
322} 315}
323 316
324impl<'a, 'de> de::SeqAccess<'de> for SeqDeserializer<'a, 'de> { 317impl<'a, 'de> de::SeqAccess<'de> for SeqDeserializer<'a, 'de> {
325 type Error = ArchiveError<StructError>; 318 type Error = StructError;
326 319
327 fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, Self::Error> 320 fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>, Self::Error>
328 where 321 where
@@ -336,7 +329,7 @@ impl<'a, 'de> de::SeqAccess<'de> for SeqDeserializer<'a, 'de> {
336} 329}
337 330
338impl<'a, 'de> de::EnumAccess<'de> for EnumDeserializer<'a, 'de> { 331impl<'a, 'de> de::EnumAccess<'de> for EnumDeserializer<'a, 'de> {
339 type Error = ArchiveError<StructError>; 332 type Error = StructError;
340 type Variant = Self; 333 type Variant = Self;
341 334
342 fn variant_seed<V>(self, seed: V) -> Result<(V::Value, Self::Variant), Self::Error> 335 fn variant_seed<V>(self, seed: V) -> Result<(V::Value, Self::Variant), Self::Error>
@@ -348,7 +341,7 @@ impl<'a, 'de> de::EnumAccess<'de> for EnumDeserializer<'a, 'de> {
348} 341}
349 342
350impl<'a, 'de> de::VariantAccess<'de> for EnumDeserializer<'a, 'de> { 343impl<'a, 'de> de::VariantAccess<'de> for EnumDeserializer<'a, 'de> {
351 type Error = ArchiveError<StructError>; 344 type Error = StructError;
352 345
353 #[inline] 346 #[inline]
354 fn unit_variant(self) -> Result<(), Self::Error> { 347 fn unit_variant(self) -> Result<(), Self::Error> {
diff --git a/src/structs/error.rs b/src/structs/error.rs
index f07163c..deb10b3 100644
--- a/src/structs/error.rs
+++ b/src/structs/error.rs
@@ -1,37 +1,42 @@
1use crate::{ArchiveError, ArchiveResult}; 1use serde::{de, ser};
2use std::error::Error; 2use std::error::Error;
3use std::fmt::Display; 3use std::fmt::Display;
4 4
5pub type StructResult<T> = ArchiveResult<T, StructError>; 5pub type StructResult<T> = Result<T, StructError>;
6 6
7#[derive(Debug)] 7#[derive(Debug, PartialEq, Eq)]
8pub enum StructError { 8pub enum StructError {
9 SerializationNotSupported { type_name: &'static str }, 9 SerializationNotSupported(&'static str),
10 DeserializationNotSupported { type_name: &'static str }, 10 DeserializationNotSupported(&'static str),
11 Serde(String),
11 UnexpectedEOF, 12 UnexpectedEOF,
12} 13}
13 14
14impl From<StructError> for ArchiveError<StructError> {
15 fn from(value: StructError) -> Self {
16 Self::Archivator {
17 module: "Struct serializer",
18 error: value,
19 }
20 }
21}
22
23impl Display for StructError { 15impl Display for StructError {
24 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 16 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25 match self { 17 match self {
26 Self::SerializationNotSupported { type_name } => { 18 Self::SerializationNotSupported(name) => {
27 writeln!(f, "Serialization for type '{type_name}' not supported") 19 writeln!(f, "Serialization for type '{}' not supported", name)
28 } 20 }
29 Self::DeserializationNotSupported { type_name } => { 21 Self::DeserializationNotSupported(name) => {
30 writeln!(f, "Deserialization for type '{type_name}' not supported") 22 writeln!(f, "Deserialization for type '{}' not supported", name)
31 } 23 }
24 Self::Serde(message) => writeln!(f, "Serde error: {}", message),
32 Self::UnexpectedEOF => writeln!(f, "Unexpected EOF"), 25 Self::UnexpectedEOF => writeln!(f, "Unexpected EOF"),
33 } 26 }
34 } 27 }
35} 28}
36 29
37impl Error for StructError {} 30impl Error for StructError {}
31
32impl ser::Error for StructError {
33 fn custom<T: Display>(msg: T) -> Self {
34 Self::Serde(msg.to_string())
35 }
36}
37
38impl de::Error for StructError {
39 fn custom<T: Display>(msg: T) -> Self {
40 Self::Serde(msg.to_string())
41 }
42}
diff --git a/src/structs/ser.rs b/src/structs/ser.rs
index f7e694a..b633dff 100644
--- a/src/structs/ser.rs
+++ b/src/structs/ser.rs
@@ -1,5 +1,4 @@
1use crate::structs::{Settings, StructError, StructResult}; 1use crate::structs::{Settings, StructError, StructResult};
2use crate::ArchiveError;
3use serde::{ser, Serialize}; 2use serde::{ser, Serialize};
4 3
5pub struct ArchiveSerializer { 4pub struct ArchiveSerializer {
@@ -22,7 +21,7 @@ impl ArchiveSerializer {
22 21
23impl ser::Serializer for &mut ArchiveSerializer { 22impl ser::Serializer for &mut ArchiveSerializer {
24 type Ok = (); 23 type Ok = ();
25 type Error = ArchiveError<StructError>; 24 type Error = StructError;
26 25
27 type SerializeSeq = Self; 26 type SerializeSeq = Self;
28 type SerializeTuple = Self; 27 type SerializeTuple = Self;
@@ -89,25 +88,22 @@ impl ser::Serializer for &mut ArchiveSerializer {
89 88
90 #[inline] 89 #[inline]
91 fn serialize_char(self, _v: char) -> StructResult<()> { 90 fn serialize_char(self, _v: char) -> StructResult<()> {
92 Err(StructError::SerializationNotSupported { type_name: "char" }.into()) 91 Err(StructError::SerializationNotSupported("char"))
93 } 92 }
94 93
95 #[inline] 94 #[inline]
96 fn serialize_str(self, _v: &str) -> StructResult<()> { 95 fn serialize_str(self, _v: &str) -> StructResult<()> {
97 Err(StructError::SerializationNotSupported { type_name: "str" }.into()) 96 Err(StructError::SerializationNotSupported("str"))
98 } 97 }
99 98
100 #[inline] 99 #[inline]
101 fn serialize_bytes(self, _v: &[u8]) -> StructResult<()> { 100 fn serialize_bytes(self, _v: &[u8]) -> StructResult<()> {
102 Err(StructError::SerializationNotSupported { type_name: "bytes" }.into()) 101 Err(StructError::SerializationNotSupported("bytes"))
103 } 102 }
104 103
105 #[inline] 104 #[inline]
106 fn serialize_none(self) -> StructResult<()> { 105 fn serialize_none(self) -> StructResult<()> {
107 Err(StructError::SerializationNotSupported { 106 Err(StructError::SerializationNotSupported("optional"))
108 type_name: "optional",
109 }
110 .into())
111 } 107 }
112 108
113 #[inline] 109 #[inline]
@@ -115,10 +111,7 @@ impl ser::Serializer for &mut ArchiveSerializer {
115 where 111 where
116 T: ?Sized + Serialize, 112 T: ?Sized + Serialize,
117 { 113 {
118 Err(StructError::SerializationNotSupported { 114 Err(StructError::SerializationNotSupported("optional"))
119 type_name: "optional",
120 }
121 .into())
122 } 115 }
123 116
124 #[inline] 117 #[inline]
@@ -175,7 +168,7 @@ impl ser::Serializer for &mut ArchiveSerializer {
175 168
176 #[inline] 169 #[inline]
177 fn serialize_seq(self, _len: Option<usize>) -> StructResult<Self::SerializeSeq> { 170 fn serialize_seq(self, _len: Option<usize>) -> StructResult<Self::SerializeSeq> {
178 Err(StructError::SerializationNotSupported { type_name: "seq" }.into()) 171 Err(StructError::SerializationNotSupported("seq"))
179 } 172 }
180 173
181 #[inline] 174 #[inline]
@@ -210,7 +203,7 @@ impl ser::Serializer for &mut ArchiveSerializer {
210 203
211 #[inline] 204 #[inline]
212 fn serialize_map(self, _len: Option<usize>) -> StructResult<Self::SerializeMap> { 205 fn serialize_map(self, _len: Option<usize>) -> StructResult<Self::SerializeMap> {
213 Err(StructError::SerializationNotSupported { type_name: "map" }.into()) 206 Err(StructError::SerializationNotSupported("map"))
214 } 207 }
215 208
216 #[inline] 209 #[inline]
@@ -241,25 +234,25 @@ impl ser::Serializer for &mut ArchiveSerializer {
241 234
242impl ser::SerializeSeq for &mut ArchiveSerializer { 235impl ser::SerializeSeq for &mut ArchiveSerializer {
243 type Ok = (); 236 type Ok = ();
244 type Error = ArchiveError<StructError>; 237 type Error = StructError;
245 238
246 #[inline] 239 #[inline]
247 fn serialize_element<T>(&mut self, _value: &T) -> StructResult<()> 240 fn serialize_element<T>(&mut self, _value: &T) -> StructResult<()>
248 where 241 where
249 T: ?Sized + Serialize, 242 T: ?Sized + Serialize,
250 { 243 {
251 Err(StructError::SerializationNotSupported { type_name: "seq" }.into()) 244 Err(StructError::SerializationNotSupported("seq"))
252 } 245 }
253 246
254 #[inline] 247 #[inline]
255 fn end(self) -> StructResult<()> { 248 fn end(self) -> StructResult<()> {
256 Err(StructError::SerializationNotSupported { type_name: "seq" }.into()) 249 Err(StructError::SerializationNotSupported("seq"))
257 } 250 }
258} 251}
259 252
260impl ser::SerializeTuple for &mut ArchiveSerializer { 253impl ser::SerializeTuple for &mut ArchiveSerializer {
261 type Ok = (); 254 type Ok = ();
262 type Error = ArchiveError<StructError>; 255 type Error = StructError;
263 256
264 #[inline] 257 #[inline]
265 fn serialize_element<T>(&mut self, value: &T) -> StructResult<()> 258 fn serialize_element<T>(&mut self, value: &T) -> StructResult<()>
@@ -277,7 +270,7 @@ impl ser::SerializeTuple for &mut ArchiveSerializer {
277 270
278impl ser::SerializeTupleStruct for &mut ArchiveSerializer { 271impl ser::SerializeTupleStruct for &mut ArchiveSerializer {
279 type Ok = (); 272 type Ok = ();
280 type Error = ArchiveError<StructError>; 273 type Error = StructError;
281 274
282 #[inline] 275 #[inline]
283 fn serialize_field<T>(&mut self, value: &T) -> StructResult<()> 276 fn serialize_field<T>(&mut self, value: &T) -> StructResult<()>
@@ -295,7 +288,7 @@ impl ser::SerializeTupleStruct for &mut ArchiveSerializer {
295 288
296impl ser::SerializeTupleVariant for &mut ArchiveSerializer { 289impl ser::SerializeTupleVariant for &mut ArchiveSerializer {
297 type Ok = (); 290 type Ok = ();
298 type Error = ArchiveError<StructError>; 291 type Error = StructError;
299 292
300 #[inline] 293 #[inline]
301 fn serialize_field<T>(&mut self, value: &T) -> StructResult<()> 294 fn serialize_field<T>(&mut self, value: &T) -> StructResult<()>
@@ -313,14 +306,14 @@ impl ser::SerializeTupleVariant for &mut ArchiveSerializer {
313 306
314impl ser::SerializeMap for &mut ArchiveSerializer { 307impl ser::SerializeMap for &mut ArchiveSerializer {
315 type Ok = (); 308 type Ok = ();
316 type Error = ArchiveError<StructError>; 309 type Error = StructError;
317 310
318 #[inline] 311 #[inline]
319 fn serialize_key<T>(&mut self, _key: &T) -> StructResult<()> 312 fn serialize_key<T>(&mut self, _key: &T) -> StructResult<()>
320 where 313 where
321 T: ?Sized + Serialize, 314 T: ?Sized + Serialize,
322 { 315 {
323 Err(StructError::SerializationNotSupported { type_name: "map" }.into()) 316 Err(StructError::SerializationNotSupported("map"))
324 } 317 }
325 318
326 #[inline] 319 #[inline]
@@ -328,18 +321,18 @@ impl ser::SerializeMap for &mut ArchiveSerializer {
328 where 321 where
329 T: ?Sized + Serialize, 322 T: ?Sized + Serialize,
330 { 323 {
331 Err(StructError::SerializationNotSupported { type_name: "map" }.into()) 324 Err(StructError::SerializationNotSupported("map"))
332 } 325 }
333 326
334 #[inline] 327 #[inline]
335 fn end(self) -> StructResult<()> { 328 fn end(self) -> StructResult<()> {
336 Err(StructError::SerializationNotSupported { type_name: "map" }.into()) 329 Err(StructError::SerializationNotSupported("map"))
337 } 330 }
338} 331}
339 332
340impl ser::SerializeStruct for &mut ArchiveSerializer { 333impl ser::SerializeStruct for &mut ArchiveSerializer {
341 type Ok = (); 334 type Ok = ();
342 type Error = ArchiveError<StructError>; 335 type Error = StructError;
343 336
344 #[inline] 337 #[inline]
345 fn serialize_field<T>(&mut self, _key: &'static str, value: &T) -> StructResult<()> 338 fn serialize_field<T>(&mut self, _key: &'static str, value: &T) -> StructResult<()>
@@ -357,7 +350,7 @@ impl ser::SerializeStruct for &mut ArchiveSerializer {
357 350
358impl ser::SerializeStructVariant for &mut ArchiveSerializer { 351impl ser::SerializeStructVariant for &mut ArchiveSerializer {
359 type Ok = (); 352 type Ok = ();
360 type Error = ArchiveError<StructError>; 353 type Error = StructError;
361 354
362 #[inline] 355 #[inline]
363 fn serialize_field<T>(&mut self, _key: &'static str, value: &T) -> StructResult<()> 356 fn serialize_field<T>(&mut self, _key: &'static str, value: &T) -> StructResult<()>
diff --git a/src/utils/read.rs b/src/utils/read.rs
index 491c89c..2a46308 100644
--- a/src/utils/read.rs
+++ b/src/utils/read.rs
@@ -1,21 +1,21 @@
1use std::io::{Read, Result as IOResult}; 1use std::io::{Read, Result as IoResult};
2 2
3pub trait ReadUtils { 3pub trait ReadUtils {
4 fn read_arr<const S: usize>(&mut self) -> IOResult<[u8; S]>; 4 fn read_arr<const S: usize>(&mut self) -> IoResult<[u8; S]>;
5 5
6 fn read_vec(&mut self, size: usize) -> IOResult<Vec<u8>>; 6 fn read_vec(&mut self, size: usize) -> IoResult<Vec<u8>>;
7} 7}
8 8
9impl<R: Read> ReadUtils for R { 9impl<R: Read> ReadUtils for R {
10 #[inline] 10 #[inline]
11 fn read_arr<const S: usize>(&mut self) -> Result<[u8; S], std::io::Error> { 11 fn read_arr<const S: usize>(&mut self) -> IoResult<[u8; S]> {
12 let mut arr = [0; S]; 12 let mut arr = [0; S];
13 self.read_exact(&mut arr)?; 13 self.read_exact(&mut arr)?;
14 Ok(arr) 14 Ok(arr)
15 } 15 }
16 16
17 #[inline] 17 #[inline]
18 fn read_vec(&mut self, size: usize) -> Result<Vec<u8>, std::io::Error> { 18 fn read_vec(&mut self, size: usize) -> IoResult<Vec<u8>> {
19 let mut vec = vec![0; size]; 19 let mut vec = vec![0; size];
20 self.read_exact(&mut vec)?; 20 self.read_exact(&mut vec)?;
21 Ok(vec) 21 Ok(vec)
diff --git a/src/zip/datetime.rs b/src/zip/datetime.rs
new file mode 100644
index 0000000..a4b1b55
--- /dev/null
+++ b/src/zip/datetime.rs
@@ -0,0 +1,45 @@
1use crate::zip::{ZipError, ZipResult};
2use chrono::{DateTime, Datelike, NaiveDate, TimeZone, Timelike};
3
4pub trait DosDateTime<Tz>: Sized {
5 fn from_dos_date_time(date: u16, time: u16, tz: Tz) -> ZipResult<Self>;
6
7 #[allow(dead_code)]
8 fn to_dos_date(&self) -> u16;
9
10 fn to_dos_time(&self) -> u16;
11}
12
13impl<Tz: TimeZone> DosDateTime<Tz> for DateTime<Tz> {
14 #[inline]
15 fn from_dos_date_time(date: u16, time: u16, tz: Tz) -> ZipResult<Self> {
16 Ok(NaiveDate::from_ymd_opt(
17 (date as i32 >> 9 & 0x7F) + 1980,
18 date as u32 >> 5 & 0xF,
19 date as u32 & 0x1F,
20 )
21 .ok_or(ZipError::InvalidDate)?
22 .and_hms_opt(
23 (time as u32 >> 11) & 0x1F,
24 (time as u32 >> 5) & 0x3F,
25 (time as u32 & 0x1F) * 2,
26 )
27 .ok_or(ZipError::InvalidTime)?
28 .and_local_timezone(tz)
29 .unwrap())
30 }
31
32 #[inline]
33 fn to_dos_date(&self) -> u16 {
34 (self.year() as u16 - 1980 & 0x7F) << 9
35 | (self.month() as u16 & 0xF) << 5
36 | self.day() as u16 & 0x1F
37 }
38
39 #[inline]
40 fn to_dos_time(&self) -> u16 {
41 (self.hour() as u16 & 0x1F) << 11
42 | (self.minute() as u16 & 0x3F) << 5
43 | self.second() as u16 / 2 & 0x1F
44 }
45}
diff --git a/src/zip/driver.rs b/src/zip/driver.rs
index e7878cf..8dc902f 100644
--- a/src/zip/driver.rs
+++ b/src/zip/driver.rs
@@ -1,48 +1,51 @@
1use crate::driver::{ArchiveRead, ArchiveWrite, Driver}; 1use crate::driver::{ArchiveRead, ArchiveWrite, Driver};
2use crate::utils::ReadUtils; 2use crate::utils::ReadUtils;
3use crate::zip::cp437::FromCp437; 3use crate::zip::cp437::FromCp437;
4use crate::zip::structs::{deserialize, Cdr, Eocdr, Eocdr64, Eocdr64Locator, ExtraHeader}; 4use crate::zip::datetime::DosDateTime;
5use crate::zip::structs::{
6 deserialize, AesField, Cdr, Eocdr, Eocdr64, Eocdr64Locator, ExtraHeader, CDR_SIGNATURE,
7 EOCDR64_LOCATOR_SIGNATURE, EOCDR64_SIGNATURE, EOCDR_SIGNATURE,
8};
5use crate::zip::{ 9use crate::zip::{
6 BitFlag, CompressionMethod, EncryptionMethod, ZipError, ZipFileInfo, ZipFileReader, 10 BitFlag, CompressionMethod, EncryptionMethod, ZipError, ZipFileInfo, ZipFileReader,
7 ZipFileWriter, ZipResult, 11 ZipFileWriter, ZipResult,
8}; 12};
9use chrono::{DateTime, Local, NaiveDate, NaiveDateTime, NaiveTime}; 13use chrono::{DateTime, Local};
10use std::collections::HashMap as Map; 14use std::collections::HashMap as Map;
11use std::fs::File; 15use std::fs::File;
12use std::io::{BufReader, Read, Seek, SeekFrom, Write}; 16use std::io::{BufReader, Read, Seek, SeekFrom, Write};
13 17
14fn dos_to_local(date: u16, time: u16) -> ZipResult<DateTime<Local>> { 18#[inline]
15 Ok(NaiveDateTime::new( 19fn split_fields(bytes: &[u8]) -> Option<Vec<(u16, &[u8])>> {
16 NaiveDate::from_ymd_opt( 20 let mut fields = Vec::new();
17 (date as i32 >> 9 & 0x7F) + 1980, 21
18 date as u32 >> 5 & 0xF, 22 let mut p = 0;
19 date as u32 & 0x1F, 23 while p < bytes.len() {
20 ) 24 let header: ExtraHeader = deserialize(bytes.get(p..p + 4)?).unwrap();
21 .ok_or(ZipError::InvalidDate)?, 25 p += 4;
22 NaiveTime::from_hms_opt( 26 let data = bytes.get(p..p + header.size as usize)?;
23 (time as u32 >> 11) & 0x1F, 27 p += header.size as usize;
24 (time as u32 >> 5) & 0x3F, 28
25 (time as u32 & 0x1F) * 2, 29 fields.push((header.id, data));
26 ) 30 }
27 .ok_or(ZipError::InvalidTime)?, 31
28 ) 32 Some(fields)
29 .and_local_timezone(Local)
30 .unwrap())
31} 33}
32 34
33fn ntfs_to_local(time: u64) -> ZipResult<DateTime<Local>> { 35#[inline]
34 Ok(DateTime::from_timestamp( 36fn ntfs_to_local(time: u64) -> Option<DateTime<Local>> {
35 (time / 10000000 - 11644473600) as i64, 37 Some(
36 (time % 10000000) as u32, 38 DateTime::from_timestamp(
39 (time / 10000000 - 11644473600) as i64,
40 (time % 10000000) as u32,
41 )?
42 .with_timezone(&Local),
37 ) 43 )
38 .ok_or(ZipError::InvalidTime)?
39 .with_timezone(&Local))
40} 44}
41 45
42fn timestamp_to_local(time: i32) -> ZipResult<DateTime<Local>> { 46#[inline]
43 Ok(DateTime::from_timestamp(time as i64, 0) 47fn timestamp_to_local(time: i32) -> Option<DateTime<Local>> {
44 .ok_or(ZipError::InvalidTime)? 48 Some(DateTime::from_timestamp(time as i64, 0)?.with_timezone(&Local))
45 .with_timezone(&Local))
46} 49}
47 50
48pub struct Zip<Io = File> { 51pub struct Zip<Io = File> {
@@ -74,7 +77,7 @@ impl<Io: Read + Seek> ArchiveRead for Zip<Io> {
74 .ok_or(ZipError::EocdrNotFound)?, 77 .ok_or(ZipError::EocdrNotFound)?,
75 )? 78 )?
76 .windows(4) 79 .windows(4)
77 .rposition(|v| u32::from_le_bytes(v.try_into().unwrap()) == 0x06054b50) 80 .rposition(|v| v == EOCDR_SIGNATURE)
78 .ok_or(ZipError::EocdrNotFound)? as u64; 81 .ok_or(ZipError::EocdrNotFound)? as u64;
79 82
80 // Read eocdr 83 // Read eocdr
@@ -88,13 +91,13 @@ impl<Io: Read + Seek> ArchiveRead for Zip<Io> {
88 let buf = io.read_arr::<20>()?; 91 let buf = io.read_arr::<20>()?;
89 let (cd_pointer, cd_size, cd_records) = 92 let (cd_pointer, cd_size, cd_records) =
90 // If locator found then read eocdr64 93 // If locator found then read eocdr64
91 if u32::from_le_bytes(buf[0..4].try_into().unwrap()) == 0x07064b50 { 94 if buf[0..4] == EOCDR64_LOCATOR_SIGNATURE {
92 let eocdr64locator: Eocdr64Locator = deserialize(&buf[4..]).unwrap(); 95 let eocdr64locator: Eocdr64Locator = deserialize(&buf[4..]).unwrap();
93 96
94 io.seek(SeekFrom::Start(eocdr64locator.eocdr64_pointer))?; 97 io.seek(SeekFrom::Start(eocdr64locator.eocdr64_pointer))?;
95 let buf = io.read_arr::<56>()?; 98 let buf = io.read_arr::<56>()?;
96 if u32::from_le_bytes(buf[0..4].try_into().unwrap()) != 0x06064b50 { 99 if buf[0..4] != EOCDR64_SIGNATURE {
97 return Err(ZipError::InvalidEOCDR64Signature.into()); 100 return Err(ZipError::InvalidEOCDR64Signature);
98 } 101 }
99 let eocdr64: Eocdr64 = deserialize(&buf[4..]).unwrap(); 102 let eocdr64: Eocdr64 = deserialize(&buf[4..]).unwrap();
100 103
@@ -116,8 +119,8 @@ impl<Io: Read + Seek> ArchiveRead for Zip<Io> {
116 for i in 0..cd_records as usize { 119 for i in 0..cd_records as usize {
117 let buf = buf_reader.read_arr::<46>()?; 120 let buf = buf_reader.read_arr::<46>()?;
118 121
119 if u32::from_le_bytes(buf[..4].try_into().unwrap()) != 0x02014b50 { 122 if buf[..4] != CDR_SIGNATURE {
120 return Err(ZipError::InvalidCDRSignature.into()); 123 return Err(ZipError::InvalidCDRSignature);
121 } 124 }
122 let cdr: Cdr = deserialize(&buf[4..46]).unwrap(); 125 let cdr: Cdr = deserialize(&buf[4..46]).unwrap();
123 let bit_flag = BitFlag::new(cdr.bit_flag); 126 let bit_flag = BitFlag::new(cdr.bit_flag);
@@ -138,91 +141,87 @@ impl<Io: Read + Seek> ArchiveRead for Zip<Io> {
138 String::from_cp437(comment) 141 String::from_cp437(comment)
139 }; 142 };
140 143
144 let mut compression_method = cdr.compression_method;
145 let mut encryption_method = if !bit_flag.is_encrypted() {
146 EncryptionMethod::None
147 } else if !bit_flag.is_strong_encryption() {
148 EncryptionMethod::Weak
149 } else {
150 EncryptionMethod::Unsupported
151 };
152
141 let mut compressed_size = cdr.compressed_size as u64; 153 let mut compressed_size = cdr.compressed_size as u64;
142 let mut size = cdr.size as u64; 154 let mut size = cdr.size as u64;
143 let mut header_pointer = cdr.header_pointer as u64; 155 let mut header_pointer = cdr.header_pointer as u64;
144 156
145 let mut mtime = dos_to_local(cdr.dos_date, cdr.dos_time)?; 157 let mut mtime = DateTime::from_dos_date_time(cdr.dos_date, cdr.dos_time, Local)?;
146 let mut atime = None; 158 let mut atime = None;
147 let mut ctime = None; 159 let mut ctime = None;
148 160
149 // Parse extensible data fields 161 // Parse extensible data fields
150 let mut ep: usize = 0; 162 for (id, mut data) in split_fields(&extra_fields).ok_or(ZipError::InvalidExtraFields)? {
151 while ep < cdr.extra_field_len as usize { 163 match id {
152 let header: ExtraHeader = deserialize(&extra_fields[ep..ep + 4]).unwrap();
153 ep += 4;
154
155 match header.id {
156 // Zip64 164 // Zip64
157 0x0001 => { 165 0x0001 => {
158 if size == 0xFFFFFFFF { 166 if size == 0xFFFFFFFF {
159 compressed_size = 167 size = u64::from_le_bytes(data.read_arr()?);
160 u64::from_le_bytes(extra_fields[ep..ep + 8].try_into().unwrap());
161 ep += 8;
162 } 168 }
163 if compressed_size == 0xFFFFFFFF { 169 if compressed_size == 0xFFFFFFFF {
164 size = u64::from_le_bytes(extra_fields[ep..ep + 8].try_into().unwrap()); 170 compressed_size = u64::from_le_bytes(data.read_arr()?);
165 ep += 8;
166 } 171 }
167 if header_pointer == 0xFFFFFFFF { 172 if header_pointer == 0xFFFFFFFF {
168 header_pointer = 173 header_pointer = u64::from_le_bytes(data.read_arr()?);
169 u64::from_le_bytes(extra_fields[ep..ep + 8].try_into().unwrap());
170 ep += 8;
171 }
172 if cdr.disk == 0xFFFF {
173 ep += 4
174 } 174 }
175 } 175 }
176 // NTFS 176 // NTFS
177 0x000a => { 177 0x000a => {
178 let mut tp = ep + 4; 178 for (id, mut data) in
179 ep += header.size as usize; 179 split_fields(&data).ok_or(ZipError::InvalidExtraFields)?
180 180 {
181 while tp < ep { 181 match id {
182 let header: ExtraHeader =
183 deserialize(&extra_fields[tp..tp + 4]).unwrap();
184 tp += 4;
185
186 match header.id {
187 0x0001 => { 182 0x0001 => {
188 mtime = ntfs_to_local(u64::from_le_bytes( 183 mtime = ntfs_to_local(u64::from_le_bytes(data.read_arr()?))
189 extra_fields[tp..tp + 8].try_into().unwrap(), 184 .unwrap_or(mtime);
190 ))?; 185 atime = ntfs_to_local(u64::from_le_bytes(data.read_arr()?));
191 tp += 8; 186 ctime = ntfs_to_local(u64::from_le_bytes(data.read_arr()?));
192 atime = Some(ntfs_to_local(u64::from_le_bytes(
193 extra_fields[tp..tp + 8].try_into().unwrap(),
194 ))?);
195 tp += 8;
196 ctime = Some(ntfs_to_local(u64::from_le_bytes(
197 extra_fields[tp..tp + 8].try_into().unwrap(),
198 ))?);
199 tp += 8;
200 }
201 _ => {
202 tp += header.size as usize;
203 } 187 }
188 _ => {}
204 } 189 }
205 } 190 }
206 } 191 }
207 // Unix 192 // Unix
208 0x000d => { 193 0x000d => {
209 atime = Some(timestamp_to_local(i32::from_le_bytes( 194 atime = timestamp_to_local(i32::from_le_bytes(data.read_arr()?));
210 extra_fields[ep..ep + 4].try_into().unwrap(), 195 mtime = timestamp_to_local(i32::from_le_bytes(data.read_arr()?))
211 ))?); 196 .unwrap_or(mtime);
212 mtime = timestamp_to_local(i32::from_le_bytes( 197 }
213 extra_fields[ep + 4..ep + 8].try_into().unwrap(), 198 // AES
214 ))?; 199 0x9901 => {
215 ep += header.size as usize 200 let aes: AesField = deserialize(&data.read_arr::<7>()?).unwrap();
201 if aes.id != 0x4541 {
202 return Err(ZipError::InvalidExtraFields);
203 }
204 encryption_method = match aes.strength {
205 0x01 => EncryptionMethod::Aes128,
206 0x02 => EncryptionMethod::Aes192,
207 0x03 => EncryptionMethod::Aes256,
208 _ => EncryptionMethod::Unsupported,
209 };
210 compression_method = aes.compression_method;
216 } 211 }
217 // Skip unrecognized header 212 // Skip unrecognized header
218 _ => ep += header.size as usize, 213 _ => {}
219 } 214 }
220 } 215 }
221 216
217 if compression_method == 99 {
218 return Err(ZipError::AesExtraFieldNotFound);
219 }
220
222 indexes.insert(name.clone(), i); 221 indexes.insert(name.clone(), i);
223 files.push(ZipFileInfo::new( 222 files.push(ZipFileInfo::new(
224 CompressionMethod::from_struct_id(cdr.compression_method)?, 223 CompressionMethod::from_struct_id(compression_method),
225 EncryptionMethod::from_bif_flag(bit_flag, cdr.crc, cdr.dos_time), 224 encryption_method,
226 bit_flag, 225 bit_flag,
227 mtime, 226 mtime,
228 atime, 227 atime,
@@ -248,15 +247,15 @@ impl<Io: Read + Seek> ArchiveRead for Zip<Io> {
248 &self.files 247 &self.files
249 } 248 }
250 249
251 fn get_file_index(&self, name: &str) -> crate::ArchiveResult<usize, Self::Error> { 250 fn get_file_index(&self, name: &str) -> ZipResult<usize> {
252 self.indexes 251 self.indexes
253 .get(name) 252 .get(name)
254 .ok_or(ZipError::FileNotFound.into()) 253 .ok_or(ZipError::FileNotFound)
255 .copied() 254 .copied()
256 } 255 }
257 256
258 fn get_file_info(&self, index: usize) -> ZipResult<&Self::FileInfo> { 257 fn get_file_info(&self, index: usize) -> ZipResult<&Self::FileInfo> {
259 self.files.get(index).ok_or(ZipError::FileNotFound.into()) 258 self.files.get(index).ok_or(ZipError::FileNotFound)
260 } 259 }
261 260
262 #[inline] 261 #[inline]
diff --git a/src/zip/encryption/aes.rs b/src/zip/encryption/aes.rs
new file mode 100644
index 0000000..6f41aaa
--- /dev/null
+++ b/src/zip/encryption/aes.rs
@@ -0,0 +1,46 @@
1use crate::utils::ReadUtils;
2use aes::cipher::generic_array::GenericArray;
3use aes::cipher::BlockEncrypt;
4use std::io::{Read, Result as IoResult};
5
6#[allow(dead_code)]
7pub struct AesDecoder<Io: Read, Aes: BlockEncrypt> {
8 io: Io,
9 aes: Aes,
10
11 counter: u128,
12 block: [u8; 16],
13 cursor: usize,
14}
15
16impl<Io: Read, Aes: BlockEncrypt> AesDecoder<Io, Aes> {
17 pub fn new(mut io: Io, aes: Aes) -> IoResult<Self> {
18 let block = io.read_arr::<16>()?;
19 let mut decoder = Self {
20 io,
21 aes,
22 counter: 1,
23 block,
24 cursor: 0,
25 };
26 decoder.decrypt_block();
27 Ok(decoder)
28 }
29
30 #[inline]
31 fn decrypt_block(&mut self) {
32 let mut mask = self.counter.to_le_bytes();
33 self.aes
34 .encrypt_block(GenericArray::from_mut_slice(&mut mask));
35 for (b, m) in self.block.iter_mut().zip(mask) {
36 *b ^= m
37 }
38 self.counter += 1;
39 }
40}
41
42impl<Io: Read, Aes: BlockEncrypt> Read for AesDecoder<Io, Aes> {
43 fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
44 todo!()
45 }
46}
diff --git a/src/zip/encryption/mod.rs b/src/zip/encryption/mod.rs
new file mode 100644
index 0000000..8cacaf9
--- /dev/null
+++ b/src/zip/encryption/mod.rs
@@ -0,0 +1,5 @@
1mod aes;
2mod weak;
3
4pub use aes::AesDecoder;
5pub use weak::{Keys, WeakDecoder};
diff --git a/src/zip/encryption.rs b/src/zip/encryption/weak.rs
index f317245..144cd53 100644
--- a/src/zip/encryption.rs
+++ b/src/zip/encryption/weak.rs
@@ -1,5 +1,3 @@
1use crate::utils::ReadUtils;
2use crate::zip::{ZipError, ZipResult};
3use std::io::{Read, Result as IoResult}; 1use std::io::{Read, Result as IoResult};
4 2
5const TABLE: [u32; 256] = generate_table(); 3const TABLE: [u32; 256] = generate_table();
@@ -32,35 +30,22 @@ fn crc32(byte: u8, crc: u32) -> u32 {
32 (crc >> 8) ^ TABLE[((crc & 0xFF) as u8 ^ byte) as usize] 30 (crc >> 8) ^ TABLE[((crc & 0xFF) as u8 ^ byte) as usize]
33} 31}
34 32
35pub struct WeakDecoder<Io: Read> { 33pub struct Keys {
36 key0: u32, 34 key0: u32,
37 key1: u32, 35 key1: u32,
38 key2: u32, 36 key2: u32,
39 io: Io,
40} 37}
41 38
42impl<Io: Read> WeakDecoder<Io> { 39impl Keys {
43 pub fn new(io: Io, check: u8, password: &[u8]) -> ZipResult<Self> { 40 pub fn new() -> Self {
44 let mut decoder = Self { 41 Self {
45 key0: 305419896, 42 key0: 305419896,
46 key1: 591751049, 43 key1: 591751049,
47 key2: 878082192, 44 key2: 878082192,
48 io,
49 };
50
51 for c in password {
52 decoder.update_keys(*c)
53 }
54
55 let buf = decoder.read_arr::<12>()?;
56 if check != buf[11] {
57 return Err(ZipError::IncorrectPassword.into());
58 } 45 }
59
60 Ok(decoder)
61 } 46 }
62 47
63 fn update_keys(&mut self, byte: u8) { 48 fn update(&mut self, byte: u8) {
64 self.key0 = crc32(byte, self.key0); 49 self.key0 = crc32(byte, self.key0);
65 self.key1 = self 50 self.key1 = self
66 .key1 51 .key1
@@ -70,19 +55,50 @@ impl<Io: Read> WeakDecoder<Io> {
70 self.key2 = crc32((self.key1 >> 24) as u8, self.key2); 55 self.key2 = crc32((self.key1 >> 24) as u8, self.key2);
71 } 56 }
72 57
73 fn decode_byte(&mut self, byte: u8) -> u8 { 58 pub fn set_password(&mut self, passwd: &[u8]) {
59 for b in passwd {
60 self.update(*b)
61 }
62 }
63
64 pub fn set_header(&mut self, header: [u8; 12]) -> u8 {
65 for b in &header[..11] {
66 self.decode_byte(*b);
67 }
68 self.decode_byte(header[11])
69 }
70
71 #[allow(dead_code)]
72 pub fn encode_bytes(&mut self, byte: u8) -> u8 {
73 let key = self.key2 | 2;
74 self.update(byte);
75 byte ^ ((key.wrapping_mul(key ^ 1)) >> 8) as u8
76 }
77
78 pub fn decode_byte(&mut self, byte: u8) -> u8 {
74 let key = self.key2 | 2; 79 let key = self.key2 | 2;
75 let byte = byte ^ ((key.wrapping_mul(key ^ 1)) >> 8) as u8; 80 let byte = byte ^ ((key.wrapping_mul(key ^ 1)) >> 8) as u8;
76 self.update_keys(byte); 81 self.update(byte);
77 byte 82 byte
78 } 83 }
79} 84}
80 85
86pub struct WeakDecoder<Io: Read> {
87 io: Io,
88 keys: Keys,
89}
90
91impl<Io: Read> WeakDecoder<Io> {
92 pub fn new(io: Io, keys: Keys) -> Self {
93 WeakDecoder { io, keys }
94 }
95}
96
81impl<Io: Read> Read for WeakDecoder<Io> { 97impl<Io: Read> Read for WeakDecoder<Io> {
82 fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> { 98 fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
83 let bytes = self.io.read(buf)?; 99 let bytes = self.io.read(buf)?;
84 for i in 0..bytes { 100 for i in 0..bytes {
85 buf[i] = self.decode_byte(buf[i]); 101 buf[i] = self.keys.decode_byte(buf[i]);
86 } 102 }
87 Ok(bytes) 103 Ok(bytes)
88 } 104 }
diff --git a/src/zip/error.rs b/src/zip/error.rs
index 5573cf8..5508177 100644
--- a/src/zip/error.rs
+++ b/src/zip/error.rs
@@ -1,22 +1,25 @@
1use crate::{ArchiveError, ArchiveResult};
2use std::error::Error; 1use std::error::Error;
3use std::fmt::Display; 2use std::fmt::Display;
3use std::io::Error as IoError;
4 4
5pub type ZipResult<T> = ArchiveResult<T, ZipError>; 5pub type ZipResult<T> = Result<T, ZipError>;
6 6
7#[derive(Debug, PartialEq, Eq)] 7#[derive(Debug)]
8pub enum ZipError { 8pub enum ZipError {
9 Io(IoError),
10
9 EocdrNotFound, 11 EocdrNotFound,
10 InvalidEOCDR64Signature, 12 InvalidEOCDR64Signature,
11 InvalidFileHeaderSignature, 13 InvalidFileHeaderSignature,
12 InvalidCDRSignature, 14 InvalidCDRSignature,
13 15
14 InvalidCompressionMethod(u16),
15 UnsupportedCompressionMethod(u16), 16 UnsupportedCompressionMethod(u16),
16 UnsupportedEncryptionMethod, 17 UnsupportedEncryptionMethod,
17 InvalidDate, 18 InvalidDate,
18 InvalidTime, 19 InvalidTime,
19 InvalidFileName, 20 InvalidFileName,
21 InvalidExtraFields,
22 AesExtraFieldNotFound,
20 InvalidFileComment, 23 InvalidFileComment,
21 24
22 FileNotFound, 25 FileNotFound,
@@ -26,18 +29,30 @@ pub enum ZipError {
26 EncryptedDataIsUnseekable, 29 EncryptedDataIsUnseekable,
27} 30}
28 31
29impl From<ZipError> for ArchiveError<ZipError> { 32impl From<IoError> for ZipError {
30 fn from(value: ZipError) -> Self { 33 fn from(value: IoError) -> Self {
31 Self::Archivator { 34 Self::Io(value)
32 module: "Zip", 35 }
33 error: value, 36}
37
38impl PartialEq for ZipError {
39 fn eq(&self, other: &Self) -> bool {
40 match (self, other) {
41 (Self::Io(l0), Self::Io(r0)) => l0.kind() == r0.kind(),
42 (Self::UnsupportedCompressionMethod(l0), Self::UnsupportedCompressionMethod(r0)) => {
43 l0 == r0
44 }
45 _ => core::mem::discriminant(self) == core::mem::discriminant(other),
34 } 46 }
35 } 47 }
36} 48}
37 49
50impl Eq for ZipError {}
51
38impl Display for ZipError { 52impl Display for ZipError {
39 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 53 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40 match self { 54 match self {
55 Self::Io(error) => write!(f, "{}", error),
41 Self::EocdrNotFound => write!(f, "End of central directory record not found"), 56 Self::EocdrNotFound => write!(f, "End of central directory record not found"),
42 Self::InvalidEOCDR64Signature => { 57 Self::InvalidEOCDR64Signature => {
43 write!( 58 write!(
@@ -52,9 +67,6 @@ impl Display for ZipError {
52 write!(f, "Invalid signature of central directory record") 67 write!(f, "Invalid signature of central directory record")
53 } 68 }
54 69
55 Self::InvalidCompressionMethod(id) => {
56 writeln!(f, "Invalid compression method {}", id)
57 }
58 Self::UnsupportedCompressionMethod(id) => { 70 Self::UnsupportedCompressionMethod(id) => {
59 writeln!(f, "Unsupported compression method {}", id) 71 writeln!(f, "Unsupported compression method {}", id)
60 } 72 }
@@ -64,6 +76,8 @@ impl Display for ZipError {
64 Self::InvalidDate => write!(f, "Invalid date"), 76 Self::InvalidDate => write!(f, "Invalid date"),
65 Self::InvalidTime => write!(f, "Invalid time"), 77 Self::InvalidTime => write!(f, "Invalid time"),
66 Self::InvalidFileName => write!(f, "Invalid file name"), 78 Self::InvalidFileName => write!(f, "Invalid file name"),
79 Self::InvalidExtraFields => write!(f, "Invalid extra fields"),
80 Self::AesExtraFieldNotFound => write!(f, "Aes extra field not found"),
67 Self::InvalidFileComment => write!(f, "Invalid file comment"), 81 Self::InvalidFileComment => write!(f, "Invalid file comment"),
68 82
69 Self::FileNotFound => write!(f, "File not found"), 83 Self::FileNotFound => write!(f, "File not found"),
diff --git a/src/zip/file/info.rs b/src/zip/file/info.rs
index 599dcc3..38ea984 100644
--- a/src/zip/file/info.rs
+++ b/src/zip/file/info.rs
@@ -1,5 +1,5 @@
1use crate::driver::ArchiveFileInfo; 1use crate::driver::ArchiveFileInfo;
2use crate::zip::{ZipError, ZipResult}; 2use crate::zip::datetime::DosDateTime;
3use chrono::{DateTime, Local}; 3use chrono::{DateTime, Local};
4 4
5#[derive(Debug, PartialEq, Eq, Clone)] 5#[derive(Debug, PartialEq, Eq, Clone)]
@@ -14,42 +14,30 @@ pub enum CompressionMethod {
14} 14}
15 15
16impl CompressionMethod { 16impl CompressionMethod {
17 pub(crate) fn from_struct_id(id: u16) -> ZipResult<Self> { 17 #[inline]
18 Ok(match id { 18 pub(crate) fn from_struct_id(id: u16) -> Self {
19 match id {
19 0 => Self::Store, 20 0 => Self::Store,
20 8 => Self::Deflate, 21 8 => Self::Deflate,
21 12 => Self::BZip2, 22 12 => Self::BZip2,
22 14 => Self::Lzma, 23 14 => Self::Lzma,
23 93 => Self::Zstd, 24 93 => Self::Zstd,
24 95 => Self::Xz, 25 95 => Self::Xz,
25 1..=7 | 9..=11 | 13 | 15..=20 | 94 | 96..=99 => Self::Unsupported(id), 26 _ => Self::Unsupported(id),
26 21..=92 | 100.. => return Err(ZipError::InvalidCompressionMethod(id).into()), 27 }
27 })
28 } 28 }
29} 29}
30 30
31#[derive(Debug, PartialEq, Eq, Clone)] 31#[derive(Debug, PartialEq, Eq, Clone)]
32pub enum EncryptionMethod { 32pub enum EncryptionMethod {
33 None, 33 None,
34 Weak(u8), 34 Weak, // ZipCrypto
35 Aes128, // WinZip encryption
36 Aes192,
37 Aes256,
35 Unsupported, 38 Unsupported,
36} 39}
37 40
38impl EncryptionMethod {
39 pub(crate) fn from_bif_flag(bit_flag: BitFlag, crc: u32, dos_time: u16) -> EncryptionMethod {
40 match (bit_flag.is_encrypted(), bit_flag.is_strong_encryption()) {
41 (false, false) => EncryptionMethod::None,
42 (true, false) => EncryptionMethod::Weak(if bit_flag.is_has_data_descriptor() {
43 (dos_time >> 8) as u8 // Info-ZIP modification
44 } else {
45 (crc >> 24) as u8
46 }),
47 (true, true) => EncryptionMethod::Unsupported,
48 _ => panic!("impossible"),
49 }
50 }
51}
52
53#[derive(Debug, PartialEq, Eq, Clone, Copy)] 41#[derive(Debug, PartialEq, Eq, Clone, Copy)]
54pub struct BitFlag { 42pub struct BitFlag {
55 flag: u16, 43 flag: u16,
@@ -159,7 +147,7 @@ pub struct ZipFileInfo {
159} 147}
160 148
161impl ZipFileInfo { 149impl ZipFileInfo {
162 pub fn new( 150 pub(crate) fn new(
163 compression_method: CompressionMethod, 151 compression_method: CompressionMethod,
164 encryption_method: EncryptionMethod, 152 encryption_method: EncryptionMethod,
165 bit_flag: BitFlag, 153 bit_flag: BitFlag,
@@ -189,6 +177,15 @@ impl ZipFileInfo {
189 } 177 }
190 } 178 }
191 179
180 #[inline]
181 pub(crate) fn password_check(&self) -> u8 {
182 if self.bit_flag.is_has_data_descriptor() {
183 (self.mtime.to_dos_time() >> 8) as u8
184 } else {
185 (self.crc >> 24) as u8
186 }
187 }
188
192 pub fn is_dir(&self) -> bool { 189 pub fn is_dir(&self) -> bool {
193 self.name.ends_with("/") 190 self.name.ends_with("/")
194 } 191 }
diff --git a/src/zip/file/read.rs b/src/zip/file/read.rs
index c26b304..80bdcfb 100644
--- a/src/zip/file/read.rs
+++ b/src/zip/file/read.rs
@@ -1,11 +1,16 @@
1use crate::driver::FileDriver; 1use crate::driver::FileDriver;
2use crate::utils::{IoCursor, ReadUtils}; 2use crate::utils::{IoCursor, ReadUtils};
3use crate::zip::encryption::WeakDecoder; 3use crate::zip::encryption::{AesDecoder, Keys, WeakDecoder};
4use crate::zip::structs::FILE_HEADER_SIGNATURE;
4use crate::zip::{CompressionMethod, EncryptionMethod, ZipError, ZipFileInfo, ZipResult}; 5use crate::zip::{CompressionMethod, EncryptionMethod, ZipError, ZipFileInfo, ZipResult};
6use aes::cipher::KeyInit;
7use aes::{Aes128, Aes192, Aes256};
5use bzip2::read::BzDecoder; 8use bzip2::read::BzDecoder;
6use flate2::read::DeflateDecoder; 9use flate2::read::DeflateDecoder;
7use liblzma::read::XzDecoder; 10use liblzma::read::XzDecoder;
8use liblzma::stream::{Filters, LzmaOptions, Stream}; 11use liblzma::stream::{Filters, LzmaOptions, Stream};
12use pbkdf2::pbkdf2_hmac_array;
13use sha1::Sha1;
9use std::io::{ 14use std::io::{
10 BufReader, Error as IoError, ErrorKind as IoErrorKind, Read, Result as IoResult, Seek, SeekFrom, 15 BufReader, Error as IoError, ErrorKind as IoErrorKind, Read, Result as IoResult, Seek, SeekFrom,
11}; 16};
@@ -14,34 +19,101 @@ use zstd::stream::Decoder as ZstdDecoder;
14enum Encryption<Io: Read> { 19enum Encryption<Io: Read> {
15 None(Io), 20 None(Io),
16 Weak(WeakDecoder<Io>), 21 Weak(WeakDecoder<Io>),
22 Aes128(AesDecoder<Io, Aes128>),
23 Aes192(AesDecoder<Io, Aes192>),
24 Aes256(AesDecoder<Io, Aes256>),
17} 25}
18 26
19impl<Io: Read> Encryption<Io> { 27impl<Io: Read> Encryption<Io> {
20 pub fn new(io: Io, info: &ZipFileInfo, password: Option<&[u8]>) -> ZipResult<Self> { 28 #[inline]
29 pub fn new(mut io: Io, info: &ZipFileInfo, password: Option<&[u8]>) -> ZipResult<Self> {
21 Ok(match info.encryption_method { 30 Ok(match info.encryption_method {
22 EncryptionMethod::None => Self::None(io), 31 EncryptionMethod::None => Self::None(io),
23 EncryptionMethod::Weak(check) => Self::Weak(WeakDecoder::new( 32 EncryptionMethod::Weak => {
24 io, 33 let mut keys = Keys::new();
25 check, 34 keys.set_password(password.ok_or(ZipError::PasswordIsNotSpecified)?);
26 password.ok_or(ZipError::PasswordIsNotSpecified)?, 35
27 )?), 36 let check = keys.set_header(io.read_arr()?);
28 EncryptionMethod::Unsupported => { 37 if check != info.password_check() {
29 return Err(ZipError::UnsupportedEncryptionMethod.into()) 38 return Err(ZipError::IncorrectPassword);
39 }
40
41 Self::Weak(WeakDecoder::new(io, keys))
42 }
43 EncryptionMethod::Aes128 => {
44 let header = io.read_arr::<10>()?;
45 let salt = &header[..8];
46 let check = &header[8..];
47
48 let hash = pbkdf2_hmac_array::<Sha1, 34>(
49 password.ok_or(ZipError::PasswordIsNotSpecified)?,
50 salt,
51 1000,
52 );
53 let key = &hash[..16];
54
55 if check != &hash[32..] {
56 return Err(ZipError::IncorrectPassword);
57 }
58
59 Self::Aes128(AesDecoder::new(io, Aes128::new(key.into()))?)
60 }
61 EncryptionMethod::Aes192 => {
62 let header = io.read_arr::<14>()?;
63 let salt = &header[..12];
64 let check = &header[12..];
65
66 let hash = pbkdf2_hmac_array::<Sha1, 50>(
67 password.ok_or(ZipError::PasswordIsNotSpecified)?,
68 salt,
69 1000,
70 );
71 let key = &hash[..24];
72
73 if check != &hash[48..] {
74 return Err(ZipError::IncorrectPassword);
75 }
76
77 Self::Aes192(AesDecoder::new(io, Aes192::new(key.into()))?)
78 }
79 EncryptionMethod::Aes256 => {
80 let header = io.read_arr::<18>()?;
81 let salt = &header[..16];
82 let check = &header[16..];
83
84 let hash = pbkdf2_hmac_array::<Sha1, 66>(
85 password.ok_or(ZipError::PasswordIsNotSpecified)?,
86 salt,
87 1000,
88 );
89 let key = &hash[..32];
90
91 if check != &hash[64..] {
92 return Err(ZipError::IncorrectPassword);
93 }
94
95 Self::Aes256(AesDecoder::new(io, Aes256::new(key.into()))?)
30 } 96 }
97 EncryptionMethod::Unsupported => return Err(ZipError::UnsupportedEncryptionMethod),
31 }) 98 })
32 } 99 }
33} 100}
34 101
35impl<Io: Read> Read for Encryption<Io> { 102impl<Io: Read> Read for Encryption<Io> {
103 #[inline]
36 fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> { 104 fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
37 match self { 105 match self {
38 Self::None(io) => io.read(buf), 106 Self::None(io) => io.read(buf),
39 Self::Weak(io) => io.read(buf), 107 Self::Weak(io) => io.read(buf),
108 Self::Aes128(io) => io.read(buf),
109 Self::Aes192(io) => io.read(buf),
110 Self::Aes256(io) => io.read(buf),
40 } 111 }
41 } 112 }
42} 113}
43 114
44impl<Io: Read + Seek> Seek for Encryption<Io> { 115impl<Io: Read + Seek> Seek for Encryption<Io> {
116 #[inline]
45 fn seek(&mut self, pos: SeekFrom) -> IoResult<u64> { 117 fn seek(&mut self, pos: SeekFrom) -> IoResult<u64> {
46 match self { 118 match self {
47 Self::None(io) => io.seek(pos), 119 Self::None(io) => io.seek(pos),
@@ -62,6 +134,7 @@ enum Compression<Io: Read> {
62} 134}
63 135
64impl<Io: Read + Seek> Compression<Io> { 136impl<Io: Read + Seek> Compression<Io> {
137 #[inline]
65 pub fn new(mut io: Io, info: &ZipFileInfo) -> ZipResult<Self> { 138 pub fn new(mut io: Io, info: &ZipFileInfo) -> ZipResult<Self> {
66 Ok(match info.compression_method { 139 Ok(match info.compression_method {
67 CompressionMethod::Store => Self::Store(io), 140 CompressionMethod::Store => Self::Store(io),
@@ -88,13 +161,14 @@ impl<Io: Read + Seek> Compression<Io> {
88 CompressionMethod::Zstd => Self::Zstd(ZstdDecoder::new(io)?), 161 CompressionMethod::Zstd => Self::Zstd(ZstdDecoder::new(io)?),
89 CompressionMethod::Xz => Self::Xz(XzDecoder::new(io)), 162 CompressionMethod::Xz => Self::Xz(XzDecoder::new(io)),
90 CompressionMethod::Unsupported(id) => { 163 CompressionMethod::Unsupported(id) => {
91 return Err(ZipError::UnsupportedCompressionMethod(id).into()) 164 return Err(ZipError::UnsupportedCompressionMethod(id))
92 } 165 }
93 }) 166 })
94 } 167 }
95} 168}
96 169
97impl<Io: Read> Read for Compression<Io> { 170impl<Io: Read> Read for Compression<Io> {
171 #[inline]
98 fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> { 172 fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
99 match self { 173 match self {
100 Compression::Store(io) => io.read(buf), 174 Compression::Store(io) => io.read(buf),
@@ -107,6 +181,7 @@ impl<Io: Read> Read for Compression<Io> {
107} 181}
108 182
109impl<Io: Read + Seek> Seek for Compression<Io> { 183impl<Io: Read + Seek> Seek for Compression<Io> {
184 #[inline]
110 fn seek(&mut self, pos: SeekFrom) -> IoResult<u64> { 185 fn seek(&mut self, pos: SeekFrom) -> IoResult<u64> {
111 match self { 186 match self {
112 Compression::Store(io) => io.seek(pos), 187 Compression::Store(io) => io.seek(pos),
@@ -137,8 +212,8 @@ impl<'d, Io: Read + Seek> ZipFileReader<'d, Io> {
137 io.seek(SeekFrom::Start(info.header_pointer))?; 212 io.seek(SeekFrom::Start(info.header_pointer))?;
138 213
139 let buf = io.read_arr::<30>()?; 214 let buf = io.read_arr::<30>()?;
140 if u32::from_le_bytes(buf[..4].try_into().unwrap()) != 0x04034b50 { 215 if buf[..4] != FILE_HEADER_SIGNATURE {
141 return Err(ZipError::InvalidFileHeaderSignature.into()); 216 return Err(ZipError::InvalidFileHeaderSignature);
142 } 217 }
143 let data_pointer = info.header_pointer 218 let data_pointer = info.header_pointer
144 + 30 219 + 30
diff --git a/src/zip/mod.rs b/src/zip/mod.rs
index fcc6161..488c06d 100644
--- a/src/zip/mod.rs
+++ b/src/zip/mod.rs
@@ -1,5 +1,6 @@
1mod archive; 1mod archive;
2mod cp437; 2mod cp437;
3mod datetime;
3mod driver; 4mod driver;
4mod encryption; 5mod encryption;
5mod error; 6mod error;
diff --git a/src/zip/structs.rs b/src/zip/structs.rs
index ebecae7..8b25400 100644
--- a/src/zip/structs.rs
+++ b/src/zip/structs.rs
@@ -1,6 +1,9 @@
1use crate::structs::{ByteOrder, Settings, StructResult, VariantIndexType}; 1use crate::structs::{ByteOrder, Settings, StructResult, VariantIndexType};
2use serde::{Deserialize, Serialize}; 2use serde::{Deserialize, Serialize};
3 3
4pub const FILE_HEADER_SIGNATURE: [u8; 4] = [0x50, 0x4b, 0x03, 0x04];
5
6pub const EOCDR_SIGNATURE: [u8; 4] = [0x50, 0x4b, 0x05, 0x06];
4#[derive(Serialize, Deserialize)] 7#[derive(Serialize, Deserialize)]
5pub struct Eocdr { 8pub struct Eocdr {
6 pub eocdr_disk: u16, 9 pub eocdr_disk: u16,
@@ -12,6 +15,7 @@ pub struct Eocdr {
12 pub comment_len: u16, 15 pub comment_len: u16,
13} 16}
14 17
18pub const EOCDR64_LOCATOR_SIGNATURE: [u8; 4] = [0x50, 0x4b, 0x06, 0x07];
15#[derive(Serialize, Deserialize)] 19#[derive(Serialize, Deserialize)]
16pub struct Eocdr64Locator { 20pub struct Eocdr64Locator {
17 pub eocdr64_disk: u32, 21 pub eocdr64_disk: u32,
@@ -19,6 +23,7 @@ pub struct Eocdr64Locator {
19 pub disks: u32, 23 pub disks: u32,
20} 24}
21 25
26pub const EOCDR64_SIGNATURE: [u8; 4] = [0x50, 0x4b, 0x06, 0x06];
22#[derive(Serialize, Deserialize)] 27#[derive(Serialize, Deserialize)]
23pub struct Eocdr64 { 28pub struct Eocdr64 {
24 pub eocdr64_size: u64, 29 pub eocdr64_size: u64,
@@ -32,6 +37,7 @@ pub struct Eocdr64 {
32 pub cd_pointer: u64, 37 pub cd_pointer: u64,
33} 38}
34 39
40pub const CDR_SIGNATURE: [u8; 4] = [0x50, 0x4b, 0x01, 0x02];
35#[derive(Serialize, Deserialize)] 41#[derive(Serialize, Deserialize)]
36pub struct Cdr { 42pub struct Cdr {
37 pub version: u16, 43 pub version: u16,
@@ -58,6 +64,14 @@ pub struct ExtraHeader {
58 pub size: u16, 64 pub size: u16,
59} 65}
60 66
67#[derive(Serialize, Deserialize)]
68pub struct AesField {
69 pub version: u16,
70 pub id: u16,
71 pub strength: u8,
72 pub compression_method: u16,
73}
74
61#[inline] 75#[inline]
62#[allow(dead_code)] 76#[allow(dead_code)]
63pub fn serialize<T: Serialize>(object: &mut T) -> StructResult<Vec<u8>> { 77pub fn serialize<T: Serialize>(object: &mut T) -> StructResult<Vec<u8>> {
diff --git a/src/zip/tests.rs b/src/zip/tests.rs
index e24cdfe..a8ac634 100644
--- a/src/zip/tests.rs
+++ b/src/zip/tests.rs
@@ -1,5 +1,7 @@
1use crate::zip::cp437::{from_char, is_cp437, to_char, FromCp437}; 1use crate::zip::cp437::{from_char, is_cp437, to_char, FromCp437};
2use crate::zip::datetime::DosDateTime;
2use crate::zip::{bit::DeflateMode, BitFlag}; 3use crate::zip::{bit::DeflateMode, BitFlag};
4use chrono::{DateTime, Local, TimeZone};
3 5
4#[test] 6#[test]
5fn test_bit_flag() { 7fn test_bit_flag() {
@@ -74,3 +76,20 @@ fn test_cp437() {
74 "abcdefghijklmnopqrstuvwxyz" 76 "abcdefghijklmnopqrstuvwxyz"
75 ); 77 );
76} 78}
79
80#[test]
81fn test_dos_datetime() {
82 let datetime = Local.with_ymd_and_hms(2001, 2, 3, 4, 5, 6).unwrap();
83 assert_eq!(
84 DateTime::from_dos_date_time(datetime.to_dos_date(), datetime.to_dos_time(), Local)
85 .unwrap(),
86 datetime
87 );
88
89 let datetime = Local.with_ymd_and_hms(1999, 9, 9, 9, 9, 9).unwrap();
90 assert_eq!(
91 DateTime::from_dos_date_time(datetime.to_dos_date(), datetime.to_dos_time(), Local)
92 .unwrap(),
93 Local.with_ymd_and_hms(1999, 9, 9, 9, 9, 8).unwrap()
94 ); // Dos format stores seconds with an accuracy of 2s
95}