use crate::format::*;
use hex_fmt::HexFmt;
use nom::{
bytes::streaming::{tag, take},
combinator::{cond, map, verify},
multi::{length_data, many0},
number::streaming::{le_u16, le_u32, le_u64, le_u8},
sequence::{preceded, tuple},
};
use std::fmt;
pub(crate) struct ExtraFieldRecord<'a> {
pub(crate) tag: u16,
pub(crate) payload: &'a [u8],
}
impl<'a> fmt::Debug for ExtraFieldRecord<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "tag 0x{:x}: {}", self.tag, HexFmt(self.payload))
}
}
impl<'a> ExtraFieldRecord<'a> {
pub(crate) fn parse(i: &'a [u8]) -> parse::Result<'a, Self> {
fields!(Self {
tag: le_u16,
payload: length_data(le_u16),
})(i)
}
}
pub(crate) struct ExtraFieldSettings {
pub(crate) needs_uncompressed_size: bool,
pub(crate) needs_compressed_size: bool,
pub(crate) needs_header_offset: bool,
}
#[derive(Debug)]
pub enum ExtraField {
Zip64(ExtraZip64Field),
Timestamp(ExtraTimestampField),
Unix(ExtraUnixField),
NewUnix(ExtraNewUnixField),
Ntfs(ExtraNtfsField),
Unknown { tag: u16 },
}
impl ExtraField {
pub(crate) fn parse<'a>(i: &'a [u8], settings: &ExtraFieldSettings) -> parse::Result<'a, Self> {
use ExtraField as EF;
let (remaining, rec) = ExtraFieldRecord::parse(i)?;
let variant = match rec.tag {
ExtraZip64Field::TAG => {
if let Ok((_, tag)) = ExtraZip64Field::parse(rec.payload, settings) {
Some(EF::Zip64(tag))
} else {
None
}
}
ExtraTimestampField::TAG => {
if let Ok((_, tag)) = ExtraTimestampField::parse(rec.payload) {
Some(EF::Timestamp(tag))
} else {
None
}
}
ExtraNtfsField::TAG => {
if let Ok((_, tag)) = ExtraNtfsField::parse(rec.payload) {
Some(EF::Ntfs(tag))
} else {
None
}
}
ExtraUnixField::TAG | ExtraUnixField::TAG_INFOZIP => {
if let Ok((_, tag)) = ExtraUnixField::parse(rec.payload) {
Some(EF::Unix(tag))
} else {
None
}
}
ExtraNewUnixField::TAG => {
if let Ok((_, tag)) = ExtraNewUnixField::parse(rec.payload) {
Some(EF::NewUnix(tag))
} else {
None
}
}
_ => None,
}
.unwrap_or(EF::Unknown { tag: rec.tag });
Ok((remaining, variant))
}
}
#[derive(Debug)]
pub struct ExtraZip64Field {
pub uncompressed_size: Option<u64>,
pub compressed_size: Option<u64>,
pub header_offset: Option<u64>,
}
impl ExtraZip64Field {
const TAG: u16 = 0x0001;
pub(crate) fn parse<'a>(i: &'a [u8], settings: &ExtraFieldSettings) -> parse::Result<'a, Self> {
fields!(Self {
uncompressed_size: cond(settings.needs_uncompressed_size, le_u64),
compressed_size: cond(settings.needs_compressed_size, le_u64),
header_offset: cond(settings.needs_header_offset, le_u64),
})(i)
}
}
#[derive(Debug)]
pub struct ExtraTimestampField {
pub mtime: u32,
}
impl ExtraTimestampField {
const TAG: u16 = 0x5455;
fn parse<'a>(i: &'a [u8]) -> parse::Result<'a, Self> {
preceded(
verify(le_u8, |x| x & 0b1 != 0),
map(le_u32, |mtime| Self { mtime }),
)(i)
}
}
#[derive(Debug)]
pub struct ExtraUnixField {
pub atime: u32,
pub mtime: u32,
pub uid: u16,
pub gid: u16,
pub data: ZipBytes,
}
impl ExtraUnixField {
const TAG: u16 = 0x000d;
const TAG_INFOZIP: u16 = 0x5855;
fn parse<'a>(i: &'a [u8]) -> parse::Result<'a, Self> {
let (i, t_size) = le_u16(i)?;
let t_size = t_size - 12;
fields!(Self {
atime: le_u32,
mtime: le_u32,
uid: le_u16,
gid: le_u16,
data: ZipBytes::parser(t_size),
})(i)
}
}
#[derive(Debug)]
pub struct ExtraNewUnixField {
pub uid: u64,
pub gid: u64,
}
impl ExtraNewUnixField {
const TAG: u16 = 0x7875;
fn parse<'a>(i: &'a [u8]) -> parse::Result<'a, Self> {
preceded(
tag("\x01"),
map(
tuple((
Self::parse_variable_length_integer,
Self::parse_variable_length_integer,
)),
|(uid, gid)| Self { uid, gid },
),
)(i)
}
fn parse_variable_length_integer<'a>(i: &'a [u8]) -> parse::Result<'a, u64> {
let (i, slice) = length_data(le_u8)(i)?;
if let Some(u) = match slice.len() {
1 => Some(le_u8(slice)?.1 as u64),
2 => Some(le_u16(slice)?.1 as u64),
4 => Some(le_u32(slice)?.1 as u64),
8 => Some(le_u64(slice)?.1),
_ => None,
} {
Ok((i, u))
} else {
Err(nom::Err::Failure((i, nom::error::ErrorKind::OneOf)))
}
}
}
#[derive(Debug)]
pub struct ExtraNtfsField {
pub attrs: Vec<NtfsAttr>,
}
impl ExtraNtfsField {
const TAG: u16 = 0x000a;
fn parse<'a>(i: &'a [u8]) -> parse::Result<'a, Self> {
preceded(
take(4usize),
map(many0(NtfsAttr::parse), |attrs| Self { attrs }),
)(i)
}
}
#[derive(Debug)]
pub enum NtfsAttr {
Attr1(NtfsAttr1),
Unknown { tag: u16 },
}
impl NtfsAttr {
fn parse<'a>(i: &'a [u8]) -> parse::Result<'a, Self> {
let (i, (tag, payload)) = tuple((le_u16, length_data(le_u16)))(i)?;
match tag {
0x0001 => NtfsAttr1::parse(payload).map(|(i, x)| (i, NtfsAttr::Attr1(x))),
_ => Ok((i, NtfsAttr::Unknown { tag })),
}
}
}
#[derive(Debug)]
pub struct NtfsAttr1 {
pub mtime: NtfsTimestamp,
pub atime: NtfsTimestamp,
pub ctime: NtfsTimestamp,
}
impl NtfsAttr1 {
fn parse<'a>(i: &'a [u8]) -> parse::Result<'a, Self> {
fields!(Self {
mtime: NtfsTimestamp::parse,
atime: NtfsTimestamp::parse,
ctime: NtfsTimestamp::parse,
})(i)
}
}