use std::{collections::BTreeMap, io};
use super::{message::*, *};
use crate::{
ltp::{
heap::{AnsiHeapNode, UnicodeHeapNode},
prop_context::{AnsiPropertyContext, BinaryValue, PropertyValue, UnicodePropertyContext},
prop_type::PropertyType,
tree::{AnsiHeapTree, UnicodeHeapTree},
},
ndb::{
block::{AnsiDataTree, UnicodeDataTree},
header::Header,
node_id::{NodeId, NodeIdType},
page::{
AnsiBlockBTree, AnsiBlockBTreeEntry, AnsiNodeBTreeEntry, NodeBTreeEntry, RootBTree,
UnicodeBlockBTree, UnicodeBlockBTreeEntry, UnicodeNodeBTreeEntry,
},
root::Root,
},
};
#[derive(Default, Debug)]
pub struct AttachmentProperties {
properties: BTreeMap<u16, PropertyValue>,
}
impl AttachmentProperties {
pub fn get(&self, id: u16) -> Option<&PropertyValue> {
self.properties.get(&id)
}
pub fn iter(&self) -> impl Iterator<Item = (&u16, &PropertyValue)> {
self.properties.iter()
}
pub fn attachment_size(&self) -> io::Result<i32> {
let attachment_size = self
.properties
.get(&0x0E20)
.ok_or(MessagingError::AttachmentSizeNotFound)?;
match attachment_size {
PropertyValue::Integer32(value) => Ok(*value),
invalid => {
Err(MessagingError::InvalidAttachmentSize(PropertyType::from(invalid)).into())
}
}
}
pub fn attachment_method(&self) -> io::Result<i32> {
let attachment_method = self
.properties
.get(&0x3705)
.ok_or(MessagingError::AttachmentMethodNotFound)?;
match attachment_method {
PropertyValue::Integer32(value) => Ok(*value),
invalid => {
Err(MessagingError::InvalidAttachmentMethod(PropertyType::from(invalid)).into())
}
}
}
pub fn rendering_position(&self) -> io::Result<i32> {
let rendering_position = self
.properties
.get(&0x370B)
.ok_or(MessagingError::AttachmentRenderingPositionNotFound)?;
match rendering_position {
PropertyValue::Integer32(value) => Ok(*value),
invalid => Err(
MessagingError::InvalidAttachmentRenderingPosition(PropertyType::from(invalid))
.into(),
),
}
}
}
#[repr(i32)]
#[derive(Clone, Copy, PartialEq, Eq, Default, Debug)]
pub enum AttachmentMethod {
#[default]
None = 0x00000000,
ByValue = 0x00000001,
ByReference = 0x00000002,
ByReferenceOnly = 0x00000004,
EmbeddedMessage = 0x00000005,
Storage = 0x00000006,
ByWebReference = 0x00000007,
}
impl TryFrom<i32> for AttachmentMethod {
type Error = MessagingError;
fn try_from(value: i32) -> Result<Self, Self::Error> {
match value {
0x00000000 => Ok(Self::None),
0x00000001 => Ok(Self::ByValue),
0x00000002 => Ok(Self::ByReference),
0x00000004 => Ok(Self::ByReferenceOnly),
0x00000005 => Ok(Self::EmbeddedMessage),
0x00000006 => Ok(Self::Storage),
0x00000007 => Ok(Self::ByWebReference),
_ => Err(MessagingError::UnknownAttachmentMethod(value)),
}
}
}
pub enum UnicodeAttachmentData<'a> {
Binary(BinaryValue),
Message(UnicodeMessage<'a>),
Storage(UnicodeBlockBTreeEntry),
}
pub struct UnicodeAttachment<'a> {
message: &'a UnicodeMessage<'a>,
properties: AttachmentProperties,
data: Option<UnicodeAttachmentData<'a>>,
}
impl<'a> UnicodeAttachment<'a> {
pub fn message(&self) -> &UnicodeMessage {
self.message
}
pub fn read(
message: &'a UnicodeMessage,
sub_node: NodeId,
prop_ids: Option<&[u16]>,
) -> io::Result<Self> {
let node_id_type = sub_node.id_type()?;
match node_id_type {
NodeIdType::Attachment => {}
_ => {
return Err(MessagingError::InvalidAttachmentNodeIdType(node_id_type).into());
}
}
let store = message.store();
let pst = store.pst();
let header = pst.header();
let root = header.root();
let (properties, data) = {
let mut file = pst
.file()
.lock()
.map_err(|_| MessagingError::FailedToLockFile)?;
let file = &mut *file;
let encoding = header.crypt_method();
let block_btree = UnicodeBlockBTree::read(file, *root.block_btree())?;
let node = message
.sub_nodes()
.get(&sub_node)
.ok_or(MessagingError::AttachmentSubNodeNotFound(sub_node))?;
let node = UnicodeNodeBTreeEntry::new(node.node(), node.block(), node.sub_node(), None);
let data = node.data();
let block = block_btree.find_entry(file, u64::from(data))?;
let heap = UnicodeHeapNode::new(UnicodeDataTree::read(file, encoding, &block)?);
let header = heap.header(file, encoding, &block_btree)?;
let tree = UnicodeHeapTree::new(heap, header.user_root());
let prop_context = UnicodePropertyContext::new(node, tree);
let properties = prop_context
.properties(file, encoding, &block_btree)?
.into_iter()
.map(|(prop_id, record)| {
prop_context
.read_property(file, encoding, &block_btree, record)
.map(|value| (prop_id, value))
})
.collect::<io::Result<BTreeMap<_, _>>>()?;
let properties = AttachmentProperties { properties };
let attachment_method = AttachmentMethod::try_from(properties.attachment_method()?)?;
let data = match attachment_method {
AttachmentMethod::ByValue => {
let binary_data = match properties
.get(0x3701)
.ok_or(MessagingError::AttachmentMessageObjectDataNotFound)?
{
PropertyValue::Binary(value) => value,
invalid => {
return Err(MessagingError::InvalidMessageObjectData(
PropertyType::from(invalid),
)
.into())
}
};
Some(UnicodeAttachmentData::Binary(binary_data.clone()))
}
AttachmentMethod::EmbeddedMessage => {
let object_data = match properties
.get(0x3701)
.ok_or(MessagingError::AttachmentMessageObjectDataNotFound)?
{
PropertyValue::Object(value) => value,
invalid => {
return Err(MessagingError::InvalidMessageObjectData(
PropertyType::from(invalid),
)
.into())
}
};
let sub_node = object_data.node();
let node = message
.sub_nodes()
.get(&sub_node)
.ok_or(MessagingError::AttachmentSubNodeNotFound(sub_node))?;
let node = UnicodeNodeBTreeEntry::new(
node.node(),
node.block(),
node.sub_node(),
None,
);
let message = UnicodeMessage::read_embedded(store, node, prop_ids)?;
Some(UnicodeAttachmentData::Message(message))
}
AttachmentMethod::Storage => {
let object_data = match properties
.get(0x3701)
.ok_or(MessagingError::AttachmentMessageObjectDataNotFound)?
{
PropertyValue::Object(value) => value,
invalid => {
return Err(MessagingError::InvalidMessageObjectData(
PropertyType::from(invalid),
)
.into())
}
};
let sub_node = object_data.node();
let node = message
.sub_nodes()
.get(&sub_node)
.ok_or(MessagingError::AttachmentSubNodeNotFound(sub_node))?;
let block = block_btree.find_entry(file, u64::from(node.block()))?;
Some(UnicodeAttachmentData::Storage(block))
}
_ => None,
};
(properties, data)
};
Ok(Self {
message,
properties,
data,
})
}
pub fn properties(&self) -> &AttachmentProperties {
&self.properties
}
pub fn data(&self) -> Option<&UnicodeAttachmentData> {
self.data.as_ref()
}
}
pub enum AnsiAttachmentData<'a> {
Binary(BinaryValue),
Message(AnsiMessage<'a>),
Storage(AnsiBlockBTreeEntry),
}
pub struct AnsiAttachment<'a> {
message: &'a AnsiMessage<'a>,
properties: AttachmentProperties,
data: Option<AnsiAttachmentData<'a>>,
}
impl<'a> AnsiAttachment<'a> {
pub fn message(&self) -> &AnsiMessage {
self.message
}
pub fn read(
message: &'a AnsiMessage,
sub_node: NodeId,
prop_ids: Option<&[u16]>,
) -> io::Result<Self> {
let node_id_type = sub_node.id_type()?;
match node_id_type {
NodeIdType::Attachment => {}
_ => {
return Err(MessagingError::InvalidAttachmentNodeIdType(node_id_type).into());
}
}
let store = message.store();
let pst = store.pst();
let header = pst.header();
let root = header.root();
let (properties, data) = {
let mut file = pst
.file()
.lock()
.map_err(|_| MessagingError::FailedToLockFile)?;
let file = &mut *file;
let encoding = header.crypt_method();
let block_btree = AnsiBlockBTree::read(file, *root.block_btree())?;
let node = message
.sub_nodes()
.get(&sub_node)
.ok_or(MessagingError::AttachmentSubNodeNotFound(sub_node))?;
let node = AnsiNodeBTreeEntry::new(node.node(), node.block(), node.sub_node(), None);
let data = node.data();
let block = block_btree.find_entry(file, u32::from(data))?;
let heap = AnsiHeapNode::new(AnsiDataTree::read(file, encoding, &block)?);
let header = heap.header(file, encoding, &block_btree)?;
let tree = AnsiHeapTree::new(heap, header.user_root());
let prop_context = AnsiPropertyContext::new(node, tree);
let properties = prop_context
.properties(file, encoding, &block_btree)?
.into_iter()
.map(|(prop_id, record)| {
prop_context
.read_property(file, encoding, &block_btree, record)
.map(|value| (prop_id, value))
})
.collect::<io::Result<BTreeMap<_, _>>>()?;
let properties = AttachmentProperties { properties };
let attachment_method = AttachmentMethod::try_from(properties.attachment_method()?)?;
let data = match attachment_method {
AttachmentMethod::ByValue => {
let binary_data = match properties
.get(0x3701)
.ok_or(MessagingError::AttachmentMessageObjectDataNotFound)?
{
PropertyValue::Binary(value) => value,
invalid => {
return Err(MessagingError::InvalidMessageObjectData(
PropertyType::from(invalid),
)
.into())
}
};
Some(AnsiAttachmentData::Binary(binary_data.clone()))
}
AttachmentMethod::EmbeddedMessage => {
let object_data = match properties
.get(0x3701)
.ok_or(MessagingError::AttachmentMessageObjectDataNotFound)?
{
PropertyValue::Object(value) => value,
invalid => {
return Err(MessagingError::InvalidMessageObjectData(
PropertyType::from(invalid),
)
.into())
}
};
let sub_node = object_data.node();
let node = message
.sub_nodes()
.get(&sub_node)
.ok_or(MessagingError::AttachmentSubNodeNotFound(sub_node))?;
let node =
AnsiNodeBTreeEntry::new(node.node(), node.block(), node.sub_node(), None);
let message = AnsiMessage::read_embedded(store, node, prop_ids)?;
Some(AnsiAttachmentData::Message(message))
}
AttachmentMethod::Storage => {
let object_data = match properties
.get(0x3701)
.ok_or(MessagingError::AttachmentMessageObjectDataNotFound)?
{
PropertyValue::Object(value) => value,
invalid => {
return Err(MessagingError::InvalidMessageObjectData(
PropertyType::from(invalid),
)
.into())
}
};
let sub_node = object_data.node();
let node = message
.sub_nodes()
.get(&sub_node)
.ok_or(MessagingError::AttachmentSubNodeNotFound(sub_node))?;
let block = block_btree.find_entry(file, u32::from(node.block()))?;
Some(AnsiAttachmentData::Storage(block))
}
_ => None,
};
(properties, data)
};
Ok(Self {
message,
properties,
data,
})
}
pub fn properties(&self) -> &AttachmentProperties {
&self.properties
}
pub fn data(&self) -> Option<&AnsiAttachmentData> {
self.data.as_ref()
}
}