Added support for extra field and utf-8 filenames

This commit is contained in:
Mathijs van de Nes 2014-09-14 21:55:02 +02:00
parent 723e8b0b12
commit 8921d3b5c2
4 changed files with 55 additions and 20 deletions

View file

@ -18,7 +18,7 @@ fn doit(filename: &str) -> std::io::IoResult<()>
let mut zip = zip::ZipWriter::new(file); let mut zip = zip::ZipWriter::new(file);
try!(zip.start_file("test/hello_world.txt", zip::types::Stored)); try!(zip.start_file("test/.txt", zip::types::Stored));
try!(zip.write(b"Hello, World!\n")); try!(zip.write(b"Hello, World!\n"));
try!(zip.start_file("test/lorem_ipsum.txt", zip::types::Deflated)); try!(zip.start_file("test/lorem_ipsum.txt", zip::types::Deflated));

View file

@ -5,6 +5,7 @@ pub fn to_string(input: &[u8]) -> String
input.iter().map(|c| to_char(*c)).collect() input.iter().map(|c| to_char(*c)).collect()
} }
#[allow(dead_code)]
pub fn from_string(input: &str) -> Vec<u8> pub fn from_string(input: &str) -> Vec<u8>
{ {
input.chars().map(|c| from_char(c)).collect() input.chars().map(|c| from_char(c)).collect()

View file

@ -40,7 +40,7 @@ pub fn central_header_to_zip_file<R: Reader+Seek>(reader: &mut R) -> IoResult<Zi
try!(reader.read_le_u32()); try!(reader.read_le_u32());
let offset = try!(reader.read_le_u32()) as i64; let offset = try!(reader.read_le_u32()) as i64;
let file_name_raw = try!(reader.read_exact(file_name_length)); let file_name_raw = try!(reader.read_exact(file_name_length));
try!(reader.read_exact(extra_field_length)); let extra_field = try!(reader.read_exact(extra_field_length));
let file_comment_raw = try!(reader.read_exact(file_comment_length)); let file_comment_raw = try!(reader.read_exact(file_comment_length));
let file_name = match is_utf8 let file_name = match is_utf8
@ -75,7 +75,7 @@ pub fn central_header_to_zip_file<R: Reader+Seek>(reader: &mut R) -> IoResult<Zi
let data_start = offset as u64 + magic_and_header + file_name_length + extra_field_length; let data_start = offset as u64 + magic_and_header + file_name_length + extra_field_length;
// Construct the result // Construct the result
let result = ZipFile let mut result = ZipFile
{ {
encrypted: encrypted, encrypted: encrypted,
compression_method: FromPrimitive::from_u16(compression_method).unwrap_or(types::Unknown), compression_method: FromPrimitive::from_u16(compression_method).unwrap_or(types::Unknown),
@ -89,17 +89,35 @@ pub fn central_header_to_zip_file<R: Reader+Seek>(reader: &mut R) -> IoResult<Zi
data_start: data_start, data_start: data_start,
}; };
try!(parse_extra_field(&mut result, extra_field.as_slice()));
// Go back after the central header // Go back after the central header
try!(reader.seek(return_position, io::SeekSet)); try!(reader.seek(return_position, io::SeekSet));
Ok(result) 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<T: Writer>(writer: &mut T, file: &ZipFile) -> IoResult<()> pub fn write_local_file_header<T: Writer>(writer: &mut T, file: &ZipFile) -> IoResult<()>
{ {
try!(writer.write_le_u32(LOCAL_FILE_HEADER_SIGNATURE)); try!(writer.write_le_u32(LOCAL_FILE_HEADER_SIGNATURE));
try!(writer.write_le_u16(20)); try!(writer.write_le_u16(20));
let flag = if file.encrypted { 1 } else { 0 }; let flag = if !file.file_name.is_ascii() { 1u16 << 11 } else { 0 };
try!(writer.write_le_u16(flag)); try!(writer.write_le_u16(flag));
try!(writer.write_le_u16(file.compression_method as u16)); 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_time(file.last_modified_time)));
@ -107,9 +125,11 @@ pub fn write_local_file_header<T: Writer>(writer: &mut T, file: &ZipFile) -> IoR
try!(writer.write_le_u32(file.crc32)); try!(writer.write_le_u32(file.crc32));
try!(writer.write_le_u32(file.compressed_size as u32)); try!(writer.write_le_u32(file.compressed_size as u32));
try!(writer.write_le_u32(file.uncompressed_size as u32)); try!(writer.write_le_u32(file.uncompressed_size as u32));
try!(writer.write_le_u16(file.file_name.len() as u16)); try!(writer.write_le_u16(file.file_name.as_bytes().len() as u16));
try!(writer.write_le_u16(0)); let extra_field = try!(build_extra_field(file));
try!(writer.write(::cp437::from_string(file.file_name.as_slice()).as_slice())); 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(()) Ok(())
} }
@ -117,9 +137,9 @@ pub fn write_local_file_header<T: Writer>(writer: &mut T, file: &ZipFile) -> IoR
pub fn write_central_directory_header<T: Writer>(writer: &mut T, file: &ZipFile) -> IoResult<()> pub fn write_central_directory_header<T: Writer>(writer: &mut T, file: &ZipFile) -> IoResult<()>
{ {
try!(writer.write_le_u32(CENTRAL_DIRECTORY_HEADER_SIGNATURE)); try!(writer.write_le_u32(CENTRAL_DIRECTORY_HEADER_SIGNATURE));
try!(writer.write_le_u16(0x00FF)); try!(writer.write_le_u16(0x14FF));
try!(writer.write_le_u16(20)); try!(writer.write_le_u16(20));
let flag = if file.encrypted { 1 } else { 0 }; let flag = if !file.file_name.is_ascii() { 1u16 << 11 } else { 0 };
try!(writer.write_le_u16(flag)); try!(writer.write_le_u16(flag));
try!(writer.write_le_u16(file.compression_method as u16)); 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_time(file.last_modified_time)));
@ -127,18 +147,27 @@ pub fn write_central_directory_header<T: Writer>(writer: &mut T, file: &ZipFile)
try!(writer.write_le_u32(file.crc32)); try!(writer.write_le_u32(file.crc32));
try!(writer.write_le_u32(file.compressed_size as u32)); try!(writer.write_le_u32(file.compressed_size as u32));
try!(writer.write_le_u32(file.uncompressed_size as u32)); try!(writer.write_le_u32(file.uncompressed_size as u32));
try!(writer.write_le_u16(file.file_name.len() as u16)); try!(writer.write_le_u16(file.file_name.as_bytes().len() as u16));
try!(writer.write_le_u16(0)); 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_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(0));
try!(writer.write_le_u32(file.header_start as u32)); try!(writer.write_le_u32(file.header_start as u32));
try!(writer.write(::cp437::from_string(file.file_name.as_slice()).as_slice())); try!(writer.write(file.file_name.as_bytes()));
try!(writer.write(extra_field.as_slice()));
Ok(()) Ok(())
} }
fn build_extra_field(_file: &ZipFile) -> IoResult<Vec<u8>>
{
let writer = io::MemWriter::new();
// Future work
Ok(writer.unwrap())
}
pub struct CentralDirectoryEnd pub struct CentralDirectoryEnd
{ {
pub disk_number: u16, pub disk_number: u16,

View file

@ -106,13 +106,8 @@ impl<W: Writer+Seek> ZipWriter<W>
{ {
let writer = self.inner.get_plain(); let writer = self.inner.get_plain();
let header_start = try!(writer.tell()); let header_start = try!(writer.tell());
try!(writer.seek(30 + name.len() as i64, io::SeekCur));
self.stats.start = header_start + 30 + name.len() as u64; let mut file = ZipFile
self.stats.bytes_written = 0;
self.stats.crc32 = 0;
self.files.push(ZipFile
{ {
encrypted: false, encrypted: false,
compression_method: compression, compression_method: compression,
@ -123,8 +118,18 @@ impl<W: Writer+Seek> ZipWriter<W>
file_name: String::from_str(name), file_name: String::from_str(name),
file_comment: String::new(), file_comment: String::new(),
header_start: header_start, header_start: header_start,
data_start: self.stats.start, data_start: 0,
}); };
try!(spec::write_local_file_header(writer, &file));
let header_end = try!(writer.tell());
self.stats.start = header_end;
file.data_start = header_end;
self.stats.bytes_written = 0;
self.stats.crc32 = 0;
self.files.push(file);
} }
try!(self.inner.switch_to(compression)); try!(self.inner.switch_to(compression));