1 //! Types that specify what is contained in a ZIP.
2 
3 use time;
4 
5 #[derive(Clone, Copy, Debug, PartialEq)]
6 pub enum System
7 {
8     Dos = 0,
9     Unix = 3,
10     Unknown,
11     #[doc(hidden)]
12     __Nonexhaustive,
13 }
14 
15 impl System {
from_u8(system: u8) -> System16     pub fn from_u8(system: u8) -> System
17     {
18         use self::System::*;
19 
20         match system {
21             0 => Dos,
22             3 => Unix,
23             _ => Unknown,
24         }
25     }
26 }
27 
28 pub const DEFAULT_VERSION: u8 = 46;
29 
30 /// Structure representing a ZIP file.
31 #[derive(Debug, Clone)]
32 pub struct ZipFileData
33 {
34     /// Compatibility of the file attribute information
35     pub system: System,
36     /// Specification version
37     pub version_made_by: u8,
38     /// True if the file is encrypted.
39     pub encrypted: bool,
40     /// Compression method used to store the file
41     pub compression_method: ::compression::CompressionMethod,
42     /// Last modified time. This will only have a 2 second precision.
43     pub last_modified_time: time::Tm,
44     /// CRC32 checksum
45     pub crc32: u32,
46     /// Size of the file in the ZIP
47     pub compressed_size: u64,
48     /// Size of the file when extracted
49     pub uncompressed_size: u64,
50     /// Name of the file
51     pub file_name: String,
52     /// Raw file name. To be used when file_name was incorrectly decoded.
53     pub file_name_raw: Vec<u8>,
54     /// File comment
55     pub file_comment: String,
56     /// Specifies where the local header of the file starts
57     pub header_start: u64,
58     /// Specifies where the compressed data of the file starts
59     pub data_start: u64,
60     /// External file attributes
61     pub external_attributes: u32,
62 }
63 
64 impl ZipFileData {
file_name_sanitized(&self) -> ::std::path::PathBuf65     pub fn file_name_sanitized(&self) -> ::std::path::PathBuf {
66         let no_null_filename = match self.file_name.find('\0') {
67             Some(index) => &self.file_name[0..index],
68             None => &self.file_name,
69         }.to_string();
70 
71         // zip files can contain both / and \ as separators regardless of the OS
72         // and as we want to return a sanitized PathBuf that only supports the
73         // OS separator let's convert incompatible separators to compatible ones
74         let separator = ::std::path::MAIN_SEPARATOR;
75         let opposite_separator = match separator {
76             '/' => '\\',
77             '\\' | _ => '/',
78         };
79         let filename =
80             no_null_filename.replace(&opposite_separator.to_string(), &separator.to_string());
81 
82         ::std::path::Path::new(&filename)
83             .components()
84             .filter(|component| match *component {
85                 ::std::path::Component::Normal(..) => true,
86                 _ => false,
87             })
88             .fold(::std::path::PathBuf::new(), |mut path, ref cur| {
89                 path.push(cur.as_os_str());
90                 path
91             })
92     }
93 
version_needed(&self) -> u1694     pub fn version_needed(&self) -> u16 {
95         match self.compression_method {
96             #[cfg(feature = "bzip2")]
97             ::compression::CompressionMethod::Bzip2 => 46,
98             _ => 20,
99         }
100     }
101 }
102 
103 #[cfg(test)]
104 mod test {
105     #[test]
system()106     fn system() {
107         use super::System;
108         assert_eq!(System::Dos as u16, 0u16);
109         assert_eq!(System::Unix as u16, 3u16);
110         assert_eq!(System::from_u8(0), System::Dos);
111         assert_eq!(System::from_u8(3), System::Unix);
112     }
113 
114     #[test]
sanitize()115     fn sanitize() {
116         use super::*;
117         let file_name = "/path/../../../../etc/./passwd\0/etc/shadow".to_string();
118         let data = ZipFileData {
119             system: System::Dos,
120             version_made_by: 0,
121             encrypted: false,
122             compression_method: ::compression::CompressionMethod::Stored,
123             last_modified_time: time::empty_tm(),
124             crc32: 0,
125             compressed_size: 0,
126             uncompressed_size: 0,
127             file_name: file_name.clone(),
128             file_name_raw: file_name.into_bytes(),
129             file_comment: String::new(),
130             header_start: 0,
131             data_start: 0,
132             external_attributes: 0,
133         };
134         assert_eq!(data.file_name_sanitized(), ::std::path::PathBuf::from("path/etc/passwd"));
135     }
136 }
137