1use core::ffi::c_void;
29use core::ptr;
30use std::os::raw::{c_char, c_int, c_uint};
31
32use crate::abi::allocator;
33use crate::abi::callbacks::xmlGenericErrorFunc;
34use crate::abi::callbacks::xmlHashDeallocator;
35use crate::abi::callbacks::xmlHashScannerFull;
36use crate::abi::structs::*;
37use crate::abi::types::xmlAttributeDefault::*;
38use crate::abi::types::xmlAttributeType::*;
39use crate::abi::types::xmlElementContentOccur::*;
40use crate::abi::types::xmlElementContentType::*;
41use crate::abi::types::xmlElementType::*;
42use crate::abi::types::xmlElementTypeVal::*;
43use crate::abi::types::*;
44use crate::xml::dtd;
45use crate::xml::entities;
46use crate::xml::hash;
47use crate::xml::string;
48
49const VALID_CTXT_DEPTH_MAX: c_int = 256;
55
56pub unsafe fn new_valid_ctxt() -> *mut _xmlValidCtxt {
70 let ctxt = allocator::xmlMallocZero(size_of::<_xmlValidCtxt>() as usize) as *mut _xmlValidCtxt;
72 if ctxt.is_null() {
73 return ptr::null_mut();
74 }
75
76 unsafe {
77 (*ctxt).valid = 1;
78 (*ctxt).node = ptr::null_mut();
79 (*ctxt).doc = ptr::null_mut();
80 (*ctxt).nodeNr = 0;
81 (*ctxt).nodeMax = 0;
82 (*ctxt).nodeTab = ptr::null_mut();
83 (*ctxt).flags = 0;
84 (*ctxt).vstate = ptr::null_mut();
85 (*ctxt).vstateNr = 0;
86 (*ctxt).vstateMax = 0;
87 (*ctxt).vstateTab = ptr::null_mut();
88 (*ctxt).am = ptr::null_mut();
89 (*ctxt).state = ptr::null_mut();
90 (*ctxt).error = None;
91 (*ctxt).warning = None;
92 (*ctxt).userData = ptr::null_mut();
93 }
94
95 ctxt
96}
97
98pub unsafe fn free_valid_ctxt(ctxt: *mut _xmlValidCtxt) {
110 if ctxt.is_null() {
111 return;
112 }
113
114 unsafe {
115 let c = &mut *ctxt;
116
117 if !c.nodeTab.is_null() {
119 allocator::xmlFree(c.nodeTab as *mut c_void);
120 }
121
122 if !c.am.is_null() {
124 }
127
128 if !c.state.is_null() {
130 }
132
133 allocator::xmlFree(ctxt as *mut c_void);
134 }
135}
136
137pub unsafe fn set_valid_errors(
153 ctxt: *mut _xmlValidCtxt,
154 err: Option<xmlGenericErrorFunc>,
155 warn: Option<xmlGenericErrorFunc>,
156 data: *mut c_void,
157) {
158 if ctxt.is_null() {
159 return;
160 }
161
162 unsafe {
163 (*ctxt).error = err;
166 (*ctxt).warning = warn;
167 (*ctxt).userData = data;
168 }
169}
170
171unsafe fn vctxt_error(ctxt: *mut _xmlValidCtxt, msg: *const c_char) {
182 if ctxt.is_null() {
183 return;
184 }
185 unsafe {
186 let c = &mut *ctxt;
187 c.valid = 0;
188 if let Some(err) = c.error {
189 err(c.userData, msg);
190 }
191 }
192}
193
194unsafe fn vctxt_push_node(ctxt: *mut _xmlValidCtxt, node: *mut _xmlNode) -> c_int {
202 unsafe {
203 let c = &mut *ctxt;
204
205 if c.nodeNr >= c.nodeMax {
206 let new_max = if c.nodeMax == 0 { 4 } else { c.nodeMax * 2 };
207 let new_tab = allocator::xmlRealloc(
208 c.nodeTab as *mut c_void,
209 (new_max as usize) * size_of::<*mut _xmlNode>(),
210 ) as *mut *mut _xmlNode;
211 if new_tab.is_null() {
212 return -1;
213 }
214 c.nodeTab = new_tab;
215 c.nodeMax = new_max;
216 }
217
218 *c.nodeTab.add(c.nodeNr as usize) = node;
219 c.nodeNr += 1;
220 c.node = node;
221 }
222 0
223}
224
225unsafe fn vctxt_pop_node(ctxt: *mut _xmlValidCtxt) {
231 unsafe {
232 let c = &mut *ctxt;
233 if c.nodeNr > 0 {
234 c.nodeNr -= 1;
235 }
236 if c.nodeNr > 0 {
237 c.node = *c.nodeTab.add((c.nodeNr - 1) as usize);
238 } else {
239 c.node = ptr::null_mut();
240 }
241 }
242}
243
244unsafe fn get_valid_dtd(doc: *mut _xmlDoc) -> *mut _xmlDtd {
252 if doc.is_null() {
253 return ptr::null_mut();
254 }
255 unsafe {
256 let d = &*doc;
257 if !d.intSubset.is_null() {
258 d.intSubset
259 } else {
260 d.extSubset
261 }
262 }
263}
264
265fn is_xml_name_start(c: char) -> bool {
279 matches!(c,
280 'a'..='z' | 'A'..='Z' | '_' | ':' |
281 '\u{C0}'..='\u{D6}' | '\u{D8}'..='\u{F6}' | '\u{F8}'..='\u{2FF}' |
282 '\u{370}'..='\u{37D}' | '\u{37F}'..='\u{1FFF}' |
283 '\u{200C}'..='\u{200D}' | '\u{2070}'..='\u{218F}' |
284 '\u{2C00}'..='\u{2FEF}' | '\u{3001}'..='\u{D7FF}' |
285 '\u{F900}'..='\u{FDCF}' | '\u{FDF0}'..='\u{FFFD}'
286 )
287}
288
289fn is_xml_name_char(c: char) -> bool {
296 is_xml_name_start(c)
297 || matches!(c,
298 '-' | '.' | '0'..='9' | '\u{B7}' |
299 '\u{0300}'..='\u{036F}' | '\u{203F}'..='\u{2040}'
300 )
301}
302
303pub unsafe fn validate_name(value: *const xmlChar) -> c_int {
321 if value.is_null() {
322 return 0;
323 }
324
325 let s = unsafe { string::xmlstr_to_bytes(value) };
326 let s = core::str::from_utf8(s).unwrap_or("");
327
328 if s.is_empty() {
329 return 0;
330 }
331
332 let mut chars = s.chars();
333
334 match chars.next() {
336 Some(c) if is_xml_name_start(c) => {}
337 _ => return 0,
338 }
339
340 for c in chars {
342 if !is_xml_name_char(c) {
343 return 0;
344 }
345 }
346
347 1
348}
349
350pub unsafe fn validate_names(value: *const xmlChar) -> c_int {
364 if value.is_null() {
365 return 0;
366 }
367
368 let s = unsafe { string::xmlstr_to_bytes(value) };
369 let s = core::str::from_utf8(s).unwrap_or("");
370
371 if s.is_empty() {
372 return 0;
373 }
374
375 for token in s.split_whitespace() {
376 if token.is_empty() {
377 return 0;
378 }
379 let mut chars = token.chars();
380 match chars.next() {
381 Some(c) if is_xml_name_start(c) => {}
382 _ => return 0,
383 }
384 for c in chars {
385 if !is_xml_name_char(c) {
386 return 0;
387 }
388 }
389 }
390
391 1
392}
393
394pub unsafe fn validate_nmtoken(value: *const xmlChar) -> c_int {
413 if value.is_null() {
414 return 0;
415 }
416
417 let s = unsafe { string::xmlstr_to_bytes(value) };
418 let s = core::str::from_utf8(s).unwrap_or("");
419
420 if s.is_empty() {
421 return 0;
422 }
423
424 for c in s.chars() {
425 if !is_xml_name_char(c) {
426 return 0;
427 }
428 }
429
430 1
431}
432
433pub unsafe fn validate_nmtokens(value: *const xmlChar) -> c_int {
447 if value.is_null() {
448 return 0;
449 }
450
451 let s = unsafe { string::xmlstr_to_bytes(value) };
452 let s = core::str::from_utf8(s).unwrap_or("");
453
454 if s.is_empty() {
455 return 0;
456 }
457
458 for token in s.split_whitespace() {
459 if token.is_empty() {
460 return 0;
461 }
462 for c in token.chars() {
463 if !is_xml_name_char(c) {
464 return 0;
465 }
466 }
467 }
468
469 1
470}
471
472pub unsafe fn validate_attribute_value(atype: c_int, value: *const xmlChar) -> c_int {
490 if value.is_null() {
491 return 0;
492 }
493
494 if atype == XML_ATTRIBUTE_CDATA as c_int {
496 return 1;
497 }
498
499 let s = unsafe { string::xmlstr_to_bytes(value) };
500 let s = core::str::from_utf8(s).unwrap_or("");
501
502 if s.is_empty() {
503 return 0;
505 }
506
507 match atype as u32 {
508 t if t == XML_ATTRIBUTE_CDATA as u32 => 1,
509
510 t if t == XML_ATTRIBUTE_ID as u32 => {
511 unsafe { validate_name(value) }
513 }
514
515 t if t == XML_ATTRIBUTE_IDREF as u32 => {
516 unsafe { validate_name(value) }
518 }
519
520 t if t == XML_ATTRIBUTE_IDREFS as u32 => {
521 unsafe { validate_names(value) }
523 }
524
525 t if t == XML_ATTRIBUTE_ENTITY as u32 => {
526 unsafe { validate_name(value) }
528 }
529
530 t if t == XML_ATTRIBUTE_ENTITIES as u32 => {
531 unsafe { validate_names(value) }
533 }
534
535 t if t == XML_ATTRIBUTE_NMTOKEN as u32 => unsafe { validate_nmtoken(value) },
536
537 t if t == XML_ATTRIBUTE_NMTOKENS as u32 => unsafe { validate_nmtokens(value) },
538
539 t if t == XML_ATTRIBUTE_ENUMERATION as u32 => {
540 1
545 }
546
547 t if t == XML_ATTRIBUTE_NOTATION as u32 => {
548 unsafe { validate_name(value) }
550 }
551
552 _ => 0,
553 }
554}
555
556pub unsafe fn validate_enumeration(
578 ctxt: *mut _xmlValidCtxt,
579 value: *const xmlChar,
580 tree: *mut _xmlEnumeration,
581) -> c_int {
582 if value.is_null() || tree.is_null() {
583 return 0;
584 }
585
586 let mut cur = tree;
587 while !cur.is_null() {
588 unsafe {
589 if string::xml_strcmp(value, (*cur).name) == 0 {
590 return 1;
591 }
592 cur = (*cur).next;
593 }
594 }
595
596 unsafe {
598 let msg = string::xmlstr_to_string(value);
599 let err_msg = format!("Value '{}' is not a valid enumeration value\0", msg);
600 vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
601 }
602 0
603}
604
605pub unsafe fn validate_notation_use(
625 ctxt: *mut _xmlValidCtxt,
626 doc: *mut _xmlDoc,
627 notation_name: *const xmlChar,
628) -> c_int {
629 if notation_name.is_null() {
630 return 0;
631 }
632
633 let dtd = unsafe { get_valid_dtd(doc) };
634 if dtd.is_null() {
635 unsafe {
636 vctxt_error(
637 ctxt,
638 b"No DTD available for notation validation\0" as *const u8 as *const c_char,
639 );
640 }
641 return 0;
642 }
643
644 unsafe {
646 let notations = (*dtd).notations;
647 if notations.is_null() {
648 vctxt_error(
649 ctxt,
650 b"No notations declared in DTD\0" as *const u8 as *const c_char,
651 );
652 return 0;
653 }
654
655 let notation = hash::hash_lookup(notations as *mut hash::HashTable, notation_name);
656 if notation.is_null() {
657 let msg = string::xmlstr_to_string(notation_name);
658 let err_msg = format!("Notation '{}' is not declared\0", msg);
659 vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
660 return 0;
661 }
662 }
663
664 1
665}
666
667pub unsafe fn validate_id(
689 ctxt: *mut _xmlValidCtxt,
690 doc: *mut _xmlDoc,
691 node: *mut _xmlNode,
692 value: *const xmlChar,
693) -> c_int {
694 if value.is_null() || doc.is_null() {
695 return 0;
696 }
697
698 if unsafe { validate_name(value) } == 0 {
700 unsafe {
701 let msg = string::xmlstr_to_string(value);
702 let err_msg = format!("ID value '{}' is not a valid XML Name\0", msg);
703 vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
704 }
705 return 0;
706 }
707
708 unsafe {
710 let doc_ref = &*doc;
711 if !doc_ref.ids.is_null() {
712 let existing = hash::hash_lookup(doc_ref.ids as *mut hash::HashTable, value);
713 if !existing.is_null() {
714 let msg = string::xmlstr_to_string(value);
715 let err_msg = format!("Duplicate ID value '{}'\0", msg);
716 vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
717 return 0;
718 }
719 }
720 }
721
722 unsafe {
724 if (*doc).ids.is_null() {
725 (*doc).ids = hash::hash_create(16) as *mut c_void;
726 }
727 hash::hash_add_entry(
728 (*doc).ids as *mut hash::HashTable,
729 value,
730 node as *mut c_void,
731 );
732 }
733
734 1
735}
736
737pub unsafe fn validate_id_ref(
754 ctxt: *mut _xmlValidCtxt,
755 doc: *mut _xmlDoc,
756 node: *mut _xmlNode,
757 value: *const xmlChar,
758) -> c_int {
759 if value.is_null() || doc.is_null() {
760 return 0;
761 }
762
763 if unsafe { validate_name(value) } == 0 {
765 unsafe {
766 let msg = string::xmlstr_to_string(value);
767 let err_msg = format!("IDREF value '{}' is not a valid XML Name\0", msg);
768 vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
769 }
770 return 0;
771 }
772
773 unsafe {
775 let doc_ref = &*doc;
776 if doc_ref.ids.is_null()
777 || hash::hash_lookup(doc_ref.ids as *mut hash::HashTable, value).is_null()
778 {
779 let msg = string::xmlstr_to_string(value);
783 let err_msg = format!("IDREF '{}' references an unknown ID\0", msg);
784 vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
785 return 0;
786 }
787 }
788
789 1
790}
791
792pub unsafe fn validate_id_refs(
809 ctxt: *mut _xmlValidCtxt,
810 doc: *mut _xmlDoc,
811 node: *mut _xmlNode,
812 value: *const xmlChar,
813) -> c_int {
814 if value.is_null() || doc.is_null() {
815 return 0;
816 }
817
818 let s = unsafe { string::xmlstr_to_bytes(value) };
819 let s = core::str::from_utf8(s).unwrap_or("");
820
821 if s.is_empty() {
822 return 0;
823 }
824
825 let mut valid = 1;
826 for token in s.split_whitespace() {
827 if token.is_empty() {
828 continue;
829 }
830 let token_ptr = unsafe { string::bytes_to_xmlstr(token.as_bytes()) };
832 if token_ptr.is_null() {
833 valid = 0;
834 break;
835 }
836 let result = unsafe { validate_id_ref(ctxt, doc, node, token_ptr) };
837 unsafe {
838 allocator::xmlFree(token_ptr as *mut c_void);
839 }
840 if result == 0 {
841 valid = 0;
842 }
843 }
844
845 valid
846}
847
848pub unsafe fn validate_attribute_decl(
875 ctxt: *mut _xmlValidCtxt,
876 doc: *mut _xmlDoc,
877 elem: *mut _xmlNode,
878 attr: *mut _xmlAttribute,
879) -> c_int {
880 if attr.is_null() {
881 return 0;
882 }
883
884 unsafe {
885 let a = &*attr;
886 let atype = a.atype as c_int;
887
888 if !a.defaultValue.is_null() {
890 if validate_attribute_value(atype, a.defaultValue) == 0 {
891 let name_str = string::xmlstr_to_string(a.name);
892 let val_str = string::xmlstr_to_string(a.defaultValue);
893 let err_msg = format!(
894 "Default value '{}' for attribute '{}' is not valid for its type\0",
895 val_str, name_str
896 );
897 vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
898 return 0;
899 }
900 }
901
902 if atype == XML_ATTRIBUTE_ENUMERATION as c_int && !a.tree.is_null() {
904 let mut cur = a.tree;
906 while !cur.is_null() {
907 if !(*cur).name.is_null() {
908 if validate_nmtoken((*cur).name) == 0 {
909 let val_str = string::xmlstr_to_string((*cur).name);
910 let err_msg =
911 format!("Enumeration value '{}' is not a valid NMTOKEN\0", val_str);
912 vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
913 return 0;
914 }
915 }
916 cur = (*cur).next;
917 }
918 }
919
920 if atype == XML_ATTRIBUTE_NOTATION as c_int && !a.tree.is_null() {
922 let mut cur = a.tree;
923 while !cur.is_null() {
924 if !(*cur).name.is_null() {
925 if validate_notation_use(ctxt, doc, (*cur).name) == 0 {
926 let val_str = string::xmlstr_to_string((*cur).name);
927 let err_msg = format!(
928 "NOTATION value '{}' references undeclared notation\0",
929 val_str
930 );
931 vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
932 return 0;
933 }
934 }
935 cur = (*cur).next;
936 }
937 }
938
939 1
940 }
941}
942
943pub unsafe fn validate_element(
972 ctxt: *mut _xmlValidCtxt,
973 doc: *mut _xmlDoc,
974 elem: *mut _xmlNode,
975) -> c_int {
976 if elem.is_null() || doc.is_null() || ctxt.is_null() {
977 return 0;
978 }
979
980 unsafe {
981 let e = &*elem;
982
983 if e.type_ != XML_ELEMENT_NODE as c_int {
985 return 1;
986 }
987
988 if vctxt_push_node(ctxt, elem) != 0 {
990 return 0;
991 }
992
993 let mut valid = 1;
994
995 let dtd = get_valid_dtd(doc);
997 if dtd.is_null() {
998 vctxt_pop_node(ctxt);
1001 return 1;
1002 }
1003
1004 let dtd_ref = &*dtd;
1005
1006 let elem_name = e.name;
1008 let elem_decl = if !dtd_ref.elements.is_null() {
1009 hash::hash_lookup(dtd_ref.elements as *mut hash::HashTable, elem_name)
1010 } else {
1011 ptr::null_mut()
1012 };
1013
1014 if elem_decl.is_null() {
1015 let name_str = string::xmlstr_to_string(elem_name);
1016 let err_msg = format!("No declaration for element {}\0", name_str);
1017 vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
1018 vctxt_pop_node(ctxt);
1019 return 0;
1020 }
1021
1022 let elem_decl_ref = &*(elem_decl as *mut _xmlElement);
1023
1024 let elem_type = elem_decl_ref.type_ as u32;
1026
1027 if elem_type == XML_ELEMENT_TYPE_EMPTY as u32 {
1028 let mut child = e.children;
1030 while !child.is_null() {
1031 let child_type = (*child).type_ as u32;
1032 if child_type != XML_TEXT_NODE as u32 && child_type != XML_CDATA_SECTION_NODE as u32
1033 {
1034 valid = 0;
1035 let name_str = string::xmlstr_to_string(elem_name);
1036 let err_msg = format!(
1037 "Element '{}' is declared EMPTY but has child elements\0",
1038 name_str
1039 );
1040 vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
1041 break;
1042 }
1043 child = (*child).next;
1044 }
1045 } else if elem_type == XML_ELEMENT_TYPE_ANY as u32 {
1046 } else if elem_type == XML_ELEMENT_TYPE_MIXED as u32 {
1048 let mut child = e.children;
1050 while !child.is_null() {
1051 let child_type = (*child).type_ as u32;
1052 if child_type == XML_ELEMENT_NODE as u32 {
1053 let child_name = (*child).name;
1055 let result = dtd::valid_content_model(elem_decl_ref.content, &[child_name]);
1056 if result != dtd::ContentModelResult::Valid {
1057 let cname_str = string::xmlstr_to_string(child_name);
1058 let ename_str = string::xmlstr_to_string(elem_name);
1059 let err_msg = format!(
1060 "Element '{}' is not allowed in mixed content of '{}'\0",
1061 cname_str, ename_str
1062 );
1063 vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
1064 valid = 0;
1065 }
1066 }
1067 child = (*child).next;
1068 }
1069 } else if elem_type == XML_ELEMENT_TYPE_ELEMENT as u32 {
1070 let mut child_names: Vec<*const xmlChar> = Vec::new();
1072 let mut child = e.children;
1073 while !child.is_null() {
1074 if (*child).type_ == XML_ELEMENT_NODE as c_int {
1075 child_names.push((*child).name);
1076 }
1077 child = (*child).next;
1078 }
1079
1080 let result = dtd::valid_content_model(elem_decl_ref.content, &child_names);
1081 if result != dtd::ContentModelResult::Valid {
1082 let ename_str = string::xmlstr_to_string(elem_name);
1083 let err_msg = format!(
1084 "Content model validation failed for element '{}'\0",
1085 ename_str
1086 );
1087 vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
1088 valid = 0;
1089 }
1090 }
1091
1092 if !dtd_ref.attributes.is_null() {
1094 let mut attr_prop = e.properties;
1096 while !attr_prop.is_null() {
1097 let attr_ref = &*attr_prop;
1098 let attr_name = attr_ref.name;
1099
1100 let attr_decl = hash::hash_lookup2(
1102 dtd_ref.attributes as *mut hash::HashTable,
1103 elem_name,
1104 attr_name,
1105 );
1106
1107 if attr_decl.is_null() {
1108 attr_prop = attr_ref.next;
1113 continue;
1114 }
1115
1116 let attr_decl_ref = &*(attr_decl as *mut _xmlAttribute);
1117 let atype = attr_decl_ref.atype as c_int;
1118
1119 let attr_value = if !attr_ref.children.is_null() {
1121 let text_node = attr_ref.children;
1123 if (*text_node).type_ == XML_TEXT_NODE as c_int
1124 || (*text_node).type_ == XML_CDATA_SECTION_NODE as c_int
1125 {
1126 (*text_node).content
1127 } else {
1128 ptr::null()
1129 }
1130 } else {
1131 ptr::null()
1132 };
1133
1134 if !attr_value.is_null() {
1136 if atype == XML_ATTRIBUTE_ENUMERATION as c_int && !attr_decl_ref.tree.is_null()
1137 {
1138 if validate_enumeration(ctxt, attr_value, attr_decl_ref.tree) == 0 {
1139 let aname_str = string::xmlstr_to_string(attr_name);
1140 let aval_str = string::xmlstr_to_string(attr_value);
1141 let err_msg = format!(
1142 "Attribute '{}' has value '{}' not in enumeration\0",
1143 aname_str, aval_str
1144 );
1145 vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
1146 valid = 0;
1147 }
1148 } else if atype == XML_ATTRIBUTE_NOTATION as c_int {
1149 if validate_notation_use(ctxt, doc, attr_value) == 0 {
1150 let aname_str = string::xmlstr_to_string(attr_name);
1151 let aval_str = string::xmlstr_to_string(attr_value);
1152 let err_msg = format!(
1153 "Attribute '{}' references undeclared notation '{}'\0",
1154 aname_str, aval_str
1155 );
1156 vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
1157 valid = 0;
1158 }
1159 } else if validate_attribute_value(atype, attr_value) == 0 {
1160 let aname_str = string::xmlstr_to_string(attr_name);
1161 let aval_str = string::xmlstr_to_string(attr_value);
1162 let err_msg = format!(
1163 "Attribute '{}' has invalid value '{}' for its type\0",
1164 aname_str, aval_str
1165 );
1166 vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
1167 valid = 0;
1168 }
1169
1170 if atype == XML_ATTRIBUTE_ID as c_int {
1172 if validate_id(ctxt, doc, elem, attr_value) == 0 {
1173 valid = 0;
1174 }
1175 } else if atype == XML_ATTRIBUTE_IDREF as c_int {
1176 if validate_id_ref(ctxt, doc, elem, attr_value) == 0 {
1177 valid = 0;
1178 }
1179 } else if atype == XML_ATTRIBUTE_IDREFS as c_int {
1180 if validate_id_refs(ctxt, doc, elem, attr_value) == 0 {
1181 valid = 0;
1182 }
1183 }
1184 }
1185
1186 attr_prop = attr_ref.next;
1187 }
1188
1189 struct RequiredAttrCheck {
1191 ctxt: *mut _xmlValidCtxt,
1192 elem_name: *const xmlChar,
1193 elem_props: *mut _xmlAttr,
1194 valid: *mut c_int,
1195 }
1196
1197 extern "C" fn check_required_attr(
1198 payload: *mut c_void,
1199 data: *mut c_void,
1200 name: *const xmlChar,
1201 name2: *const xmlChar,
1202 _name3: *const xmlChar,
1203 ) {
1204 if payload.is_null() || data.is_null() || name2.is_null() {
1205 return;
1206 }
1207
1208 let check = unsafe { &*(data as *mut RequiredAttrCheck) };
1210 unsafe {
1211 if string::xml_strcmp(name, check.elem_name) != 0 {
1213 return;
1214 }
1215
1216 let attr_decl = &*(payload as *mut _xmlAttribute);
1217
1218 if attr_decl.def == XML_ATTRIBUTE_REQUIRED as c_int {
1220 let mut found = 0;
1222 let mut prop = check.elem_props;
1223 while !prop.is_null() {
1224 if string::xml_strcmp((*prop).name, name2) == 0 {
1225 found = 1;
1226 break;
1227 }
1228 prop = (*prop).next;
1229 }
1230
1231 if found == 0 {
1232 let aname_str = string::xmlstr_to_string(name2);
1233 let ename_str = string::xmlstr_to_string(check.elem_name);
1234 let err_msg = format!(
1235 "Required attribute '{}' missing on element '{}'\0",
1236 aname_str, ename_str
1237 );
1238 vctxt_error(check.ctxt, err_msg.as_ptr() as *const c_char);
1239 *(check.valid) = 0;
1240 }
1241 }
1242 }
1243 }
1244
1245 let mut required_valid = valid;
1246 let check = RequiredAttrCheck {
1247 ctxt,
1248 elem_name,
1249 elem_props: e.properties,
1250 valid: &mut required_valid,
1251 };
1252
1253 hash::hash_scan_full(
1254 dtd_ref.attributes as *mut hash::HashTable,
1255 Some(check_required_attr),
1256 &check as *const RequiredAttrCheck as *mut c_void,
1257 );
1258
1259 valid = required_valid;
1260 }
1261
1262 let mut child = e.children;
1264 while !child.is_null() {
1265 if (*child).type_ == XML_ELEMENT_NODE as c_int {
1266 if validate_element(ctxt, doc, child) == 0 {
1267 valid = 0;
1268 }
1269 }
1270 child = (*child).next;
1271 }
1272
1273 vctxt_pop_node(ctxt);
1274 valid
1275 }
1276}
1277
1278pub unsafe fn validate_document(ctxt: *mut _xmlValidCtxt, doc: *mut _xmlDoc) -> c_int {
1298 if ctxt.is_null() || doc.is_null() {
1299 return 0;
1300 }
1301
1302 unsafe {
1303 let c = &mut *ctxt;
1304 c.doc = doc;
1305 c.valid = 1;
1306
1307 let d = &*doc;
1308
1309 if d.intSubset.is_null() && d.extSubset.is_null() {
1319 vctxt_error(ctxt, b"no DTD found!\0" as *const u8 as *const c_char);
1320 return 0;
1321 }
1322
1323 let mut root = d.children;
1325 while !root.is_null() {
1326 if (*root).type_ == XML_ELEMENT_NODE as c_int {
1327 break;
1328 }
1329 root = (*root).next;
1330 }
1331
1332 if root.is_null() {
1333 vctxt_error(
1334 ctxt,
1335 b"No root element found in document\0" as *const u8 as *const c_char,
1336 );
1337 return 0;
1338 }
1339
1340 if validate_element(ctxt, doc, root) == 0 {
1342 return 0;
1343 }
1344
1345 c.valid
1346 }
1347}
1348
1349pub unsafe fn validate_document_final(ctxt: *mut _xmlValidCtxt, doc: *mut _xmlDoc) -> c_int {
1371 if ctxt.is_null() || doc.is_null() {
1372 return 0;
1373 }
1374
1375 unsafe {
1376 let c = &mut *ctxt;
1377 c.doc = doc;
1378
1379 let d = &*doc;
1380
1381 if d.refs.is_null() {
1383 return c.valid;
1384 }
1385
1386 struct IdRefCheckContext {
1388 ctxt: *mut _xmlValidCtxt,
1389 doc: *mut _xmlDoc,
1390 }
1391
1392 extern "C" fn check_idref(
1393 _payload: *mut c_void,
1394 data: *mut c_void,
1395 _name: *const xmlChar,
1396 name2: *const xmlChar,
1397 _name3: *const xmlChar,
1398 ) {
1399 if data.is_null() || name2.is_null() {
1400 return;
1401 }
1402
1403 let cx = unsafe { &*(data as *mut IdRefCheckContext) };
1405 unsafe {
1406 let doc_ref = &*cx.doc;
1407
1408 if doc_ref.ids.is_null()
1410 || hash::hash_lookup(doc_ref.ids as *mut hash::HashTable, name2).is_null()
1411 {
1412 let ref_str = string::xmlstr_to_string(name2);
1413 let err_msg = format!("IDREF '{}' does not reference a declared ID\0", ref_str);
1414 vctxt_error(cx.ctxt, err_msg.as_ptr() as *const c_char);
1415 }
1416 }
1417 }
1418
1419 let ctx = IdRefCheckContext { ctxt, doc };
1420 hash::hash_scan_full(
1421 d.refs as *mut hash::HashTable,
1422 Some(check_idref),
1423 &ctx as *const IdRefCheckContext as *mut c_void,
1424 );
1425
1426 c.valid
1427 }
1428}
1429
1430pub unsafe fn validate_root(ctxt: *mut _xmlValidCtxt, doc: *mut _xmlDoc) -> c_int {
1448 if ctxt.is_null() || doc.is_null() {
1449 return 0;
1450 }
1451
1452 unsafe {
1453 let c = &mut *ctxt;
1454 c.doc = doc;
1455 c.valid = 1;
1456
1457 let d = &*doc;
1458
1459 let mut root = d.children;
1461 while !root.is_null() {
1462 if (*root).type_ == XML_ELEMENT_NODE as c_int {
1463 break;
1464 }
1465 root = (*root).next;
1466 }
1467
1468 if root.is_null() {
1469 vctxt_error(
1470 ctxt,
1471 b"No root element found\0" as *const u8 as *const c_char,
1472 );
1473 return 0;
1474 }
1475
1476 let dtd = get_valid_dtd(doc);
1478 if dtd.is_null() {
1479 return 1;
1481 }
1482
1483 let dtd_ref = &*dtd;
1486 if !dtd_ref.name.is_null() {
1487 if string::xml_strcmp((*root).name, dtd_ref.name) != 0 {
1488 let root_str = string::xmlstr_to_string((*root).name);
1489 let dtd_str = string::xmlstr_to_string(dtd_ref.name);
1490 let err_msg = format!(
1491 "Root element '{}' does not match DTD root '{}'\0",
1492 root_str, dtd_str
1493 );
1494 vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
1495 return 0;
1496 }
1497 }
1498
1499 c.valid
1500 }
1501}
1502
1503pub unsafe fn validate_content(
1523 ctxt: *mut _xmlValidCtxt,
1524 node: *mut _xmlNode,
1525 doc: *mut _xmlDoc,
1526) -> c_int {
1527 if node.is_null() || doc.is_null() || ctxt.is_null() {
1528 return 0;
1529 }
1530
1531 unsafe {
1532 let n = &*node;
1533 if n.type_ != XML_ELEMENT_NODE as c_int {
1534 return 1;
1535 }
1536
1537 let dtd = get_valid_dtd(doc);
1538 if dtd.is_null() {
1539 return 1;
1540 }
1541
1542 let dtd_ref = &*dtd;
1543 if dtd_ref.elements.is_null() {
1544 return 1;
1545 }
1546
1547 let elem_decl = hash::hash_lookup(dtd_ref.elements as *mut hash::HashTable, n.name);
1548 if elem_decl.is_null() {
1549 return 1;
1550 }
1551
1552 let elem_decl_ref = &*(elem_decl as *mut _xmlElement);
1553 if elem_decl_ref.content.is_null() {
1554 return 1;
1555 }
1556
1557 let elem_type = elem_decl_ref.type_ as u32;
1558 if elem_type == XML_ELEMENT_TYPE_EMPTY as u32 {
1559 let mut child = n.children;
1561 while !child.is_null() {
1562 if (*child).type_ == XML_ELEMENT_NODE as c_int {
1563 let name_str = string::xmlstr_to_string(n.name);
1564 let err_msg =
1565 format!("Element '{}' is EMPTY but has child elements\0", name_str);
1566 vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
1567 return 0;
1568 }
1569 child = (*child).next;
1570 }
1571 return 1;
1572 }
1573
1574 if elem_type == XML_ELEMENT_TYPE_ANY as u32 {
1575 return 1;
1576 }
1577
1578 let mut child_names: Vec<*const xmlChar> = Vec::new();
1580 let mut child = n.children;
1581 while !child.is_null() {
1582 if (*child).type_ == XML_ELEMENT_NODE as c_int {
1583 child_names.push((*child).name);
1584 }
1585 child = (*child).next;
1586 }
1587
1588 let result = dtd::valid_content_model(elem_decl_ref.content, &child_names);
1589 if result != dtd::ContentModelResult::Valid {
1590 let name_str = string::xmlstr_to_string(n.name);
1591 let err_msg = format!(
1592 "Content model validation failed for element '{}'\0",
1593 name_str
1594 );
1595 vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
1596 0
1597 } else {
1598 1
1599 }
1600 }
1601}
1602
1603pub unsafe fn is_mixed_element(doc: *mut _xmlDoc, name: *const xmlChar) -> c_int {
1621 if doc.is_null() || name.is_null() {
1622 return 0;
1623 }
1624
1625 let dtd = unsafe { get_valid_dtd(doc) };
1626 if dtd.is_null() {
1627 return 0;
1628 }
1629
1630 unsafe {
1631 let dtd_ref = &*dtd;
1632 if dtd_ref.elements.is_null() {
1633 return 0;
1634 }
1635
1636 let elem_decl = hash::hash_lookup(dtd_ref.elements as *mut hash::HashTable, name);
1637 if elem_decl.is_null() {
1638 return 0;
1639 }
1640
1641 let elem_decl_ref = &*(elem_decl as *mut _xmlElement);
1642 ((elem_decl_ref.type_ as u32) == XML_ELEMENT_TYPE_MIXED as u32) as c_int
1643 }
1644}
1645
1646pub unsafe fn is_empty_element(doc: *mut _xmlDoc, name: *const xmlChar) -> c_int {
1660 if doc.is_null() || name.is_null() {
1661 return 0;
1662 }
1663
1664 let dtd = unsafe { get_valid_dtd(doc) };
1665 if dtd.is_null() {
1666 return 0;
1667 }
1668
1669 unsafe {
1670 let dtd_ref = &*dtd;
1671 if dtd_ref.elements.is_null() {
1672 return 0;
1673 }
1674
1675 let elem_decl = hash::hash_lookup(dtd_ref.elements as *mut hash::HashTable, name);
1676 if elem_decl.is_null() {
1677 return 0;
1678 }
1679
1680 let elem_decl_ref = &*(elem_decl as *mut _xmlElement);
1681 ((elem_decl_ref.type_ as u32) == XML_ELEMENT_TYPE_EMPTY as u32) as c_int
1682 }
1683}
1684
1685pub unsafe fn validate_dtd(
1709 ctxt: *mut _xmlValidCtxt,
1710 doc: *mut _xmlDoc,
1711 dtd: *mut _xmlDtd,
1712) -> c_int {
1713 if ctxt.is_null() || dtd.is_null() {
1714 return 0;
1715 }
1716
1717 let c = unsafe { &mut *ctxt };
1718 c.doc = doc;
1719 c.valid = 1;
1720
1721 struct ValidateDtdCtx {
1722 ctxt: *mut _xmlValidCtxt,
1723 doc: *mut _xmlDoc,
1724 }
1725
1726 extern "C" fn validate_attr_decl_cb(
1727 payload: *mut c_void,
1728 data: *mut c_void,
1729 _name: *const xmlChar,
1730 _name2: *const xmlChar,
1731 _name3: *const xmlChar,
1732 ) {
1733 if payload.is_null() || data.is_null() {
1734 return;
1735 }
1736
1737 let ctx = unsafe { &*(data as *mut ValidateDtdCtx) };
1739 unsafe {
1740 let attr = payload as *mut _xmlAttribute;
1741 validate_attribute_decl(ctx.ctxt, ctx.doc, ptr::null_mut(), attr);
1742 }
1743 }
1744
1745 extern "C" fn validate_elem_content_cb(
1746 payload: *mut c_void,
1747 data: *mut c_void,
1748 _name: *const xmlChar,
1749 _name2: *const xmlChar,
1750 _name3: *const xmlChar,
1751 ) {
1752 if payload.is_null() || data.is_null() {
1753 return;
1754 }
1755
1756 let ctx = unsafe { &*(data as *mut ValidateDtdCtx) };
1758 unsafe {
1759 let elem = &*(payload as *mut _xmlElement);
1760 if !elem.content.is_null() {
1761 validate_content_model_refs(ctx.ctxt, ctx.doc, elem.content);
1762 }
1763 }
1764 }
1765
1766 unsafe {
1767 let dtd_ref = &*dtd;
1768
1769 if !dtd_ref.attributes.is_null() {
1771 let ctx = ValidateDtdCtx { ctxt, doc };
1772 hash::hash_scan_full(
1773 dtd_ref.attributes as *mut hash::HashTable,
1774 Some(validate_attr_decl_cb),
1775 &ctx as *const ValidateDtdCtx as *mut c_void,
1776 );
1777 }
1778
1779 if !dtd_ref.elements.is_null() {
1781 let ctx = ValidateDtdCtx { ctxt, doc };
1782 hash::hash_scan_full(
1783 dtd_ref.elements as *mut hash::HashTable,
1784 Some(validate_elem_content_cb),
1785 &ctx as *const ValidateDtdCtx as *mut c_void,
1786 );
1787 }
1788
1789 c.valid
1790 }
1791}
1792
1793unsafe fn validate_content_model_refs(
1800 ctxt: *mut _xmlValidCtxt,
1801 doc: *mut _xmlDoc,
1802 content: *mut _xmlElementContent,
1803) {
1804 if content.is_null() {
1805 return;
1806 }
1807
1808 unsafe {
1809 let c = &*content;
1810
1811 match c.type_ as u32 {
1812 t if t == XML_ELEMENT_CONTENT_ELEMENT as u32 => {
1813 if !c.name.is_null() {
1815 let dtd = get_valid_dtd(doc);
1816 if !dtd.is_null() {
1817 let dtd_ref = &*dtd;
1818 if !dtd_ref.elements.is_null() {
1819 let decl =
1820 hash::hash_lookup(dtd_ref.elements as *mut hash::HashTable, c.name);
1821 if decl.is_null() {
1822 let name_str = string::xmlstr_to_string(c.name);
1823 let err_msg = format!(
1824 "Element '{}' referenced in content model is not declared\0",
1825 name_str
1826 );
1827 vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
1828 }
1829 }
1830 }
1831 }
1832 }
1833 t if t == XML_ELEMENT_CONTENT_SEQ as u32 || t == XML_ELEMENT_CONTENT_OR as u32 => {
1834 validate_content_model_refs(ctxt, doc, c.c1);
1835 validate_content_model_refs(ctxt, doc, c.c2);
1836 }
1837 _ => {}
1838 }
1839 }
1840}
1841
1842pub unsafe fn validate_dtd_final(ctxt: *mut _xmlValidCtxt, doc: *mut _xmlDoc) -> c_int {
1863 unsafe { validate_document_final(ctxt, doc) }
1864}
1865
1866#[cfg(test)]
1871mod tests {
1872 use super::*;
1873 use crate::abi::allocator;
1874 use crate::abi::types::xmlElementTypeVal::*;
1875 use crate::xml::dtd;
1876 use crate::xml::tree;
1877
1878 unsafe fn c_str(s: &str) -> *const xmlChar {
1882 let bytes = s.as_bytes();
1883 let ptr = allocator::xmlMalloc(bytes.len() + 1) as *mut xmlChar;
1884 assert!(!ptr.is_null());
1885 std::ptr::copy_nonoverlapping(bytes.as_ptr(), ptr, bytes.len());
1886 *ptr.add(bytes.len()) = 0;
1887 ptr
1888 }
1889
1890 unsafe fn make_test_doc() -> (*mut _xmlDoc, *mut _xmlDtd) {
1892 let doc = tree::new_doc(b"1.0\0" as *const u8 as *const xmlChar);
1893 assert!(!doc.is_null());
1894
1895 let name = c_str("root");
1896 let ext_id = c_str("--//Test//DTD//EN");
1897 let sys_id = c_str("test.dtd");
1898 let dtd = dtd::create_int_subset(doc, name, ext_id, sys_id);
1899 assert!(!dtd.is_null());
1900
1901 (doc, dtd)
1902 }
1903
1904 #[allow(unused)]
1906 unsafe fn add_elem_decl(
1907 dtd: *mut _xmlDtd,
1908 name: *const xmlChar,
1909 elem_type: c_int,
1910 content: *mut _xmlElementContent,
1911 ) -> *mut _xmlElement {
1912 let result = dtd::add_element_decl(dtd, name, elem_type, content);
1913 result
1914 }
1915
1916 unsafe fn create_root_elem(doc: *mut _xmlDoc, name: *const xmlChar) -> *mut _xmlNode {
1918 let node = tree::new_node(ptr::null_mut(), name);
1919 assert!(!node.is_null());
1920 tree::add_child(doc as *mut _xmlNode, node);
1921 node
1922 }
1923
1924 #[allow(unused)]
1926 unsafe fn create_child_elem(parent: *mut _xmlNode, name: *const xmlChar) -> *mut _xmlNode {
1927 let node = tree::new_node(ptr::null_mut(), name);
1928 assert!(!node.is_null());
1929 tree::add_child(parent, node);
1930 node
1931 }
1932
1933 #[test]
1936 fn test_validate_name_null() {
1937 unsafe {
1938 assert_eq!(validate_name(ptr::null()), 0);
1939 }
1940 }
1941
1942 #[test]
1943 fn test_validate_name_empty() {
1944 unsafe {
1945 let s = b"\0" as *const u8 as *const xmlChar;
1946 assert_eq!(validate_name(s), 0);
1947 }
1948 }
1949
1950 #[test]
1951 fn test_validate_name_valid() {
1952 unsafe {
1953 let tests = ["foo", "_bar", ":baz", "hello-world", "ns:elem", "a123"];
1954 for t in &tests {
1955 let s = c_str(t);
1956 assert_eq!(validate_name(s), 1, "Expected '{}' to be a valid Name", t);
1957 allocator::xmlFree(s as *mut c_void);
1958 }
1959 }
1960 }
1961
1962 #[test]
1963 fn test_validate_name_invalid() {
1964 unsafe {
1965 let tests = ["123abc", "-foo", ".bar", "foo bar", "a b"];
1966 for t in &tests {
1967 let s = c_str(t);
1968 assert_eq!(validate_name(s), 0, "Expected '{}' to be invalid", t);
1969 allocator::xmlFree(s as *mut c_void);
1970 }
1971 }
1972 }
1973
1974 #[test]
1975 fn test_validate_names_valid() {
1976 unsafe {
1977 let s = c_str("foo bar baz");
1978 assert_eq!(validate_names(s), 1);
1979 allocator::xmlFree(s as *mut c_void);
1980 }
1981 }
1982
1983 #[test]
1984 fn test_validate_names_invalid() {
1985 unsafe {
1986 let s = c_str("foo 123bar baz");
1987 assert_eq!(validate_names(s), 0);
1988 allocator::xmlFree(s as *mut c_void);
1989 }
1990 }
1991
1992 #[test]
1995 fn test_validate_nmtoken_null() {
1996 unsafe {
1997 assert_eq!(validate_nmtoken(ptr::null()), 0);
1998 }
1999 }
2000
2001 #[test]
2002 fn test_validate_nmtoken_valid() {
2003 unsafe {
2004 let tests = ["foo", "123abc", "-foo", ".bar", "_test", ":ns"];
2005 for t in &tests {
2006 let s = c_str(t);
2007 assert_eq!(
2008 validate_nmtoken(s),
2009 1,
2010 "Expected '{}' to be a valid NMTOKEN",
2011 t
2012 );
2013 allocator::xmlFree(s as *mut c_void);
2014 }
2015 }
2016 }
2017
2018 #[test]
2019 fn test_validate_nmtoken_invalid() {
2020 unsafe {
2021 let s = c_str("foo bar");
2022 assert_eq!(validate_nmtoken(s), 0);
2023 allocator::xmlFree(s as *mut c_void);
2024 }
2025 }
2026
2027 #[test]
2028 fn test_validate_nmtokens_valid() {
2029 unsafe {
2030 let s = c_str("foo 123bar -baz");
2031 assert_eq!(validate_nmtokens(s), 1);
2032 allocator::xmlFree(s as *mut c_void);
2033 }
2034 }
2035
2036 #[test]
2039 fn test_validate_attribute_value_cdata() {
2040 unsafe {
2041 let s = c_str("anything goes here!@#$%^&*()");
2042 assert_eq!(validate_attribute_value(XML_ATTRIBUTE_CDATA as c_int, s), 1);
2043 allocator::xmlFree(s as *mut c_void);
2044
2045 let empty = b"\0" as *const u8 as *const xmlChar;
2047 assert_eq!(
2048 validate_attribute_value(XML_ATTRIBUTE_CDATA as c_int, empty),
2049 1
2050 );
2051 }
2052 }
2053
2054 #[test]
2055 fn test_validate_attribute_value_id() {
2056 unsafe {
2057 let valid = c_str("myId");
2058 assert_eq!(
2059 validate_attribute_value(XML_ATTRIBUTE_ID as c_int, valid),
2060 1
2061 );
2062 allocator::xmlFree(valid as *mut c_void);
2063
2064 let invalid = c_str("123id");
2065 assert_eq!(
2066 validate_attribute_value(XML_ATTRIBUTE_ID as c_int, invalid),
2067 0
2068 );
2069 allocator::xmlFree(invalid as *mut c_void);
2070 }
2071 }
2072
2073 #[test]
2074 fn test_validate_attribute_value_idref() {
2075 unsafe {
2076 let valid = c_str("someId");
2077 assert_eq!(
2078 validate_attribute_value(XML_ATTRIBUTE_IDREF as c_int, valid),
2079 1
2080 );
2081 allocator::xmlFree(valid as *mut c_void);
2082 }
2083 }
2084
2085 #[test]
2086 fn test_validate_attribute_value_idrefs() {
2087 unsafe {
2088 let valid = c_str("id1 id2 id3");
2089 assert_eq!(
2090 validate_attribute_value(XML_ATTRIBUTE_IDREFS as c_int, valid),
2091 1
2092 );
2093 allocator::xmlFree(valid as *mut c_void);
2094
2095 let invalid = c_str("id1 123id");
2096 assert_eq!(
2097 validate_attribute_value(XML_ATTRIBUTE_IDREFS as c_int, invalid),
2098 0
2099 );
2100 allocator::xmlFree(invalid as *mut c_void);
2101 }
2102 }
2103
2104 #[test]
2105 fn test_validate_attribute_value_entity() {
2106 unsafe {
2107 let valid = c_str("myEntity");
2108 assert_eq!(
2109 validate_attribute_value(XML_ATTRIBUTE_ENTITY as c_int, valid),
2110 1
2111 );
2112 allocator::xmlFree(valid as *mut c_void);
2113 }
2114 }
2115
2116 #[test]
2117 fn test_validate_attribute_value_nmtoken() {
2118 unsafe {
2119 let valid = c_str("123abc");
2120 assert_eq!(
2121 validate_attribute_value(XML_ATTRIBUTE_NMTOKEN as c_int, valid),
2122 1
2123 );
2124 allocator::xmlFree(valid as *mut c_void);
2125
2126 let invalid = c_str("foo bar");
2127 assert_eq!(
2128 validate_attribute_value(XML_ATTRIBUTE_NMTOKEN as c_int, invalid),
2129 0
2130 );
2131 allocator::xmlFree(invalid as *mut c_void);
2132 }
2133 }
2134
2135 #[test]
2136 fn test_validate_attribute_value_null() {
2137 unsafe {
2138 assert_eq!(
2139 validate_attribute_value(XML_ATTRIBUTE_CDATA as c_int, ptr::null()),
2140 0
2141 );
2142 }
2143 }
2144
2145 #[test]
2148 fn test_validate_enumeration_valid() {
2149 unsafe {
2150 let ctxt = new_valid_ctxt();
2151 assert!(!ctxt.is_null());
2152
2153 let red = c_str("red");
2154 let green = c_str("green");
2155 let blue = c_str("blue");
2156
2157 let e3 = allocator::xmlMallocZero(size_of::<_xmlEnumeration>()) as *mut _xmlEnumeration;
2158 (*e3).name = string::xml_strdup(blue);
2159 (*e3).next = ptr::null_mut();
2160
2161 let e2 = allocator::xmlMallocZero(size_of::<_xmlEnumeration>()) as *mut _xmlEnumeration;
2162 (*e2).name = string::xml_strdup(green);
2163 (*e2).next = e3;
2164
2165 let e1 = allocator::xmlMallocZero(size_of::<_xmlEnumeration>()) as *mut _xmlEnumeration;
2166 (*e1).name = string::xml_strdup(red);
2167 (*e1).next = e2;
2168
2169 let value = c_str("green");
2170 assert_eq!(validate_enumeration(ctxt, value, e1), 1);
2171 assert_eq!((*ctxt).valid, 1);
2172
2173 allocator::xmlFree(value as *mut c_void);
2174 allocator::xmlFree(red as *mut c_void);
2175 allocator::xmlFree(green as *mut c_void);
2176 allocator::xmlFree(blue as *mut c_void);
2177 free_valid_ctxt(ctxt);
2178 }
2179 }
2180
2181 #[test]
2182 fn test_validate_enumeration_invalid() {
2183 unsafe {
2184 let ctxt = new_valid_ctxt();
2185 assert!(!ctxt.is_null());
2186
2187 let e1 = allocator::xmlMallocZero(size_of::<_xmlEnumeration>()) as *mut _xmlEnumeration;
2188 (*e1).name = string::xml_strdup(b"red\0" as *const u8 as *const xmlChar);
2189 (*e1).next = ptr::null_mut();
2190
2191 let value = c_str("yellow");
2192 assert_eq!(validate_enumeration(ctxt, value, e1), 0);
2193
2194 allocator::xmlFree(value as *mut c_void);
2195 free_valid_ctxt(ctxt);
2196 }
2197 }
2198
2199 #[test]
2202 fn test_validate_notation_use_valid() {
2203 unsafe {
2204 let (doc, dtd) = make_test_doc();
2205
2206 let notation_name = c_str("GIF");
2207 dtd::add_notation_decl(dtd, notation_name, ptr::null(), ptr::null());
2208
2209 let ctxt = new_valid_ctxt();
2210 assert!(!ctxt.is_null());
2211
2212 assert_eq!(validate_notation_use(ctxt, doc, notation_name), 1);
2213
2214 free_valid_ctxt(ctxt);
2215 tree::free_doc(doc);
2216 }
2217 }
2218
2219 #[test]
2220 fn test_validate_notation_use_invalid() {
2221 unsafe {
2222 let (doc, _dtd) = make_test_doc();
2223
2224 let ctxt = new_valid_ctxt();
2225 assert!(!ctxt.is_null());
2226
2227 let notation_name = c_str("UNDECLARED");
2228 assert_eq!(validate_notation_use(ctxt, doc, notation_name), 0);
2229
2230 free_valid_ctxt(ctxt);
2231 allocator::xmlFree(notation_name as *mut c_void);
2232 tree::free_doc(doc);
2233 }
2234 }
2235
2236 #[test]
2239 fn test_new_free_valid_ctxt() {
2240 unsafe {
2241 let ctxt = new_valid_ctxt();
2242 assert!(!ctxt.is_null());
2243 assert_eq!((*ctxt).valid, 1);
2244 assert!((*ctxt).node.is_null());
2245 free_valid_ctxt(ctxt);
2246 }
2247 }
2248
2249 #[test]
2250 fn test_free_valid_ctxt_null() {
2251 unsafe {
2252 free_valid_ctxt(ptr::null_mut());
2253 }
2254 }
2255
2256 #[test]
2259 fn test_set_valid_errors_null() {
2260 unsafe {
2261 set_valid_errors(ptr::null_mut(), None, None, ptr::null_mut());
2262 }
2263 }
2264
2265 #[test]
2268 fn test_validate_element_no_dtd() {
2269 unsafe {
2270 let doc = tree::new_doc(b"1.0\0" as *const u8 as *const xmlChar);
2271 assert!(!doc.is_null());
2272
2273 let root_name = c_str("root");
2274 let root = create_root_elem(doc, root_name);
2275
2276 let ctxt = new_valid_ctxt();
2277 assert!(!ctxt.is_null());
2278
2279 assert_eq!(validate_element(ctxt, doc, root), 1);
2281
2282 free_valid_ctxt(ctxt);
2283 tree::free_doc(doc);
2284 }
2285 }
2286
2287 #[test]
2288 fn test_validate_element_empty_valid() {
2289 unsafe {
2290 let (doc, dtd) = make_test_doc();
2291
2292 let root_name = c_str("root");
2293 add_elem_decl(
2294 dtd,
2295 root_name,
2296 XML_ELEMENT_TYPE_EMPTY as c_int,
2297 ptr::null_mut(),
2298 );
2299
2300 let root = create_root_elem(doc, root_name);
2301
2302 let ctxt = new_valid_ctxt();
2303 assert!(!ctxt.is_null());
2304
2305 assert_eq!(validate_element(ctxt, doc, root), 1);
2306
2307 free_valid_ctxt(ctxt);
2308 tree::free_doc(doc);
2309 }
2310 }
2311
2312 #[test]
2313 fn test_validate_element_undeclared() {
2314 unsafe {
2315 let (doc, _dtd) = make_test_doc();
2316
2317 let root_name = c_str("root");
2318 let root = create_root_elem(doc, root_name);
2319
2320 let ctxt = new_valid_ctxt();
2321 assert!(!ctxt.is_null());
2322
2323 assert_eq!(validate_element(ctxt, doc, root), 0);
2325
2326 free_valid_ctxt(ctxt);
2327 tree::free_doc(doc);
2328 }
2329 }
2330
2331 #[test]
2332 fn test_validate_element_with_content() {
2333 unsafe {
2334 let (doc, dtd) = make_test_doc();
2335
2336 let root_name = c_str("root");
2338 let child_name = c_str("child");
2339
2340 let child_content =
2342 dtd::create_content_model(child_name, XML_ELEMENT_CONTENT_ELEMENT as c_int);
2343 assert!(!child_content.is_null());
2344 (*child_content).ocur = XML_ELEMENT_CONTENT_PLUS as c_int;
2345
2346 add_elem_decl(
2347 dtd,
2348 root_name,
2349 XML_ELEMENT_TYPE_ELEMENT as c_int,
2350 child_content,
2351 );
2352 add_elem_decl(
2353 dtd,
2354 child_name,
2355 XML_ELEMENT_TYPE_EMPTY as c_int,
2356 ptr::null_mut(),
2357 );
2358
2359 let root = create_root_elem(doc, root_name);
2360 let _child = create_child_elem(root, child_name);
2361
2362 let ctxt = new_valid_ctxt();
2363 assert!(!ctxt.is_null());
2364
2365 assert_eq!(validate_element(ctxt, doc, root), 1);
2366
2367 free_valid_ctxt(ctxt);
2368 tree::free_doc(doc);
2369 }
2370 }
2371
2372 #[test]
2373 fn test_validate_element_invalid_content() {
2374 unsafe {
2375 let (doc, dtd) = make_test_doc();
2376
2377 let root_name = c_str("root");
2378 let child_name = c_str("child");
2379 let wrong_name = c_str("wrong");
2380
2381 let child_content =
2383 dtd::create_content_model(child_name, XML_ELEMENT_CONTENT_ELEMENT as c_int);
2384 assert!(!child_content.is_null());
2385 (*child_content).ocur = XML_ELEMENT_CONTENT_PLUS as c_int;
2386
2387 add_elem_decl(
2388 dtd,
2389 root_name,
2390 XML_ELEMENT_TYPE_ELEMENT as c_int,
2391 child_content,
2392 );
2393 add_elem_decl(
2394 dtd,
2395 child_name,
2396 XML_ELEMENT_TYPE_EMPTY as c_int,
2397 ptr::null_mut(),
2398 );
2399 add_elem_decl(
2400 dtd,
2401 wrong_name,
2402 XML_ELEMENT_TYPE_EMPTY as c_int,
2403 ptr::null_mut(),
2404 );
2405
2406 let root = create_root_elem(doc, root_name);
2407 create_child_elem(root, wrong_name);
2409
2410 let ctxt = new_valid_ctxt();
2411 assert!(!ctxt.is_null());
2412
2413 assert_eq!(validate_element(ctxt, doc, root), 0);
2414
2415 free_valid_ctxt(ctxt);
2416 tree::free_doc(doc);
2417 }
2418 }
2419
2420 #[test]
2423 fn test_validate_root_match() {
2424 unsafe {
2425 let (doc, dtd) = make_test_doc();
2426
2427 let root_name = c_str("root");
2428 add_elem_decl(
2429 dtd,
2430 root_name,
2431 XML_ELEMENT_TYPE_EMPTY as c_int,
2432 ptr::null_mut(),
2433 );
2434 create_root_elem(doc, root_name);
2435
2436 let ctxt = new_valid_ctxt();
2437 assert!(!ctxt.is_null());
2438
2439 assert_eq!(validate_root(ctxt, doc), 1);
2440
2441 free_valid_ctxt(ctxt);
2442 tree::free_doc(doc);
2443 }
2444 }
2445
2446 #[test]
2447 fn test_validate_root_no_dtd() {
2448 unsafe {
2449 let doc = tree::new_doc(b"1.0\0" as *const u8 as *const xmlChar);
2450 assert!(!doc.is_null());
2451
2452 let root_name = c_str("root");
2453 create_root_elem(doc, root_name);
2454
2455 let ctxt = new_valid_ctxt();
2456 assert!(!ctxt.is_null());
2457
2458 assert_eq!(validate_root(ctxt, doc), 1);
2460
2461 free_valid_ctxt(ctxt);
2462 tree::free_doc(doc);
2463 }
2464 }
2465
2466 #[test]
2469 fn test_validate_document_valid() {
2470 unsafe {
2471 let (doc, dtd) = make_test_doc();
2472
2473 let root_name = c_str("root");
2474 add_elem_decl(
2475 dtd,
2476 root_name,
2477 XML_ELEMENT_TYPE_EMPTY as c_int,
2478 ptr::null_mut(),
2479 );
2480 create_root_elem(doc, root_name);
2481
2482 let ctxt = new_valid_ctxt();
2483 assert!(!ctxt.is_null());
2484
2485 assert_eq!(validate_document(ctxt, doc), 1);
2486
2487 free_valid_ctxt(ctxt);
2488 tree::free_doc(doc);
2489 }
2490 }
2491
2492 #[test]
2493 fn test_validate_document_no_root() {
2494 unsafe {
2495 let doc = tree::new_doc(b"1.0\0" as *const u8 as *const xmlChar);
2496 assert!(!doc.is_null());
2497
2498 let ctxt = new_valid_ctxt();
2499 assert!(!ctxt.is_null());
2500
2501 assert_eq!(validate_document(ctxt, doc), 0);
2502
2503 free_valid_ctxt(ctxt);
2504 tree::free_doc(doc);
2505 }
2506 }
2507
2508 #[test]
2511 fn test_validate_content_valid() {
2512 unsafe {
2513 let (doc, dtd) = make_test_doc();
2514
2515 let root_name = c_str("root");
2516 let child_name = c_str("child");
2517
2518 let child_content =
2519 dtd::create_content_model(child_name, XML_ELEMENT_CONTENT_ELEMENT as c_int);
2520 assert!(!child_content.is_null());
2521
2522 add_elem_decl(
2523 dtd,
2524 root_name,
2525 XML_ELEMENT_TYPE_ELEMENT as c_int,
2526 child_content,
2527 );
2528 add_elem_decl(
2529 dtd,
2530 child_name,
2531 XML_ELEMENT_TYPE_EMPTY as c_int,
2532 ptr::null_mut(),
2533 );
2534
2535 let root = create_root_elem(doc, root_name);
2536 create_child_elem(root, child_name);
2537
2538 let ctxt = new_valid_ctxt();
2539 assert!(!ctxt.is_null());
2540
2541 assert_eq!(validate_content(ctxt, root, doc), 1);
2542
2543 free_valid_ctxt(ctxt);
2544 tree::free_doc(doc);
2545 }
2546 }
2547
2548 #[test]
2551 fn test_is_mixed_element() {
2552 unsafe {
2553 let (doc, dtd) = make_test_doc();
2554 let name = c_str("mixedElem");
2555 add_elem_decl(dtd, name, XML_ELEMENT_TYPE_MIXED as c_int, ptr::null_mut());
2556
2557 assert_eq!(is_mixed_element(doc, name), 1);
2558
2559 let other = c_str("other");
2560 assert_eq!(is_mixed_element(doc, other), 0);
2561
2562 allocator::xmlFree(other as *mut c_void);
2563 tree::free_doc(doc);
2564 }
2565 }
2566
2567 #[test]
2568 fn test_is_empty_element() {
2569 unsafe {
2570 let (doc, dtd) = make_test_doc();
2571 let name = c_str("emptyElem");
2572 add_elem_decl(dtd, name, XML_ELEMENT_TYPE_EMPTY as c_int, ptr::null_mut());
2573
2574 assert_eq!(is_empty_element(doc, name), 1);
2575
2576 let other = c_str("other");
2577 assert_eq!(is_empty_element(doc, other), 0);
2578
2579 allocator::xmlFree(other as *mut c_void);
2580 tree::free_doc(doc);
2581 }
2582 }
2583
2584 #[test]
2585 fn test_is_mixed_element_no_dtd() {
2586 unsafe {
2587 let doc = tree::new_doc(b"1.0\0" as *const u8 as *const xmlChar);
2588 assert!(!doc.is_null());
2589
2590 let name = c_str("foo");
2591 assert_eq!(is_mixed_element(doc, name), 0);
2592
2593 allocator::xmlFree(name as *mut c_void);
2594 tree::free_doc(doc);
2595 }
2596 }
2597
2598 #[test]
2599 fn test_is_empty_element_no_dtd() {
2600 unsafe {
2601 let doc = tree::new_doc(b"1.0\0" as *const u8 as *const xmlChar);
2602 assert!(!doc.is_null());
2603
2604 let name = c_str("foo");
2605 assert_eq!(is_empty_element(doc, name), 0);
2606
2607 allocator::xmlFree(name as *mut c_void);
2608 tree::free_doc(doc);
2609 }
2610 }
2611
2612 #[test]
2615 fn test_validate_dtd_null() {
2616 unsafe {
2617 let ctxt = new_valid_ctxt();
2618 assert!(!ctxt.is_null());
2619 assert_eq!(validate_dtd(ctxt, ptr::null_mut(), ptr::null_mut()), 0);
2620 free_valid_ctxt(ctxt);
2621 }
2622 }
2623
2624 #[test]
2627 fn test_validate_element_null() {
2628 unsafe {
2629 let (doc, _dtd) = make_test_doc();
2630 let ctxt = new_valid_ctxt();
2631 assert!(!ctxt.is_null());
2632
2633 assert_eq!(validate_element(ctxt, doc, ptr::null_mut()), 0);
2634
2635 free_valid_ctxt(ctxt);
2636 tree::free_doc(doc);
2637 }
2638 }
2639
2640 #[test]
2641 fn test_validate_document_null() {
2642 unsafe {
2643 let ctxt = new_valid_ctxt();
2644 assert!(!ctxt.is_null());
2645
2646 assert_eq!(validate_document(ctxt, ptr::null_mut()), 0);
2647 assert_eq!(validate_document(ptr::null_mut(), ptr::null_mut()), 0);
2648
2649 free_valid_ctxt(ctxt);
2650 }
2651 }
2652
2653 #[test]
2654 fn test_validate_document_final_null() {
2655 unsafe {
2656 let ctxt = new_valid_ctxt();
2657 assert!(!ctxt.is_null());
2658
2659 assert_eq!(validate_document_final(ctxt, ptr::null_mut()), 0);
2660 assert_eq!(validate_document_final(ptr::null_mut(), ptr::null_mut()), 0);
2661
2662 free_valid_ctxt(ctxt);
2663 }
2664 }
2665
2666 #[test]
2667 fn test_validate_attribute_decl_null() {
2668 unsafe {
2669 let ctxt = new_valid_ctxt();
2670 assert!(!ctxt.is_null());
2671
2672 assert_eq!(
2673 validate_attribute_decl(ctxt, ptr::null_mut(), ptr::null_mut(), ptr::null_mut()),
2674 0
2675 );
2676
2677 free_valid_ctxt(ctxt);
2678 }
2679 }
2680
2681 #[test]
2682 fn test_validate_content_null() {
2683 unsafe {
2684 let ctxt = new_valid_ctxt();
2685 assert!(!ctxt.is_null());
2686
2687 assert_eq!(validate_content(ctxt, ptr::null_mut(), ptr::null_mut()), 0);
2688
2689 free_valid_ctxt(ctxt);
2690 }
2691 }
2692
2693 #[test]
2694 fn test_validate_root_null() {
2695 unsafe {
2696 assert_eq!(validate_root(ptr::null_mut(), ptr::null_mut()), 0);
2697 }
2698 }
2699
2700 #[test]
2701 fn test_validate_enumeration_null() {
2702 unsafe {
2703 let ctxt = new_valid_ctxt();
2704 assert!(!ctxt.is_null());
2705
2706 assert_eq!(validate_enumeration(ctxt, ptr::null(), ptr::null_mut()), 0);
2707
2708 free_valid_ctxt(ctxt);
2709 }
2710 }
2711
2712 #[test]
2713 fn test_validate_notation_use_null() {
2714 unsafe {
2715 let ctxt = new_valid_ctxt();
2716 assert!(!ctxt.is_null());
2717
2718 assert_eq!(validate_notation_use(ctxt, ptr::null_mut(), ptr::null()), 0);
2719
2720 free_valid_ctxt(ctxt);
2721 }
2722 }
2723
2724 #[test]
2725 fn test_validate_name_start_characters() {
2726 unsafe {
2727 let name = c_str("\u{C0}lph\u{E0}");
2729 assert_eq!(validate_name(name), 1);
2730 allocator::xmlFree(name as *mut c_void);
2731 }
2732 }
2733
2734 #[test]
2735 fn test_validate_names_single() {
2736 unsafe {
2737 let s = c_str("singleName");
2738 assert_eq!(validate_names(s), 1);
2739 allocator::xmlFree(s as *mut c_void);
2740 }
2741 }
2742
2743 #[test]
2744 fn test_validate_nmtokens_single() {
2745 unsafe {
2746 let s = c_str("123abc");
2747 assert_eq!(validate_nmtokens(s), 1);
2748 allocator::xmlFree(s as *mut c_void);
2749 }
2750 }
2751
2752 #[test]
2753 fn test_validate_nmtokens_invalid() {
2754 unsafe {
2755 let s = c_str("foo\tbar"); assert_eq!(validate_nmtokens(s), 1); allocator::xmlFree(s as *mut c_void);
2758
2759 let s2 = c_str("foo@bar");
2761 assert_eq!(validate_nmtokens(s2), 0);
2762 allocator::xmlFree(s2 as *mut c_void);
2763 }
2764 }
2765
2766 #[test]
2767 fn test_validate_attribute_value_empty_non_cdata() {
2768 unsafe {
2769 let empty = b"\0" as *const u8 as *const xmlChar;
2770 assert_eq!(
2771 validate_attribute_value(XML_ATTRIBUTE_ID as c_int, empty),
2772 0
2773 );
2774 assert_eq!(
2775 validate_attribute_value(XML_ATTRIBUTE_IDREF as c_int, empty),
2776 0
2777 );
2778 assert_eq!(
2779 validate_attribute_value(XML_ATTRIBUTE_NMTOKEN as c_int, empty),
2780 0
2781 );
2782 }
2783 }
2784
2785 #[test]
2786 fn test_validate_attribute_value_unknown_type() {
2787 unsafe {
2788 let s = c_str("test");
2789 assert_eq!(validate_attribute_value(999, s), 0);
2790 allocator::xmlFree(s as *mut c_void);
2791 }
2792 }
2793
2794 #[test]
2795 fn test_validate_element_any_content() {
2796 unsafe {
2797 let (doc, dtd) = make_test_doc();
2798
2799 let root_name = c_str("root");
2800 add_elem_decl(
2801 dtd,
2802 root_name,
2803 XML_ELEMENT_TYPE_ANY as c_int,
2804 ptr::null_mut(),
2805 );
2806
2807 let child_name = c_str("child");
2808 add_elem_decl(
2809 dtd,
2810 child_name,
2811 XML_ELEMENT_TYPE_EMPTY as c_int,
2812 ptr::null_mut(),
2813 );
2814
2815 let root = create_root_elem(doc, root_name);
2816 create_child_elem(root, child_name);
2817
2818 let ctxt = new_valid_ctxt();
2819 assert!(!ctxt.is_null());
2820
2821 assert_eq!(validate_element(ctxt, doc, root), 1);
2823
2824 free_valid_ctxt(ctxt);
2825 tree::free_doc(doc);
2826 }
2827 }
2828
2829 #[test]
2830 fn test_validate_element_empty_with_child() {
2831 unsafe {
2832 let (doc, dtd) = make_test_doc();
2833
2834 let root_name = c_str("root");
2835 add_elem_decl(
2836 dtd,
2837 root_name,
2838 XML_ELEMENT_TYPE_EMPTY as c_int,
2839 ptr::null_mut(),
2840 );
2841
2842 let child_name = c_str("child");
2843 add_elem_decl(
2844 dtd,
2845 child_name,
2846 XML_ELEMENT_TYPE_EMPTY as c_int,
2847 ptr::null_mut(),
2848 );
2849
2850 let root = create_root_elem(doc, root_name);
2851 create_child_elem(root, child_name);
2852
2853 let ctxt = new_valid_ctxt();
2854 assert!(!ctxt.is_null());
2855
2856 assert_eq!(validate_element(ctxt, doc, root), 0);
2858
2859 free_valid_ctxt(ctxt);
2860 tree::free_doc(doc);
2861 }
2862 }
2863
2864 #[test]
2865 fn test_validate_dtd_final_null() {
2866 unsafe {
2867 assert_eq!(validate_dtd_final(ptr::null_mut(), ptr::null_mut()), 0);
2868 }
2869 }
2870}