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 let mut root = d.children;
1311 while !root.is_null() {
1312 if (*root).type_ == XML_ELEMENT_NODE as c_int {
1313 break;
1314 }
1315 root = (*root).next;
1316 }
1317
1318 if root.is_null() {
1319 vctxt_error(
1320 ctxt,
1321 b"No root element found in document\0" as *const u8 as *const c_char,
1322 );
1323 return 0;
1324 }
1325
1326 if validate_element(ctxt, doc, root) == 0 {
1328 return 0;
1329 }
1330
1331 c.valid
1332 }
1333}
1334
1335pub unsafe fn validate_document_final(ctxt: *mut _xmlValidCtxt, doc: *mut _xmlDoc) -> c_int {
1357 if ctxt.is_null() || doc.is_null() {
1358 return 0;
1359 }
1360
1361 unsafe {
1362 let c = &mut *ctxt;
1363 c.doc = doc;
1364
1365 let d = &*doc;
1366
1367 if d.refs.is_null() {
1369 return c.valid;
1370 }
1371
1372 struct IdRefCheckContext {
1374 ctxt: *mut _xmlValidCtxt,
1375 doc: *mut _xmlDoc,
1376 }
1377
1378 extern "C" fn check_idref(
1379 _payload: *mut c_void,
1380 data: *mut c_void,
1381 _name: *const xmlChar,
1382 name2: *const xmlChar,
1383 _name3: *const xmlChar,
1384 ) {
1385 if data.is_null() || name2.is_null() {
1386 return;
1387 }
1388
1389 let cx = unsafe { &*(data as *mut IdRefCheckContext) };
1391 unsafe {
1392 let doc_ref = &*cx.doc;
1393
1394 if doc_ref.ids.is_null()
1396 || hash::hash_lookup(doc_ref.ids as *mut hash::HashTable, name2).is_null()
1397 {
1398 let ref_str = string::xmlstr_to_string(name2);
1399 let err_msg = format!("IDREF '{}' does not reference a declared ID\0", ref_str);
1400 vctxt_error(cx.ctxt, err_msg.as_ptr() as *const c_char);
1401 }
1402 }
1403 }
1404
1405 let ctx = IdRefCheckContext { ctxt, doc };
1406 hash::hash_scan_full(
1407 d.refs as *mut hash::HashTable,
1408 Some(check_idref),
1409 &ctx as *const IdRefCheckContext as *mut c_void,
1410 );
1411
1412 c.valid
1413 }
1414}
1415
1416pub unsafe fn validate_root(ctxt: *mut _xmlValidCtxt, doc: *mut _xmlDoc) -> c_int {
1434 if ctxt.is_null() || doc.is_null() {
1435 return 0;
1436 }
1437
1438 unsafe {
1439 let c = &mut *ctxt;
1440 c.doc = doc;
1441 c.valid = 1;
1442
1443 let d = &*doc;
1444
1445 let mut root = d.children;
1447 while !root.is_null() {
1448 if (*root).type_ == XML_ELEMENT_NODE as c_int {
1449 break;
1450 }
1451 root = (*root).next;
1452 }
1453
1454 if root.is_null() {
1455 vctxt_error(
1456 ctxt,
1457 b"No root element found\0" as *const u8 as *const c_char,
1458 );
1459 return 0;
1460 }
1461
1462 let dtd = get_valid_dtd(doc);
1464 if dtd.is_null() {
1465 return 1;
1467 }
1468
1469 let dtd_ref = &*dtd;
1472 if !dtd_ref.name.is_null() {
1473 if string::xml_strcmp((*root).name, dtd_ref.name) != 0 {
1474 let root_str = string::xmlstr_to_string((*root).name);
1475 let dtd_str = string::xmlstr_to_string(dtd_ref.name);
1476 let err_msg = format!(
1477 "Root element '{}' does not match DTD root '{}'\0",
1478 root_str, dtd_str
1479 );
1480 vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
1481 return 0;
1482 }
1483 }
1484
1485 c.valid
1486 }
1487}
1488
1489pub unsafe fn validate_content(
1509 ctxt: *mut _xmlValidCtxt,
1510 node: *mut _xmlNode,
1511 doc: *mut _xmlDoc,
1512) -> c_int {
1513 if node.is_null() || doc.is_null() || ctxt.is_null() {
1514 return 0;
1515 }
1516
1517 unsafe {
1518 let n = &*node;
1519 if n.type_ != XML_ELEMENT_NODE as c_int {
1520 return 1;
1521 }
1522
1523 let dtd = get_valid_dtd(doc);
1524 if dtd.is_null() {
1525 return 1;
1526 }
1527
1528 let dtd_ref = &*dtd;
1529 if dtd_ref.elements.is_null() {
1530 return 1;
1531 }
1532
1533 let elem_decl = hash::hash_lookup(dtd_ref.elements as *mut hash::HashTable, n.name);
1534 if elem_decl.is_null() {
1535 return 1;
1536 }
1537
1538 let elem_decl_ref = &*(elem_decl as *mut _xmlElement);
1539 if elem_decl_ref.content.is_null() {
1540 return 1;
1541 }
1542
1543 let elem_type = elem_decl_ref.type_ as u32;
1544 if elem_type == XML_ELEMENT_TYPE_EMPTY as u32 {
1545 let mut child = n.children;
1547 while !child.is_null() {
1548 if (*child).type_ == XML_ELEMENT_NODE as c_int {
1549 let name_str = string::xmlstr_to_string(n.name);
1550 let err_msg =
1551 format!("Element '{}' is EMPTY but has child elements\0", name_str);
1552 vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
1553 return 0;
1554 }
1555 child = (*child).next;
1556 }
1557 return 1;
1558 }
1559
1560 if elem_type == XML_ELEMENT_TYPE_ANY as u32 {
1561 return 1;
1562 }
1563
1564 let mut child_names: Vec<*const xmlChar> = Vec::new();
1566 let mut child = n.children;
1567 while !child.is_null() {
1568 if (*child).type_ == XML_ELEMENT_NODE as c_int {
1569 child_names.push((*child).name);
1570 }
1571 child = (*child).next;
1572 }
1573
1574 let result = dtd::valid_content_model(elem_decl_ref.content, &child_names);
1575 if result != dtd::ContentModelResult::Valid {
1576 let name_str = string::xmlstr_to_string(n.name);
1577 let err_msg = format!(
1578 "Content model validation failed for element '{}'\0",
1579 name_str
1580 );
1581 vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
1582 0
1583 } else {
1584 1
1585 }
1586 }
1587}
1588
1589pub unsafe fn is_mixed_element(doc: *mut _xmlDoc, name: *const xmlChar) -> c_int {
1607 if doc.is_null() || name.is_null() {
1608 return 0;
1609 }
1610
1611 let dtd = unsafe { get_valid_dtd(doc) };
1612 if dtd.is_null() {
1613 return 0;
1614 }
1615
1616 unsafe {
1617 let dtd_ref = &*dtd;
1618 if dtd_ref.elements.is_null() {
1619 return 0;
1620 }
1621
1622 let elem_decl = hash::hash_lookup(dtd_ref.elements as *mut hash::HashTable, name);
1623 if elem_decl.is_null() {
1624 return 0;
1625 }
1626
1627 let elem_decl_ref = &*(elem_decl as *mut _xmlElement);
1628 ((elem_decl_ref.type_ as u32) == XML_ELEMENT_TYPE_MIXED as u32) as c_int
1629 }
1630}
1631
1632pub unsafe fn is_empty_element(doc: *mut _xmlDoc, name: *const xmlChar) -> c_int {
1646 if doc.is_null() || name.is_null() {
1647 return 0;
1648 }
1649
1650 let dtd = unsafe { get_valid_dtd(doc) };
1651 if dtd.is_null() {
1652 return 0;
1653 }
1654
1655 unsafe {
1656 let dtd_ref = &*dtd;
1657 if dtd_ref.elements.is_null() {
1658 return 0;
1659 }
1660
1661 let elem_decl = hash::hash_lookup(dtd_ref.elements as *mut hash::HashTable, name);
1662 if elem_decl.is_null() {
1663 return 0;
1664 }
1665
1666 let elem_decl_ref = &*(elem_decl as *mut _xmlElement);
1667 ((elem_decl_ref.type_ as u32) == XML_ELEMENT_TYPE_EMPTY as u32) as c_int
1668 }
1669}
1670
1671pub unsafe fn validate_dtd(
1695 ctxt: *mut _xmlValidCtxt,
1696 doc: *mut _xmlDoc,
1697 dtd: *mut _xmlDtd,
1698) -> c_int {
1699 if ctxt.is_null() || dtd.is_null() {
1700 return 0;
1701 }
1702
1703 let c = unsafe { &mut *ctxt };
1704 c.doc = doc;
1705 c.valid = 1;
1706
1707 struct ValidateDtdCtx {
1708 ctxt: *mut _xmlValidCtxt,
1709 doc: *mut _xmlDoc,
1710 }
1711
1712 extern "C" fn validate_attr_decl_cb(
1713 payload: *mut c_void,
1714 data: *mut c_void,
1715 _name: *const xmlChar,
1716 _name2: *const xmlChar,
1717 _name3: *const xmlChar,
1718 ) {
1719 if payload.is_null() || data.is_null() {
1720 return;
1721 }
1722
1723 let ctx = unsafe { &*(data as *mut ValidateDtdCtx) };
1725 unsafe {
1726 let attr = payload as *mut _xmlAttribute;
1727 validate_attribute_decl(ctx.ctxt, ctx.doc, ptr::null_mut(), attr);
1728 }
1729 }
1730
1731 extern "C" fn validate_elem_content_cb(
1732 payload: *mut c_void,
1733 data: *mut c_void,
1734 _name: *const xmlChar,
1735 _name2: *const xmlChar,
1736 _name3: *const xmlChar,
1737 ) {
1738 if payload.is_null() || data.is_null() {
1739 return;
1740 }
1741
1742 let ctx = unsafe { &*(data as *mut ValidateDtdCtx) };
1744 unsafe {
1745 let elem = &*(payload as *mut _xmlElement);
1746 if !elem.content.is_null() {
1747 validate_content_model_refs(ctx.ctxt, ctx.doc, elem.content);
1748 }
1749 }
1750 }
1751
1752 unsafe {
1753 let dtd_ref = &*dtd;
1754
1755 if !dtd_ref.attributes.is_null() {
1757 let ctx = ValidateDtdCtx { ctxt, doc };
1758 hash::hash_scan_full(
1759 dtd_ref.attributes as *mut hash::HashTable,
1760 Some(validate_attr_decl_cb),
1761 &ctx as *const ValidateDtdCtx as *mut c_void,
1762 );
1763 }
1764
1765 if !dtd_ref.elements.is_null() {
1767 let ctx = ValidateDtdCtx { ctxt, doc };
1768 hash::hash_scan_full(
1769 dtd_ref.elements as *mut hash::HashTable,
1770 Some(validate_elem_content_cb),
1771 &ctx as *const ValidateDtdCtx as *mut c_void,
1772 );
1773 }
1774
1775 c.valid
1776 }
1777}
1778
1779unsafe fn validate_content_model_refs(
1786 ctxt: *mut _xmlValidCtxt,
1787 doc: *mut _xmlDoc,
1788 content: *mut _xmlElementContent,
1789) {
1790 if content.is_null() {
1791 return;
1792 }
1793
1794 unsafe {
1795 let c = &*content;
1796
1797 match c.type_ as u32 {
1798 t if t == XML_ELEMENT_CONTENT_ELEMENT as u32 => {
1799 if !c.name.is_null() {
1801 let dtd = get_valid_dtd(doc);
1802 if !dtd.is_null() {
1803 let dtd_ref = &*dtd;
1804 if !dtd_ref.elements.is_null() {
1805 let decl =
1806 hash::hash_lookup(dtd_ref.elements as *mut hash::HashTable, c.name);
1807 if decl.is_null() {
1808 let name_str = string::xmlstr_to_string(c.name);
1809 let err_msg = format!(
1810 "Element '{}' referenced in content model is not declared\0",
1811 name_str
1812 );
1813 vctxt_error(ctxt, err_msg.as_ptr() as *const c_char);
1814 }
1815 }
1816 }
1817 }
1818 }
1819 t if t == XML_ELEMENT_CONTENT_SEQ as u32 || t == XML_ELEMENT_CONTENT_OR as u32 => {
1820 validate_content_model_refs(ctxt, doc, c.c1);
1821 validate_content_model_refs(ctxt, doc, c.c2);
1822 }
1823 _ => {}
1824 }
1825 }
1826}
1827
1828pub unsafe fn validate_dtd_final(ctxt: *mut _xmlValidCtxt, doc: *mut _xmlDoc) -> c_int {
1849 unsafe { validate_document_final(ctxt, doc) }
1850}
1851
1852#[cfg(test)]
1857mod tests {
1858 use super::*;
1859 use crate::abi::allocator;
1860 use crate::abi::types::xmlElementTypeVal::*;
1861 use crate::xml::dtd;
1862 use crate::xml::tree;
1863
1864 unsafe fn c_str(s: &str) -> *const xmlChar {
1868 let bytes = s.as_bytes();
1869 let ptr = allocator::xmlMalloc(bytes.len() + 1) as *mut xmlChar;
1870 assert!(!ptr.is_null());
1871 std::ptr::copy_nonoverlapping(bytes.as_ptr(), ptr, bytes.len());
1872 *ptr.add(bytes.len()) = 0;
1873 ptr
1874 }
1875
1876 unsafe fn make_test_doc() -> (*mut _xmlDoc, *mut _xmlDtd) {
1878 let doc = tree::new_doc(b"1.0\0" as *const u8 as *const xmlChar);
1879 assert!(!doc.is_null());
1880
1881 let name = c_str("root");
1882 let ext_id = c_str("--//Test//DTD//EN");
1883 let sys_id = c_str("test.dtd");
1884 let dtd = dtd::create_int_subset(doc, name, ext_id, sys_id);
1885 assert!(!dtd.is_null());
1886
1887 (doc, dtd)
1888 }
1889
1890 #[allow(unused)]
1892 unsafe fn add_elem_decl(
1893 dtd: *mut _xmlDtd,
1894 name: *const xmlChar,
1895 elem_type: c_int,
1896 content: *mut _xmlElementContent,
1897 ) -> *mut _xmlElement {
1898 let result = dtd::add_element_decl(dtd, name, elem_type, content);
1899 result
1900 }
1901
1902 unsafe fn create_root_elem(doc: *mut _xmlDoc, name: *const xmlChar) -> *mut _xmlNode {
1904 let node = tree::new_node(ptr::null_mut(), name);
1905 assert!(!node.is_null());
1906 tree::add_child(doc as *mut _xmlNode, node);
1907 node
1908 }
1909
1910 #[allow(unused)]
1912 unsafe fn create_child_elem(parent: *mut _xmlNode, name: *const xmlChar) -> *mut _xmlNode {
1913 let node = tree::new_node(ptr::null_mut(), name);
1914 assert!(!node.is_null());
1915 tree::add_child(parent, node);
1916 node
1917 }
1918
1919 #[test]
1922 fn test_validate_name_null() {
1923 unsafe {
1924 assert_eq!(validate_name(ptr::null()), 0);
1925 }
1926 }
1927
1928 #[test]
1929 fn test_validate_name_empty() {
1930 unsafe {
1931 let s = b"\0" as *const u8 as *const xmlChar;
1932 assert_eq!(validate_name(s), 0);
1933 }
1934 }
1935
1936 #[test]
1937 fn test_validate_name_valid() {
1938 unsafe {
1939 let tests = ["foo", "_bar", ":baz", "hello-world", "ns:elem", "a123"];
1940 for t in &tests {
1941 let s = c_str(t);
1942 assert_eq!(validate_name(s), 1, "Expected '{}' to be a valid Name", t);
1943 allocator::xmlFree(s as *mut c_void);
1944 }
1945 }
1946 }
1947
1948 #[test]
1949 fn test_validate_name_invalid() {
1950 unsafe {
1951 let tests = ["123abc", "-foo", ".bar", "foo bar", "a b"];
1952 for t in &tests {
1953 let s = c_str(t);
1954 assert_eq!(validate_name(s), 0, "Expected '{}' to be invalid", t);
1955 allocator::xmlFree(s as *mut c_void);
1956 }
1957 }
1958 }
1959
1960 #[test]
1961 fn test_validate_names_valid() {
1962 unsafe {
1963 let s = c_str("foo bar baz");
1964 assert_eq!(validate_names(s), 1);
1965 allocator::xmlFree(s as *mut c_void);
1966 }
1967 }
1968
1969 #[test]
1970 fn test_validate_names_invalid() {
1971 unsafe {
1972 let s = c_str("foo 123bar baz");
1973 assert_eq!(validate_names(s), 0);
1974 allocator::xmlFree(s as *mut c_void);
1975 }
1976 }
1977
1978 #[test]
1981 fn test_validate_nmtoken_null() {
1982 unsafe {
1983 assert_eq!(validate_nmtoken(ptr::null()), 0);
1984 }
1985 }
1986
1987 #[test]
1988 fn test_validate_nmtoken_valid() {
1989 unsafe {
1990 let tests = ["foo", "123abc", "-foo", ".bar", "_test", ":ns"];
1991 for t in &tests {
1992 let s = c_str(t);
1993 assert_eq!(
1994 validate_nmtoken(s),
1995 1,
1996 "Expected '{}' to be a valid NMTOKEN",
1997 t
1998 );
1999 allocator::xmlFree(s as *mut c_void);
2000 }
2001 }
2002 }
2003
2004 #[test]
2005 fn test_validate_nmtoken_invalid() {
2006 unsafe {
2007 let s = c_str("foo bar");
2008 assert_eq!(validate_nmtoken(s), 0);
2009 allocator::xmlFree(s as *mut c_void);
2010 }
2011 }
2012
2013 #[test]
2014 fn test_validate_nmtokens_valid() {
2015 unsafe {
2016 let s = c_str("foo 123bar -baz");
2017 assert_eq!(validate_nmtokens(s), 1);
2018 allocator::xmlFree(s as *mut c_void);
2019 }
2020 }
2021
2022 #[test]
2025 fn test_validate_attribute_value_cdata() {
2026 unsafe {
2027 let s = c_str("anything goes here!@#$%^&*()");
2028 assert_eq!(validate_attribute_value(XML_ATTRIBUTE_CDATA as c_int, s), 1);
2029 allocator::xmlFree(s as *mut c_void);
2030
2031 let empty = b"\0" as *const u8 as *const xmlChar;
2033 assert_eq!(
2034 validate_attribute_value(XML_ATTRIBUTE_CDATA as c_int, empty),
2035 1
2036 );
2037 }
2038 }
2039
2040 #[test]
2041 fn test_validate_attribute_value_id() {
2042 unsafe {
2043 let valid = c_str("myId");
2044 assert_eq!(
2045 validate_attribute_value(XML_ATTRIBUTE_ID as c_int, valid),
2046 1
2047 );
2048 allocator::xmlFree(valid as *mut c_void);
2049
2050 let invalid = c_str("123id");
2051 assert_eq!(
2052 validate_attribute_value(XML_ATTRIBUTE_ID as c_int, invalid),
2053 0
2054 );
2055 allocator::xmlFree(invalid as *mut c_void);
2056 }
2057 }
2058
2059 #[test]
2060 fn test_validate_attribute_value_idref() {
2061 unsafe {
2062 let valid = c_str("someId");
2063 assert_eq!(
2064 validate_attribute_value(XML_ATTRIBUTE_IDREF as c_int, valid),
2065 1
2066 );
2067 allocator::xmlFree(valid as *mut c_void);
2068 }
2069 }
2070
2071 #[test]
2072 fn test_validate_attribute_value_idrefs() {
2073 unsafe {
2074 let valid = c_str("id1 id2 id3");
2075 assert_eq!(
2076 validate_attribute_value(XML_ATTRIBUTE_IDREFS as c_int, valid),
2077 1
2078 );
2079 allocator::xmlFree(valid as *mut c_void);
2080
2081 let invalid = c_str("id1 123id");
2082 assert_eq!(
2083 validate_attribute_value(XML_ATTRIBUTE_IDREFS as c_int, invalid),
2084 0
2085 );
2086 allocator::xmlFree(invalid as *mut c_void);
2087 }
2088 }
2089
2090 #[test]
2091 fn test_validate_attribute_value_entity() {
2092 unsafe {
2093 let valid = c_str("myEntity");
2094 assert_eq!(
2095 validate_attribute_value(XML_ATTRIBUTE_ENTITY as c_int, valid),
2096 1
2097 );
2098 allocator::xmlFree(valid as *mut c_void);
2099 }
2100 }
2101
2102 #[test]
2103 fn test_validate_attribute_value_nmtoken() {
2104 unsafe {
2105 let valid = c_str("123abc");
2106 assert_eq!(
2107 validate_attribute_value(XML_ATTRIBUTE_NMTOKEN as c_int, valid),
2108 1
2109 );
2110 allocator::xmlFree(valid as *mut c_void);
2111
2112 let invalid = c_str("foo bar");
2113 assert_eq!(
2114 validate_attribute_value(XML_ATTRIBUTE_NMTOKEN as c_int, invalid),
2115 0
2116 );
2117 allocator::xmlFree(invalid as *mut c_void);
2118 }
2119 }
2120
2121 #[test]
2122 fn test_validate_attribute_value_null() {
2123 unsafe {
2124 assert_eq!(
2125 validate_attribute_value(XML_ATTRIBUTE_CDATA as c_int, ptr::null()),
2126 0
2127 );
2128 }
2129 }
2130
2131 #[test]
2134 fn test_validate_enumeration_valid() {
2135 unsafe {
2136 let ctxt = new_valid_ctxt();
2137 assert!(!ctxt.is_null());
2138
2139 let red = c_str("red");
2140 let green = c_str("green");
2141 let blue = c_str("blue");
2142
2143 let e3 = allocator::xmlMallocZero(size_of::<_xmlEnumeration>()) as *mut _xmlEnumeration;
2144 (*e3).name = string::xml_strdup(blue);
2145 (*e3).next = ptr::null_mut();
2146
2147 let e2 = allocator::xmlMallocZero(size_of::<_xmlEnumeration>()) as *mut _xmlEnumeration;
2148 (*e2).name = string::xml_strdup(green);
2149 (*e2).next = e3;
2150
2151 let e1 = allocator::xmlMallocZero(size_of::<_xmlEnumeration>()) as *mut _xmlEnumeration;
2152 (*e1).name = string::xml_strdup(red);
2153 (*e1).next = e2;
2154
2155 let value = c_str("green");
2156 assert_eq!(validate_enumeration(ctxt, value, e1), 1);
2157 assert_eq!((*ctxt).valid, 1);
2158
2159 allocator::xmlFree(value as *mut c_void);
2160 allocator::xmlFree(red as *mut c_void);
2161 allocator::xmlFree(green as *mut c_void);
2162 allocator::xmlFree(blue as *mut c_void);
2163 free_valid_ctxt(ctxt);
2164 }
2165 }
2166
2167 #[test]
2168 fn test_validate_enumeration_invalid() {
2169 unsafe {
2170 let ctxt = new_valid_ctxt();
2171 assert!(!ctxt.is_null());
2172
2173 let e1 = allocator::xmlMallocZero(size_of::<_xmlEnumeration>()) as *mut _xmlEnumeration;
2174 (*e1).name = string::xml_strdup(b"red\0" as *const u8 as *const xmlChar);
2175 (*e1).next = ptr::null_mut();
2176
2177 let value = c_str("yellow");
2178 assert_eq!(validate_enumeration(ctxt, value, e1), 0);
2179
2180 allocator::xmlFree(value as *mut c_void);
2181 free_valid_ctxt(ctxt);
2182 }
2183 }
2184
2185 #[test]
2188 fn test_validate_notation_use_valid() {
2189 unsafe {
2190 let (doc, dtd) = make_test_doc();
2191
2192 let notation_name = c_str("GIF");
2193 dtd::add_notation_decl(dtd, notation_name, ptr::null(), ptr::null());
2194
2195 let ctxt = new_valid_ctxt();
2196 assert!(!ctxt.is_null());
2197
2198 assert_eq!(validate_notation_use(ctxt, doc, notation_name), 1);
2199
2200 free_valid_ctxt(ctxt);
2201 tree::free_doc(doc);
2202 }
2203 }
2204
2205 #[test]
2206 fn test_validate_notation_use_invalid() {
2207 unsafe {
2208 let (doc, _dtd) = make_test_doc();
2209
2210 let ctxt = new_valid_ctxt();
2211 assert!(!ctxt.is_null());
2212
2213 let notation_name = c_str("UNDECLARED");
2214 assert_eq!(validate_notation_use(ctxt, doc, notation_name), 0);
2215
2216 free_valid_ctxt(ctxt);
2217 allocator::xmlFree(notation_name as *mut c_void);
2218 tree::free_doc(doc);
2219 }
2220 }
2221
2222 #[test]
2225 fn test_new_free_valid_ctxt() {
2226 unsafe {
2227 let ctxt = new_valid_ctxt();
2228 assert!(!ctxt.is_null());
2229 assert_eq!((*ctxt).valid, 1);
2230 assert!((*ctxt).node.is_null());
2231 free_valid_ctxt(ctxt);
2232 }
2233 }
2234
2235 #[test]
2236 fn test_free_valid_ctxt_null() {
2237 unsafe {
2238 free_valid_ctxt(ptr::null_mut());
2239 }
2240 }
2241
2242 #[test]
2245 fn test_set_valid_errors_null() {
2246 unsafe {
2247 set_valid_errors(ptr::null_mut(), None, None, ptr::null_mut());
2248 }
2249 }
2250
2251 #[test]
2254 fn test_validate_element_no_dtd() {
2255 unsafe {
2256 let doc = tree::new_doc(b"1.0\0" as *const u8 as *const xmlChar);
2257 assert!(!doc.is_null());
2258
2259 let root_name = c_str("root");
2260 let root = create_root_elem(doc, root_name);
2261
2262 let ctxt = new_valid_ctxt();
2263 assert!(!ctxt.is_null());
2264
2265 assert_eq!(validate_element(ctxt, doc, root), 1);
2267
2268 free_valid_ctxt(ctxt);
2269 tree::free_doc(doc);
2270 }
2271 }
2272
2273 #[test]
2274 fn test_validate_element_empty_valid() {
2275 unsafe {
2276 let (doc, dtd) = make_test_doc();
2277
2278 let root_name = c_str("root");
2279 add_elem_decl(
2280 dtd,
2281 root_name,
2282 XML_ELEMENT_TYPE_EMPTY as c_int,
2283 ptr::null_mut(),
2284 );
2285
2286 let root = create_root_elem(doc, root_name);
2287
2288 let ctxt = new_valid_ctxt();
2289 assert!(!ctxt.is_null());
2290
2291 assert_eq!(validate_element(ctxt, doc, root), 1);
2292
2293 free_valid_ctxt(ctxt);
2294 tree::free_doc(doc);
2295 }
2296 }
2297
2298 #[test]
2299 fn test_validate_element_undeclared() {
2300 unsafe {
2301 let (doc, _dtd) = make_test_doc();
2302
2303 let root_name = c_str("root");
2304 let root = create_root_elem(doc, root_name);
2305
2306 let ctxt = new_valid_ctxt();
2307 assert!(!ctxt.is_null());
2308
2309 assert_eq!(validate_element(ctxt, doc, root), 0);
2311
2312 free_valid_ctxt(ctxt);
2313 tree::free_doc(doc);
2314 }
2315 }
2316
2317 #[test]
2318 fn test_validate_element_with_content() {
2319 unsafe {
2320 let (doc, dtd) = make_test_doc();
2321
2322 let root_name = c_str("root");
2324 let child_name = c_str("child");
2325
2326 let child_content =
2328 dtd::create_content_model(child_name, XML_ELEMENT_CONTENT_ELEMENT as c_int);
2329 assert!(!child_content.is_null());
2330 (*child_content).ocur = XML_ELEMENT_CONTENT_PLUS as c_int;
2331
2332 add_elem_decl(
2333 dtd,
2334 root_name,
2335 XML_ELEMENT_TYPE_ELEMENT as c_int,
2336 child_content,
2337 );
2338 add_elem_decl(
2339 dtd,
2340 child_name,
2341 XML_ELEMENT_TYPE_EMPTY as c_int,
2342 ptr::null_mut(),
2343 );
2344
2345 let root = create_root_elem(doc, root_name);
2346 let _child = create_child_elem(root, child_name);
2347
2348 let ctxt = new_valid_ctxt();
2349 assert!(!ctxt.is_null());
2350
2351 assert_eq!(validate_element(ctxt, doc, root), 1);
2352
2353 free_valid_ctxt(ctxt);
2354 tree::free_doc(doc);
2355 }
2356 }
2357
2358 #[test]
2359 fn test_validate_element_invalid_content() {
2360 unsafe {
2361 let (doc, dtd) = make_test_doc();
2362
2363 let root_name = c_str("root");
2364 let child_name = c_str("child");
2365 let wrong_name = c_str("wrong");
2366
2367 let child_content =
2369 dtd::create_content_model(child_name, XML_ELEMENT_CONTENT_ELEMENT as c_int);
2370 assert!(!child_content.is_null());
2371 (*child_content).ocur = XML_ELEMENT_CONTENT_PLUS as c_int;
2372
2373 add_elem_decl(
2374 dtd,
2375 root_name,
2376 XML_ELEMENT_TYPE_ELEMENT as c_int,
2377 child_content,
2378 );
2379 add_elem_decl(
2380 dtd,
2381 child_name,
2382 XML_ELEMENT_TYPE_EMPTY as c_int,
2383 ptr::null_mut(),
2384 );
2385 add_elem_decl(
2386 dtd,
2387 wrong_name,
2388 XML_ELEMENT_TYPE_EMPTY as c_int,
2389 ptr::null_mut(),
2390 );
2391
2392 let root = create_root_elem(doc, root_name);
2393 create_child_elem(root, wrong_name);
2395
2396 let ctxt = new_valid_ctxt();
2397 assert!(!ctxt.is_null());
2398
2399 assert_eq!(validate_element(ctxt, doc, root), 0);
2400
2401 free_valid_ctxt(ctxt);
2402 tree::free_doc(doc);
2403 }
2404 }
2405
2406 #[test]
2409 fn test_validate_root_match() {
2410 unsafe {
2411 let (doc, dtd) = make_test_doc();
2412
2413 let root_name = c_str("root");
2414 add_elem_decl(
2415 dtd,
2416 root_name,
2417 XML_ELEMENT_TYPE_EMPTY as c_int,
2418 ptr::null_mut(),
2419 );
2420 create_root_elem(doc, root_name);
2421
2422 let ctxt = new_valid_ctxt();
2423 assert!(!ctxt.is_null());
2424
2425 assert_eq!(validate_root(ctxt, doc), 1);
2426
2427 free_valid_ctxt(ctxt);
2428 tree::free_doc(doc);
2429 }
2430 }
2431
2432 #[test]
2433 fn test_validate_root_no_dtd() {
2434 unsafe {
2435 let doc = tree::new_doc(b"1.0\0" as *const u8 as *const xmlChar);
2436 assert!(!doc.is_null());
2437
2438 let root_name = c_str("root");
2439 create_root_elem(doc, root_name);
2440
2441 let ctxt = new_valid_ctxt();
2442 assert!(!ctxt.is_null());
2443
2444 assert_eq!(validate_root(ctxt, doc), 1);
2446
2447 free_valid_ctxt(ctxt);
2448 tree::free_doc(doc);
2449 }
2450 }
2451
2452 #[test]
2455 fn test_validate_document_valid() {
2456 unsafe {
2457 let (doc, dtd) = make_test_doc();
2458
2459 let root_name = c_str("root");
2460 add_elem_decl(
2461 dtd,
2462 root_name,
2463 XML_ELEMENT_TYPE_EMPTY as c_int,
2464 ptr::null_mut(),
2465 );
2466 create_root_elem(doc, root_name);
2467
2468 let ctxt = new_valid_ctxt();
2469 assert!(!ctxt.is_null());
2470
2471 assert_eq!(validate_document(ctxt, doc), 1);
2472
2473 free_valid_ctxt(ctxt);
2474 tree::free_doc(doc);
2475 }
2476 }
2477
2478 #[test]
2479 fn test_validate_document_no_root() {
2480 unsafe {
2481 let doc = tree::new_doc(b"1.0\0" as *const u8 as *const xmlChar);
2482 assert!(!doc.is_null());
2483
2484 let ctxt = new_valid_ctxt();
2485 assert!(!ctxt.is_null());
2486
2487 assert_eq!(validate_document(ctxt, doc), 0);
2488
2489 free_valid_ctxt(ctxt);
2490 tree::free_doc(doc);
2491 }
2492 }
2493
2494 #[test]
2497 fn test_validate_content_valid() {
2498 unsafe {
2499 let (doc, dtd) = make_test_doc();
2500
2501 let root_name = c_str("root");
2502 let child_name = c_str("child");
2503
2504 let child_content =
2505 dtd::create_content_model(child_name, XML_ELEMENT_CONTENT_ELEMENT as c_int);
2506 assert!(!child_content.is_null());
2507
2508 add_elem_decl(
2509 dtd,
2510 root_name,
2511 XML_ELEMENT_TYPE_ELEMENT as c_int,
2512 child_content,
2513 );
2514 add_elem_decl(
2515 dtd,
2516 child_name,
2517 XML_ELEMENT_TYPE_EMPTY as c_int,
2518 ptr::null_mut(),
2519 );
2520
2521 let root = create_root_elem(doc, root_name);
2522 create_child_elem(root, child_name);
2523
2524 let ctxt = new_valid_ctxt();
2525 assert!(!ctxt.is_null());
2526
2527 assert_eq!(validate_content(ctxt, root, doc), 1);
2528
2529 free_valid_ctxt(ctxt);
2530 tree::free_doc(doc);
2531 }
2532 }
2533
2534 #[test]
2537 fn test_is_mixed_element() {
2538 unsafe {
2539 let (doc, dtd) = make_test_doc();
2540 let name = c_str("mixedElem");
2541 add_elem_decl(dtd, name, XML_ELEMENT_TYPE_MIXED as c_int, ptr::null_mut());
2542
2543 assert_eq!(is_mixed_element(doc, name), 1);
2544
2545 let other = c_str("other");
2546 assert_eq!(is_mixed_element(doc, other), 0);
2547
2548 allocator::xmlFree(other as *mut c_void);
2549 tree::free_doc(doc);
2550 }
2551 }
2552
2553 #[test]
2554 fn test_is_empty_element() {
2555 unsafe {
2556 let (doc, dtd) = make_test_doc();
2557 let name = c_str("emptyElem");
2558 add_elem_decl(dtd, name, XML_ELEMENT_TYPE_EMPTY as c_int, ptr::null_mut());
2559
2560 assert_eq!(is_empty_element(doc, name), 1);
2561
2562 let other = c_str("other");
2563 assert_eq!(is_empty_element(doc, other), 0);
2564
2565 allocator::xmlFree(other as *mut c_void);
2566 tree::free_doc(doc);
2567 }
2568 }
2569
2570 #[test]
2571 fn test_is_mixed_element_no_dtd() {
2572 unsafe {
2573 let doc = tree::new_doc(b"1.0\0" as *const u8 as *const xmlChar);
2574 assert!(!doc.is_null());
2575
2576 let name = c_str("foo");
2577 assert_eq!(is_mixed_element(doc, name), 0);
2578
2579 allocator::xmlFree(name as *mut c_void);
2580 tree::free_doc(doc);
2581 }
2582 }
2583
2584 #[test]
2585 fn test_is_empty_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_empty_element(doc, name), 0);
2592
2593 allocator::xmlFree(name as *mut c_void);
2594 tree::free_doc(doc);
2595 }
2596 }
2597
2598 #[test]
2601 fn test_validate_dtd_null() {
2602 unsafe {
2603 let ctxt = new_valid_ctxt();
2604 assert!(!ctxt.is_null());
2605 assert_eq!(validate_dtd(ctxt, ptr::null_mut(), ptr::null_mut()), 0);
2606 free_valid_ctxt(ctxt);
2607 }
2608 }
2609
2610 #[test]
2613 fn test_validate_element_null() {
2614 unsafe {
2615 let (doc, _dtd) = make_test_doc();
2616 let ctxt = new_valid_ctxt();
2617 assert!(!ctxt.is_null());
2618
2619 assert_eq!(validate_element(ctxt, doc, ptr::null_mut()), 0);
2620
2621 free_valid_ctxt(ctxt);
2622 tree::free_doc(doc);
2623 }
2624 }
2625
2626 #[test]
2627 fn test_validate_document_null() {
2628 unsafe {
2629 let ctxt = new_valid_ctxt();
2630 assert!(!ctxt.is_null());
2631
2632 assert_eq!(validate_document(ctxt, ptr::null_mut()), 0);
2633 assert_eq!(validate_document(ptr::null_mut(), ptr::null_mut()), 0);
2634
2635 free_valid_ctxt(ctxt);
2636 }
2637 }
2638
2639 #[test]
2640 fn test_validate_document_final_null() {
2641 unsafe {
2642 let ctxt = new_valid_ctxt();
2643 assert!(!ctxt.is_null());
2644
2645 assert_eq!(validate_document_final(ctxt, ptr::null_mut()), 0);
2646 assert_eq!(validate_document_final(ptr::null_mut(), ptr::null_mut()), 0);
2647
2648 free_valid_ctxt(ctxt);
2649 }
2650 }
2651
2652 #[test]
2653 fn test_validate_attribute_decl_null() {
2654 unsafe {
2655 let ctxt = new_valid_ctxt();
2656 assert!(!ctxt.is_null());
2657
2658 assert_eq!(
2659 validate_attribute_decl(ctxt, ptr::null_mut(), ptr::null_mut(), ptr::null_mut()),
2660 0
2661 );
2662
2663 free_valid_ctxt(ctxt);
2664 }
2665 }
2666
2667 #[test]
2668 fn test_validate_content_null() {
2669 unsafe {
2670 let ctxt = new_valid_ctxt();
2671 assert!(!ctxt.is_null());
2672
2673 assert_eq!(validate_content(ctxt, ptr::null_mut(), ptr::null_mut()), 0);
2674
2675 free_valid_ctxt(ctxt);
2676 }
2677 }
2678
2679 #[test]
2680 fn test_validate_root_null() {
2681 unsafe {
2682 assert_eq!(validate_root(ptr::null_mut(), ptr::null_mut()), 0);
2683 }
2684 }
2685
2686 #[test]
2687 fn test_validate_enumeration_null() {
2688 unsafe {
2689 let ctxt = new_valid_ctxt();
2690 assert!(!ctxt.is_null());
2691
2692 assert_eq!(validate_enumeration(ctxt, ptr::null(), ptr::null_mut()), 0);
2693
2694 free_valid_ctxt(ctxt);
2695 }
2696 }
2697
2698 #[test]
2699 fn test_validate_notation_use_null() {
2700 unsafe {
2701 let ctxt = new_valid_ctxt();
2702 assert!(!ctxt.is_null());
2703
2704 assert_eq!(validate_notation_use(ctxt, ptr::null_mut(), ptr::null()), 0);
2705
2706 free_valid_ctxt(ctxt);
2707 }
2708 }
2709
2710 #[test]
2711 fn test_validate_name_start_characters() {
2712 unsafe {
2713 let name = c_str("\u{C0}lph\u{E0}");
2715 assert_eq!(validate_name(name), 1);
2716 allocator::xmlFree(name as *mut c_void);
2717 }
2718 }
2719
2720 #[test]
2721 fn test_validate_names_single() {
2722 unsafe {
2723 let s = c_str("singleName");
2724 assert_eq!(validate_names(s), 1);
2725 allocator::xmlFree(s as *mut c_void);
2726 }
2727 }
2728
2729 #[test]
2730 fn test_validate_nmtokens_single() {
2731 unsafe {
2732 let s = c_str("123abc");
2733 assert_eq!(validate_nmtokens(s), 1);
2734 allocator::xmlFree(s as *mut c_void);
2735 }
2736 }
2737
2738 #[test]
2739 fn test_validate_nmtokens_invalid() {
2740 unsafe {
2741 let s = c_str("foo\tbar"); assert_eq!(validate_nmtokens(s), 1); allocator::xmlFree(s as *mut c_void);
2744
2745 let s2 = c_str("foo@bar");
2747 assert_eq!(validate_nmtokens(s2), 0);
2748 allocator::xmlFree(s2 as *mut c_void);
2749 }
2750 }
2751
2752 #[test]
2753 fn test_validate_attribute_value_empty_non_cdata() {
2754 unsafe {
2755 let empty = b"\0" as *const u8 as *const xmlChar;
2756 assert_eq!(
2757 validate_attribute_value(XML_ATTRIBUTE_ID as c_int, empty),
2758 0
2759 );
2760 assert_eq!(
2761 validate_attribute_value(XML_ATTRIBUTE_IDREF as c_int, empty),
2762 0
2763 );
2764 assert_eq!(
2765 validate_attribute_value(XML_ATTRIBUTE_NMTOKEN as c_int, empty),
2766 0
2767 );
2768 }
2769 }
2770
2771 #[test]
2772 fn test_validate_attribute_value_unknown_type() {
2773 unsafe {
2774 let s = c_str("test");
2775 assert_eq!(validate_attribute_value(999, s), 0);
2776 allocator::xmlFree(s as *mut c_void);
2777 }
2778 }
2779
2780 #[test]
2781 fn test_validate_element_any_content() {
2782 unsafe {
2783 let (doc, dtd) = make_test_doc();
2784
2785 let root_name = c_str("root");
2786 add_elem_decl(
2787 dtd,
2788 root_name,
2789 XML_ELEMENT_TYPE_ANY as c_int,
2790 ptr::null_mut(),
2791 );
2792
2793 let child_name = c_str("child");
2794 add_elem_decl(
2795 dtd,
2796 child_name,
2797 XML_ELEMENT_TYPE_EMPTY as c_int,
2798 ptr::null_mut(),
2799 );
2800
2801 let root = create_root_elem(doc, root_name);
2802 create_child_elem(root, child_name);
2803
2804 let ctxt = new_valid_ctxt();
2805 assert!(!ctxt.is_null());
2806
2807 assert_eq!(validate_element(ctxt, doc, root), 1);
2809
2810 free_valid_ctxt(ctxt);
2811 tree::free_doc(doc);
2812 }
2813 }
2814
2815 #[test]
2816 fn test_validate_element_empty_with_child() {
2817 unsafe {
2818 let (doc, dtd) = make_test_doc();
2819
2820 let root_name = c_str("root");
2821 add_elem_decl(
2822 dtd,
2823 root_name,
2824 XML_ELEMENT_TYPE_EMPTY as c_int,
2825 ptr::null_mut(),
2826 );
2827
2828 let child_name = c_str("child");
2829 add_elem_decl(
2830 dtd,
2831 child_name,
2832 XML_ELEMENT_TYPE_EMPTY as c_int,
2833 ptr::null_mut(),
2834 );
2835
2836 let root = create_root_elem(doc, root_name);
2837 create_child_elem(root, child_name);
2838
2839 let ctxt = new_valid_ctxt();
2840 assert!(!ctxt.is_null());
2841
2842 assert_eq!(validate_element(ctxt, doc, root), 0);
2844
2845 free_valid_ctxt(ctxt);
2846 tree::free_doc(doc);
2847 }
2848 }
2849
2850 #[test]
2851 fn test_validate_dtd_final_null() {
2852 unsafe {
2853 assert_eq!(validate_dtd_final(ptr::null_mut(), ptr::null_mut()), 0);
2854 }
2855 }
2856}