use crate::{
error::Error,
zip::{Member, MemberId, ReadFailure},
};
use structured_asn1::{
Reader, Span,
memory::{OwnedVec, Work},
range::RangeSource,
};
use structured_cms::{ContentInfo, SignedData, evidence::EvidenceRecord, timestamp::Token};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Syntax {
Empty,
Trailing,
ContentType,
}
#[derive(Debug)]
pub enum CarriedError<E, D, G> {
Archive(Error<E>),
Encoding {
member: MemberId<D, G>,
physical: Span,
cause: structured_asn1::Error,
},
Cms {
member: MemberId<D, G>,
physical: Span,
cause: structured_cms::Error,
},
Syntax {
member: MemberId<D, G>,
physical: Span,
kind: Syntax,
},
}
pub struct Content<'v, 's, S: RangeSource> {
member: Member<'v, 's, S>,
bytes: OwnedVec<u8>,
}
impl<'v, 's, S: RangeSource> Member<'v, 's, S> {
pub async fn content<W: Work>(
&self,
work: &W,
) -> Result<Content<'v, 's, S>, ReadFailure<S::Error, S::Document, S::Generation>> {
let bytes = self.read_bytes(work).await?;
let member =
self.archive()
.original(self.id(), work)
.map_err(|cause| ReadFailure::Processing {
member: self.id(),
cause,
})?;
Ok(Content { member, bytes })
}
}
pub type ResultOf<T, S> = Result<
T,
CarriedError<
<S as RangeSource>::Error,
<S as RangeSource>::Document,
<S as RangeSource>::Generation,
>,
>;
impl<'v, 's, S: RangeSource> Content<'v, 's, S> {
pub(crate) fn into_parts(self) -> (Member<'v, 's, S>, OwnedVec<u8>) {
(self.member, self.bytes)
}
#[must_use]
pub fn member(&self) -> &Member<'v, 's, S> {
&self.member
}
#[must_use]
pub fn bytes(&self) -> &[u8] {
&self.bytes
}
#[must_use]
pub fn enclosure(&self) -> Span {
self.member
.local_span()
.expect("complete original content has a local enclosure")
}
fn cursor<'q, W: Work + 'q>(&'q self, work: &W) -> ResultOf<Reader<'q>, S> {
self.member
.archive()
.admit(work, self.enclosure())
.map_err(CarriedError::Archive)?;
self.member
.session()
.carried(&self.bytes, work)
.map_err(|cause| self.encoding(cause))
}
fn encoding(
&self,
cause: structured_asn1::Error,
) -> CarriedError<S::Error, S::Document, S::Generation> {
CarriedError::Encoding {
member: self.member.id(),
physical: self.enclosure(),
cause,
}
}
fn cms(
&self,
cause: structured_cms::Error,
) -> CarriedError<S::Error, S::Document, S::Generation> {
CarriedError::Cms {
member: self.member.id(),
physical: self.enclosure(),
cause,
}
}
fn syntax(&self, kind: Syntax) -> CarriedError<S::Error, S::Document, S::Generation> {
CarriedError::Syntax {
member: self.member.id(),
physical: self.enclosure(),
kind,
}
}
pub fn read<'q, W: Work + 'q>(&'q self, work: &W) -> ResultOf<(SignedData<'q>, Reader<'q>), S> {
let mut root = self.cursor(work)?;
let (content, parent) = ContentInfo::read(&mut root).map_err(|cause| self.cms(cause))?;
parent
.execution()
.charge_work(content.kind().as_bytes().len() as u64)
.map_err(|cause| {
CarriedError::Archive(crate::error::resource(self.enclosure(), cause))
})?;
if content.kind() != structured_cms::oid::SIGNED_DATA {
return Err(self.syntax(Syntax::ContentType));
}
let message = SignedData::read_element(&parent, content.content())
.map_err(|cause| self.cms(cause))?;
self.require(&message, &parent, work)
.map_err(CarriedError::Archive)?;
Ok((message, parent))
}
pub fn read_timestamp<'q, W: Work + 'q>(
&'q self,
work: &W,
) -> ResultOf<(Token<'q>, Reader<'q>), S> {
let mut reader = self.cursor(work)?;
let token = Token::read_from(&mut reader).map_err(|cause| self.cms(cause))?;
self.require(token.message(), &reader, work)
.map_err(CarriedError::Archive)?;
Ok((token, reader))
}
pub fn read_evidence<'q, W: Work + 'q>(
&'q self,
work: &W,
) -> ResultOf<(EvidenceRecord<'q>, Reader<'q>), S> {
let mut reader = self.cursor(work)?;
let element = reader
.next_element()
.map_err(|cause| self.encoding(cause))?
.ok_or_else(|| self.syntax(Syntax::Empty))?;
if !reader.is_empty() {
return Err(self.syntax(Syntax::Trailing));
}
let record = EvidenceRecord::read(&reader, &element).map_err(|cause| self.cms(cause))?;
Ok((record, reader))
}
pub(crate) fn require<W: Work>(
&self,
message: &SignedData<'_>,
parent: &Reader<'_>,
work: &W,
) -> Result<(), Error<S::Error>> {
self.require_original(message.as_bytes(), message.span().start, parent, work)
}
pub(crate) fn require_original<W: Work>(
&self,
original: &[u8],
offset: u64,
parent: &Reader<'_>,
work: &W,
) -> Result<(), Error<S::Error>> {
self.member.archive().admit(work, self.enclosure())?;
let at = usize::try_from(offset).map_err(|_| Error::Foreign {
range: self.enclosure(),
})?;
let end = at.checked_add(original.len()).ok_or(Error::Foreign {
range: self.enclosure(),
})?;
if !self.member.session().owns(parent)
|| self
.bytes
.get(at..end)
.is_none_or(|bytes| !core::ptr::eq(bytes, original))
{
return Err(Error::Foreign {
range: self.enclosure(),
});
}
Ok(())
}
}