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