zip/extra_fields/
extended_timestamp.rs1use crate::result::invalid;
2use crate::result::{ZipError, ZipResult};
3use crate::unstable::LittleEndianReadExt;
4use std::io::Read;
5
6#[derive(Debug, Clone)]
9pub struct ExtendedTimestamp {
10 mod_time: Option<u32>,
11 ac_time: Option<u32>,
12 cr_time: Option<u32>,
13}
14
15impl ExtendedTimestamp {
16 pub fn try_from_reader<R>(reader: &mut R, len: u16) -> ZipResult<Self>
21 where
22 R: Read,
23 {
24 if len == 0 {
25 return Err(invalid!("Extended timestamp field is empty"));
26 }
27 let mut flags = [0u8];
28 let mut bytes_to_read = len as usize;
29 reader.read_exact(&mut flags)?;
30 bytes_to_read -= flags.len();
31 let flags = flags[0];
32
33 if len != 5 && len as u32 != 1 + 4 * flags.count_ones() {
43 return Err(ZipError::UnsupportedArchive(
45 "flags and len don't match in extended timestamp field",
46 ));
47 }
48
49 let mod_time = if (flags & 0b00000001u8 == 0b00000001u8) || len == 5 {
52 bytes_to_read -= size_of::<u32>();
53 Some(reader.read_u32_le()?)
54 } else {
55 None
56 };
57
58 let ac_time = if flags & 0b00000010u8 == 0b00000010u8 && len > 5 {
59 bytes_to_read -= size_of::<u32>();
60 Some(reader.read_u32_le()?)
61 } else {
62 None
63 };
64
65 let cr_time = if flags & 0b00000100u8 == 0b00000100u8 && len > 5 {
66 bytes_to_read -= size_of::<u32>();
67 Some(reader.read_u32_le()?)
68 } else {
69 None
70 };
71
72 if bytes_to_read > 0 {
73 reader.read_exact(&mut vec![0; bytes_to_read])?
75 }
76
77 Ok(Self {
78 mod_time,
79 ac_time,
80 cr_time,
81 })
82 }
83
84 pub fn mod_time(&self) -> Option<u32> {
86 self.mod_time
87 }
88
89 pub fn ac_time(&self) -> Option<u32> {
91 self.ac_time
92 }
93
94 pub fn cr_time(&self) -> Option<u32> {
96 self.cr_time
97 }
98}
99
100#[test]
101pub fn test_bad_extended_timestamp() -> ZipResult<()> {
103 use crate::ZipArchive;
104 use std::io::Cursor;
105
106 assert!(ZipArchive::new(Cursor::new(include_bytes!(
107 "../../tests/data/extended_timestamp_bad.zip"
108 )))
109 .is_err());
110 Ok(())
111}