use crate::result::invalid;
use crate::result::{ZipError, ZipResult};
use crate::unstable::LittleEndianReadExt;
use std::io::Read;
#[derive(Debug, Clone)]
pub struct ExtendedTimestamp {
mod_time: Option<u32>,
ac_time: Option<u32>,
cr_time: Option<u32>,
}
impl ExtendedTimestamp {
pub fn try_from_reader<R>(reader: &mut R, len: u16) -> ZipResult<Self>
where
R: Read,
{
if len == 0 {
return Err(invalid!("Extended timestamp field is empty"));
}
let mut flags = [0u8];
let mut bytes_to_read = len as usize;
reader.read_exact(&mut flags)?;
bytes_to_read -= flags.len();
let flags = flags[0];
if len != 5 && u32::from(len) != 1 + 4 * flags.count_ones() {
return Err(ZipError::Io(std::io::Error::other(format!(
"flags and len don't match in extended timestamp field len={len} flags={flags:08b}"
))));
}
let mod_time = if (flags & 0b0000_0001_u8 == 0b0000_0001_u8) || len == 5 {
bytes_to_read -= size_of::<u32>();
Some(reader.read_u32_le()?)
} else {
None
};
let ac_time = if flags & 0b0000_0010_u8 == 0b0000_0010_u8 && len > 5 {
bytes_to_read -= size_of::<u32>();
Some(reader.read_u32_le()?)
} else {
None
};
let cr_time = if flags & 0b0000_0100_u8 == 0b0000_0100_u8 && len > 5 {
bytes_to_read -= size_of::<u32>();
Some(reader.read_u32_le()?)
} else {
None
};
if bytes_to_read > 0 {
reader.read_exact(&mut vec![0; bytes_to_read])?;
}
Ok(Self {
mod_time,
ac_time,
cr_time,
})
}
#[must_use]
pub fn mod_time(&self) -> Option<u32> {
self.mod_time
}
#[must_use]
pub fn ac_time(&self) -> Option<u32> {
self.ac_time
}
#[must_use]
pub fn cr_time(&self) -> Option<u32> {
self.cr_time
}
}
#[cfg(test)]
mod test {
#[test]
pub fn test_bad_extended_timestamp() {
use crate::ZipArchive;
use std::io::Cursor;
assert!(
ZipArchive::new(Cursor::new(include_bytes!(
"../../tests/data/extended_timestamp_bad.zip"
)))
.is_err()
);
}
}