//! Types that specify what is contained in a ZIP. use time; #[derive(Clone, Copy, Debug, PartialEq)] pub enum System { Dos = 0, Unix = 3, Unknown, #[doc(hidden)] __Nonexhaustive, } impl System { pub fn from_u8(system: u8) -> System { use self::System::*; match system { 0 => Dos, 3 => Unix, _ => Unknown, } } } pub const DEFAULT_VERSION: u8 = 46; /// Structure representing a ZIP file. #[derive(Debug, Clone)] pub struct ZipFileData { /// Compatibility of the file attribute information pub system: System, /// Specification version pub version_made_by: u8, /// True if the file is encrypted. pub encrypted: bool, /// Compression method used to store the file pub compression_method: ::compression::CompressionMethod, /// Last modified time. This will only have a 2 second precision. pub last_modified_time: time::Tm, /// CRC32 checksum pub crc32: u32, /// Size of the file in the ZIP pub compressed_size: u64, /// Size of the file when extracted pub uncompressed_size: u64, /// Name of the file pub file_name: String, /// Raw file name. To be used when file_name was incorrectly decoded. pub file_name_raw: Vec, /// File comment pub file_comment: String, /// Specifies where the local header of the file starts pub header_start: u64, /// Specifies where the compressed data of the file starts pub data_start: u64, /// External file attributes pub external_attributes: u32, } impl ZipFileData { pub fn file_name_sanitized(&self) -> ::std::path::PathBuf { let no_null_filename = match self.file_name.find('\0') { Some(index) => &self.file_name[0..index], None => &self.file_name, }.to_string(); // zip files can contain both / and \ as separators regardless of the OS // and as we want to return a sanitized PathBuf that only supports the // OS separator let's convert incompatible separators to compatible ones let separator = ::std::path::MAIN_SEPARATOR; let opposite_separator = match separator { '/' => '\\', '\\' | _ => '/', }; let filename = no_null_filename.replace(&opposite_separator.to_string(), &separator.to_string()); ::std::path::Path::new(&filename) .components() .filter(|component| match *component { ::std::path::Component::Normal(..) => true, _ => false, }) .fold(::std::path::PathBuf::new(), |mut path, ref cur| { path.push(cur.as_os_str()); path }) } pub fn version_needed(&self) -> u16 { match self.compression_method { #[cfg(feature = "bzip2")] ::compression::CompressionMethod::Bzip2 => 46, _ => 20, } } } #[cfg(test)] mod test { #[test] fn system() { use super::System; assert_eq!(System::Dos as u16, 0u16); assert_eq!(System::Unix as u16, 3u16); assert_eq!(System::from_u8(0), System::Dos); assert_eq!(System::from_u8(3), System::Unix); } #[test] fn sanitize() { use super::*; let file_name = "/path/../../../../etc/./passwd\0/etc/shadow".to_string(); let data = ZipFileData { system: System::Dos, version_made_by: 0, encrypted: false, compression_method: ::compression::CompressionMethod::Stored, last_modified_time: time::empty_tm(), crc32: 0, compressed_size: 0, uncompressed_size: 0, file_name: file_name.clone(), file_name_raw: file_name.into_bytes(), file_comment: String::new(), header_start: 0, data_start: 0, external_attributes: 0, }; assert_eq!(data.file_name_sanitized(), ::std::path::PathBuf::from("path/etc/passwd")); } }