use std::io::Read;
use std::path::PathBuf;
use crate::asymmetric::BottleSecretKey;
use crate::bottle::{BottleReader, BottleStream};
use crate::bottle_cap::{BottleCap, BottleType};
use crate::bottle_error::{BottleError, BottleResult};
use crate::compressed_bottle::CompressedBottleReader;
use crate::compression::CompressionAlgorithm;
use crate::encrypted_bottle::{
EncryptedBottleInfo, EncryptedBottleReader, EncryptedBottleReaderOptions,
EncryptionKey
};
use crate::file_atlas::FileAtlasRef;
use crate::file_bottle::FileListBottleReader;
use crate::file_list::{FileList, FileListRef, Symlink};
use crate::hashing::HashType;
use crate::signed_bottle::{SignatureAlgorithm, SignedBottleReader};
use crate::streams::{drain_stream, ReadStreamRef, StreamBottle, WrappedRead};
use crate::BottlePublicKey;
pub enum ArchiveReaderEvent {
Error(BottleError),
BottleCap(BottleCap),
Signed {
algorithm: SignatureAlgorithm,
public_key: Vec<u8>,
signed_by: String,
verify_key_name: Option<String>,
},
Encrypted(EncryptedBottleInfo),
Compressed(CompressionAlgorithm),
FileListStart { hash_type: HashType, block_count: usize, file_count: usize },
FileAtlas(FileAtlasRef),
FileListDone { file_list: FileListRef, bad_path_list: Vec<(PathBuf, PathBuf)>, stray_symlinks: Vec<Symlink> },
FileBlock { size: usize, hash: Vec<u8>, data: Option<Vec<u8>>, bottle_cap: BottleCap },
FileBlockWritten { size: usize, write_count: usize },
UnknownBottleType,
UnknownBottleData { size: usize, data: Option<Vec<u8>> },
BottleEnd(BottleType),
Done,
}
pub struct ArchiveReaderOptions {
pub read_blocks: bool,
pub process_bottles: bool,
pub verify_keys: Vec<Box<dyn BottlePublicKey>>,
pub secret_keys: Vec<Box<dyn BottleSecretKey>>,
pub encryption_key: Option<EncryptionKey>,
pub max_unknown_data_capture: usize,
pub allow_missing_key_commitment: bool,
}
impl ArchiveReaderOptions {
pub fn new() -> ArchiveReaderOptions {
ArchiveReaderOptions {
read_blocks: true,
process_bottles: true,
verify_keys: vec![],
secret_keys: vec![],
encryption_key: None,
max_unknown_data_capture: 128,
allow_missing_key_commitment: false,
}
}
}
impl Default for ArchiveReaderOptions {
fn default() -> ArchiveReaderOptions {
ArchiveReaderOptions::new()
}
}
enum State {
NewBottle,
Streams,
Draining,
EndBottle,
}
pub struct ArchiveReader {
options: ArchiveReaderOptions,
stack: Vec<(ReadStreamRef, BottleType)>,
bottle_type: BottleType,
current_bottle: Option<BottleReader<ReadStreamRef>>,
file_bottle: Option<FileListBottleReader<ReadStreamRef>>,
file_list: FileListRef,
bad_path_list: Vec<(PathBuf, PathBuf)>,
in_blocks: bool,
fail_bad_signature: bool,
reader_stack: Vec<BottleReader<ReadStreamRef>>,
state: State,
}
impl ArchiveReader {
pub fn new(
reader: Box<dyn Read>,
options: ArchiveReaderOptions,
) -> BottleResult<ArchiveReader> {
let stream = WrappedRead::new(reader);
Ok(ArchiveReader {
options,
stack: vec![ (stream.stream(), BottleType::Test) ],
bottle_type: BottleType::Test,
current_bottle: None,
file_bottle: None,
file_list: FileList::new().to_shared(),
bad_path_list: Vec::new(),
in_blocks: false,
fail_bad_signature: false,
reader_stack: Vec::new(),
state: State::NewBottle,
})
}
fn push_bottle(&mut self, next_bottle: ReadStreamRef, bottle_type: BottleType) {
self.stack.push((next_bottle, bottle_type));
self.state = State::NewBottle;
}
fn pop_bottle(&mut self) -> BottleResult<BottleType> {
let (mut stream, bottle_type) = self.stack.pop().unwrap();
stream.finish()?;
Ok(bottle_type)
}
fn process_file_list(
&mut self,
mut file_bottle: FileListBottleReader<ReadStreamRef>
) -> BottleResult<ArchiveReaderEvent> {
if !file_bottle.is_in_files() {
if !self.in_blocks {
self.in_blocks = true;
self.file_list.borrow_mut().build_file_map();
let mut bad_path_list: Vec<(PathBuf, PathBuf)> = Vec::new();
bad_path_list.append(&mut self.bad_path_list);
let stray_symlinks = self.file_list.borrow_mut().drop_stray_symlinks();
self.file_bottle = Some(file_bottle);
Ok(ArchiveReaderEvent::FileListDone {
file_list: self.file_list.clone(),
bad_path_list,
stray_symlinks,
})
} else if let Some(block) = file_bottle.next_block()? {
self.file_bottle = Some(file_bottle);
Ok(ArchiveReaderEvent::FileBlock {
size: block.block.size,
hash: block.block.hash.to_vec(),
data: self.options.read_blocks.then_some(block.data),
bottle_cap: block.bottle_cap,
})
} else {
file_bottle.close()?;
self.state = State::EndBottle;
Ok(ArchiveReaderEvent::BottleEnd(BottleType::FileList))
}
} else if let Some(atlas) = file_bottle.next_file()? {
let bad_path = atlas.borrow_mut().fix_bad_path();
if let Some(bad_path) = bad_path {
self.bad_path_list.push((bad_path, atlas.borrow().normalized_path.clone()));
}
self.file_list.borrow_mut().files.push(atlas.clone());
self.file_bottle = Some(file_bottle);
Ok(ArchiveReaderEvent::FileAtlas(atlas.clone()))
} else {
Err(BottleError::InvalidBottleState)
}
}
fn open_bottle(&mut self, mut bottle_reader: BottleReader<ReadStreamRef>) -> BottleResult<ArchiveReaderEvent> {
if !self.options.process_bottles {
self.state = State::Draining;
self.current_bottle = Some(bottle_reader);
return Ok(ArchiveReaderEvent::UnknownBottleType);
}
let event = match self.bottle_type {
BottleType::Signed => {
let reader = SignedBottleReader::new(bottle_reader)?;
let algorithm = reader.algorithm;
let public_key = reader.public_key.as_bytes(false).to_vec();
let signed_by = reader.public_key.name().to_string();
let verify_keys = &self.options.verify_keys;
if !verify_keys.is_empty() && !verify_keys.iter().any(|vk| vk.as_bytes(false) == public_key) {
self.fail_bad_signature = true;
}
let verify_key_name = verify_keys.iter()
.find(|vk| vk.as_bytes(false) == public_key)
.map(|pk| pk.name().to_string());
self.push_bottle(reader.stream(), BottleType::Signed);
ArchiveReaderEvent::Signed { algorithm, public_key, signed_by, verify_key_name }
}
BottleType::Encrypted => {
let info = EncryptedBottleReader::unpack_info(&mut bottle_reader)?;
let encryption_key = self.options.encryption_key.clone().unwrap_or(EncryptionKey::Generated);
let reader = EncryptedBottleReader::build_with_info(
bottle_reader,
&info,
EncryptedBottleReaderOptions {
key_type: encryption_key,
possible_keys: Some(&self.options.secret_keys),
allow_missing_key_commitment: self.options.allow_missing_key_commitment,
},
)?;
self.push_bottle(reader.stream(), BottleType::Encrypted);
ArchiveReaderEvent::Encrypted(info)
},
BottleType::Compressed => {
let reader = CompressedBottleReader::new(bottle_reader)?;
let algorithm = reader.algorithm;
self.push_bottle(reader.stream(), BottleType::Compressed);
ArchiveReaderEvent::Compressed(algorithm)
},
BottleType::FileList => {
let file_bottle = FileListBottleReader::new(bottle_reader, self.options.read_blocks)?;
let event = ArchiveReaderEvent::FileListStart {
hash_type: file_bottle.hash_type,
block_count: file_bottle.block_count,
file_count: file_bottle.file_count,
};
self.file_bottle = Some(file_bottle);
self.file_list.borrow_mut().clear();
self.in_blocks = false;
event
},
_ => {
self.state = State::Draining;
self.current_bottle = Some(bottle_reader);
ArchiveReaderEvent::UnknownBottleType
},
};
Ok(event)
}
fn drain_bottle(&mut self, mut bottle_reader: BottleReader<ReadStreamRef>) -> BottleResult<ArchiveReaderEvent> {
match bottle_reader.next_stream()? {
BottleStream::Data => {
let (size, data) = drain_stream(bottle_reader.data_stream()?, self.options.max_unknown_data_capture)?;
bottle_reader.close_stream()?;
self.current_bottle = Some(bottle_reader);
Ok(ArchiveReaderEvent::UnknownBottleData { size, data })
},
BottleStream::Bottle => {
let reader = bottle_reader.take_bottle_reader()?;
let bottle_cap = reader.bottle_cap.clone();
self.current_bottle = Some(*reader);
self.reader_stack.push(bottle_reader);
Ok(ArchiveReaderEvent::BottleCap(bottle_cap))
},
BottleStream::End => {
let bottle_type = bottle_reader.bottle_cap.bottle_type;
bottle_reader.close()?;
if !self.reader_stack.is_empty() {
self.current_bottle = Some(self.reader_stack.pop().unwrap());
} else {
self.state = State::EndBottle;
}
Ok(ArchiveReaderEvent::BottleEnd(bottle_type))
},
}
}
}
impl Iterator for ArchiveReader {
type Item = ArchiveReaderEvent;
fn next(&mut self) -> Option<ArchiveReaderEvent> {
if self.stack.is_empty() { return None }
if self.fail_bad_signature { return Some(ArchiveReaderEvent::Error(BottleError::NoAcceptableSignature)); }
let event = if let Some(file_bottle) = self.file_bottle.take() {
self.process_file_list(file_bottle)
} else {
match self.state {
State::NewBottle => {
self.state = State::Streams;
BottleReader::new(self.stack.last_mut().unwrap().0.clone()).map(|bottle_reader| {
let bottle_cap = bottle_reader.bottle_cap.clone();
self.bottle_type = bottle_cap.bottle_type;
self.current_bottle = Some(bottle_reader);
ArchiveReaderEvent::BottleCap(bottle_cap)
})
},
State::Streams => {
let bottle_reader = self.current_bottle.take().unwrap();
self.open_bottle(bottle_reader)
},
State::Draining => {
let bottle_reader = self.current_bottle.take().unwrap();
self.drain_bottle(bottle_reader)
},
State::EndBottle => {
self.pop_bottle().map(|bottle_type| {
if self.stack.is_empty() {
ArchiveReaderEvent::Done
} else {
ArchiveReaderEvent::BottleEnd(bottle_type)
}
})
},
}
};
Some(event.unwrap_or_else(|e| {
self.current_bottle = None;
self.stack.clear();
ArchiveReaderEvent::Error(e)
}))
}
}