1 use crate::result::{ZipError, ZipResult};
2 use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
3 use std::io;
4 use std::io::prelude::*;
5 
6 pub const LOCAL_FILE_HEADER_SIGNATURE: u32 = 0x04034b50;
7 pub const CENTRAL_DIRECTORY_HEADER_SIGNATURE: u32 = 0x02014b50;
8 const CENTRAL_DIRECTORY_END_SIGNATURE: u32 = 0x06054b50;
9 pub const ZIP64_CENTRAL_DIRECTORY_END_SIGNATURE: u32 = 0x06064b50;
10 const ZIP64_CENTRAL_DIRECTORY_END_LOCATOR_SIGNATURE: u32 = 0x07064b50;
11 
12 pub struct CentralDirectoryEnd {
13     pub disk_number: u16,
14     pub disk_with_central_directory: u16,
15     pub number_of_files_on_this_disk: u16,
16     pub number_of_files: u16,
17     pub central_directory_size: u32,
18     pub central_directory_offset: u32,
19     pub zip_file_comment: Vec<u8>,
20 }
21 
22 impl CentralDirectoryEnd {
parse<T: Read>(reader: &mut T) -> ZipResult<CentralDirectoryEnd>23     pub fn parse<T: Read>(reader: &mut T) -> ZipResult<CentralDirectoryEnd> {
24         let magic = reader.read_u32::<LittleEndian>()?;
25         if magic != CENTRAL_DIRECTORY_END_SIGNATURE {
26             return Err(ZipError::InvalidArchive("Invalid digital signature header"));
27         }
28         let disk_number = reader.read_u16::<LittleEndian>()?;
29         let disk_with_central_directory = reader.read_u16::<LittleEndian>()?;
30         let number_of_files_on_this_disk = reader.read_u16::<LittleEndian>()?;
31         let number_of_files = reader.read_u16::<LittleEndian>()?;
32         let central_directory_size = reader.read_u32::<LittleEndian>()?;
33         let central_directory_offset = reader.read_u32::<LittleEndian>()?;
34         let zip_file_comment_length = reader.read_u16::<LittleEndian>()? as usize;
35         let mut zip_file_comment = vec![0; zip_file_comment_length];
36         reader.read_exact(&mut zip_file_comment)?;
37 
38         Ok(CentralDirectoryEnd {
39             disk_number,
40             disk_with_central_directory,
41             number_of_files_on_this_disk,
42             number_of_files,
43             central_directory_size,
44             central_directory_offset,
45             zip_file_comment,
46         })
47     }
48 
find_and_parse<T: Read + io::Seek>( reader: &mut T, ) -> ZipResult<(CentralDirectoryEnd, u64)>49     pub fn find_and_parse<T: Read + io::Seek>(
50         reader: &mut T,
51     ) -> ZipResult<(CentralDirectoryEnd, u64)> {
52         const HEADER_SIZE: u64 = 22;
53         const BYTES_BETWEEN_MAGIC_AND_COMMENT_SIZE: u64 = HEADER_SIZE - 6;
54         let file_length = reader.seek(io::SeekFrom::End(0))?;
55 
56         let search_upper_bound = file_length.saturating_sub(HEADER_SIZE + ::std::u16::MAX as u64);
57 
58         if file_length < HEADER_SIZE {
59             return Err(ZipError::InvalidArchive("Invalid zip header"));
60         }
61 
62         let mut pos = file_length - HEADER_SIZE;
63         while pos >= search_upper_bound {
64             reader.seek(io::SeekFrom::Start(pos as u64))?;
65             if reader.read_u32::<LittleEndian>()? == CENTRAL_DIRECTORY_END_SIGNATURE {
66                 reader.seek(io::SeekFrom::Current(
67                     BYTES_BETWEEN_MAGIC_AND_COMMENT_SIZE as i64,
68                 ))?;
69                 let comment_length = reader.read_u16::<LittleEndian>()? as u64;
70                 if file_length - pos - HEADER_SIZE == comment_length {
71                     let cde_start_pos = reader.seek(io::SeekFrom::Start(pos as u64))?;
72                     return CentralDirectoryEnd::parse(reader).map(|cde| (cde, cde_start_pos));
73                 }
74             }
75             pos = match pos.checked_sub(1) {
76                 Some(p) => p,
77                 None => break,
78             };
79         }
80         Err(ZipError::InvalidArchive(
81             "Could not find central directory end",
82         ))
83     }
84 
write<T: Write>(&self, writer: &mut T) -> ZipResult<()>85     pub fn write<T: Write>(&self, writer: &mut T) -> ZipResult<()> {
86         writer.write_u32::<LittleEndian>(CENTRAL_DIRECTORY_END_SIGNATURE)?;
87         writer.write_u16::<LittleEndian>(self.disk_number)?;
88         writer.write_u16::<LittleEndian>(self.disk_with_central_directory)?;
89         writer.write_u16::<LittleEndian>(self.number_of_files_on_this_disk)?;
90         writer.write_u16::<LittleEndian>(self.number_of_files)?;
91         writer.write_u32::<LittleEndian>(self.central_directory_size)?;
92         writer.write_u32::<LittleEndian>(self.central_directory_offset)?;
93         writer.write_u16::<LittleEndian>(self.zip_file_comment.len() as u16)?;
94         writer.write_all(&self.zip_file_comment)?;
95         Ok(())
96     }
97 }
98 
99 pub struct Zip64CentralDirectoryEndLocator {
100     pub disk_with_central_directory: u32,
101     pub end_of_central_directory_offset: u64,
102     pub number_of_disks: u32,
103 }
104 
105 impl Zip64CentralDirectoryEndLocator {
parse<T: Read>(reader: &mut T) -> ZipResult<Zip64CentralDirectoryEndLocator>106     pub fn parse<T: Read>(reader: &mut T) -> ZipResult<Zip64CentralDirectoryEndLocator> {
107         let magic = reader.read_u32::<LittleEndian>()?;
108         if magic != ZIP64_CENTRAL_DIRECTORY_END_LOCATOR_SIGNATURE {
109             return Err(ZipError::InvalidArchive(
110                 "Invalid zip64 locator digital signature header",
111             ));
112         }
113         let disk_with_central_directory = reader.read_u32::<LittleEndian>()?;
114         let end_of_central_directory_offset = reader.read_u64::<LittleEndian>()?;
115         let number_of_disks = reader.read_u32::<LittleEndian>()?;
116 
117         Ok(Zip64CentralDirectoryEndLocator {
118             disk_with_central_directory,
119             end_of_central_directory_offset,
120             number_of_disks,
121         })
122     }
123 }
124 
125 pub struct Zip64CentralDirectoryEnd {
126     pub version_made_by: u16,
127     pub version_needed_to_extract: u16,
128     pub disk_number: u32,
129     pub disk_with_central_directory: u32,
130     pub number_of_files_on_this_disk: u64,
131     pub number_of_files: u64,
132     pub central_directory_size: u64,
133     pub central_directory_offset: u64,
134     //pub extensible_data_sector: Vec<u8>, <-- We don't do anything with this at the moment.
135 }
136 
137 impl Zip64CentralDirectoryEnd {
find_and_parse<T: Read + io::Seek>( reader: &mut T, nominal_offset: u64, search_upper_bound: u64, ) -> ZipResult<(Zip64CentralDirectoryEnd, u64)>138     pub fn find_and_parse<T: Read + io::Seek>(
139         reader: &mut T,
140         nominal_offset: u64,
141         search_upper_bound: u64,
142     ) -> ZipResult<(Zip64CentralDirectoryEnd, u64)> {
143         let mut pos = nominal_offset;
144 
145         while pos <= search_upper_bound {
146             reader.seek(io::SeekFrom::Start(pos))?;
147 
148             if reader.read_u32::<LittleEndian>()? == ZIP64_CENTRAL_DIRECTORY_END_SIGNATURE {
149                 let archive_offset = pos - nominal_offset;
150 
151                 let _record_size = reader.read_u64::<LittleEndian>()?;
152                 // We would use this value if we did anything with the "zip64 extensible data sector".
153 
154                 let version_made_by = reader.read_u16::<LittleEndian>()?;
155                 let version_needed_to_extract = reader.read_u16::<LittleEndian>()?;
156                 let disk_number = reader.read_u32::<LittleEndian>()?;
157                 let disk_with_central_directory = reader.read_u32::<LittleEndian>()?;
158                 let number_of_files_on_this_disk = reader.read_u64::<LittleEndian>()?;
159                 let number_of_files = reader.read_u64::<LittleEndian>()?;
160                 let central_directory_size = reader.read_u64::<LittleEndian>()?;
161                 let central_directory_offset = reader.read_u64::<LittleEndian>()?;
162 
163                 return Ok((
164                     Zip64CentralDirectoryEnd {
165                         version_made_by,
166                         version_needed_to_extract,
167                         disk_number,
168                         disk_with_central_directory,
169                         number_of_files_on_this_disk,
170                         number_of_files,
171                         central_directory_size,
172                         central_directory_offset,
173                     },
174                     archive_offset,
175                 ));
176             }
177 
178             pos += 1;
179         }
180 
181         Err(ZipError::InvalidArchive(
182             "Could not find ZIP64 central directory end",
183         ))
184     }
185 }
186