1use crate::binding::{Bound, Coverage, Status};
4use crate::container::{DS, XADES};
5use crate::properties::{DataObjectRole, Error, Reading};
6use crate::reference::{Completeness, LexicalSource, Parent, ReferenceXml, Resolution, VisitError};
7use crate::xml::{Access, AccessError, Name, Node, Snapshot, Xml};
8use structured_asn1::memory::{OwnedVec, Work};
9
10#[derive(Debug)]
12pub enum FormatError<E, N> {
13 Selection {
15 signature: N,
17 index: usize,
19 },
20 Property(Error<E, N>),
22 Reference {
25 node: N,
27 status: Status,
29 },
30 Foreign {
32 node: N,
34 },
35 Metadata {
37 node: N,
39 object: N,
41 local: &'static str,
43 },
44}
45
46#[derive(Debug, Clone, Copy)]
49pub struct Resolved<N> {
50 pub reference: N,
52 pub parent: Parent<N>,
54 pub target: Option<N>,
56 pub coverage: Coverage,
58}
59
60#[derive(Debug)]
62pub enum CoverageError<E, N> {
63 Format(FormatError<E, N>),
65 Access(AccessError<E, N>),
67 Processing(E),
69 Lookup {
71 node: N,
73 resolution: Resolution<()>,
75 },
76 Missing {
78 reference: N,
80 },
81 Repeated {
83 reference: N,
85 },
86 Unused {
88 node: N,
90 },
91 ForeignCounter {
93 signature: N,
95 },
96 Incomplete {
98 node: N,
100 },
101 Unchecked {
103 reference: N,
105 },
106}
107
108#[derive(Debug)]
110pub struct CoveredFormat<N, D, G> {
111 original: Format<N, D, G>,
112 reference: N,
113 seen: bool,
114}
115
116impl<N: Copy, D: Copy, G: Copy> CoveredFormat<N, D, G> {
117 #[must_use]
119 pub fn original(&self) -> &Format<N, D, G> {
120 &self.original
121 }
122 #[must_use]
124 pub fn reference(&self) -> N {
125 self.reference
126 }
127}
128
129pub type Covered<X, W = structured_asn1::memory::Memory> =
132 OwnedVec<CoveredFormat<<X as Xml>::Id, <X as Xml>::Document, <X as Xml>::Generation>, W>;
133pub type CoverageResult<T, X> = Result<T, CoverageError<<X as Xml>::Error, <X as Xml>::Id>>;
135
136pub fn check_references<X: ReferenceXml, W: Work>(
145 access: &Access<'_, X, W>,
146 bound: &Bound<X::Id, X::Document, X::Generation>,
147 signed: &Reading<X>,
148 counter: Option<&crate::counter::Binding<X>>,
149) -> CoverageResult<Covered<X>, X> {
150 let layout = bound.containers();
151 let counter_value = bind_sources(access, layout, signed, counter)?;
152 let mut formats = prepare_formats(access, bound, signed)?;
153 let mut indices = access
154 .query(layout.signature(), |_, _, work| Ok(work.map()))
155 .map_err(CoverageError::Access)?;
156 for index in 0..formats.len() {
157 admit(access, formats[index].original.node)?;
158 let reference = formats[index].reference;
159 if indices
160 .try_insert(reference, index)
161 .map_err(|cause| {
162 CoverageError::Access(AccessError::Resource {
163 node: formats[index].original.node,
164 cause,
165 })
166 })?
167 .is_some()
168 {
169 return Err(CoverageError::Repeated { reference });
170 }
171 }
172 let visited = access
173 .query(layout.signature(), |xml, snapshot, work| {
174 Ok(xml.visit_authenticated_references(
175 snapshot,
176 layout.signature(),
177 work,
178 |descriptor| {
179 let r = descriptor.reference;
180 if let Some(target) = r.target {
181 let equal = |other| {
182 access.equal_id(target, other).map_err(|cause| {
183 CoverageError::Access(AccessError::Resource {
184 node: r.node,
185 cause,
186 })
187 })
188 };
189 if equal(layout.signed())? {
190 return Ok(());
191 }
192 if let Some(value) = counter_value
193 && equal(value)?
194 {
195 return Ok(());
196 }
197 }
198 if r.status != Status::Valid {
199 return Err(CoverageError::Unchecked { reference: r.node });
200 }
201 let Some(&index) = indices.get(&r.node, work).map_err(|cause| {
206 CoverageError::Access(AccessError::Resource {
207 node: r.node,
208 cause,
209 })
210 })?
211 else {
212 return Err(CoverageError::Missing { reference: r.node });
213 };
214 debug_assert!(index < formats.len());
215 formats[index].seen = true;
216 Ok(())
217 },
218 ))
219 })
220 .map_err(CoverageError::Access)?;
221 let completeness = match visited {
222 Ok(value) => value,
223 Err(VisitError::Processing(cause)) => return Err(CoverageError::Processing(cause)),
224 Err(VisitError::Visitor(cause)) => return Err(cause),
225 };
226 match completeness {
227 Completeness::Complete => {}
228 Completeness::Unchecked(node) | Completeness::Incomplete(node) => {
229 return Err(CoverageError::Incomplete { node });
230 }
231 }
232 for index in 0..formats.len() {
233 admit(access, signed.parent())?;
234 if !formats[index].seen {
235 return Err(CoverageError::Unused {
236 node: formats[index].original.node,
237 });
238 }
239 }
240 Ok(formats.into_owned())
241}
242
243fn bind_sources<X: Xml, W: Work>(
244 access: &Access<'_, X, W>,
245 layout: &crate::container::Layout<X>,
246 signed: &Reading<X>,
247 counter: Option<&crate::counter::Binding<X>>,
248) -> CoverageResult<Option<X::Id>, X> {
249 if access.snapshot() != layout.snapshot() {
250 return Err(CoverageError::Access(AccessError::Changed {
251 node: layout.signature(),
252 }));
253 }
254 if signed.snapshot() != layout.snapshot() {
255 return Err(CoverageError::Format(FormatError::Foreign {
256 node: signed.parent(),
257 }));
258 }
259 if !access
260 .equal_id(signed.signature(), layout.signature())
261 .map_err(|cause| {
262 CoverageError::Access(AccessError::Resource {
263 node: signed.parent(),
264 cause,
265 })
266 })?
267 {
268 return Err(CoverageError::Format(FormatError::Foreign {
269 node: signed.parent(),
270 }));
271 }
272 if let Some(counter) = counter {
273 if counter.original().snapshot() != layout.snapshot()
274 || !access
275 .equal_id(counter.original().child(), layout.signature())
276 .map_err(|cause| {
277 CoverageError::Access(AccessError::Resource {
278 node: counter.original().node(),
279 cause,
280 })
281 })?
282 {
283 return Err(CoverageError::ForeignCounter {
284 signature: layout.signature(),
285 });
286 }
287 Ok(Some(counter.original().value()))
288 } else {
289 Ok(None)
290 }
291}
292
293fn admit<X: Xml, W: Work>(access: &Access<'_, X, W>, node: X::Id) -> CoverageResult<(), X> {
294 access
295 .query(node, |_, _, work| Ok(work.charge_work(1)))
296 .map_err(CoverageError::Access)?
297 .map_err(|cause| CoverageError::Access(AccessError::Resource { node, cause }))
298}
299
300fn prepare_formats<X: ReferenceXml, W: Work>(
301 access: &Access<'_, X, W>,
302 bound: &Bound<X::Id, X::Document, X::Generation>,
303 signed: &Reading<X>,
304) -> CoverageResult<Covered<X, W>, X> {
305 let mut formats = access
306 .query(signed.parent(), |_, _, work| Ok(work.vector()))
307 .map_err(CoverageError::Access)?;
308 if let Some(objects) = signed.data_objects() {
309 for index in 0..objects.entries().len() {
310 admit(access, objects.parent())?;
311 if objects.entries()[index].role != DataObjectRole::Format {
312 continue;
313 }
314 let original = read(access, signed, index).map_err(CoverageError::Format)?;
315 let resolved = match resolve(access, bound, &original).map_err(CoverageError::Format)? {
316 Resolution::Resolved(value) => value,
317 other => {
318 return Err(CoverageError::Lookup {
319 node: original.node,
320 resolution: other.map(|_| ()),
321 });
322 }
323 };
324 let node = original.node;
325 formats
326 .try_push(CoveredFormat {
327 original,
328 reference: resolved.reference,
329 seen: false,
330 })
331 .map_err(|cause| CoverageError::Access(AccessError::Resource { node, cause }))?;
332 }
333 }
334 Ok(formats)
335}
336
337pub fn resolve<X: ReferenceXml, W: Work>(
346 access: &Access<'_, X, W>,
347 bound: &Bound<X::Id, X::Document, X::Generation>,
348 format: &Original<X>,
349) -> FormatResult<Resolution<Resolved<X::Id>>, X> {
350 let layout = bound.containers();
351 if format.snapshot != layout.snapshot() {
352 return Err(FormatError::Foreign { node: format.node });
353 }
354 if !access
355 .equal_id(format.signature, layout.signature())
356 .map_err(|cause| {
357 FormatError::Property(Error::Access(AccessError::Resource {
358 node: format.node,
359 cause,
360 }))
361 })?
362 {
363 return Err(FormatError::Foreign { node: format.node });
364 }
365 if access.snapshot() != format.snapshot {
366 return Err(FormatError::Property(Error::Access(AccessError::Changed {
367 node: format.node,
368 })));
369 }
370 let resolved = access
371 .query(format.node, |xml, snapshot, work| {
372 xml.resolve_reference(
373 snapshot,
374 format.signature,
375 format.node,
376 LexicalSource::Attribute(Name {
377 namespace: "",
378 local: "ObjectReference",
379 }),
380 work,
381 |descriptor| {
382 let r = descriptor.reference;
383 if r.status != Status::Valid {
384 return Err(FormatError::Reference {
385 node: r.node,
386 status: r.status,
387 });
388 }
389 if let Some(object) = r.target {
390 if access.equal_id(object, layout.signed()).map_err(|cause| {
391 FormatError::Property(Error::Access(AccessError::Resource {
392 node: r.node,
393 cause,
394 }))
395 })? {
396 return Err(FormatError::Reference {
397 node: r.node,
398 status: r.status,
399 });
400 }
401 object_metadata(access, format, object)?;
402 }
403 Ok(Resolved {
404 reference: r.node,
405 parent: descriptor.parent,
406 target: r.target,
407 coverage: r.coverage,
408 })
409 },
410 )
411 })
412 .map_err(|cause| FormatError::Property(Error::Access(cause)))?;
413 Ok(match resolved {
414 Resolution::Resolved(value) => Resolution::Resolved(value?),
415 Resolution::Missing => Resolution::Missing,
416 Resolution::Unresolved => Resolution::Unresolved,
417 Resolution::Unauthenticated => Resolution::Unauthenticated,
418 Resolution::Unsupported => Resolution::Unsupported,
419 })
420}
421
422fn object_metadata<X: Xml, W: Work>(
423 access: &Access<'_, X, W>,
424 format: &Original<X>,
425 object: X::Id,
426) -> FormatResult<(), X> {
427 let access_error = |cause| FormatError::Property(Error::Access(cause));
428 if !access
429 .has_name(
430 object,
431 Name {
432 namespace: DS,
433 local: "Object",
434 },
435 )
436 .map_err(access_error)?
437 {
438 return Ok(());
439 }
440 for (node, local) in [
445 (Some(format.mime), "MimeType"),
446 (format.encoding, "Encoding"),
447 ] {
448 let Some(node) = node else { continue };
449 access
450 .attribute(
451 object,
452 Name {
453 namespace: "",
454 local,
455 },
456 |expected| {
457 let Some(expected) = expected else {
458 return Ok(());
459 };
460 if simple_content_matches(access, node, expected)? {
461 Ok(())
462 } else {
463 Err(FormatError::Metadata {
464 node,
465 object,
466 local,
467 })
468 }
469 },
470 )
471 .map_err(access_error)??;
472 }
473 Ok(())
474}
475
476fn simple_content_matches<X: Xml, W: Work>(
477 access: &Access<'_, X, W>,
478 parent: X::Id,
479 expected: &str,
480) -> FormatResult<bool, X> {
481 let access_error = |cause| FormatError::Property(Error::Access(cause));
482 let mut child = access.first_child(parent).map_err(access_error)?;
483 let mut offset = 0;
484 let mut same = true;
485 while let Some(node) = child {
486 let compared = access
487 .inspect(node, |value| match value {
488 Node::Element(_) => Err(FormatError::Property(Error::Unexpected { node })),
489 Node::Other => Ok(()),
490 Node::Text(text) => {
491 if same {
492 if text.len() > expected.len() - offset {
493 same = false;
494 } else {
495 access
496 .query(node, |_, _, work| {
497 Ok(u64::try_from(text.len())
498 .map_err(|_| {
499 structured_asn1::memory::MemoryError::WorkExceeded
500 })
501 .and_then(|length| work.charge_work(length)))
502 })
503 .map_err(access_error)?
504 .map_err(|cause| {
505 access_error(AccessError::Resource { node, cause })
506 })?;
507 let end = offset + text.len();
508 same = text.as_bytes() == &expected.as_bytes()[offset..end];
509 offset = end;
510 }
511 }
512 Ok(())
513 }
514 })
515 .map_err(access_error)?;
516 compared?;
517 child = access.next_sibling(parent, node).map_err(access_error)?;
518 }
519 Ok(same && offset == expected.len())
520}
521
522#[derive(Debug)]
524pub struct Format<N, D, G> {
525 snapshot: Snapshot<D, G>,
526 signature: N,
527 node: N,
528 description: Option<N>,
529 identifier: Option<N>,
530 mime: N,
531 encoding: Option<N>,
532}
533
534impl<N: Copy, D: Copy, G: Copy> Format<N, D, G> {
535 #[must_use]
537 pub fn snapshot(&self) -> Snapshot<D, G> {
538 self.snapshot
539 }
540 #[must_use]
542 pub fn signature(&self) -> N {
543 self.signature
544 }
545 #[must_use]
548 pub fn node(&self) -> N {
549 self.node
550 }
551 #[must_use]
553 pub fn description(&self) -> Option<N> {
554 self.description
555 }
556 #[must_use]
558 pub fn identifier(&self) -> Option<N> {
559 self.identifier
560 }
561 #[must_use]
563 pub fn mime(&self) -> N {
564 self.mime
565 }
566 #[must_use]
568 pub fn encoding(&self) -> Option<N> {
569 self.encoding
570 }
571}
572
573pub type FormatResult<T, X> = Result<T, FormatError<<X as Xml>::Error, <X as Xml>::Id>>;
575pub type Original<X> = Format<<X as Xml>::Id, <X as Xml>::Document, <X as Xml>::Generation>;
577
578pub fn check_encoding<X: crate::scalar::ScalarXml, W: Work>(
587 access: &Access<'_, X, W>,
588 original: &Original<X>,
589) -> crate::scalar::ScalarResult<X> {
590 use crate::scalar::{Datatype, Error as ScalarError};
591 if original.snapshot != access.snapshot() {
592 return Err(ScalarError::Access(AccessError::Changed {
593 node: original.node,
594 }));
595 }
596 access
597 .query(original.node, |_, _, _| Ok(()))
598 .map_err(ScalarError::Access)?;
599 if let Some(node) = original.encoding {
600 crate::scalar::check(access, node, LexicalSource::SimpleContent, Datatype::AnyUri)?;
601 }
602 Ok(())
603}
604
605pub fn read<X: Xml, W: Work>(
614 access: &Access<'_, X, W>,
615 signed: &Reading<X>,
616 index: usize,
617) -> FormatResult<Original<X>, X> {
618 let select = || FormatError::Selection {
619 signature: signed.signature(),
620 index,
621 };
622 let property = FormatError::Property;
623 if access.snapshot() != signed.snapshot() {
624 return Err(property(Error::Access(AccessError::Changed {
625 node: signed.parent(),
626 })));
627 }
628 access
629 .query(signed.parent(), |_, _, work| Ok(work.charge_work(1)))
630 .map_err(|cause| property(Error::Access(cause)))?
631 .map_err(|cause| {
632 property(Error::Resource {
633 node: signed.parent(),
634 cause,
635 })
636 })?;
637 let objects = signed.data_objects().ok_or_else(select)?;
638 let selected = objects.entries().get(index).ok_or_else(select)?;
639 if selected.role != DataObjectRole::Format {
640 return Err(select());
641 }
642 let node = selected.node;
643 let has_reference = access
644 .attribute(
645 node,
646 Name {
647 namespace: "",
648 local: "ObjectReference",
649 },
650 |value| value.is_some(),
651 )
652 .map_err(|cause| property(Error::Access(cause)))?;
653 if !has_reference {
654 return Err(property(Error::Missing {
655 parent: node,
656 local: "ObjectReference",
657 }));
658 }
659 let mut fields = [None; 4];
660 let mut previous = 0;
661 for child in access.children(node) {
662 let child = child.map_err(|cause| property(Error::Child(cause)))?;
663 let mut selected = None;
664 for (rank, local) in ["Description", "ObjectIdentifier", "MimeType", "Encoding"]
665 .iter()
666 .enumerate()
667 {
668 if access
669 .has_name(
670 child,
671 Name {
672 namespace: XADES,
673 local,
674 },
675 )
676 .map_err(|cause| property(Error::Access(cause)))?
677 {
678 selected = Some(rank);
679 break;
680 }
681 }
682 let rank = selected.ok_or_else(|| property(Error::Unexpected { node: child }))?;
683 if fields[rank].is_some() {
684 return Err(property(Error::Duplicate { node: child }));
685 }
686 if rank < previous {
687 return Err(property(Error::Order { node: child }));
688 }
689 fields[rank] = Some(child);
690 previous = rank;
691 match rank {
692 1 => {
693 crate::identifier::read(access, child).map_err(property)?;
694 }
695 2 => crate::mime::check(access, child).map_err(property)?,
696 _ => crate::properties::simple_content(access, child).map_err(property)?,
697 }
698 }
699 let mime = fields[2].ok_or_else(|| {
700 property(Error::Missing {
701 parent: node,
702 local: "MimeType",
703 })
704 })?;
705 Ok(Format {
706 snapshot: signed.snapshot(),
707 signature: signed.signature(),
708 node,
709 description: fields[0],
710 identifier: fields[1],
711 mime,
712 encoding: fields[3],
713 })
714}