1 use super::*;
2 use crate::preproc::rga_preproc;
3 use ::zip::read::ZipFile;
4 use failure::*;
5 use lazy_static::lazy_static;
6 use log::*;
7 
8 // todo:
9 // maybe todo: read list of extensions from
10 //ffmpeg -demuxers | tail -n+5 | awk '{print $2}' | while read demuxer; do echo MUX=$demuxer; ffmpeg -h demuxer=$demuxer | grep 'Common extensions'; done 2>/dev/null
11 static EXTENSIONS: &[&str] = &["zip"];
12 
13 lazy_static! {
14     static ref METADATA: AdapterMeta = AdapterMeta {
15         name: "zip".to_owned(),
16         version: 1,
17         description: "Reads a zip file as a stream and recurses down into its contents".to_owned(),
18         recurses: true,
19         fast_matchers: EXTENSIONS
20             .iter()
21             .map(|s| FastMatcher::FileExtension(s.to_string()))
22             .collect(),
23         slow_matchers: Some(vec![SlowMatcher::MimeType("application/zip".to_owned())])
24     };
25 }
26 #[derive(Default)]
27 pub struct ZipAdapter;
28 
29 impl ZipAdapter {
new() -> ZipAdapter30     pub fn new() -> ZipAdapter {
31         ZipAdapter
32     }
33 }
34 impl GetMetadata for ZipAdapter {
metadata(&self) -> &AdapterMeta35     fn metadata(&self) -> &AdapterMeta {
36         &METADATA
37     }
38 }
39 
40 // https://github.com/mvdnes/zip-rs/commit/b9af51e654793931af39f221f143b9dea524f349
is_dir(f: &ZipFile) -> bool41 fn is_dir(f: &ZipFile) -> bool {
42     f.name()
43         .chars()
44         .rev()
45         .next()
46         .map_or(false, |c| c == '/' || c == '\\')
47 }
48 
49 impl FileAdapter for ZipAdapter {
adapt(&self, ai: AdaptInfo, _detection_reason: &SlowMatcher) -> Fallible<()>50     fn adapt(&self, ai: AdaptInfo, _detection_reason: &SlowMatcher) -> Fallible<()> {
51         let AdaptInfo {
52             filepath_hint,
53             mut inp,
54             oup,
55             line_prefix,
56             archive_recursion_depth,
57             config,
58             ..
59         } = ai;
60         loop {
61             match ::zip::read::read_zipfile_from_stream(&mut inp) {
62                 Ok(None) => break,
63                 Ok(Some(mut file)) => {
64                     if is_dir(&file) {
65                         continue;
66                     }
67                     debug!(
68                         "{}{}|{}: {} bytes ({} bytes packed)",
69                         line_prefix,
70                         filepath_hint.to_string_lossy(),
71                         file.name(),
72                         file.size(),
73                         file.compressed_size()
74                     );
75                     let line_prefix = &format!("{}{}: ", line_prefix, file.name());
76                     rga_preproc(AdaptInfo {
77                         filepath_hint: &file.sanitized_name(),
78                         is_real_file: false,
79                         inp: &mut file,
80                         oup,
81                         line_prefix,
82                         archive_recursion_depth: archive_recursion_depth + 1,
83                         config: config.clone(),
84                     })?;
85                 }
86                 Err(e) => return Err(e.into()),
87             }
88         }
89         Ok(())
90     }
91 }
92