// DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE // Version 2, December 2004 // // Copyright (C) 2018 Thomas Bailleux // // Everyone is permitted to copy and distribute verbatim or modified // copies of this license document, and changing it is allowed as long // as the name is changed. // // DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE // TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION // // 0. You just DO WHAT THE FUCK YOU WANT TO. // // Author: zadig use std; /// An OLE file reader. /// /// The parsing method follows the same method described here: /// /// /// # Basic Example /// /// ``` /// use ole::Reader; /// /// let mut reader = /// Reader::from_path("assets/Thumbs.db").unwrap(); /// /// println!("This OLE file stores the following entries: "); /// for entry in reader.iterate() { /// println!("{}", entry); /// } /// ``` pub struct Reader<'ole> { /// Buffer for reading from the source. pub(crate) buf_reader: Option>>, /// Unique identifier. pub(crate) uid: std::vec::Vec, /// Revision number. pub(crate) revision_number: Option, /// Version number. pub(crate) version_number: Option, /// Size of one sector. pub(crate) sec_size: Option, /// Size of one short sector. pub(crate) short_sec_size: Option, /// Sector Allocation Table. pub(crate) sat: Option>, /// Directory Sector Allocation Table. pub(crate) dsat: Option>, /// Minimum size of a standard stream size. pub(crate) minimum_standard_stream_size: Option, /// Short Sector Allocation Table. pub(crate) ssat: Option>, /// Master Sector Allocation Table. pub(crate) msat: Option>, /// Body of the file. pub(crate) body: Option>, /// Directory entries. pub(crate) entries: Option>, /// DirID of the root entry. pub(crate) root_entry: Option } impl<'ole> Reader<'ole> { /// Constructs a new `Reader`. /// /// # Examples /// /// ``` /// use ole; /// let mut my_resume = std::fs::File::open("assets/Thumbs.db").unwrap(); /// let mut parser = ole::Reader::new(my_resume).unwrap(); /// ``` pub fn new(readable: T) -> std::result::Result, super::error::Error> where T: std::io::Read { let mut t = Reader { buf_reader: Some(std::io::BufReader::new(Box::new(readable))), uid: vec![0u8; super::constants::UID_SIZE], revision_number: None, version_number: None, sec_size: None, short_sec_size: None, sat: None, dsat: None, minimum_standard_stream_size: None, ssat: None, msat: None, body: None, entries: None, root_entry: None }; t.parse_header()?; t.build_sat()?; t.build_directory_entries()?; Ok(t) } /// Constructs a new `Reader` from a file. /// /// # Examples /// /// ``` /// use ole; /// let mut parser = ole::Reader::from_path("assets/Thumbs.db").unwrap(); /// ``` pub fn from_path(path: &str) -> Result { let f = std::fs::File::open(path).map_err(super::error::Error::IOError)?; Reader::new(f) } /// Returns an iterator for directory entries of the OLE file. /// /// # Examples /// /// ``` /// use ole; /// let mut parser = ole::Reader::from_path("assets/Thumbs.db").unwrap(); /// /// for entry in parser.iterate() { /// println!("Entry {}", entry.name()); /// } /// ``` pub fn iterate(&self) -> super::iterator::OLEIterator { super::iterator::OLEIterator::new(self) } /// Read some bytes from the source. pub(crate) fn read(&mut self, buf: &mut [u8]) -> Result { use std::io::Read; self.buf_reader.as_mut().unwrap().read_exact(buf) .map_err(super::error::Error::IOError)?; Ok(buf.len()) } } #[cfg(test)] mod tests { use std; use super::Reader; use std::error::Error as e; use super::super::error::Error; #[test] fn instance_nok() { let path = "Thumbs.db"; let o : Result = Reader::from_path(path); assert_eq!(o.is_ok(), false); let e = o.err().unwrap(); println!("NOK: {}", e.description()); } #[test] fn instance_ok() { let path = "./assets/Thumbs.db"; let o: Result = Reader::from_path(path); assert_eq!(o.is_ok(), true); } #[test] fn sector_sizes() { let ole: Reader = Reader::from_path("./assets/Thumbs.db").unwrap(); assert_eq!(ole.sec_size, Some(512)); assert_eq!(ole.short_sec_size, Some(64)); } #[test] fn array_bad_identifier() { let mut vec = super::super::constants::IDENTIFIER.to_vec(); vec[0] = 0xD1; fill(&mut vec); let ole = Reader::new(&vec[..]); assert_eq!(ole.is_ok(), false); println!("BAD IDENTIFIER: {}", ole.err().unwrap()); } fn fill(buf: &mut std::vec::Vec) { let missing = vec![0u8; super::super::constants::HEADER_SIZE - buf.len()]; buf.extend(missing); } #[test] fn array_bad_endianness_identifier() { let mut vec = super::super::constants::IDENTIFIER.to_vec(); vec.extend(vec![0u8; 20]); vec.push(0xFE); vec.push(0xFE); fill(&mut vec); let ole = Reader::new(&vec[..]); assert_eq!(ole.is_ok(), false); println!("BAD ENDIANNESS: {}", ole.err().unwrap()); } #[test] fn uid() { let ole = Reader::from_path("./assets/Thumbs.db"); assert_eq!(ole.is_ok(), true); let ole = ole.unwrap(); assert_eq!(&[0x0u8; 16] == &ole.uid[..], true); } #[test] fn bad_sec_size() { let mut vec = super::super::constants::IDENTIFIER.to_vec(); vec.extend(vec![0x42u8; 20]); vec.extend(&super::super::constants::LITTLE_ENDIAN_IDENTIFIER); vec.extend(vec![0xFF, 0xFF, 0xFF, 0xFF]); vec.extend(vec![0u8; 10]); vec.extend(vec![0xFF, 0xFF, 0xFF, 0xFF]); fill(&mut vec); let ole = Reader::new(&vec[..]); assert_eq!(ole.is_ok(), false); } #[test] fn several_values() { let ole = Reader::from_path("./assets/Thumbs.db").unwrap(); assert_eq!(ole.sat.as_ref().unwrap().capacity(), 128usize); assert_eq!(ole.msat.as_ref().unwrap().len(), 1usize); assert_eq!(ole.ssat.as_ref().unwrap().capacity(), 512usize); } #[test] fn print_things() { use std::io::{Read, Write}; let ole = Reader::from_path("./assets/sample.ppt").unwrap(); println!("STREAM SIZE: {}", *ole.minimum_standard_stream_size.as_ref().unwrap()); println!("MSAT: {:?}", ole.msat.as_ref().unwrap()); println!("SAT: {:?}", ole.sat.as_ref().unwrap()); println!("SSAT: {:?}", ole.ssat.as_ref().unwrap()); println!("DSAT: {:?}", ole.dsat.as_ref().unwrap()); for entry in ole.iterate() { println!("{}", entry); if let Ok(mut slice) = ole.get_entry_slice(entry) { let mut buf = vec![0u8; slice.len()]; let read_size = slice.read(&mut buf).unwrap(); let mut file = std::fs::File::create(format!("./assets/streams/{}.bin", entry.name())).unwrap(); println!("Real len: {}", slice.real_len()); file.write_all(&buf).unwrap(); assert_eq!(read_size, slice.real_len()); assert_eq!(read_size, slice.len()); } } } }