use std::{collections::BTreeMap, io, rc::Rc};
use super::{message::*, read_write::*, *};
use crate::{
ltp::{
heap::HeapNode,
prop_context::{BinaryValue, PropertyContext, PropertyValue},
prop_type::PropertyType,
read_write::*,
},
ndb::{
block::{DataTree, IntermediateTreeBlock},
block_id::BlockId,
header::Header,
node_id::{NodeId, NodeIdType},
page::{BTreePage, NodeBTreeEntry, RootBTree},
read_write::*,
root::Root,
},
AnsiPstFile, PstFile, PstFileLock, UnicodePstFile,
};
#[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 AttachmentData {
Binary(BinaryValue),
Message(Rc<dyn Message>),
}
pub trait Attachment {
fn message(&self) -> Rc<dyn Message>;
fn properties(&self) -> &AttachmentProperties;
fn data(&self) -> Option<&AttachmentData>;
}
struct AttachmentInner<Pst>
where
Pst: PstFile,
{
message: Rc<Pst::Message>,
properties: AttachmentProperties,
data: Option<AttachmentData>,
}
impl<Pst> AttachmentInner<Pst>
where
Pst: PstFile + PstFileLock<Pst>,
<Pst as PstFile>::BTreeKey: BTreePageKeyReadWrite,
<Pst as PstFile>::NodeBTreeEntry: NodeBTreeEntryReadWrite,
<Pst as PstFile>::NodeBTree: RootBTreeReadWrite,
<<Pst as PstFile>::NodeBTree as RootBTree>::IntermediatePage:
RootBTreeIntermediatePageReadWrite<
Pst,
<Pst as PstFile>::NodeBTreeEntry,
<<Pst as PstFile>::NodeBTree as RootBTree>::LeafPage,
>,
<<<Pst as PstFile>::NodeBTree as RootBTree>::IntermediatePage as BTreePage>::Entry:
BTreePageEntryReadWrite,
<<Pst as PstFile>::NodeBTree as RootBTree>::LeafPage: RootBTreeLeafPageReadWrite<Pst>,
<Pst as PstFile>::BlockBTreeEntry: BlockBTreeEntryReadWrite,
<Pst as PstFile>::BlockBTree: RootBTreeReadWrite,
<<Pst as PstFile>::BlockBTree as RootBTree>::Entry: BTreeEntryReadWrite,
<<Pst as PstFile>::BlockBTree as RootBTree>::IntermediatePage:
RootBTreeIntermediatePageReadWrite<
Pst,
<<Pst as PstFile>::BlockBTree as RootBTree>::Entry,
<<Pst as PstFile>::BlockBTree as RootBTree>::LeafPage,
>,
<<Pst as PstFile>::BlockBTree as RootBTree>::LeafPage:
RootBTreeLeafPageReadWrite<Pst> + BTreePageReadWrite,
<Pst as PstFile>::BlockTrailer: BlockTrailerReadWrite,
<Pst as PstFile>::DataTreeBlock: IntermediateTreeBlockReadWrite,
<<Pst as PstFile>::DataTreeBlock as IntermediateTreeBlock>::Entry:
IntermediateTreeEntryReadWrite,
<Pst as PstFile>::DataBlock: BlockReadWrite + Clone,
<Pst as PstFile>::HeapNode: HeapNodeReadWrite<Pst>,
<Pst as PstFile>::PropertyTree: HeapTreeReadWrite<Pst>,
<Pst as PstFile>::PropertyContext: PropertyContextReadWrite<Pst>,
<Pst as PstFile>::Store: StoreReadWrite<Pst>,
<Pst as PstFile>::Message: MessageReadWrite<Pst> + 'static,
{
fn read(
message: Rc<<Pst as PstFile>::Message>,
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.pst_store();
let pst = store.pst();
let header = pst.header();
let root = header.root();
let (properties, data) = {
let mut file = pst
.reader()
.lock()
.map_err(|_| MessagingError::FailedToLockFile)?;
let file = &mut *file;
let encoding = header.crypt_method();
let block_btree = <<Pst as PstFile>::BlockBTree as RootBTreeReadWrite>::read(
file,
*root.block_btree(),
)?;
let node = message
.sub_nodes()
.get(&sub_node)
.ok_or(MessagingError::AttachmentSubNodeNotFound(sub_node))?;
let node = <<Pst as PstFile>::NodeBTreeEntry as NodeBTreeEntryReadWrite>::new(
node.node(),
node.block(),
node.sub_node(),
None,
);
let mut page_cache = pst.block_cache();
let data = node.data();
let heap = <<Pst as PstFile>::HeapNode as HeapNodeReadWrite<Pst>>::read(
file,
&block_btree,
&mut page_cache,
encoding,
data.search_key(),
)?;
let header = heap.header()?;
let tree = <Pst as PstFile>::PropertyTree::new(heap, header.user_root());
let prop_context = <<Pst as PstFile>::PropertyContext as PropertyContextReadWrite<
Pst,
>>::new(node, tree);
let properties = prop_context
.properties()?
.into_iter()
.map(|(prop_id, record)| {
prop_context
.read_property(file, encoding, &block_btree, &mut page_cache, 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(AttachmentData::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 = <<Pst as PstFile>::NodeBTreeEntry as NodeBTreeEntryReadWrite>::new(
node.node(),
node.block(),
node.sub_node(),
None,
);
let message =
<<Pst as PstFile>::Message as MessageReadWrite<Pst>>::read_embedded(
store.clone(),
node,
prop_ids,
)?;
Some(AttachmentData::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, node.block().search_key(), &mut page_cache)?;
let block = DataTree::read(file, encoding, &block)?;
let mut data = vec![];
let _ = block
.reader(
file,
encoding,
&block_btree,
&mut page_cache,
&mut Default::default(),
)?
.read_to_end(&mut data)?;
Some(AttachmentData::Binary(BinaryValue::new(data)))
}
_ => None,
};
(properties, data)
};
Ok(Self {
message,
properties,
data,
})
}
}
pub struct UnicodeAttachment {
inner: AttachmentInner<UnicodePstFile>,
}
impl UnicodeAttachment {
pub fn read(
message: Rc<UnicodeMessage>,
sub_node: NodeId,
prop_ids: Option<&[u16]>,
) -> io::Result<Rc<Self>> {
<Self as AttachmentReadWrite<UnicodePstFile>>::read(message, sub_node, prop_ids)
}
}
impl Attachment for UnicodeAttachment {
fn message(&self) -> Rc<dyn Message> {
self.inner.message.clone()
}
fn properties(&self) -> &AttachmentProperties {
&self.inner.properties
}
fn data(&self) -> Option<&AttachmentData> {
self.inner.data.as_ref()
}
}
impl AttachmentReadWrite<UnicodePstFile> for UnicodeAttachment {
fn read(
message: Rc<UnicodeMessage>,
sub_node: NodeId,
prop_ids: Option<&[u16]>,
) -> io::Result<Rc<Self>> {
let inner = AttachmentInner::read(message, sub_node, prop_ids)?;
Ok(Rc::new(Self { inner }))
}
}
pub struct AnsiAttachment {
inner: AttachmentInner<AnsiPstFile>,
}
impl AnsiAttachment {
pub fn read(
message: Rc<AnsiMessage>,
sub_node: NodeId,
prop_ids: Option<&[u16]>,
) -> io::Result<Rc<Self>> {
<Self as AttachmentReadWrite<AnsiPstFile>>::read(message, sub_node, prop_ids)
}
}
impl Attachment for AnsiAttachment {
fn message(&self) -> Rc<dyn Message> {
self.inner.message.clone()
}
fn properties(&self) -> &AttachmentProperties {
&self.inner.properties
}
fn data(&self) -> Option<&AttachmentData> {
self.inner.data.as_ref()
}
}
impl AttachmentReadWrite<AnsiPstFile> for AnsiAttachment {
fn read(
message: Rc<AnsiMessage>,
sub_node: NodeId,
prop_ids: Option<&[u16]>,
) -> io::Result<Rc<Self>> {
let inner = AttachmentInner::read(message, sub_node, prop_ids)?;
Ok(Rc::new(Self { inner }))
}
}