use crate::result::ZipResult;
use crate::result::invalid;
use core::fmt::Display;
use std::io::Write;
mod aex_encryption;
mod data_stream_alignment;
mod extended_timestamp;
mod extra_field;
mod ntfs;
mod zip64_extended_information;
mod zipinfo_utf8;
pub(crate) use aex_encryption::AexEncryption;
pub(crate) use zip64_extended_information::Zip64ExtendedInformation;
pub use data_stream_alignment::DataStreamAlignment;
pub use extended_timestamp::ExtendedTimestamp;
pub use extra_field::{ExtraField, ExtraFields};
pub use ntfs::Ntfs;
pub use zipinfo_utf8::UnicodeExtraField;
pub trait ExtraFieldVersion {}
#[derive(Debug, Clone)]
pub struct LocalHeaderVersion;
#[derive(Debug, Clone)]
pub struct CentralHeaderVersion;
impl ExtraFieldVersion for LocalHeaderVersion {}
impl ExtraFieldVersion for CentralHeaderVersion {}
#[repr(u16)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub(crate) enum UsedExtraField {
Zip64ExtendedInfo = 0x0001,
Ntfs = 0x000a,
ExtendedTimestamp = 0x5455,
UnicodeComment = 0x6375,
UnicodePath = 0x7075,
AeXEncryption = 0x9901,
DataStreamAlignment = 0xa11e,
}
impl UsedExtraField {
pub const fn to_le_bytes(self) -> [u8; 2] {
let field_u16 = self.as_u16();
field_u16.to_le_bytes()
}
pub const fn as_u16(self) -> u16 {
self as u16
}
}
impl From<UsedExtraField> for u16 {
fn from(value: UsedExtraField) -> Self {
value.as_u16()
}
}
impl Display for UsedExtraField {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "0x{:04X}", *self as u16)
}
}
macro_rules! extra_field_match {
($x:expr, $( $variant:path ),+ $(,)?) => {
match $x {
$(
v if v == $variant as u16 => Ok($variant),
)+
_ => Err(()),
}
};
}
impl TryFrom<u16> for UsedExtraField {
type Error = ();
fn try_from(value: u16) -> Result<Self, Self::Error> {
extra_field_match!(
value,
UsedExtraField::Zip64ExtendedInfo,
UsedExtraField::Ntfs,
UsedExtraField::ExtendedTimestamp,
UsedExtraField::UnicodeComment,
UsedExtraField::UnicodePath,
UsedExtraField::DataStreamAlignment,
UsedExtraField::AeXEncryption,
)
}
}
pub const EXTRA_FIELD_MAPPING: [u16; 59] = [
UsedExtraField::Zip64ExtendedInfo.as_u16(),
0x0007, 0x0008, 0x0009, UsedExtraField::Ntfs.as_u16(),
0x000c, 0x000d, 0x000e, 0x000f, 0x0014, 0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x0020, 0x0021, 0x0022, 0x0023, 0x0065, 0x0066, 0x07c8, 0x1986, 0x2605, 0x2705, 0x2805, 0x334d, 0x4154, 0x4341, 0x4453, 0x4690, 0x4704, 0x470f, 0x4854, 0x4b46, 0x4c41, 0x4d49, 0x4d63, 0x4f4c, 0x5356, UsedExtraField::ExtendedTimestamp.as_u16(),
0x554e, 0x5855, UsedExtraField::UnicodeComment.as_u16(),
0x6542, 0x6854, UsedExtraField::UnicodePath.as_u16(),
0x7441, 0x756e, 0x7855, 0x7875, UsedExtraField::AeXEncryption.as_u16(),
0x9902, UsedExtraField::DataStreamAlignment.as_u16(),
0xa220, 0xcafe, 0xd935, 0xe57a, 0xfd4a, ];
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct CustomExtraField {
pub(crate) central_only: bool,
pub(crate) header_id: u16,
data: Box<[u8]>,
}
impl CustomExtraField {
pub(crate) fn new(central_only: bool, header_id: u16, data: &[u8]) -> Self {
Self {
central_only,
header_id,
data: data.into(),
}
}
#[allow(unused)] pub(crate) fn new_from_raw(central_only: bool, data: &[u8]) -> ZipResult<Self> {
if data.len() < 2 {
return Err(invalid!("Cannot build a CustomExtraField: no header_id"));
}
if data.len() < 4 {
return Err(invalid!("Cannot build a CustomExtraField: no size"));
}
let header_id = u16::from_le_bytes([data[0], data[1]]);
let size = u16::from_le_bytes([data[2], data[3]]) as usize;
if size > (u16::MAX - 4) as usize {
return Err(invalid!("Cannot build a CustomExtraField: size too big"));
}
let data_rest = &data[4..];
if size != data_rest.len() {
return Err(invalid!("Cannot build a CustomExtraField: incorrect size"));
}
Ok(Self {
central_only,
header_id,
data: data_rest.to_vec().into_boxed_slice(),
})
}
pub(crate) fn len_with_header(&self) -> usize {
let size = self.data.len();
size_of::<u16>() + size_of::<u16>() + size
}
pub(crate) fn write<W: Write>(&self, write: &mut W) -> ZipResult<()> {
write.write_all(&self.header_id.to_le_bytes())?;
let size = self.data.len() as u16;
write.write_all(&size.to_le_bytes())?;
write.write_all(&self.data)?;
Ok(())
}
}
#[cfg(feature = "_arbitrary")]
impl arbitrary::Arbitrary<'_> for CustomExtraField {
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
Ok(CustomExtraField {
central_only: u.arbitrary()?,
header_id: u.arbitrary()?,
data: u.arbitrary()?,
})
}
}