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