1use crate::binding::{Bound, Status};
4use crate::container::XADES;
5use crate::identifier;
6use crate::properties::{self, DataObjectRole, Error, PropertyResult};
7use crate::reference::{
8 Completeness, Descriptor, LexicalSource, Parent, ReferenceXml, Resolution, VisitError,
9};
10use crate::xml::{Access, AccessError, Name, Snapshot, Xml};
11use structured_asn1::memory::{OwnedVec, Work};
12
13#[derive(Debug)]
15pub enum Objects<N> {
16 All(N),
18 References(OwnedVec<N>),
20}
21
22pub struct Reading<N, D, G> {
24 snapshot: Snapshot<D, G>,
25 signature: N,
26 node: N,
27 kind: identifier::Reading<N>,
28 objects: Objects<N>,
29 qualifiers: Option<N>,
30}
31
32impl<N: Copy, D: Copy, G: Copy> Reading<N, D, G> {
33 #[must_use]
35 pub fn node(&self) -> N {
36 self.node
37 }
38 #[must_use]
41 pub fn kind(&self) -> &identifier::Reading<N> {
42 &self.kind
43 }
44 #[must_use]
46 pub fn objects(&self) -> &Objects<N> {
47 &self.objects
48 }
49 #[must_use]
52 pub fn qualifiers(&self) -> Option<N> {
53 self.qualifiers
54 }
55}
56
57pub type Original<X> = Reading<<X as Xml>::Id, <X as Xml>::Document, <X as Xml>::Generation>;
59
60pub fn read<X: Xml, W: Work>(
65 access: &Access<'_, X, W>,
66 signed: &properties::Reading<X>,
67 index: usize,
68) -> PropertyResult<Original<X>, X> {
69 let node = selected(access, signed, index)?;
70 let mut kind = None;
71 let mut all = None;
72 let mut references = access
73 .query(node, |_, _, work| Ok(work.vector()))
74 .map_err(Error::Access)?;
75 let mut qualifiers = None;
76 for child in access.children(node) {
77 let child = child.map_err(Error::Child)?;
78 if named(access, child, "CommitmentTypeId")? {
79 if kind.is_some() {
80 return Err(Error::Duplicate { node: child });
81 }
82 if all.is_some() || !references.is_empty() || qualifiers.is_some() {
83 return Err(Error::Order { node: child });
84 }
85 let original = identifier::read(access, child)?;
86 if original.qualifier.is_some() {
87 return Err(Error::Forbidden {
88 node: original.identifier,
89 });
90 }
91 kind = Some(original);
92 } else if named(access, child, "ObjectReference")? {
93 if kind.is_none() || qualifiers.is_some() {
94 return Err(Error::Order { node: child });
95 }
96 if all.is_some() {
97 return Err(Error::Unexpected { node: child });
98 }
99 properties::simple_content(access, child)?;
100 references
101 .try_push(child)
102 .map_err(|cause| Error::Resource { node: child, cause })?;
103 } else if named(access, child, "AllSignedDataObjects")? {
104 if kind.is_none() || qualifiers.is_some() {
105 return Err(Error::Order { node: child });
106 }
107 if all.is_some() {
108 return Err(Error::Duplicate { node: child });
109 }
110 if !references.is_empty() {
111 return Err(Error::Unexpected { node: child });
112 }
113 if let Some(nested) = access.children(child).next() {
114 return Err(Error::Unexpected {
115 node: nested.map_err(Error::Child)?,
116 });
117 }
118 all = Some(child);
119 } else if named(access, child, "CommitmentTypeQualifiers")? {
120 if qualifiers.is_some() {
121 return Err(Error::Duplicate { node: child });
122 }
123 if kind.is_none() || (all.is_none() && references.is_empty()) {
124 return Err(Error::Order { node: child });
125 }
126 for qualifier in access.children(child) {
127 let qualifier = qualifier.map_err(Error::Child)?;
128 if !named(access, qualifier, "CommitmentTypeQualifier")? {
129 return Err(Error::Unexpected { node: qualifier });
130 }
131 }
132 qualifiers = Some(child);
133 } else {
134 return Err(Error::Unexpected { node: child });
135 }
136 }
137 let kind = kind.ok_or(Error::Missing {
138 parent: node,
139 local: "CommitmentTypeId",
140 })?;
141 let objects = if let Some(all) = all {
142 Objects::All(all)
143 } else {
144 if references.is_empty() {
145 return Err(Error::Missing {
146 parent: node,
147 local: "ObjectReference or AllSignedDataObjects",
148 });
149 }
150 Objects::References(references.into_owned())
151 };
152 Ok(Reading {
153 snapshot: signed.snapshot(),
154 signature: signed.signature(),
155 node,
156 kind,
157 objects,
158 qualifiers,
159 })
160}
161
162#[derive(Debug, Clone, Copy)]
164pub struct Object<N> {
165 pub reference: N,
167 pub parent: Parent<N>,
169}
170
171pub struct Association<N, D, G> {
174 original: Reading<N, D, G>,
175 objects: OwnedVec<Object<N>>,
176}
177
178impl<N, D, G> Association<N, D, G> {
179 #[must_use]
181 pub fn original(&self) -> &Reading<N, D, G> {
182 &self.original
183 }
184 #[must_use]
186 pub fn objects(&self) -> &[Object<N>] {
187 &self.objects
188 }
189}
190
191pub type Prepared<X> = Association<<X as Xml>::Id, <X as Xml>::Document, <X as Xml>::Generation>;
194
195pub fn prepare<X: ReferenceXml, W: Work>(
198 access: &Access<'_, X, W>,
199 bound: &Bound<X::Id, X::Document, X::Generation>,
200 signed: &properties::Reading<X>,
201 index: usize,
202) -> Result<Prepared<X>, AssociationError<X::Error, X::Id>> {
203 let original = read(access, signed, index).map_err(AssociationError::Property)?;
204 let objects = associate(access, bound, &original)?;
205 Ok(Association { original, objects })
206}
207
208#[derive(Debug)]
210pub enum AssociationError<E, N> {
211 Property(Error<E, N>),
213 Processing(E),
215 Lookup {
217 node: N,
219 resolution: Resolution<()>,
221 },
222 Ineligible {
224 node: N,
226 },
227 Incomplete {
229 node: N,
231 },
232}
233
234pub fn associate<X: ReferenceXml, W: Work>(
238 access: &Access<'_, X, W>,
239 bound: &Bound<X::Id, X::Document, X::Generation>,
240 original: &Original<X>,
241) -> AssociationResult<OwnedVec<Object<X::Id>>, X> {
242 let property = AssociationError::Property;
243 bind_original(access, bound, original)?;
244 let mut output = access
245 .query(original.node, |_, _, work| Ok(work.vector()))
246 .map_err(|cause| property(Error::Access(cause)))?;
247 let is_properties = |r: crate::binding::Reference<'_, X::Id>| match r.target {
248 Some(target) => access
249 .equal_id(target, bound.containers().signed())
250 .map_err(|cause| {
251 property(Error::Resource {
252 node: r.node,
253 cause,
254 })
255 }),
256 None => Ok(false),
257 };
258 let mut retain = |descriptor: Descriptor<'_, X::Id>| {
259 let r = descriptor.reference;
260 if r.status != Status::Valid {
261 return Err(AssociationError::Ineligible { node: r.node });
262 }
263 output
264 .try_push(Object {
265 reference: r.node,
266 parent: descriptor.parent,
267 })
268 .map_err(|cause| {
269 property(Error::Resource {
270 node: r.node,
271 cause,
272 })
273 })
274 };
275 match &original.objects {
276 Objects::References(nodes) => {
277 for index in 0..nodes.len() {
278 admit(access, original.node).map_err(property)?;
279 let node = nodes[index];
280 let resolved = access
281 .query(node, |xml, snapshot, work| {
282 xml.resolve_reference(
283 snapshot,
284 original.signature,
285 node,
286 LexicalSource::SimpleContent,
287 work,
288 |descriptor| {
289 let r = descriptor.reference;
290 if r.status != Status::Valid || is_properties(r)? {
291 return Err(AssociationError::Ineligible { node: r.node });
292 }
293 retain(descriptor)
294 },
295 )
296 })
297 .map_err(|cause| property(Error::Access(cause)))?;
298 match resolved {
299 Resolution::Resolved(value) => value?,
300 other => {
301 return Err(AssociationError::Lookup {
302 node,
303 resolution: other.map(|_| ()),
304 });
305 }
306 }
307 }
308 }
309 Objects::All(_) => {
310 let visited = access
311 .query(original.node, |xml, snapshot, work| {
312 Ok(xml.visit_authenticated_references(
313 snapshot,
314 original.signature,
315 work,
316 |descriptor| {
317 if is_properties(descriptor.reference)? {
318 return Ok(());
319 }
320 retain(descriptor)
321 },
322 ))
323 })
324 .map_err(|cause| property(Error::Access(cause)))?;
325 let complete = match visited {
326 Ok(value) => value,
327 Err(VisitError::Processing(cause)) => {
328 return Err(AssociationError::Processing(cause));
329 }
330 Err(VisitError::Visitor(cause)) => return Err(cause),
331 };
332 match complete {
333 Completeness::Complete => {}
334 Completeness::Unchecked(node) | Completeness::Incomplete(node) => {
335 return Err(AssociationError::Incomplete { node });
336 }
337 }
338 }
339 }
340 Ok(output.into_owned())
341}
342
343fn bind_original<X: Xml, W: Work>(
344 access: &Access<'_, X, W>,
345 bound: &Bound<X::Id, X::Document, X::Generation>,
346 original: &Original<X>,
347) -> AssociationResult<(), X> {
348 let property = AssociationError::Property;
349 if access.snapshot() != original.snapshot || bound.containers().snapshot() != original.snapshot
350 {
351 return Err(property(Error::Access(AccessError::Changed {
352 node: original.node,
353 })));
354 }
355 if !access
356 .equal_id(bound.containers().signature(), original.signature)
357 .map_err(|cause| {
358 property(Error::Resource {
359 node: original.node,
360 cause,
361 })
362 })?
363 {
364 return Err(property(Error::Access(AccessError::Changed {
365 node: original.node,
366 })));
367 }
368 Ok(())
369}
370
371pub type AssociationResult<T, X> = Result<T, AssociationError<<X as Xml>::Error, <X as Xml>::Id>>;
373
374fn selected<X: Xml, W: Work>(
375 access: &Access<'_, X, W>,
376 signed: &properties::Reading<X>,
377 index: usize,
378) -> PropertyResult<X::Id, X> {
379 if access.snapshot() != signed.snapshot() {
380 return Err(Error::Access(AccessError::Changed {
381 node: signed.parent(),
382 }));
383 }
384 admit(access, signed.parent())?;
385 let selection = || Error::Selection {
386 parent: signed.parent(),
387 index,
388 };
389 let entry = signed
390 .data_objects()
391 .and_then(|objects| objects.entries().get(index))
392 .ok_or_else(selection)?;
393 if entry.role != DataObjectRole::Commitment {
394 return Err(selection());
395 }
396 Ok(entry.node)
397}
398
399fn admit<X: Xml, W: Work>(access: &Access<'_, X, W>, node: X::Id) -> PropertyResult<(), X> {
400 access
401 .query(node, |_, _, work| Ok(work.charge_work(1)))
402 .map_err(Error::Access)?
403 .map_err(|cause| Error::Resource { node, cause })
404}
405
406fn named<X: Xml, W: Work>(
407 access: &Access<'_, X, W>,
408 node: X::Id,
409 local: &str,
410) -> PropertyResult<bool, X> {
411 access
412 .has_name(
413 node,
414 Name {
415 namespace: XADES,
416 local,
417 },
418 )
419 .map_err(Error::Access)
420}