1#![forbid(unsafe_code)]
4
5mod c14n;
6mod rng;
7mod xsd;
8mod schematron;
9
10use rusty_xml_tree::{AttrDefault, ElementDecl, NodeId, NodeKind, XmlDoc, XmlDtd};
11
12pub use c14n::*;
13pub use rng::*;
14pub use xsd::*;
15pub use schematron::*;
16
17#[doc(alias = "xmlValidateDocument")]
19pub fn xml_validate_document(doc: &XmlDoc) -> Result<(), String> {
20 let dtd = doc.dtd.as_ref().ok_or("no DTD")?;
21 xml_validate_dtd(doc, dtd)
22}
23
24#[doc(alias = "xmlValidateDtd")]
26pub fn xml_validate_dtd(doc: &XmlDoc, dtd: &XmlDtd) -> Result<(), String> {
27 let root = doc
28 .xml_doc_get_root_element()
29 .ok_or("document has no root")?;
30 if let Some(n) = &dtd.name {
31 if doc.qname(root) != *n {
34 return Err(format!("root element {} does not match DOCTYPE {n}", doc.qname(root)));
35 }
36 }
37 if let Some(e) = doc.undeclared_entity_refs.first() {
44 return Err(format!("Entity '{e}' not defined"));
45 }
46 for n in &dtd.ndata_notations {
50 if !dtd.notations.contains(n) {
51 return Err(format!("Notation {n} is not declared"));
52 }
53 }
54 for ((elem, aname), ad) in &dtd.attributes {
55 if ad.att_type != "NOTATION" {
56 continue;
57 }
58 for n in &ad.enumerated {
59 if !dtd.notations.contains(n) {
60 return Err(format!(
61 "Notation {n} in attribute {aname} of {elem} is not declared"
62 ));
63 }
64 }
65 }
66 if let Some(dup) = dtd.duplicate_elements.first() {
69 return Err(format!("Redefinition of element {dup}"));
70 }
71 for (elem, decl) in &dtd.elements {
74 if let ElementDecl::Mixed(names) = decl {
75 let mut seen = std::collections::HashSet::new();
76 for n in names {
77 if !seen.insert(n) {
78 return Err(format!(
79 "Definition of {elem} has duplicate references of {n}"
80 ));
81 }
82 }
83 }
84 }
85 for ((elem, aname), ad) in &dtd.attributes {
86 let mut seen = std::collections::HashSet::new();
88 for e in &ad.enumerated {
89 if !seen.insert(e) {
90 return Err(format!(
91 "attribute {aname} of {elem}: enumeration value token {e} duplicated"
92 ));
93 }
94 }
95 if ad.att_type == "ID"
99 && matches!(ad.default, AttrDefault::Value | AttrDefault::Fixed)
100 {
101 return Err(format!(
102 "ID attribute {aname} of {elem} is not valid, must be #IMPLIED or #REQUIRED"
103 ));
104 }
105 if ad.att_type == "NOTATION" && matches!(dtd.elements.get(elem), Some(ElementDecl::Empty)) {
109 return Err(format!(
110 "NOTATION attribute type declared for EMPTY element {elem}"
111 ));
112 }
113 let Some(def) = ad.default_value.as_deref() else {
116 continue;
117 };
118 let ok = match ad.att_type.as_str() {
119 "ID" | "IDREF" | "ENTITY" => is_name(def),
120 "IDREFS" | "ENTITIES" => {
121 def.split_space().next().is_some()
122 && def.split_space().all(is_name)
123 }
124 "NMTOKEN" => is_nmtoken(def),
125 "NMTOKENS" => {
126 def.split_space().next().is_some()
127 && def.split_space().all(is_nmtoken)
128 }
129 _ => true,
130 };
131 if !ok {
132 return Err(format!("invalid default value for attribute {aname} of {elem}"));
133 }
134 if !ad.enumerated.is_empty() && !ad.enumerated.iter().any(|e| e == def) {
135 return Err(format!("invalid default value for attribute {aname} of {elem}"));
136 }
137 }
138
139 let mut ids: std::collections::HashMap<String, ()> = Default::default();
142 let mut idrefs: Vec<String> = Vec::new();
143 let mut stack = vec![root];
144 while let Some(id) = stack.pop() {
145 validate_element(doc, id, dtd, &mut ids, &mut idrefs)?;
146 let mut c = doc.last_child(id);
147 while let Some(x) = c {
148 if doc.kind(x) == NodeKind::Element {
149 stack.push(x);
150 }
151 c = doc.prev_sibling(x);
152 }
153 }
154 for r in &idrefs {
157 if !ids.contains_key(r) {
158 return Err(format!("IDREF attribute references unknown ID \"{r}\""));
159 }
160 }
161 Ok(())
162}
163
164trait SplitSpace {
172 fn split_space(&self) -> std::iter::Filter<std::str::Split<'_, char>, fn(&&str) -> bool>;
173}
174
175impl SplitSpace for str {
176 fn split_space(&self) -> std::iter::Filter<std::str::Split<'_, char>, fn(&&str) -> bool> {
177 fn non_empty(s: &&str) -> bool {
178 !s.is_empty()
179 }
180 self.split(' ').filter(non_empty as fn(&&str) -> bool)
181 }
182}
183
184fn is_name(v: &str) -> bool {
186 let mut cs = v.chars();
187 match cs.next() {
188 Some(c) if rusty_xml_parser::chvalid::xml_is_name_start_char(c as u32, false) => {}
189 _ => return false,
190 }
191 cs.all(|c| rusty_xml_parser::chvalid::xml_is_name_char(c as u32, false))
192}
193
194fn is_nmtoken(v: &str) -> bool {
196 !v.is_empty()
197 && v.chars()
198 .all(|c| rusty_xml_parser::chvalid::xml_is_name_char(c as u32, false))
199}
200
201fn validate_element(
202 doc: &XmlDoc,
203 id: NodeId,
204 dtd: &XmlDtd,
205 ids: &mut std::collections::HashMap<String, ()>,
206 idrefs: &mut Vec<String>,
207) -> Result<(), String> {
208 if doc.kind(id) != NodeKind::Element {
209 return Ok(());
210 }
211 let name = doc.qname(id);
215 if !dtd.elements.contains_key(&name) {
223 return Err(format!("No declaration for element {name}"));
224 }
225 if let Some(decl) = dtd.elements.get(&name) {
226 match decl {
227 ElementDecl::Empty => {
228 if doc.first_child(id).is_some()
232 || doc.elements_with_entity_refs.contains(&id)
233 {
234 return Err(format!("element {name} must be EMPTY"));
235 }
236 }
237 ElementDecl::Any => {}
238 ElementDecl::Mixed(_) => {
239 let mut c = doc.first_child(id);
240 while let Some(x) = c {
241 match doc.kind(x) {
242 NodeKind::Element => {
243 if let ElementDecl::Mixed(allowed) = decl {
244 if !allowed.is_empty() && !allowed.iter().any(|n| *n == doc.qname(x)) {
245 return Err(format!("element {} not allowed in mixed {name}", doc.qname(x)));
246 }
247 }
248 }
249 NodeKind::Text | NodeKind::CData | NodeKind::Comment | NodeKind::Pi => {}
250 _ => {}
251 }
252 c = doc.next_sibling(x);
253 }
254 }
255 ElementDecl::Children(spec) => {
256 let kids: Vec<String> = {
257 let mut v = Vec::new();
258 let mut c = doc.first_child(id);
259 while let Some(x) = c {
260 if doc.kind(x) == NodeKind::Element {
261 v.push(doc.qname(x));
262 } else if doc.kind(x) == NodeKind::Text
263 && (!doc.xml_is_blank_node(x) || doc.reference_text.contains(&x))
264 {
265 return Err(format!("character data not allowed in {name}"));
269 } else if doc.kind(x) == NodeKind::CData {
270 return Err(format!("character data not allowed in {name}"));
276 }
277 c = doc.next_sibling(x);
278 }
279 v
280 };
281 if !match_children_spec(spec, &kids) {
282 return Err(format!("content of {name} does not match {spec}"));
283 }
284 }
285 }
286 }
287 for ((elem, aname), ad) in &dtd.attributes {
288 if elem != &name {
289 continue;
290 }
291 let have = {
297 let mut found = None;
298 let mut a = doc.first_attr(id);
299 while let Some(x) = a {
300 if doc.qname(x) == *aname {
301 found = Some(doc.content(x).to_string());
302 break;
303 }
304 a = doc.next_sibling(x);
305 }
306 found
307 };
308 match ad.default {
309 AttrDefault::Required if have.is_none() => {
310 return Err(format!("attribute {aname} of {name} is required"));
311 }
312 AttrDefault::Fixed => {
313 if let (Some(v), Some(fix)) = (&have, &ad.default_value) {
314 if v != fix {
315 return Err(format!("attribute {aname} must be {fix}"));
316 }
317 }
318 }
319 _ => {}
320 }
321 if let Some(v) = &have {
322 if !ad.enumerated.is_empty() && !ad.enumerated.iter().any(|e| e == v) {
323 return Err(format!("attribute {aname} value not in enumeration"));
324 }
325 match ad.att_type.as_str() {
329 "ID" | "IDREF" => {
330 if v.contains(':') {
336 return Err(format!(
337 "Value {v} for attribute {aname} of {name} is not an NCName"
338 ));
339 }
340 if !is_name(v) {
341 return Err(format!(
342 "Syntax of value for attribute {aname} of {name} is not valid"
343 ));
344 }
345 if ad.att_type == "ID" {
346 if ids.insert(v.clone(), ()).is_some() {
347 return Err(format!("ID {v} already defined"));
348 }
349 } else {
350 idrefs.push(v.clone());
351 }
352 }
353 "IDREFS" => {
354 let mut any = false;
355 for part in v.split_space() {
356 any = true;
357 if part.contains(':') {
358 return Err(format!(
359 "Value {part} for attribute {aname} of {name} is not an NCName"
360 ));
361 }
362 if !is_name(part) {
363 return Err(format!(
364 "Syntax of value for attribute {aname} of {name} is not valid"
365 ));
366 }
367 idrefs.push(part.to_string());
368 }
369 if !any {
370 return Err(format!(
371 "Syntax of value for attribute {aname} of {name} is not valid"
372 ));
373 }
374 }
375 "NMTOKEN" => {
376 if !is_nmtoken(v) {
377 return Err(format!(
378 "Syntax of value for attribute {aname} of {name} is not valid"
379 ));
380 }
381 }
382 "NMTOKENS" => {
383 if v.split_space().next().is_none()
384 || !v.split_space().all(is_nmtoken)
385 {
386 return Err(format!(
387 "Syntax of value for attribute {aname} of {name} is not valid"
388 ));
389 }
390 }
391 "ENTITY" | "ENTITIES" => {
392 for part in v.split_space() {
393 if !is_name(part) {
394 return Err(format!(
395 "Syntax of value for attribute {aname} of {name} is not valid"
396 ));
397 }
398 if !dtd.unparsed_entities.contains(part) {
399 return Err(format!(
400 "ENTITY attribute {aname} references an unknown entity \"{part}\""
401 ));
402 }
403 }
404 }
405 _ => {}
406 }
407 }
408 }
409 for (pre, uri) in doc.ns_defs(id) {
417 let q = match pre {
418 Some(p) => format!("xmlns:{p}"),
419 None => "xmlns".to_string(),
420 };
421 match dtd.attributes.get(&(name.clone(), q.clone())) {
422 None => return Err(format!("No declaration for attribute {q} of element {name}")),
423 Some(ad) => {
424 if ad.default == AttrDefault::Fixed {
425 if let Some(fix) = ad.default_value.as_deref() {
426 if uri != fix {
427 return Err(format!(
428 "Value for attribute {q} of {name} is different from default {fix}"
429 ));
430 }
431 }
432 }
433 }
434 }
435 }
436 {
437 let mut a = doc.first_attr(id);
438 while let Some(x) = a {
439 let q = doc.qname(x);
440 if !dtd.attributes.contains_key(&(name.clone(), q.clone())) {
441 return Err(format!("No declaration for attribute {q} of element {name}"));
442 }
443 a = doc.next_sibling(x);
444 }
445 }
446 let id_attrs = dtd
449 .attributes
450 .iter()
451 .filter(|((e, _), ad)| e == &name && ad.att_type == "ID")
452 .count();
453 if id_attrs > 1 {
454 return Err(format!("Element {name} has {id_attrs} ID attributes"));
455 }
456 Ok(())
457}
458
459fn match_children_spec(spec: &str, kids: &[String]) -> bool {
460 let toks = tokenize_content(spec);
461 match_seq(&toks, kids, 0).contains(&kids.len())
462}
463
464#[derive(Clone, Debug)]
465enum Tok {
466 Name(String),
467 Seq(Vec<Tok>),
468 Choice(Vec<Tok>),
469 Star,
470 Plus,
471 Q,
472}
473
474fn tokenize_content(spec: &str) -> Vec<Tok> {
475 let p = spec.trim();
477 fn parse_choice<'a>(p: &mut &'a str) -> Vec<Tok> {
478 let mut alts = vec![Tok::Seq(parse_seq(p))];
479 loop {
480 skip(p);
481 if p.starts_with('|') {
482 *p = &p[1..];
483 alts.push(Tok::Seq(parse_seq(p)));
484 } else {
485 break;
486 }
487 }
488 alts
489 }
490 fn parse_seq<'a>(p: &mut &'a str) -> Vec<Tok> {
491 let mut v = Vec::new();
492 loop {
493 skip(p);
494 if p.is_empty() || p.starts_with('|') || p.starts_with(')') {
495 break;
496 }
497 if p.starts_with(',') {
498 *p = &p[1..];
499 continue;
500 }
501 let before = p.len();
510 let particle = parse_particle(p);
511 if p.len() == before {
512 break;
513 }
514 v.push(particle);
515 }
516 v
517 }
518 fn parse_particle<'a>(p: &mut &'a str) -> Tok {
519 skip(p);
520 let mut inner = if p.starts_with('(') {
521 *p = &p[1..];
522 let c = parse_choice(p);
523 skip(p);
524 if p.starts_with(')') {
525 *p = &p[1..];
526 }
527 if c.len() == 1 {
528 Tok::Seq(match c.into_iter().next().unwrap() {
529 Tok::Seq(s) => s,
530 other => vec![other],
531 })
532 } else {
533 Tok::Choice(c)
534 }
535 } else {
536 let name = take_name(p);
537 Tok::Name(name)
538 };
539 skip(p);
540 inner = match p.chars().next() {
541 Some('*') => {
542 *p = &p[1..];
543 Tok::Seq(vec![inner, Tok::Star])
544 }
545 Some('+') => {
546 *p = &p[1..];
547 Tok::Seq(vec![inner, Tok::Plus])
548 }
549 Some('?') => {
550 *p = &p[1..];
551 Tok::Seq(vec![inner, Tok::Q])
552 }
553 _ => inner,
554 };
555 inner
556 }
557 fn take_name<'a>(p: &mut &'a str) -> String {
558 let bytes = p.as_bytes();
559 let mut i = 0;
560 while i < bytes.len() {
561 let c = bytes[i] as char;
562 if c.is_ascii_alphanumeric() || "-._:".contains(c) {
563 i += 1;
564 } else {
565 break;
566 }
567 }
568 let s = p[..i].to_string();
569 *p = &p[i..];
570 s
571 }
572 fn skip(p: &mut &str) {
573 *p = p.trim_start();
574 }
575 let mut tmp = p;
576 parse_choice(&mut tmp)
577}
578
579fn match_seq(toks: &[Tok], kids: &[String], i: usize) -> Vec<usize> {
580 if toks.is_empty() {
581 return vec![i];
582 }
583 match &toks[0] {
584 Tok::Star => {
585 let rest = &toks[1..];
586 match_seq(rest, kids, i)
588 }
589 Tok::Plus | Tok::Q => match_seq(&toks[1..], kids, i),
590 Tok::Name(n) => {
591 if i < kids.len() && &kids[i] == n {
592 match_seq(&toks[1..], kids, i + 1)
593 } else {
594 vec![]
595 }
596 }
597 Tok::Seq(inner) => {
598 let (body, quant) = split_quant(inner);
599 apply_quant(body, quant, &toks[1..], kids, i)
600 }
601 Tok::Choice(alts) => {
602 let mut out = Vec::new();
603 for a in alts {
604 let one = match_seq(&[a.clone()], kids, i);
605 for pos in one {
606 out.extend(match_seq(&toks[1..], kids, pos));
607 }
608 }
609 out.sort();
610 out.dedup();
611 out
612 }
613 }
614}
615
616enum Quant {
617 One,
618 Q,
619 Star,
620 Plus,
621}
622
623fn split_quant(inner: &[Tok]) -> (&[Tok], Quant) {
624 if inner.len() >= 2 {
625 match inner.last() {
626 Some(Tok::Star) => return (&inner[..inner.len() - 1], Quant::Star),
627 Some(Tok::Plus) => return (&inner[..inner.len() - 1], Quant::Plus),
628 Some(Tok::Q) => return (&inner[..inner.len() - 1], Quant::Q),
629 _ => {}
630 }
631 }
632 (inner, Quant::One)
633}
634
635fn apply_quant(body: &[Tok], q: Quant, rest: &[Tok], kids: &[String], i: usize) -> Vec<usize> {
636 match q {
637 Quant::One => {
638 let mut out = Vec::new();
639 for p in match_seq(body, kids, i) {
640 out.extend(match_seq(rest, kids, p));
641 }
642 out
643 }
644 Quant::Q => {
645 let mut out = match_seq(rest, kids, i);
646 for p in match_seq(body, kids, i) {
647 out.extend(match_seq(rest, kids, p));
648 }
649 out.sort();
650 out.dedup();
651 out
652 }
653 Quant::Star => {
654 let mut out = match_seq(rest, kids, i);
655 let mut frontier = vec![i];
656 while let Some(p) = frontier.pop() {
657 for n in match_seq(body, kids, p) {
658 if n > p {
659 out.extend(match_seq(rest, kids, n));
660 frontier.push(n);
661 }
662 }
663 }
664 out.sort();
665 out.dedup();
666 out
667 }
668 Quant::Plus => {
669 let mut out = Vec::new();
670 for p in match_seq(body, kids, i) {
671 out.extend(apply_quant(body, Quant::Star, rest, kids, p));
672 }
673 out.sort();
674 out.dedup();
675 out
676 }
677 }
678}