#![allow(unused)]
use std::fmt;
use crate::utils::Timestamp;
#[derive(Clone)]
struct DataParser<'a>(&'a [u8]);
impl<'a> DataParser<'a> {
fn read_u8(&mut self) -> Option<u8> {
let (x, data) = self.0.split_first()?;
self.0 = data;
Some(*x)
}
fn read_u16(&mut self) -> Option<u16> {
let (bytes, data) = self.0.split_first_chunk()?;
self.0 = data;
Some(u16::from_le_bytes(*bytes))
}
fn read_u32(&mut self) -> Option<u32> {
let (bytes, data) = self.0.split_first_chunk()?;
self.0 = data;
Some(u32::from_le_bytes(*bytes))
}
fn read_u64(&mut self) -> Option<u64> {
let (bytes, data) = self.0.split_first_chunk()?;
self.0 = data;
Some(u64::from_le_bytes(*bytes))
}
fn read_variable(&mut self, len: usize) -> Option<&'a [u8]> {
let (bytes, data) = self.0.split_at_checked(len)?;
self.0 = data;
Some(bytes)
}
fn end(self) -> Option<()> {
if self.0.is_empty() { Some(()) } else { None }
}
}
#[derive(Clone)]
pub(crate) struct ExtraFields<'a>(pub &'a [u8]);
impl ExtraFields<'_> {
pub fn iter(&self) -> ExtraFieldIterator<'_> {
ExtraFieldIterator(DataParser(self.0))
}
}
impl fmt::Debug for ExtraFields<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
struct FromFn<F: Fn(&mut fmt::Formatter<'_>) -> fmt::Result>(F);
impl<F: Fn(&mut fmt::Formatter<'_>) -> fmt::Result> fmt::Debug for FromFn<F> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
(self.0)(f)
}
}
f.debug_list()
.entries(self.iter().map(|e| FromFn(move |f| write!(f, "{e:?}"))))
.finish()
}
}
pub struct ExtraFieldIterator<'a>(DataParser<'a>);
impl<'a> Iterator for ExtraFieldIterator<'a> {
type Item = ExtraField<'a>;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
ExtraField::parse(&mut self.0)
}
}
#[derive(Debug, Clone)]
pub enum ExtraField<'a> {
Zip64ExtendedInformation(Zip64ExtendedInformation<'a>),
Ntfs(Ntfs),
ExtendedTimestamp(ExtendedTimestamp),
UnicodeComment(UnicodeComment<'a>),
UnicodeName(UnicodeName<'a>),
UnixNew(UnixNew<'a>),
Aes(Aes),
Unknown,
Invalid,
}
impl<'a> ExtraField<'a> {
fn parse(extra_data: &mut DataParser<'a>) -> Option<Self> {
if matches!(extra_data.0, [] | [0] | [0, 0] | [0, 0, 0]) {
return None;
}
let field = (|| {
let id = extra_data.read_u16()?;
let len = extra_data.read_u16()?;
let data = DataParser(extra_data.read_variable(len as usize)?);
Some(match id {
0x0001 => {
ExtraField::Zip64ExtendedInformation(Zip64ExtendedInformation::parse(data)?)
}
0x000a => ExtraField::Ntfs(Ntfs::parse(data)?),
0x5455 => ExtraField::ExtendedTimestamp(ExtendedTimestamp::parse(data)?),
0x6375 => ExtraField::UnicodeComment(UnicodeComment::parse(data)?),
0x7075 => ExtraField::UnicodeName(UnicodeName::parse(data)?),
0x7875 => ExtraField::UnixNew(UnixNew::parse(data)?),
0x9901 => ExtraField::Aes(Aes::parse(data)?),
_ => ExtraField::Unknown,
})
})();
Some(field.unwrap_or(ExtraField::Invalid))
}
}
#[derive(Clone)]
pub struct Zip64ExtendedInformation<'a> {
info: DataParser<'a>,
}
impl<'a> Zip64ExtendedInformation<'a> {
fn parse(data: DataParser<'a>) -> Option<Self> {
Some(Self { info: data })
}
pub(crate) fn next(&mut self) -> Option<u64> {
self.info.read_u64()
}
pub(crate) fn end(self) -> Option<()> {
self.info.end()
}
}
impl<'a> fmt::Debug for Zip64ExtendedInformation<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Zip64ExtendedInformation")
.field("info", &self.info.0)
.finish()
}
}
#[derive(Default, Debug, Clone)]
pub struct NtfsTimes {
pub mtime: Option<Timestamp>,
pub atime: Option<Timestamp>,
pub ctime: Option<Timestamp>,
}
#[derive(Default, Debug, Clone)]
pub struct Ntfs {
pub times: NtfsTimes,
}
impl Ntfs {
fn parse(mut data: DataParser<'_>) -> Option<Self> {
let _reserved = data.read_u32()?;
let mut ntfs = Ntfs::default();
while !data.0.is_empty() {
let typ = data.read_u16()?;
let len = data.read_u16()?;
let mut data = DataParser(data.read_variable(len as _)?);
match typ {
0x0001 => {
let mut read_time = || {
let ts = data.read_u64()?;
Some((ts != 0).then(|| Timestamp::from_ntfs(ts)))
};
ntfs.times = NtfsTimes {
mtime: read_time()?,
atime: read_time()?,
ctime: read_time()?,
};
}
_ => continue, }
}
Some(ntfs)
}
}
#[derive(Debug, Clone)]
pub struct ExtendedTimestamp {
pub modification_time: Option<Timestamp>,
pub access_time: Option<Timestamp>,
pub creation_time: Option<Timestamp>,
}
impl ExtendedTimestamp {
fn parse(mut data: DataParser<'_>) -> Option<Self> {
let flags = data.read_u8()?;
if data.0.len() == 4 && flags.count_ones() != 1 {
return Some(ExtendedTimestamp {
modification_time: Some(Timestamp::from_unix(data.read_u32()? as _)),
access_time: None,
creation_time: None,
});
}
let modification_time = if flags & 1 != 0 {
Some(Timestamp::from_unix(data.read_u32()? as _))
} else {
None
};
let access_time = if flags & 2 != 0 {
Some(Timestamp::from_unix(data.read_u32()? as _))
} else {
None
};
let creation_time = if flags & 4 != 0 {
Some(Timestamp::from_unix(data.read_u32()? as _))
} else {
None
};
data.end()?;
Some(ExtendedTimestamp {
modification_time,
access_time,
creation_time,
})
}
}
#[derive(Debug, Clone)]
pub struct UnicodeComment<'a> {
pub version: u8,
pub header_comment_crc32: u32,
pub comment: &'a str,
}
impl<'a> UnicodeComment<'a> {
fn parse(mut data: DataParser<'a>) -> Option<Self> {
let version = data.read_u8()?;
if version != 1 {
return None;
}
let header_comment_crc32 = data.read_u32()?;
let comment = std::str::from_utf8(data.0).ok()?;
Some(UnicodeComment {
version,
header_comment_crc32,
comment,
})
}
}
#[derive(Debug, Clone)]
pub struct UnicodeName<'a> {
pub version: u8,
pub header_name_crc32: u32,
pub name: &'a str,
}
impl<'a> UnicodeName<'a> {
fn parse(mut data: DataParser<'a>) -> Option<Self> {
let version = data.read_u8()?;
if version != 1 {
return None;
}
let header_name_crc32 = data.read_u32()?;
let name = std::str::from_utf8(data.0).ok()?;
Some(UnicodeName {
version,
header_name_crc32,
name,
})
}
}
#[derive(Debug, Clone)]
pub struct UnixNew<'a> {
pub version: u8,
pub uid: &'a [u8],
pub gid: &'a [u8],
}
impl<'a> UnixNew<'a> {
fn parse(mut data: DataParser<'a>) -> Option<Self> {
let version = data.read_u8()?;
let uid_size = data.read_u8()?;
let uid = data.read_variable(uid_size as _)?;
let gid_size = data.read_u8()?;
let gid = data.read_variable(gid_size as _)?;
data.end()?;
Some(UnixNew { version, uid, gid })
}
}
#[derive(Debug, Clone)]
pub struct Aes {
pub check_crc32: bool,
pub key_size: u16,
pub compression: crate::CompressionMethod,
}
impl Aes {
fn parse(mut data: DataParser<'_>) -> Option<Self> {
let check_crc32 = match data.read_u16()? {
1 => true,
2 => false,
_ => return None,
};
let vendor_id = data.read_u16()?;
if vendor_id != u16::from_ne_bytes(*b"AE") {
return None;
}
let mode = match data.read_u8()? {
1 => 128,
2 => 192,
3 => 256,
_ => return None,
};
let compression = crate::CompressionMethod(data.read_u16()?);
data.end()?;
Some(Aes {
check_crc32,
key_size: mode,
compression,
})
}
}