use super::Kind;
use crate::container::XADES;
use crate::properties::{Error, PropertyResult};
use crate::xml::{Access, Name, Snapshot, Xml};
use structured_asn1::memory::{OwnedVec, Work};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Entry<N> {
Encapsulated {
node: N,
kind: Kind,
},
OtherCertificate {
node: N,
},
OtherStatus {
node: N,
},
}
#[derive(Debug)]
pub struct Catalogue<N, D, G> {
snapshot: Snapshot<D, G>,
parent: N,
entries: OwnedVec<Entry<N>>,
}
impl<N: Copy, D: Copy, G: Copy> Catalogue<N, D, G> {
#[must_use]
pub fn snapshot(&self) -> Snapshot<D, G> {
self.snapshot
}
#[must_use]
pub fn parent(&self) -> N {
self.parent
}
#[must_use]
pub fn entries(&self) -> &[Entry<N>] {
&self.entries
}
}
pub type Reading<X> = Catalogue<<X as Xml>::Id, <X as Xml>::Document, <X as Xml>::Generation>;
#[derive(Clone, Copy)]
pub(super) enum ContainerKind {
Certificates,
Revocation,
AttributeCertificates,
AttributeRevocation,
}
impl ContainerKind {
pub(super) fn attribute(self) -> bool {
matches!(
self,
Self::AttributeCertificates | Self::AttributeRevocation
)
}
pub(super) fn certificates(self) -> bool {
matches!(self, Self::Certificates | Self::AttributeCertificates)
}
pub(super) fn inventory(role: crate::unsigned::Role) -> Option<Self> {
match role {
crate::unsigned::Role::Certificates => Some(Self::Certificates),
crate::unsigned::Role::Revocation => Some(Self::Revocation),
crate::unsigned::Role::AttributeCertificates => Some(Self::AttributeCertificates),
crate::unsigned::Role::AttributeRevocation => Some(Self::AttributeRevocation),
_ => None,
}
}
}
pub(super) fn container_kind<X: Xml, W: Work>(
access: &Access<'_, X, W>,
node: X::Id,
) -> PropertyResult<Option<ContainerKind>, X> {
for (local, kind) in [
("CertificateValues", ContainerKind::Certificates),
("RevocationValues", ContainerKind::Revocation),
(
"AttrAuthoritiesCertValues",
ContainerKind::AttributeCertificates,
),
(
"AttributeRevocationValues",
ContainerKind::AttributeRevocation,
),
] {
if named(access, node, local)? {
return Ok(Some(kind));
}
}
Ok(None)
}
pub fn read<X: Xml, W: Work>(
access: &Access<'_, X, W>,
parent: X::Id,
) -> PropertyResult<Reading<X>, X> {
let kind = container_kind(access, parent)?.ok_or(Error::Unexpected { node: parent })?;
let certificates = kind.certificates();
let mut entries = access
.query(parent, |_, _, work| Ok(work.vector()))
.map_err(Error::Access)?;
if certificates {
for child in access.children(parent) {
let node = child.map_err(Error::Child)?;
let entry = if named(access, node, "EncapsulatedX509Certificate")? {
Entry::Encapsulated {
node,
kind: Kind::Certificate,
}
} else if named(access, node, "OtherCertificate")? {
Entry::OtherCertificate { node }
} else {
return Err(Error::Unexpected { node });
};
entries
.try_push(entry)
.map_err(|cause| Error::Resource { node, cause })?;
}
} else {
let mut previous = None;
for child in access.children(parent) {
let group = child.map_err(Error::Child)?;
let (rank, member, kind) = if named(access, group, "CRLValues")? {
(0, "EncapsulatedCRLValue", Some(Kind::Crl))
} else if named(access, group, "OCSPValues")? {
(1, "EncapsulatedOCSPValue", Some(Kind::Ocsp))
} else if named(access, group, "OtherValues")? {
(2, "OtherValue", None)
} else {
return Err(Error::Unexpected { node: group });
};
if let Some(earlier) = previous {
if rank == earlier {
return Err(Error::Duplicate { node: group });
}
if rank < earlier {
return Err(Error::Order { node: group });
}
}
previous = Some(rank);
let mut present = false;
for child in access.children(group) {
let node = child.map_err(Error::Child)?;
if !named(access, node, member)? {
return Err(Error::Unexpected { node });
}
let entry = match kind {
Some(kind) => Entry::Encapsulated { node, kind },
None => Entry::OtherStatus { node },
};
entries
.try_push(entry)
.map_err(|cause| Error::Resource { node, cause })?;
present = true;
}
if !present {
return Err(Error::Missing {
parent: group,
local: member,
});
}
}
}
Ok(Catalogue {
snapshot: access.snapshot(),
parent,
entries: entries.into_owned(),
})
}
fn named<X: Xml, W: Work>(
access: &Access<'_, X, W>,
node: X::Id,
local: &'static str,
) -> PropertyResult<bool, X> {
access
.has_name(
node,
Name {
namespace: XADES,
local,
},
)
.map_err(Error::Access)
}