xml_sec/xmldsig/extension/
tree.rs1use crate::document::{DocumentView, NodeIdentity, XmlDocumentError};
8use structured_asn1::memory::{MemoryError, Work};
9
10#[derive(Debug, thiserror::Error)]
12pub enum TreeError {
13 #[error(transparent)]
15 Document(#[from] XmlDocumentError),
16 #[error("XML node is not a direct child of the selected parent")]
18 NotChild,
19 #[error("XML binary value owner is not an element")]
21 NotElement,
22 #[error("malformed XML base64 content: {0}")]
24 Base64(&'static str),
25 #[error("XML tree resource failure: {0}")]
27 Resource(MemoryError),
28}
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32pub enum NodeView<'a> {
33 Element {
35 namespace: Option<&'a str>,
37 local_name: &'a str,
39 },
40 Text(&'a str),
42 Other,
44}
45
46fn charge<W: Work>(work: &W, units: usize) -> Result<(), TreeError> {
47 let units = u64::try_from(units).map_err(|_| TreeError::Resource(MemoryError::WorkExceeded))?;
48 work.charge_work(units).map_err(TreeError::Resource)
49}
50
51pub fn inspect_node<W: Work, R>(
53 view: DocumentView<'_>,
54 node: NodeIdentity,
55 work: &W,
56 visitor: impl for<'a> FnOnce(NodeView<'a>) -> R,
57) -> Result<R, TreeError> {
58 charge(work, 1)?;
59 let node = view.resolve_node(node)?;
60 Ok(visitor(if node.is_element() {
61 let name = node.tag_name();
62 NodeView::Element {
63 namespace: name.namespace(),
64 local_name: name.name(),
65 }
66 } else if node.is_text() {
67 NodeView::Text(node.text().unwrap_or_default())
68 } else {
69 NodeView::Other
70 }))
71}
72
73pub fn first_child<W: Work>(
75 view: DocumentView<'_>,
76 parent: NodeIdentity,
77 work: &W,
78) -> Result<Option<NodeIdentity>, TreeError> {
79 charge(work, 1)?;
80 let parent = view.resolve_node(parent)?;
81 Ok(parent.first_child().map(|child| view.node_identity(child)))
82}
83
84pub fn next_sibling<W: Work>(
87 view: DocumentView<'_>,
88 parent: NodeIdentity,
89 node: NodeIdentity,
90 work: &W,
91) -> Result<Option<NodeIdentity>, TreeError> {
92 charge(work, 1)?;
93 let parent = view.resolve_node(parent)?;
94 let node = view.resolve_node(node)?;
95 if node.parent() != Some(parent) {
96 return Err(TreeError::NotChild);
97 }
98 Ok(node
99 .following_sibling()
100 .map(|sibling| view.node_identity(sibling)))
101}
102
103pub fn subtree_contains<W: Work>(
105 view: DocumentView<'_>,
106 ancestor: NodeIdentity,
107 node: NodeIdentity,
108 work: &W,
109) -> Result<bool, TreeError> {
110 charge(work, 1)?;
111 let ancestor = view.resolve_node(ancestor)?;
112 let node = view.resolve_node(node)?;
113 Ok(crate::xmldsig::types::subtree_node_id_range(ancestor).contains(&node.id().get()))
114}
115
116pub fn with_base64<W: Work, R>(
123 view: DocumentView<'_>,
124 owner: NodeIdentity,
125 work: &W,
126 visitor: impl for<'a> FnOnce(&'a [u8]) -> R,
127) -> Result<R, TreeError> {
128 charge(work, 1)?;
129 let node = view.resolve_node(owner)?;
130 if !node.is_element() {
131 return Err(TreeError::NotElement);
132 }
133 let mut text = 0usize;
136 for child in node.children() {
137 charge(work, 1)?;
138 if child.is_text() {
139 text = text
140 .checked_add(child.text().map_or(0, str::len))
141 .ok_or(TreeError::Resource(MemoryError::WorkExceeded))?;
142 }
143 }
144 charge(
145 work,
146 text.checked_mul(2)
147 .ok_or(TreeError::Resource(MemoryError::WorkExceeded))?,
148 )?;
149 let payload =
150 crate::xmldsig::whitespace::XmlBase64Payload::new(node).map_err(TreeError::Base64)?;
151 let mut decoded = work.vector();
152 decoded
153 .try_reserve_exact(payload.decoded_len)
154 .map_err(TreeError::Resource)?;
155 const ZEROS: [u8; 512] = [0; 512];
158 let mut remaining = payload.decoded_len;
159 while remaining != 0 {
160 let chunk = remaining.min(ZEROS.len());
161 decoded
162 .try_extend_from_slice(&ZEROS[..chunk])
163 .map_err(TreeError::Resource)?;
164 remaining -= chunk;
165 }
166 payload
167 .decode_into(&mut decoded)
168 .map_err(TreeError::Base64)?;
169 Ok(visitor(&decoded))
170}