the_lock_lib/
lib.rs

1#[cfg(any(feature = "serde", feature = "signers-list"))]
2extern crate serde;
3pub extern crate rsa;
4extern crate chacha20poly1305;
5extern crate sha2;
6extern crate rand;
7
8use std::borrow::BorrowMut;
9use std::fs::File;
10use std::io::{Read, Write, Seek};
11use std::path::Path;
12
13use asymetric_key::{PublicKey, PrivateKey};
14use rsa::{RsaPublicKey, RsaPrivateKey, Pss};
15use sha2::Sha512;
16#[cfg(feature = "signers-list")]
17use signers_list::SignersList;
18use trie_rs::TrieBuilder;
19use zip::{ZipArchive, ZipWriter};
20use rand::rngs::OsRng;
21
22pub use zip::{write::FileOptions, CompressionMethod, DateTime};
23
24mod symmertic_cipher;
25pub mod directory_content;
26#[cfg(feature = "signers-list")]
27pub mod signers_list;
28pub mod error;
29pub mod asymetric_key;
30
31use directory_content::{DirectoryContent, DirectoryContentPath};
32use symmertic_cipher::{SymmetricCipher, SymmetricKey};
33use error::{EncryptedFileError, EncryptedFileResult};
34
35const FILE_CONETENT_DIR: &str = "content";
36const FILE_CONTENT_NAME: &str = "file";
37const FILE_KEY_NAME: &str = "key";
38const FILE_SIGNATURE_NAME: &str = "signature";
39const FILE_DIGEST_NAME: &str = "digest";
40
41/**
42 * TODO
43 * checking file sizes
44 * Update add_file_digest when cheking stream size will be stable
45 * unwraps to expect
46 */
47
48pub struct EncryptedFile {
49    file: File,
50    directory_content: Option<DirectoryContent>,
51    symmetric_cipher: SymmetricCipher,
52    associated_data: Vec<u8>,
53    file_options: FileOptions,
54}
55
56pub type DecryptFileResult = EncryptedFileResult<bool>;
57pub type DecryptFileAndVerifyResult = EncryptedFileResult<(bool, EncryptedFileResult<()>)>;
58#[cfg(feature = "signers-list")]
59pub type DecryptFileAndFindSignerResult = EncryptedFileResult<(bool, Option<String>)>;
60
61impl EncryptedFile {
62    pub fn new<P: AsRef<Path>>(path: P) -> std::io::Result<Self> {
63        let path = path.as_ref();
64        if !path.exists() {
65            let file = File::create(path)?;
66            ZipWriter::new(file);
67        }
68        Ok(Self {
69            file: File::options().read(true).write(true).open(path)?,
70            directory_content: None,
71            symmetric_cipher: SymmetricCipher::default(),
72            associated_data: b"@s3ue9lWmFBMthC%NQnes1@2!SK@drScEQV6GPr^s$v@US&N6lI$uCirVwr8@6HkqStAS%%9T#Fn5Axom%C2#3&Ss0wQQL8J&1w*QKb64Mlt!cH4DaV0v^ZFh8^oCh@Y".to_vec(),
73            file_options: FileOptions::default(),
74        })
75    }
76
77    pub fn zip_file_options(&self) -> &FileOptions {
78        &self.file_options
79    }
80
81    pub fn set_zip_file_options(&mut self, file_options: FileOptions) {
82        self.file_options = file_options
83    }
84
85    pub fn associated_data(&self) -> &[u8] {
86        &self.associated_data
87    }
88
89    pub fn change_associated_data(&mut self, associated_data: Vec<u8>) {
90        self.associated_data = associated_data;
91    } 
92
93    pub fn change_buffor_size(&mut self, buffor_size: usize) {
94        self.symmetric_cipher.change_buffor_size(buffor_size);
95    }
96
97    pub fn buffor_size(&self) -> usize {
98        self.symmetric_cipher.buffor_size()
99    }
100
101    pub fn get_directory_content_soft(&self) -> Option<&DirectoryContent> {
102        self.directory_content.as_ref()
103    }
104
105    pub fn get_directory_content(&mut self) -> EncryptedFileResult<&DirectoryContent> {
106        match self.directory_content.is_some() {
107            true => Ok(self.get_directory_content_soft().unwrap()),
108            false => self.get_directory_content_hard(),
109        }
110    }
111
112    pub fn get_directory_content_hard(&mut self) -> EncryptedFileResult<&DirectoryContent> {
113        let archive = ZipArchive::new(&self.file)?;
114        let mut ans = DirectoryContent::new();
115        for name in archive.file_names() {
116            if name.len() < FILE_CONETENT_DIR.len() {
117                continue;
118            }
119            let mut path = DirectoryContentPath::from(name.get(FILE_CONETENT_DIR.len()..).expect("Check if it's long enough"));
120            if let Some(name) = path.pop() {
121                match name.as_str() {
122                    FILE_CONTENT_NAME => drop(ans.get_or_create_file_mut(&path)?.content(true)),
123                    FILE_KEY_NAME => drop(ans.get_or_create_file_mut(&path)?.key(true)),
124                    FILE_SIGNATURE_NAME => drop(ans.get_or_create_file_mut(&path)?.signed(true)),
125                    FILE_DIGEST_NAME => drop(ans.get_or_create_file_mut(&path)?.digest(true)),
126                    &_ => (),
127                }
128            }
129        }
130        self.directory_content = Some(ans);
131        Ok(self.directory_content.as_ref().unwrap())
132    }
133
134    fn sign_file(&self, dst_path: &DirectoryContentPath, file_hash: &[u8; 64], private_key: &RsaPrivateKey) -> EncryptedFileResult<()> {
135        let mut zip = ZipWriter::new_append(&self.file)?;
136        zip.start_file(format!("{FILE_CONETENT_DIR}/{dst_path}/{FILE_SIGNATURE_NAME}"), self.file_options)?;
137        zip.write(&private_key.sign_with_rng(&mut OsRng, Pss::new::<Sha512>(), file_hash)?)?;
138        Ok(())
139    }
140
141    fn verify_signature(&self, src_path: &DirectoryContentPath, file_hash: &[u8; 64], public_key: &RsaPublicKey) -> EncryptedFileResult<()> {
142        let mut zip = ZipArchive::new(&self.file)?;
143        let mut buf = Vec::new();
144        // todo!("Check size");
145        zip.by_name(&format!("{FILE_CONETENT_DIR}/{src_path}/{FILE_SIGNATURE_NAME}"))?.read_to_end(&mut buf)?;
146        public_key.verify(Pss::new::<Sha512>(), file_hash, &buf)?;
147        Ok(())
148    }
149
150    fn add_file_digest<I: Read>(&self, src: I, dst_path: &DirectoryContentPath, public_key: &PublicKey) -> EncryptedFileResult<Box<[u8; 64]>> {
151        let dst = format!("{FILE_CONETENT_DIR}/{dst_path}");
152        let key = SymmetricKey::new();
153        let mut zip = ZipWriter::new_append(&self.file)?;
154        zip.start_file(format!("{}/{}", dst, FILE_CONTENT_NAME), self.file_options)?;
155        let dig = self.symmetric_cipher.encrypt_file(&key, self.associated_data(), src, &mut zip)?;
156        let key_bytes: [u8; 51] = key.into();
157        let encrypted_key = public_key.encrypt_symmetric_key(&key_bytes)?;
158        zip.start_file(format!("{}/{}", dst, FILE_KEY_NAME), self.file_options)?;
159        zip.write_all(&encrypted_key)?;
160        zip.start_file(format!("{}/{}", dst, FILE_DIGEST_NAME), self.file_options)?;
161        zip.write_all(dig.as_ref())?;
162        Ok(dig)
163    }
164
165    fn decrypt_file_digest<O: Write>(&self, src: &DirectoryContentPath, mut dst: O, private_key: &PrivateKey) -> EncryptedFileResult<(Box<[u8; 64]>, bool)> {
166        let mut zip = ZipArchive::new(&self.file)?;
167        let key = {
168            let mut zipfile = zip.by_name(format!("{FILE_CONETENT_DIR}/{src}/{FILE_KEY_NAME}").as_str())?;
169            let mut buf: Vec<u8> = Vec::new();
170            // todo!("Check size");
171            zipfile.read_to_end(&mut buf)?;
172            SymmetricKey::try_from(private_key.decrypt_symmetric_key(&buf)?)?
173        };
174        let ans = self.symmetric_cipher.decrypt_file(&key, self.associated_data(), &mut zip.by_name(format!("{FILE_CONETENT_DIR}/{src}/{FILE_CONTENT_NAME}").as_str())?, &mut dst)?;
175        let is_digest_correct = *ans == {
176            let mut zipfile = zip.by_name(format!("{FILE_CONETENT_DIR}/{src}/{FILE_DIGEST_NAME}").as_str())?;
177            let mut buf = [0; 64];
178            zipfile.read_exact(&mut buf)?;
179            buf
180        };
181        Ok((
182            ans,
183            is_digest_correct
184        ))
185    }
186
187    pub fn add_file<I: Read>(&mut self, src: I, dst_path: &DirectoryContentPath, public_key: &PublicKey) -> EncryptedFileResult<()> {
188        if self.get_directory_content().unwrap().get_file(dst_path).is_some() {
189            return Err(EncryptedFileError::FileAlreadyExists.into());
190        }
191        self.add_file_digest(src, dst_path, public_key)?;
192        self.directory_content.as_mut().unwrap().borrow_mut().add_file_with_path(dst_path)?.content(true).key(true).digest(true);
193        Ok(())
194    }
195    
196    pub fn add_file_and_sign<I: Read>(&mut self, src: I, dst_path: &DirectoryContentPath, public_key: &PublicKey, private_key: &RsaPrivateKey) -> EncryptedFileResult<()> {
197        if self.get_directory_content().unwrap().get_file(dst_path).is_some() {
198            return Err(EncryptedFileError::FileAlreadyExists.into());
199        }
200        self.sign_file(dst_path, self.add_file_digest(src, dst_path, public_key)?.as_ref(), private_key)?;
201        self.directory_content.as_mut().unwrap().borrow_mut().add_file_with_path(dst_path)?.content(true).key(true).signed(true).digest(true);
202        Ok(())
203    }
204
205    pub fn decrypt_file<O: Write>(&self, src: &DirectoryContentPath, dst: O, private_key: &PrivateKey) -> DecryptFileResult {
206        if self.get_directory_content_soft().is_none() {
207            return Err(EncryptedFileError::ContentIsUnknown.into());
208        }
209        let file = self.get_directory_content_soft().unwrap().get_file(src).ok_or(EncryptedFileError::FileDoesNotExist)?;
210        if !file.has_content() {
211            Err(EncryptedFileError::FileContentIsMissing.into())
212        }
213        else if !file.has_key() {
214            Err(EncryptedFileError::FileKeyIsMissing.into())
215        }
216        else {
217            Ok(self.decrypt_file_digest(src, dst, private_key)?.1)
218        }
219    }
220
221
222    pub fn decrypt_file_and_verify<O: Write>(&self, src: &DirectoryContentPath, dst: O, private_key: &PrivateKey, public_key: &RsaPublicKey) -> DecryptFileAndVerifyResult {
223        if self.get_directory_content_soft().is_none() {
224            return Err(EncryptedFileError::ContentIsUnknown.into());
225        }
226        let file = self.get_directory_content_soft().unwrap().get_file(src).ok_or(EncryptedFileError::FileDoesNotExist)?;
227        if !file.has_content() {
228            Err(EncryptedFileError::FileContentIsMissing.into())
229        }
230        else if !file.has_key() {
231            Err(EncryptedFileError::FileKeyIsMissing.into())
232        }
233        else if !file.is_signed() {
234            Err(EncryptedFileError::FileIsNotSigned.into())
235        }
236        else {
237            let (dig, ans) = self.decrypt_file_digest(src, dst, private_key)?;
238            let signature_verification = self.verify_signature(src, dig.as_ref(), public_key);
239            Ok((ans, signature_verification))
240        }
241    }
242
243    #[cfg(feature = "signers-list")]
244    pub fn decrypt_file_and_find_signer<O: Write>(&self, src: &DirectoryContentPath, dst: O, private_key: &PrivateKey, signers_list: &SignersList) -> DecryptFileAndFindSignerResult {
245        if self.get_directory_content_soft().is_none() {
246            return Err(EncryptedFileError::ContentIsUnknown.into());
247        }
248        let file = self.get_directory_content_soft().unwrap().get_file(src).ok_or(EncryptedFileError::FileDoesNotExist)?;
249        if !file.has_content() {
250            Err(EncryptedFileError::FileContentIsMissing.into())
251        }
252        else if !file.has_key() {
253            Err(EncryptedFileError::FileKeyIsMissing.into())
254        }
255        else if !file.is_signed() {
256            Err(EncryptedFileError::FileIsNotSigned.into())
257        }
258        else {
259            let (digest, is_valid) = self.decrypt_file_digest(src, dst, private_key)?;
260            for (signer, key) in signers_list {
261                if self.verify_signature(src, &digest, &key).is_ok() {
262                    return Ok((is_valid, Some(signer.to_owned())));
263                }
264            }
265            Ok((is_valid, None))
266        }
267    }
268
269    fn add_dir<P, E, R>(&mut self, src: P, mut dst: DirectoryContentPath, encryptor: &mut E) -> EncryptedFileResult<Vec<(Box<Path>, EncryptedFileResult<R>)>> 
270        where P: AsRef<Path>,
271        E: FnMut(&mut Self, &Path, &DirectoryContentPath) -> EncryptedFileResult<R> {
272            if !src.as_ref().is_dir() {
273                return Err(EncryptedFileError::ThisIsNotADirectory.into());
274            }
275            dst.push(src.as_ref().file_name().ok_or(EncryptedFileError::InvalidPath)?.to_str().ok_or(EncryptedFileError::InvalidPath)?).expect("Names are not empty");
276            let mut ans = Vec::new();
277            for path in src.as_ref().read_dir()?.filter(|path| path.is_ok()).map(|path| path.unwrap()) {
278                if path.path().is_file() {
279                    dst.push(path.file_name().to_str().unwrap()).expect("Names are not empty");
280                    ans.push((Box::from(path.path()), encryptor(self, path.path().as_path(), &dst)));
281                    dst.pop();
282                }
283                else {
284                    match self.add_dir(path.path(), dst.clone(), encryptor) {
285                        Ok(mut res) => ans.append(&mut res),
286                        Err(err) => ans.push((Box::from(path.path()), Err(err))),
287                    }
288                }
289            }
290            Ok(ans)
291    }
292
293    fn count_files<P: AsRef<Path>>(path: P) -> EncryptedFileResult<usize> {
294        let mut ans = 0;
295        for obj in path.as_ref().read_dir()?.filter(|path| path.is_ok()).map(|path| path.unwrap()) {
296            ans += match obj.path().is_dir() {
297                true => Self::count_files(obj.path())?,
298                false => 1,
299            }
300        }
301        Ok(ans)
302    }
303
304    pub fn add_directory<P: AsRef<Path>>(&mut self, src: P, dst: DirectoryContentPath, public_key: &PublicKey) -> EncryptedFileResult<Vec<(Box<Path>, EncryptedFileResult<()>)>> {
305        self.add_directory_callback(src, dst, public_key, |_| {}, |_, _, _| {}, |_| {})
306    }
307
308    pub fn add_directory_callback<P, B, E, A>(&mut self, src: P, dst: DirectoryContentPath, public_key: &PublicKey, before: B, mut callback: E, after: A) -> EncryptedFileResult<Vec<(Box<Path>, EncryptedFileResult<()>)>>
309        where P: AsRef<Path>,
310        B: FnOnce(usize),
311        E: FnMut(&str, &DirectoryContentPath, &EncryptedFileResult<()>),
312        A: FnOnce(bool) {
313        before(Self::count_files(src.as_ref())?);
314        let ans = self.add_dir(src, dst, &mut |s, src, dst_path| {
315            let file = File::open(src)?;
316            let ans = match file.metadata() {
317                Ok(metadata) => {
318                    s.file_options = s.file_options.large_file(metadata.len() >= 4*1024*1024*1024);
319                    s.add_file(file, dst_path, public_key)
320                }
321                Err(err) => Err(EncryptedFileError::from(err)),
322            };
323            callback(src.to_str().unwrap(), &dst_path, &ans);
324            ans
325        });
326        after(ans.is_ok());
327        ans
328    }
329
330    pub fn add_directory_and_sign<P: AsRef<Path>>(&mut self, src: P, dst: DirectoryContentPath, public_key: &PublicKey, private_key: &RsaPrivateKey) -> EncryptedFileResult<Vec<(Box<Path>, EncryptedFileResult<()>)>> {
331        self.add_directory_and_sign_callback(src, dst, public_key, private_key, |_| {}, |_, _, _| {}, |_| {})
332    }
333
334    // Directory is not signed, but only every file it contains
335    pub fn add_directory_and_sign_callback<P, B, E, A>(&mut self, src: P, dst: DirectoryContentPath, public_key: &PublicKey, private_key: &RsaPrivateKey, before: B, mut callback: E, after: A) -> EncryptedFileResult<Vec<(Box<Path>, EncryptedFileResult<()>)>>
336        where P: AsRef<Path>,
337        B: FnOnce(usize),
338        E: FnMut(&str, &DirectoryContentPath, &EncryptedFileResult<()>),
339        A: FnOnce(bool) {
340        before(Self::count_files(src.as_ref())?);
341        let ans = self.add_dir(src, dst, &mut |s, src, dst_path| {
342            let file = File::open(src)?;
343            let ans = match file.metadata() {
344                Ok(metadata) => {
345                    s.file_options = s.file_options.large_file(metadata.len() >= 4*1024*1024*1024);
346                    s.add_file_and_sign(file, dst_path, public_key, private_key)
347                }
348                Err(err) => Err(EncryptedFileError::from(err)),
349            };
350            callback(src.to_str().unwrap(), dst_path, &ans);
351            ans
352        });
353        after(ans.is_ok());
354        ans
355    }
356
357    fn decrypt_dir<P, D, R>(&self, mut src: DirectoryContentPath, dst: P, decryptor: &mut D) -> EncryptedFileResult<Vec<(String, EncryptedFileResult<R>)>>
358        where P: AsRef<Path>,
359        D: FnMut(DirectoryContentPath, &Path) -> EncryptedFileResult<R> {
360        if self.get_directory_content_soft().ok_or(EncryptedFileError::ContentIsUnknown)?.get_dir(&src).is_none() {
361            return Err(EncryptedFileError::DirectoryDoesNotExist.into());
362        }
363        if !dst.as_ref().is_dir() {
364            return Err(EncryptedFileError::ThisIsNotADirectory.into());
365        }
366        let dst: Box<Path> = Box::from(dst.as_ref().join(src.file_name().unwrap_or("content")).as_path());
367        std::fs::create_dir_all(dst.as_ref())?;
368        let mut ans = Vec::new();
369        let directory_content = self.directory_content.as_ref().unwrap().get_dir(&src).ok_or(EncryptedFileError::DirectoryDoesNotExist)?;
370        for (name, _) in directory_content.get_files_iter() {
371            src.push(&name).expect("Names are not empty");
372            ans.push((name.to_owned(), decryptor(src.clone(), dst.join(name).as_path())));
373            src.pop();
374        }
375        for (name, _) in directory_content.get_dir_iter() {
376            src.push(&name).expect("Names are not empty");
377            ans.append(&mut self.decrypt_dir(src.clone(), dst.as_ref(), decryptor)?);
378            src.pop();
379        }
380        Ok(ans)
381    }
382
383    pub fn decrypt_directory<P: AsRef<Path>>(&self, src: DirectoryContentPath, dst: P, private_key: &PrivateKey) -> EncryptedFileResult<Vec<(String, DecryptFileResult)>> {
384        self.decrypt_directory_callback(src, dst, private_key, |_| {},|_, _, _| {}, |_| {})
385    }
386
387    pub fn decrypt_directory_callback<P, B, E, A>(&self, src: DirectoryContentPath, dst: P, private_key: &PrivateKey, before: B, mut callback: E, after: A) -> EncryptedFileResult<Vec<(String, DecryptFileResult)>>
388        where P: AsRef<Path>,
389        B: FnOnce(usize),
390        E: FnMut(&DirectoryContentPath, &str, &DecryptFileResult),
391        A: FnOnce(bool) {
392        before(self.get_directory_content_soft().ok_or(EncryptedFileError::ContentIsUnknown)?.get_dir(&src).ok_or(EncryptedFileError::DirectoryDoesNotExist)?.get_total_file_count());
393        let ans = self.decrypt_dir(src, dst, &mut |src, dst| {
394            let ans = self.decrypt_file(&src, File::create(dst)?, private_key);
395            callback(&src, dst.to_str().unwrap(), &ans);
396            ans
397        });
398        after(ans.is_ok());
399        ans
400    }
401
402    pub fn decrypt_directory_and_verify<P: AsRef<Path>>(&self, src: DirectoryContentPath, dst: P, private_key: &PrivateKey, public_key: &RsaPublicKey) -> EncryptedFileResult<Vec<(String, DecryptFileAndVerifyResult)>> {
403        self.decrypt_directory_and_verify_callback(src, dst, private_key, public_key, |_| {},|_, _, _| {}, |_| {})
404    }
405
406    pub fn decrypt_directory_and_verify_callback<P, B, E, A>(&self, src: DirectoryContentPath, dst: P, private_key: &PrivateKey, public_key: &RsaPublicKey, before: B, mut callback: E, after: A) -> EncryptedFileResult<Vec<(String, DecryptFileAndVerifyResult)>>
407        where P: AsRef<Path>,
408        B: FnOnce(usize),
409        E: FnMut(&DirectoryContentPath, &str, &DecryptFileAndVerifyResult),
410        A: FnOnce(bool) {
411        before(self.get_directory_content_soft().ok_or(EncryptedFileError::ContentIsUnknown)?.get_dir(&src).ok_or(EncryptedFileError::DirectoryDoesNotExist)?.get_total_file_count());
412        let ans = self.decrypt_dir(src, dst, &mut |src, dst| {
413            let ans = self.decrypt_file_and_verify(&src, File::create(dst)?, private_key, public_key);
414            callback(&src, dst.to_str().unwrap(), &ans);
415            ans
416        });
417        after(ans.is_ok());
418        ans
419    }
420
421    #[cfg(feature = "signers-list")]
422    pub fn decrypt_directory_and_find_signer<P: AsRef<Path>>(&self, src: DirectoryContentPath, dst: P, private_key: &PrivateKey, signers_list: &SignersList) -> EncryptedFileResult<Vec<(String, DecryptFileAndFindSignerResult)>> {
423        self.decrypt_directory_and_find_signer_callback(src, dst, private_key, signers_list, |_| {},|_, _, _| {}, |_| {})
424    }
425
426    #[cfg(feature = "signers-list")]
427    pub fn decrypt_directory_and_find_signer_callback<P, B, E, A>(&self, src: DirectoryContentPath, dst: P, private_key: &PrivateKey, signers_list: &SignersList, before: B, mut callback: E, after: A) -> EncryptedFileResult<Vec<(String, DecryptFileAndFindSignerResult)>>
428        where P: AsRef<Path>,
429        B: FnOnce(usize),
430        E: FnMut(&DirectoryContentPath, &str, &DecryptFileAndFindSignerResult),
431        A: FnOnce(bool) {
432        before(self.get_directory_content_soft().ok_or(EncryptedFileError::ContentIsUnknown)?.get_dir(&src).ok_or(EncryptedFileError::DirectoryDoesNotExist)?.get_total_file_count());
433        let ans = self.decrypt_dir(src, dst, &mut |src, dst| {
434            let ans = self.decrypt_file_and_find_signer(&src, File::create(dst)?, private_key, signers_list);
435            callback(&src, dst.to_str().unwrap(), &ans);
436            ans
437        });
438        after(ans.is_ok());
439        ans
440    }
441
442    // Impossible with current ways
443    // pub fn add_empty_directory(&mut self, path: &str) -> SResult<()> {
444    //     self.get_directory_content()?;
445    //     let result = self.directory_content.as_mut().unwrap().add_directory(path);
446    //     if let Err(err) = result {
447    //         Err(err.into())
448    //     }
449    //     else {
450    //         if let Err(err) = ZipWriter::new_append(&self.file)?.add_directory(format!("{FILE_CONETENT_DIR}/{path}"), self.file_options) {
451    //             Err(err.into())
452    //         }
453    //         else {
454    //             Ok(())
455    //         }
456    //     }
457    // }
458
459    pub fn delete_path<O: Write + Seek>(&self, output: O, to_delete: &Vec<DirectoryContentPath>) -> EncryptedFileResult<()> {
460        let mut output = ZipWriter::new(output);
461        let mut zip = ZipArchive::new(&self.file)?;
462        let trie = {
463            let mut trie_builder:TrieBuilder<&str> = TrieBuilder::new();
464            for path in to_delete {
465                trie_builder.push(path.iter().map(|v| v.as_str()).collect::<Vec<&str>>());
466            }
467            trie_builder.build()
468        };
469        for file_name in zip.file_names().map(|s| s.to_owned()).collect::<Vec<String>>() {
470            if trie.common_prefix_search(&DirectoryContentPath::from(file_name.clone()).iter().skip(1).rev().skip(1).rev().map(|v| v.as_str()).collect::<Vec<&str>>()).is_empty() {
471                output.raw_copy_file(zip.by_name(&file_name)?)?;
472            }
473        }
474        Ok(())
475    }
476}
477
478#[cfg(test)]
479mod tests;