1use crate::CompressionMethod;
4use crate::DateTime;
5use crate::HasZipMetadata;
6use crate::ZIP64_BYTES_THR;
7use crate::ZipReadOptions;
8use crate::format::data_descriptor::ZipDataDescriptor;
9use crate::format::ffi;
10use crate::format::flags::ZipFileFlags;
11use crate::format::system::System;
12use crate::read::ExtraField;
13use crate::read::extract::RootDirFilter;
14use crate::read::readers::{ZipFileReader, ZipFileSeekReader};
15use crate::result::ZipResult;
16use crate::result::invalid;
17use crate::types::ZipFileData;
18use crate::write::options::SimpleFileOptions;
19
20use core::mem::replace;
21use std::borrow::Cow;
22use std::ffi::OsStr;
23use std::io::{self, Cursor, Read, Seek, SeekFrom, copy, sink};
24use std::path::{Path, PathBuf};
25
26#[derive(Debug)]
32pub struct ZipFile<'a, R: Read + ?Sized> {
33 pub(crate) file_name_raw: Cow<'a, [u8]>,
34 pub(crate) data: Cow<'a, ZipFileData>,
35 pub(crate) reader: ZipFileReader<'a, R>,
36}
37
38#[derive(Debug)]
42pub struct ZipFileEntryWithDataDescriptor<'a> {
43 pub(crate) file_name_raw: Cow<'a, [u8]>,
44 pub(crate) data: Cow<'a, ZipFileData>,
45 pub(crate) data_descriptor: Option<ZipDataDescriptor>,
46}
47
48#[derive(Debug)]
50pub struct ZipFileEntry<'a> {
51 pub(crate) file_name_raw: Cow<'a, [u8]>,
52 pub(crate) data: Cow<'a, ZipFileData>,
53}
54
55pub struct ZipFileSeek<'a, R> {
57 pub(crate) file_name_raw: Cow<'a, [u8]>,
58 pub(crate) data: Cow<'a, ZipFileData>,
59 pub(crate) reader: ZipFileSeekReader<'a, R>,
60}
61
62macro_rules! zip_file_methods {
63 () => {
64 pub fn name_raw(&self) -> &[u8] {
68 &self.file_name_raw
69 }
70
71 pub fn name(&self) -> ZipResult<Cow<'_, str>> {
84 self.get_metadata().name(&self.file_name_raw)
85 }
86
87 pub fn mangled_name(&self) -> ZipResult<PathBuf> {
100 let file_name = self.name()?;
101 let sanitized = self.get_metadata().file_name_sanitized(&file_name);
102 Ok(sanitized)
103 }
104
105 pub fn enclosed_name(&self) -> Option<PathBuf> {
116 self.get_metadata().enclosed_name(&self.name().ok()?)
117 }
118
119 pub fn header_start(&self) -> u64 {
121 self.get_metadata().header_start
122 }
123 pub fn central_header_start(&self) -> u64 {
125 self.get_metadata().central_header_start
126 }
127
128 pub fn version_made_by(&self) -> (u8, u8) {
130 (
131 self.get_metadata().version_made_by / 10,
132 self.get_metadata().version_made_by % 10,
133 )
134 }
135
136 pub fn version_needed(&self) -> u16 {
138 self.get_metadata().version_needed()
139 }
140
141 pub fn comment(&self) -> &str {
143 &self.get_metadata().file_comment
144 }
145
146 pub fn system(&self) -> System {
148 self.get_metadata().system
149 }
150
151 pub fn external_attributes(&self) -> u32 {
153 self.get_metadata().external_attributes
154 }
155
156 pub fn flags(&self) -> ZipFileFlags {
158 self.get_metadata().flags
159 }
160
161 pub fn compression(&self) -> CompressionMethod {
163 self.get_metadata().compression_method
164 }
165
166 pub fn encrypted(&self) -> bool {
168 self.data.is_encrypted()
169 }
170
171 pub fn compressed_size(&self) -> u64 {
173 self.get_metadata().compressed_size
174 }
175
176 pub fn size(&self) -> u64 {
178 self.get_metadata().uncompressed_size
179 }
180
181 pub fn last_modified(&self) -> Option<DateTime> {
183 self.get_metadata().last_modified_time
184 }
185 pub fn is_dir(&self) -> bool {
187 self.get_metadata().is_dir(&self.file_name_raw)
188 }
189
190 pub fn is_symlink(&self) -> bool {
192 self.unix_mode()
193 .is_some_and(|mode| mode & ffi::S_IFLNK == ffi::S_IFLNK)
194 }
195
196 pub fn is_file(&self) -> bool {
198 !self.is_dir() && !self.is_symlink()
199 }
200
201 pub fn is_read_only(&self) -> bool {
204 self.unix_mode().is_some_and(|mode| mode & 0o222 == 0)
205 }
206
207 pub fn unix_mode(&self) -> Option<u32> {
209 self.get_metadata().unix_mode()
210 }
211
212 pub fn crc32(&self) -> u32 {
214 self.get_metadata().crc32
215 }
216 };
217}
218impl<'a> ZipFileEntryWithDataDescriptor<'a> {
219 zip_file_methods!();
220
221 pub fn data_descriptor(&self) -> &Option<ZipDataDescriptor> {
223 &self.data_descriptor
224 }
225}
226
227impl HasZipMetadata for ZipFileEntryWithDataDescriptor<'_> {
228 fn get_metadata(&self) -> &ZipFileData {
229 self.data.as_ref()
230 }
231}
232
233impl<'a> ZipFileEntry<'a> {
234 zip_file_methods!();
235
236 pub fn with_reader<R: Read + Seek>(
238 self,
239 reader: &'a mut R,
240 options: ZipReadOptions<'_>,
241 ) -> ZipResult<ZipFile<'a, R>> {
242 let limit_reader = self.data.find_content(reader)?;
243
244 let reader = self.data.make_reader(limit_reader, options)?;
245 Ok(ZipFile {
246 file_name_raw: self.file_name_raw,
247 data: self.data,
248 reader,
249 })
250 }
251}
252
253impl HasZipMetadata for ZipFileEntry<'_> {
254 fn get_metadata(&self) -> &ZipFileData {
255 self.data.as_ref()
256 }
257}
258
259impl<'a, R: Read + ?Sized> ZipFile<'a, R> {
261 zip_file_methods!();
262
263 pub(crate) fn take_raw_reader(&mut self) -> io::Result<io::Take<&'a mut R>> {
264 replace(&mut self.reader, ZipFileReader::NoReader).into_inner()
265 }
266
267 pub(crate) fn safe_prepare_path(
271 &self,
272 base_path: &Path,
273 outpath: &mut PathBuf,
274 root_dir: Option<&(Vec<&OsStr>, impl RootDirFilter)>,
275 ) -> ZipResult<()> {
276 let file_name = self.name()?;
277 let components = self
278 .data
279 .simplified_components(&file_name)
280 .ok_or(invalid!("Invalid file path"))?;
281
282 let components = match root_dir {
283 Some((root_dir, filter)) => match components.strip_prefix(&**root_dir) {
284 Some(components) => components,
285
286 None => {
290 debug_assert!(
298 !filter(&PathBuf::from_iter(components.iter())),
299 "Root directory filter should not match at this point"
300 );
301
302 &components[..]
304 }
305 },
306
307 None => &components[..],
308 };
309
310 *outpath = crate::path::resolve_enclosed(
311 base_path,
312 base_path,
313 components.iter().map(|c| c.to_os_string()),
314 true, )?;
316
317 Ok(())
318 }
319
320 pub fn extra_data(&self) -> Option<Vec<u8>> {
322 let out_buffer = Vec::new();
323 let mut cursor = Cursor::new(out_buffer);
324 let extra_fields = self.data.extra_fields.local_extra_fields();
325 for one_extra_field in extra_fields {
326 one_extra_field.write(&mut cursor, true).ok()?;
327 }
328 let extra_fields_data = cursor.into_inner();
329 if extra_fields_data.is_empty() {
330 None
331 } else {
332 Some(extra_fields_data)
333 }
334 }
335
336 pub fn data_start(&self) -> Option<u64> {
338 self.data.data_start.get().copied()
339 }
340
341 pub fn options(&self) -> SimpleFileOptions {
344 let mut options = SimpleFileOptions::default()
345 .large_file(self.compressed_size().max(self.size()) >= ZIP64_BYTES_THR)
346 .compression_method(self.compression())
347 .system(self.system())
348 .external_attributes(self.external_attributes())
349 .last_modified_time(
350 self.last_modified()
351 .filter(DateTime::is_valid)
352 .unwrap_or_else(DateTime::default_for_write),
353 );
354
355 options.normalize();
356 #[cfg(feature = "aes-crypto")]
357 if let Some((mode, vendor_version)) = self.get_metadata().aes_settings() {
358 use crate::write::options::EncryptWith;
359 options.encrypt_with = Some(EncryptWith::Aes {
362 mode,
363 vendor_version,
364 salt: None,
365 password: None,
366 });
367 }
368 options
369 }
370}
371
372impl<R: Read> ZipFile<'_, R> {
374 pub fn extra_data_fields(&self) -> impl Iterator<Item = &ExtraField> {
376 self.data.extra_fields.inner.iter()
377 }
378}
379
380impl<R: Read + ?Sized> HasZipMetadata for ZipFile<'_, R> {
381 fn get_metadata(&self) -> &ZipFileData {
382 self.data.as_ref()
383 }
384}
385
386impl<R: Read + ?Sized> Read for ZipFile<'_, R> {
387 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
388 self.reader.read(buf)
389 }
390
391 fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
392 self.reader.read_to_end(buf)
393 }
394
395 fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
396 self.reader.read_to_string(buf)
397 }
398
399 fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
400 self.reader.read_exact(buf)
401 }
402}
403
404impl<R: Read> Read for ZipFileSeek<'_, R> {
405 fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
406 match &mut self.reader {
407 ZipFileSeekReader::Raw(r) => r.read(buf),
408 }
409 }
410}
411
412impl<R: Seek> Seek for ZipFileSeek<'_, R> {
413 fn seek(&mut self, pos: SeekFrom) -> io::Result<u64> {
414 match &mut self.reader {
415 ZipFileSeekReader::Raw(r) => r.seek(pos),
416 }
417 }
418}
419
420impl<R> HasZipMetadata for ZipFileSeek<'_, R> {
421 fn get_metadata(&self) -> &ZipFileData {
422 self.data.as_ref()
423 }
424}
425
426impl<'a, R> ZipFileSeek<'a, R> {
427 zip_file_methods!();
428}
429
430impl<R: Read + ?Sized> Drop for ZipFile<'_, R> {
431 fn drop(&mut self) {
432 if let Cow::Owned(_) = self.data {
435 if let Ok(mut inner) = self.take_raw_reader() {
437 let _ = copy(&mut inner, &mut sink());
438 }
439 }
440 }
441}