zip/extra_fields/
extended_timestamp.rs

1use crate::result::{ZipError, ZipResult};
2use crate::unstable::LittleEndianReadExt;
3use std::io::Read;
4
5/// extended timestamp, as described in <https://libzip.org/specifications/extrafld.txt>
6
7#[derive(Debug, Clone)]
8pub struct ExtendedTimestamp {
9    mod_time: Option<u32>,
10    ac_time: Option<u32>,
11    cr_time: Option<u32>,
12}
13
14impl ExtendedTimestamp {
15    /// creates an extended timestamp struct by reading the required bytes from the reader.
16    ///
17    /// This method assumes that the length has already been read, therefore
18    /// it must be passed as an argument
19    pub fn try_from_reader<R>(reader: &mut R, len: u16) -> ZipResult<Self>
20    where
21        R: Read,
22    {
23        let mut flags = [0u8];
24        reader.read_exact(&mut flags)?;
25        let flags = flags[0];
26
27        // the `flags` field refers to the local headers and might not correspond
28        // to the len field. If the length field is 1+4, we assume that only
29        // the modification time has been set
30
31        // > Those times that are present will appear in the order indicated, but
32        // > any combination of times may be omitted.  (Creation time may be
33        // > present without access time, for example.)  TSize should equal
34        // > (1 + 4*(number of set bits in Flags)), as the block is currently
35        // > defined.
36        if len != 5 && len as u32 != 1 + 4 * flags.count_ones() {
37            //panic!("found len {len} and flags {flags:08b}");
38            return Err(ZipError::UnsupportedArchive(
39                "flags and len don't match in extended timestamp field",
40            ));
41        }
42
43        if flags & 0b11111000 != 0 {
44            return Err(ZipError::UnsupportedArchive(
45                "found unsupported timestamps in the extended timestamp header",
46            ));
47        }
48
49        let mod_time = if (flags & 0b00000001u8 == 0b00000001u8) || len == 5 {
50            Some(reader.read_u32_le()?)
51        } else {
52            None
53        };
54
55        let ac_time = if flags & 0b00000010u8 == 0b00000010u8 && len > 5 {
56            Some(reader.read_u32_le()?)
57        } else {
58            None
59        };
60
61        let cr_time = if flags & 0b00000100u8 == 0b00000100u8 && len > 5 {
62            Some(reader.read_u32_le()?)
63        } else {
64            None
65        };
66        Ok(Self {
67            mod_time,
68            ac_time,
69            cr_time,
70        })
71    }
72
73    /// returns the last modification timestamp, if defined, as UNIX epoch seconds
74    pub fn mod_time(&self) -> Option<u32> {
75        self.mod_time
76    }
77
78    /// returns the last access timestamp, if defined, as UNIX epoch seconds
79    pub fn ac_time(&self) -> Option<u32> {
80        self.ac_time
81    }
82
83    /// returns the creation timestamp, if defined, as UNIX epoch seconds
84    pub fn cr_time(&self) -> Option<u32> {
85        self.cr_time
86    }
87}