Skip to main content

zip/extra_fields/
zipinfo_utf8.rs

1use crate::result::{ZipResult, invalid};
2use crate::unstable::LittleEndianReadExt;
3use core::mem;
4use std::io::{Read, Write};
5
6/// Info-ZIP Unicode Path Extra Field (0x7075) or Unicode Comment Extra Field (0x6375), as
7/// specified in APPNOTE 4.6.8 and 4.6.9
8#[derive(Clone, Debug, PartialEq)]
9pub struct UnicodeExtraField {
10    crc32: u32,
11    pub(crate) content: Box<[u8]>,
12}
13
14impl UnicodeExtraField {
15    /// Verifies the checksum and returns the content.
16    ///
17    /// # Errors
18    ///
19    /// Returns an error when the crc32 from the extra field does not match the crc32 of the
20    /// `ascii_field`.
21    pub fn unwrap_valid(self, ascii_field: &[u8]) -> ZipResult<Box<[u8]>> {
22        let computed_crc32 = crc32fast::hash(ascii_field);
23        if self.crc32 != computed_crc32 {
24            return Err(invalid!(
25                "CRC32 checksum failed on Unicode extra field, it is '{:#08X}' and it should be '{:#08X}'",
26                self.crc32,
27                computed_crc32
28            ));
29        }
30        Ok(self.content)
31    }
32
33    /// Check if the crc32 is valid
34    pub(crate) fn is_crc32_valid(&self, ascii_field: &[u8]) -> bool {
35        let computed_crc32 = crc32fast::hash(ascii_field);
36        self.crc32 == computed_crc32
37    }
38
39    pub(crate) fn try_from_reader<R: Read>(reader: &mut R, len: u16) -> ZipResult<Self> {
40        // Read and discard version byte
41        reader.read_exact(&mut [0u8])?;
42
43        let crc32 = reader.read_u32_le()?;
44        let content_len = (len as usize)
45            .checked_sub(mem::size_of::<u8>() + mem::size_of::<u32>())
46            .ok_or(invalid!("Unicode extra field is too small"))?;
47        let mut content = vec![0u8; content_len].into_boxed_slice();
48        reader.read_exact(&mut content)?;
49        Ok(Self { crc32, content })
50    }
51
52    pub(crate) fn full_size(&self) -> usize {
53        mem::size_of::<u16>() // magic
54            + mem::size_of::<u16>() // length
55        + self.size()
56    }
57
58    pub(crate) fn size(&self) -> usize {
59        mem::size_of::<u8>() // version
60            + mem::size_of::<u32>() // crc32
61            + self.content.len() // content
62    }
63
64    /// Write the Unicode extra field. Magic header should already be written
65    pub(crate) fn write<W: Write>(&self, writer: &mut W) -> ZipResult<()> {
66        // Magic header should already be written
67        let size = self.size() as u16;
68        writer.write_all(&size.to_le_bytes())?;
69        let version = 1u8;
70        writer.write_all(&version.to_le_bytes())?;
71        writer.write_all(&self.crc32.to_le_bytes())?;
72        writer.write_all(&self.content)?;
73        Ok(())
74    }
75}
76
77#[cfg(test)]
78mod tests {
79    use crate::extra_fields::UnicodeExtraField;
80    #[test]
81    fn unicode_extra_field_crc32_correct() {
82        let data = [0x01, 0xef, 0x39, 0x8e, 0x4b, b'u', b't', b'f', b'-', b'8'];
83        let extra =
84            UnicodeExtraField::try_from_reader(&mut std::io::Cursor::new(data), 10).unwrap();
85        let res = extra.unwrap_valid(b"abcdef");
86        assert!(res.is_ok());
87        let content = res.unwrap();
88        assert_eq!(content.as_ref(), b"utf-8");
89    }
90
91    #[test]
92    fn unicode_extra_field_crc32_incorrect() {
93        let data = [0x01, 0x00, 0x00, 0x00, 0x00, b'u', b't', b'f', b'-', b'8'];
94        let extra =
95            UnicodeExtraField::try_from_reader(&mut std::io::Cursor::new(data), 10).unwrap();
96        let res = extra.unwrap_valid(b"abcdef");
97        assert!(res.is_err());
98    }
99
100    #[test]
101    fn unicode_extra_field_write_test() {
102        let data = [0x01, 0xef, 0x39, 0x8e, 0x4b, b'u', b't', b'f', b'-', b'8'];
103        assert_eq!(data.len(), 10); // 0x0A
104        let extra =
105            UnicodeExtraField::try_from_reader(&mut std::io::Cursor::new(data), 10).unwrap();
106        let mut data = Vec::new();
107        extra.write(&mut data).unwrap();
108
109        // The magic is NOT written
110        // Size is 10
111        assert_eq!(
112            data,
113            [
114                0x0A, 0x00, 0x01, 0xef, 0x39, 0x8e, 0x4b, b'u', b't', b'f', b'-', b'8'
115            ]
116        );
117    }
118}