From 91cbe91d6286a1944cf7cfbc44972eff939831b3 Mon Sep 17 00:00:00 2001 From: Mathijs van de Nes Date: Wed, 17 Sep 2014 15:01:22 +0200 Subject: [PATCH] Split spec in reader/writer/general --- src/lib.rs | 2 + src/reader.rs | 5 +- src/reader_spec.rs | 110 ++++++++++++++++++++++++++++++ src/spec.rs | 166 +-------------------------------------------- src/writer.rs | 7 +- src/writer_spec.rs | 60 ++++++++++++++++ 6 files changed, 181 insertions(+), 169 deletions(-) create mode 100644 src/reader_spec.rs create mode 100644 src/writer_spec.rs diff --git a/src/lib.rs b/src/lib.rs index a453d296..82a63844 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,6 +14,8 @@ pub use types::ZipFile; mod util; mod spec; +mod reader_spec; +mod writer_spec; mod crc32; mod reader; mod types; diff --git a/src/reader.rs b/src/reader.rs index e24cd4da..45e47123 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -1,7 +1,8 @@ -use spec; use crc32::Crc32Reader; use types::ZipFile; use compression; +use spec; +use reader_spec; use std::io; use std::io::{IoResult, IoError}; use std::cell::RefCell; @@ -64,7 +65,7 @@ impl ZipReader try!(reader.seek(directory_start, io::SeekSet)); for i in range(0, number_of_files) { - files.push(try!(spec::central_header_to_zip_file(&mut reader))); + files.push(try!(reader_spec::central_header_to_zip_file(&mut reader))); } Ok(ZipReader { inner: RefCell::new(reader), files: files }) diff --git a/src/reader_spec.rs b/src/reader_spec.rs new file mode 100644 index 00000000..70c4aaa9 --- /dev/null +++ b/src/reader_spec.rs @@ -0,0 +1,110 @@ +use std::io; +use std::io::{IoResult, IoError}; +use compression; +use types::ZipFile; +use spec; +use util; + +pub fn central_header_to_zip_file(reader: &mut R) -> IoResult +{ + // Parse central header + let signature = try!(reader.read_le_u32()); + if signature != spec::CENTRAL_DIRECTORY_HEADER_SIGNATURE + { + return Err(IoError { + kind: io::MismatchedFileTypeForOperation, + desc: "Invalid central directory header", + detail: None }) + } + + try!(reader.read_le_u16()); + try!(reader.read_le_u16()); + let flags = try!(reader.read_le_u16()); + let encrypted = flags & 1 == 1; + let is_utf8 = flags & (1 << 11) != 0; + let compression_method = try!(reader.read_le_u16()); + let last_mod_time = try!(reader.read_le_u16()); + let last_mod_date = try!(reader.read_le_u16()); + let crc32 = try!(reader.read_le_u32()); + let compressed_size = try!(reader.read_le_u32()); + let uncompressed_size = try!(reader.read_le_u32()); + let file_name_length = try!(reader.read_le_u16()) as uint; + let extra_field_length = try!(reader.read_le_u16()) as uint; + let file_comment_length = try!(reader.read_le_u16()) as uint; + try!(reader.read_le_u16()); + try!(reader.read_le_u16()); + try!(reader.read_le_u32()); + let offset = try!(reader.read_le_u32()) as i64; + let file_name_raw = try!(reader.read_exact(file_name_length)); + let extra_field = try!(reader.read_exact(extra_field_length)); + let file_comment_raw = try!(reader.read_exact(file_comment_length)); + + let file_name = match is_utf8 + { + true => String::from_utf8_lossy(file_name_raw.as_slice()).into_string(), + false => ::cp437::to_string(file_name_raw.as_slice()), + }; + let file_comment = match is_utf8 + { + true => String::from_utf8_lossy(file_comment_raw.as_slice()).into_string(), + false => ::cp437::to_string(file_comment_raw.as_slice()), + }; + + // Remember end of central header + let return_position = try!(reader.tell()) as i64; + + // Parse local header + try!(reader.seek(offset, io::SeekSet)); + let signature = try!(reader.read_le_u32()); + if signature != spec::LOCAL_FILE_HEADER_SIGNATURE + { + return Err(IoError { + kind: io::MismatchedFileTypeForOperation, + desc: "Invalid local file header", + detail: None }) + } + + try!(reader.seek(22, io::SeekCur)); + let file_name_length = try!(reader.read_le_u16()) as u64; + let extra_field_length = try!(reader.read_le_u16()) as u64; + let magic_and_header = 4 + 22 + 2 + 2; + let data_start = offset as u64 + magic_and_header + file_name_length + extra_field_length; + + // Construct the result + let mut result = ZipFile + { + encrypted: encrypted, + compression_method: FromPrimitive::from_u16(compression_method).unwrap_or(compression::Unknown), + last_modified_time: util::msdos_datetime_to_tm(last_mod_time, last_mod_date), + crc32: crc32, + compressed_size: compressed_size as u64, + uncompressed_size: uncompressed_size as u64, + file_name: file_name, + file_comment: file_comment, + header_start: offset as u64, + data_start: data_start, + }; + + try!(parse_extra_field(&mut result, extra_field.as_slice())); + + // Go back after the central header + try!(reader.seek(return_position, io::SeekSet)); + + Ok(result) +} + +fn parse_extra_field(_file: &mut ZipFile, data: &[u8]) -> IoResult<()> +{ + let mut reader = io::BufReader::new(data); + while !reader.eof() + { + let kind = try!(reader.read_le_u16()); + let len = try!(reader.read_le_u16()); + debug!("Parsing extra block {:04x}", kind); + match kind + { + _ => try!(reader.seek(len as i64, io::SeekCur)), + } + } + Ok(()) +} diff --git a/src/spec.rs b/src/spec.rs index d7152189..b938dfe3 100644 --- a/src/spec.rs +++ b/src/spec.rs @@ -1,173 +1,11 @@ use std::io; use std::io::{IoResult, IoError}; use std::iter::range_step_inclusive; -use compression; -use types::ZipFile; -use util; -static LOCAL_FILE_HEADER_SIGNATURE : u32 = 0x04034b50; -static CENTRAL_DIRECTORY_HEADER_SIGNATURE : u32 = 0x02014b50; +pub static LOCAL_FILE_HEADER_SIGNATURE : u32 = 0x04034b50; +pub static CENTRAL_DIRECTORY_HEADER_SIGNATURE : u32 = 0x02014b50; static CENTRAL_DIRECTORY_END_SIGNATURE : u32 = 0x06054b50; -pub fn central_header_to_zip_file(reader: &mut R) -> IoResult -{ - // Parse central header - let signature = try!(reader.read_le_u32()); - if signature != CENTRAL_DIRECTORY_HEADER_SIGNATURE - { - return Err(IoError { - kind: io::MismatchedFileTypeForOperation, - desc: "Invalid central directory header", - detail: None }) - } - - try!(reader.read_le_u16()); - try!(reader.read_le_u16()); - let flags = try!(reader.read_le_u16()); - let encrypted = flags & 1 == 1; - let is_utf8 = flags & (1 << 11) != 0; - let compression_method = try!(reader.read_le_u16()); - let last_mod_time = try!(reader.read_le_u16()); - let last_mod_date = try!(reader.read_le_u16()); - let crc32 = try!(reader.read_le_u32()); - let compressed_size = try!(reader.read_le_u32()); - let uncompressed_size = try!(reader.read_le_u32()); - let file_name_length = try!(reader.read_le_u16()) as uint; - let extra_field_length = try!(reader.read_le_u16()) as uint; - let file_comment_length = try!(reader.read_le_u16()) as uint; - try!(reader.read_le_u16()); - try!(reader.read_le_u16()); - try!(reader.read_le_u32()); - let offset = try!(reader.read_le_u32()) as i64; - let file_name_raw = try!(reader.read_exact(file_name_length)); - let extra_field = try!(reader.read_exact(extra_field_length)); - let file_comment_raw = try!(reader.read_exact(file_comment_length)); - - let file_name = match is_utf8 - { - true => String::from_utf8_lossy(file_name_raw.as_slice()).into_string(), - false => ::cp437::to_string(file_name_raw.as_slice()), - }; - let file_comment = match is_utf8 - { - true => String::from_utf8_lossy(file_comment_raw.as_slice()).into_string(), - false => ::cp437::to_string(file_comment_raw.as_slice()), - }; - - // Remember end of central header - let return_position = try!(reader.tell()) as i64; - - // Parse local header - try!(reader.seek(offset, io::SeekSet)); - let signature = try!(reader.read_le_u32()); - if signature != LOCAL_FILE_HEADER_SIGNATURE - { - return Err(IoError { - kind: io::MismatchedFileTypeForOperation, - desc: "Invalid local file header", - detail: None }) - } - - try!(reader.seek(22, io::SeekCur)); - let file_name_length = try!(reader.read_le_u16()) as u64; - let extra_field_length = try!(reader.read_le_u16()) as u64; - let magic_and_header = 4 + 22 + 2 + 2; - let data_start = offset as u64 + magic_and_header + file_name_length + extra_field_length; - - // Construct the result - let mut result = ZipFile - { - encrypted: encrypted, - compression_method: FromPrimitive::from_u16(compression_method).unwrap_or(compression::Unknown), - last_modified_time: util::msdos_datetime_to_tm(last_mod_time, last_mod_date), - crc32: crc32, - compressed_size: compressed_size as u64, - uncompressed_size: uncompressed_size as u64, - file_name: file_name, - file_comment: file_comment, - header_start: offset as u64, - data_start: data_start, - }; - - try!(parse_extra_field(&mut result, extra_field.as_slice())); - - // Go back after the central header - try!(reader.seek(return_position, io::SeekSet)); - - Ok(result) -} - -fn parse_extra_field(_file: &mut ZipFile, data: &[u8]) -> IoResult<()> -{ - let mut reader = io::BufReader::new(data); - while !reader.eof() - { - let kind = try!(reader.read_le_u16()); - let len = try!(reader.read_le_u16()); - debug!("Parsing extra block {:04x}", kind); - match kind - { - _ => try!(reader.seek(len as i64, io::SeekCur)), - } - } - Ok(()) -} - -pub fn write_local_file_header(writer: &mut T, file: &ZipFile) -> IoResult<()> -{ - try!(writer.write_le_u32(LOCAL_FILE_HEADER_SIGNATURE)); - try!(writer.write_le_u16(20)); - let flag = if !file.file_name.is_ascii() { 1u16 << 11 } else { 0 }; - try!(writer.write_le_u16(flag)); - try!(writer.write_le_u16(file.compression_method as u16)); - try!(writer.write_le_u16(util::tm_to_msdos_time(file.last_modified_time))); - try!(writer.write_le_u16(util::tm_to_msdos_date(file.last_modified_time))); - try!(writer.write_le_u32(file.crc32)); - try!(writer.write_le_u32(file.compressed_size as u32)); - try!(writer.write_le_u32(file.uncompressed_size as u32)); - try!(writer.write_le_u16(file.file_name.as_bytes().len() as u16)); - let extra_field = try!(build_extra_field(file)); - try!(writer.write_le_u16(extra_field.len() as u16)); - try!(writer.write(file.file_name.as_bytes())); - try!(writer.write(extra_field.as_slice())); - - Ok(()) -} - -pub fn write_central_directory_header(writer: &mut T, file: &ZipFile) -> IoResult<()> -{ - try!(writer.write_le_u32(CENTRAL_DIRECTORY_HEADER_SIGNATURE)); - try!(writer.write_le_u16(0x14FF)); - try!(writer.write_le_u16(20)); - let flag = if !file.file_name.is_ascii() { 1u16 << 11 } else { 0 }; - try!(writer.write_le_u16(flag)); - try!(writer.write_le_u16(file.compression_method as u16)); - try!(writer.write_le_u16(util::tm_to_msdos_time(file.last_modified_time))); - try!(writer.write_le_u16(util::tm_to_msdos_date(file.last_modified_time))); - try!(writer.write_le_u32(file.crc32)); - try!(writer.write_le_u32(file.compressed_size as u32)); - try!(writer.write_le_u32(file.uncompressed_size as u32)); - try!(writer.write_le_u16(file.file_name.as_bytes().len() as u16)); - let extra_field = try!(build_extra_field(file)); - try!(writer.write_le_u16(extra_field.len() as u16)); - try!(writer.write_le_u16(0)); - try!(writer.write_le_u16(0)); - try!(writer.write_le_u16(0)); - try!(writer.write_le_u32(0)); - try!(writer.write_le_u32(file.header_start as u32)); - try!(writer.write(file.file_name.as_bytes())); - try!(writer.write(extra_field.as_slice())); - - Ok(()) -} - -fn build_extra_field(_file: &ZipFile) -> IoResult> -{ - let writer = io::MemWriter::new(); - // Future work - Ok(writer.unwrap()) -} - pub struct CentralDirectoryEnd { pub disk_number: u16, diff --git a/src/writer.rs b/src/writer.rs index b577950f..2b5f4d0b 100644 --- a/src/writer.rs +++ b/src/writer.rs @@ -1,6 +1,7 @@ use compression; use types::ZipFile; use spec; +use writer_spec; use crc32; use std::default::Default; use std::io; @@ -120,7 +121,7 @@ impl ZipWriter header_start: header_start, data_start: 0, }; - try!(spec::write_local_file_header(writer, &file)); + try!(writer_spec::write_local_file_header(writer, &file)); let header_end = try!(writer.tell()); self.stats.start = header_end; @@ -152,7 +153,7 @@ impl ZipWriter file.compressed_size = try!(writer.tell()) - self.stats.start; try!(writer.seek(file.header_start as i64, io::SeekSet)); - try!(spec::write_local_file_header(writer, file)); + try!(writer_spec::write_local_file_header(writer, file)); try!(writer.seek(0, io::SeekEnd)); Ok(()) } @@ -168,7 +169,7 @@ impl ZipWriter let central_start = try!(writer.tell()); for file in self.files.iter() { - try!(spec::write_central_directory_header(writer, file)); + try!(writer_spec::write_central_directory_header(writer, file)); } let central_size = try!(writer.tell()) - central_start; diff --git a/src/writer_spec.rs b/src/writer_spec.rs new file mode 100644 index 00000000..2dd8c82f --- /dev/null +++ b/src/writer_spec.rs @@ -0,0 +1,60 @@ +use std::io; +use std::io::IoResult; +use types::ZipFile; +use spec; +use util; + +pub fn write_local_file_header(writer: &mut T, file: &ZipFile) -> IoResult<()> +{ + try!(writer.write_le_u32(spec::LOCAL_FILE_HEADER_SIGNATURE)); + try!(writer.write_le_u16(20)); + let flag = if !file.file_name.is_ascii() { 1u16 << 11 } else { 0 }; + try!(writer.write_le_u16(flag)); + try!(writer.write_le_u16(file.compression_method as u16)); + try!(writer.write_le_u16(util::tm_to_msdos_time(file.last_modified_time))); + try!(writer.write_le_u16(util::tm_to_msdos_date(file.last_modified_time))); + try!(writer.write_le_u32(file.crc32)); + try!(writer.write_le_u32(file.compressed_size as u32)); + try!(writer.write_le_u32(file.uncompressed_size as u32)); + try!(writer.write_le_u16(file.file_name.as_bytes().len() as u16)); + let extra_field = try!(build_extra_field(file)); + try!(writer.write_le_u16(extra_field.len() as u16)); + try!(writer.write(file.file_name.as_bytes())); + try!(writer.write(extra_field.as_slice())); + + Ok(()) +} + +pub fn write_central_directory_header(writer: &mut T, file: &ZipFile) -> IoResult<()> +{ + try!(writer.write_le_u32(spec::CENTRAL_DIRECTORY_HEADER_SIGNATURE)); + try!(writer.write_le_u16(0x14FF)); + try!(writer.write_le_u16(20)); + let flag = if !file.file_name.is_ascii() { 1u16 << 11 } else { 0 }; + try!(writer.write_le_u16(flag)); + try!(writer.write_le_u16(file.compression_method as u16)); + try!(writer.write_le_u16(util::tm_to_msdos_time(file.last_modified_time))); + try!(writer.write_le_u16(util::tm_to_msdos_date(file.last_modified_time))); + try!(writer.write_le_u32(file.crc32)); + try!(writer.write_le_u32(file.compressed_size as u32)); + try!(writer.write_le_u32(file.uncompressed_size as u32)); + try!(writer.write_le_u16(file.file_name.as_bytes().len() as u16)); + let extra_field = try!(build_extra_field(file)); + try!(writer.write_le_u16(extra_field.len() as u16)); + try!(writer.write_le_u16(0)); + try!(writer.write_le_u16(0)); + try!(writer.write_le_u16(0)); + try!(writer.write_le_u32(0)); + try!(writer.write_le_u32(file.header_start as u32)); + try!(writer.write(file.file_name.as_bytes())); + try!(writer.write(extra_field.as_slice())); + + Ok(()) +} + +fn build_extra_field(_file: &ZipFile) -> IoResult> +{ + let writer = io::MemWriter::new(); + // Future work + Ok(writer.unwrap()) +}