zip-rs-wasm/src/types.rs
2018-06-20 01:38:43 +02:00

136 lines
4 KiB
Rust

//! 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<u8>,
/// 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"));
}
}