1use rusty_xml_sax::{SaxAttr, SaxHandler};
4use rusty_xml_tree::{NodeId, NodeKind, XmlDoc};
5
6use crate::chvalid::{xml_is_char, xml_is_name_char, xml_is_name_start_char};
7use crate::error::*;
8
9pub const XML_PARSE_RECOVER: i32 = 1 << 0;
11pub const XML_PARSE_NOENT: i32 = 1 << 1;
12pub const XML_PARSE_DTDLOAD: i32 = 1 << 2;
13pub const XML_PARSE_DTDATTR: i32 = 1 << 3;
14pub const XML_PARSE_DTDVALID: i32 = 1 << 4;
15pub const XML_PARSE_NOERROR: i32 = 1 << 5;
16pub const XML_PARSE_NOWARNING: i32 = 1 << 6;
17pub const XML_PARSE_PEDANTIC: i32 = 1 << 7;
18pub const XML_PARSE_NOBLANKS: i32 = 1 << 8;
19pub const XML_PARSE_SAX1: i32 = 1 << 9;
20pub const XML_PARSE_XINCLUDE: i32 = 1 << 10;
21pub const XML_PARSE_NONET: i32 = 1 << 11;
22pub const XML_PARSE_NODICT: i32 = 1 << 12;
23pub const XML_PARSE_NSCLEAN: i32 = 1 << 13;
24pub const XML_PARSE_NOCDATA: i32 = 1 << 14;
25pub const XML_PARSE_NOXINCNODE: i32 = 1 << 15;
26pub const XML_PARSE_COMPACT: i32 = 1 << 16;
27pub const XML_PARSE_OLD10: i32 = 1 << 17;
28pub const XML_PARSE_NOBASEFIX: i32 = 1 << 18;
29pub const XML_PARSE_HUGE: i32 = 1 << 19;
30pub const XML_PARSE_OLDSAX: i32 = 1 << 20;
31pub const XML_PARSE_IGNORE_ENC: i32 = 1 << 21;
32pub const XML_PARSE_BIG_LINES: i32 = 1 << 22;
33pub const XML_PARSE_NO_XXE: i32 = 1 << 23;
34pub const XML_PARSE_UNZIP: i32 = 1 << 24;
35
36pub const XML_PARSE_NO_TREE: i32 = 1 << 30;
50pub const XML_PARSE_NO_SYS_CATALOG: i32 = 1 << 25;
51pub const XML_PARSE_CATALOG_PI: i32 = 1 << 26;
52pub const XML_PARSE_SKIP_IDS: i32 = 1 << 27;
53
54const XML_NS: &str = "http://www.w3.org/XML/1998/namespace";
55const XMLNS_NS: &str = "http://www.w3.org/2000/xmlns/";
56
57const MAX_DEPTH: u32 = 5_000;
68
69const MAX_DEPTH_HUGE: u32 = 1_000_000;
72
73const MAX_NAME: usize = 50_000;
87const MAX_TEXT: usize = 10_000_000;
88
89pub fn default_parse_options() -> i32 {
91 XML_PARSE_NONET | XML_PARSE_NO_XXE
92}
93
94#[doc(alias = "xmlInitParser")]
96pub fn xml_init_parser() {}
97
98#[doc(alias = "xmlCleanupParser")]
100pub fn xml_cleanup_parser() {}
101
102#[derive(Debug, Default)]
104pub struct XmlParserCtxt {
105 pub options: i32,
106 pub last_error: Option<XmlError>,
107 pub doc: Option<XmlDoc>,
108}
109
110#[doc(alias = "xmlNewParserCtxt")]
112pub fn xml_new_parser_ctxt() -> XmlParserCtxt {
113 XmlParserCtxt {
114 options: default_parse_options(),
115 last_error: None,
116 doc: None,
117 }
118}
119
120#[doc(alias = "xmlCtxtUseOptions")]
122pub fn xml_ctxt_use_options(ctxt: &mut XmlParserCtxt, options: i32) -> i32 {
123 ctxt.options = options | XML_PARSE_NONET | XML_PARSE_NO_XXE;
124 0
125}
126
127#[doc(alias = "xmlCtxtSetOptions")]
129pub fn xml_ctxt_set_options(ctxt: &mut XmlParserCtxt, options: i32) -> i32 {
130 xml_ctxt_use_options(ctxt, options)
131}
132
133#[doc(alias = "xmlCtxtGetOptions")]
135pub fn xml_ctxt_get_options(ctxt: &XmlParserCtxt) -> i32 {
136 ctxt.options
137}
138
139#[doc(alias = "xmlCtxtGetLastError")]
141pub fn xml_ctxt_get_last_error(ctxt: &XmlParserCtxt) -> Option<&XmlError> {
142 ctxt.last_error.as_ref()
143}
144
145#[doc(alias = "xmlCtxtGetDocument")]
147pub fn xml_ctxt_get_document(ctxt: &XmlParserCtxt) -> Option<&XmlDoc> {
148 ctxt.doc.as_ref()
149}
150
151#[doc(alias = "xmlReadMemory")]
153pub fn xml_read_memory(
154 buffer: &[u8],
155 url: Option<&str>,
156 encoding: Option<&str>,
157 options: i32,
158) -> Result<XmlDoc, XmlError> {
159 let mut sink = rusty_xml_sax::NullSax;
160 parse_doc(buffer, url, encoding, options, &mut sink)
161}
162
163#[doc(alias = "xmlReadDoc")]
165pub fn xml_read_doc(
166 cur: &str,
167 url: Option<&str>,
168 encoding: Option<&str>,
169 options: i32,
170) -> Result<XmlDoc, XmlError> {
171 xml_read_memory(cur.as_bytes(), url, encoding, options)
172}
173
174#[doc(alias = "xmlReadFile")]
176pub fn xml_read_file(
177 filename: &str,
178 encoding: Option<&str>,
179 options: i32,
180) -> Result<XmlDoc, XmlError> {
181 let bytes = std::fs::read(filename).map_err(|e| {
182 XmlError::new(XML_ERR_DOCUMENT_START, e.to_string(), 0, 0)
183 })?;
184 xml_read_memory(&bytes, Some(filename), encoding, options)
185}
186
187#[doc(alias = "xmlCtxtReadMemory")]
189pub fn xml_ctxt_read_memory(
190 ctxt: &mut XmlParserCtxt,
191 buffer: &[u8],
192 url: Option<&str>,
193 encoding: Option<&str>,
194 options: i32,
195) -> Result<XmlDoc, XmlError> {
196 let opts = if options != 0 { options } else { ctxt.options };
197 match xml_read_memory(buffer, url, encoding, opts) {
198 Ok(doc) => {
199 ctxt.doc = Some(doc.clone());
200 ctxt.last_error = None;
201 Ok(doc)
202 }
203 Err(e) => {
204 ctxt.last_error = Some(e.clone());
205 Err(e)
206 }
207 }
208}
209
210pub fn xml_sax_parse_memory(
212 buffer: &[u8],
213 options: i32,
214 sax: &mut dyn SaxHandler,
215) -> Result<XmlDoc, XmlError> {
216 parse_doc(buffer, None, None, options, sax)
217}
218
219pub struct XmlPushParserCtxt {
221 buf: Vec<u8>,
222 options: i32,
223 url: Option<String>,
224 encoding: Option<String>,
225 last_error: Option<XmlError>,
226 state: Option<PushState>,
229 consumed: usize,
230 no_stream: bool,
234}
235
236impl XmlPushParserCtxt {
237 pub fn last_error(&self) -> Option<&XmlError> {
239 self.last_error.as_ref()
240 }
241
242 pub fn buffered(&self) -> usize {
247 self.buf.len()
248 }
249
250 pub fn consumed(&self) -> usize {
252 self.consumed
253 }
254}
255
256#[doc(alias = "xmlCreatePushParserCtxt")]
258pub fn xml_create_push_parser_ctxt(
259 chunk: &[u8],
260 url: Option<&str>,
261 encoding: Option<&str>,
262 options: i32,
263) -> XmlPushParserCtxt {
264 XmlPushParserCtxt {
265 buf: chunk.to_vec(),
266 options: options | XML_PARSE_NONET | XML_PARSE_NO_XXE,
267 url: url.map(str::to_string),
268 encoding: encoding.map(str::to_string),
269 last_error: None,
270 state: None,
271 consumed: 0,
272 no_stream: false,
273 }
274}
275
276#[doc(alias = "xmlParseChunk")]
278pub fn xml_parse_chunk(
279 ctxt: &mut XmlPushParserCtxt,
280 chunk: &[u8],
281 terminate: i32,
282) -> Result<Option<XmlDoc>, XmlError> {
283 ctxt.buf.extend_from_slice(chunk);
284 let terminate = terminate != 0;
285 let opts = ctxt.options;
286
287 if ctxt.state.is_none() && !ctxt.no_stream {
291 match crate::encoding::xml_convert_to_utf8_cow(&ctxt.buf, ctxt.encoding.as_deref()) {
294 Ok((std::borrow::Cow::Borrowed(b), _)) if b.len() == ctxt.buf.len() => {}
295 _ => {
296 ctxt.no_stream = true;
297 }
298 }
299 }
300 if ctxt.state.is_none() && !ctxt.no_stream {
301 let mut sink = rusty_xml_sax::NullSax;
302 let started = {
303 let mut p = fresh_parser(&ctxt.buf, opts, &mut sink);
304 match p.parse_prolog().and_then(|()| p.open_element(NodeId::DOCUMENT)) {
305 Ok(Some(root)) => {
306 let at = p.pos;
307 Some((p.suspend(vec![root], false), at))
308 }
309 _ => None,
313 }
314 };
315 match started {
316 Some((st, at)) => {
317 ctxt.state = Some(st);
318 ctxt.buf.drain(..at);
319 ctxt.consumed += at;
320 }
321 None => {
322 if !terminate {
323 return Ok(None);
324 }
325 return finish_whole(ctxt);
326 }
327 }
328 }
329
330 if ctxt.no_stream {
331 if !terminate {
332 return Ok(None);
333 }
334 return finish_whole(ctxt);
335 }
336
337 let mut sink = rusty_xml_sax::NullSax;
341 let mut st = ctxt.state.take().expect("state is present past the prolog");
342 let mut open = std::mem::take(&mut st.open);
343 let was_closed = st.root_closed;
344 let mut p = Parser::resume(&ctxt.buf, opts, &mut sink, st);
345
346 let safe = if was_closed {
349 0
350 } else {
351 match p.parse_content_inner(NodeId::DOCUMENT, &mut open, !terminate, true) {
352 Ok(at) => at,
353 Err(e) => {
354 ctxt.last_error = Some(e.clone());
355 return Err(e);
356 }
357 }
358 };
359 let root_closed = was_closed || open.is_empty();
360
361 if !terminate {
362 let at = safe.min(ctxt.buf.len());
363 ctxt.state = Some(p.suspend(open, root_closed));
364 ctxt.buf.drain(..at);
365 ctxt.buf.shrink_to_fit();
366 ctxt.consumed += at;
367 return Ok(None);
368 }
369
370 if let Some(o) = open.last() {
371 let (_, local) = Parser::split_qname(&o.qname).unwrap_or((None, &o.qname));
372 let e = p.err(
373 XML_ERR_TAG_NOT_FINISHED,
374 format!("Premature end of data in tag {local}"),
375 );
376 ctxt.last_error = Some(e.clone());
377 return Err(e);
378 }
379
380 if let Err(e) = p.parse_epilog() {
381 ctxt.last_error = Some(e.clone());
382 return Err(e);
383 }
384 let total = ctxt.consumed + ctxt.buf.len();
385 let mut doc = p.suspend(open, true).doc;
386 apply_dtd_defaults(&mut doc, total, ctxt.options)?;
387 ctxt.buf = Vec::new();
388 ctxt.buf.shrink_to_fit();
389 ctxt.last_error = None;
390 Ok(Some(doc))
391}
392
393fn fresh_parser<'a>(
395 input: &'a [u8],
396 options: i32,
397 sax: &'a mut dyn SaxHandler,
398) -> Parser<'a> {
399 Parser {
400 input,
401 pos: 0,
402 line: 1,
403 col: 1,
404 options,
405 old10: (options & XML_PARSE_OLD10) != 0,
406 depth: 0,
407 ns_stack: Vec::new(),
408 sax,
409 doc: XmlDoc::with_node_capacity(
410 Some("1.0"),
411 if (options & XML_PARSE_NO_TREE) != 0 {
412 input.len() / 32
413 } else {
414 input.len() / 10
415 },
416 ),
417 stack: Vec::new(),
418 char_buf: String::new(),
419 no_tree: (options & XML_PARSE_NO_TREE) != 0,
420 recover: (options & XML_PARSE_RECOVER) != 0,
421 entity_budget: input.len().saturating_mul(10).max(1 << 16),
424 scratch_raw: Vec::new(),
425 scratch_sax: Vec::new(),
426 started: false,
427 }
428}
429
430fn finish_whole(ctxt: &mut XmlPushParserCtxt) -> Result<Option<XmlDoc>, XmlError> {
432 match xml_read_memory(
433 &ctxt.buf,
434 ctxt.url.as_deref(),
435 ctxt.encoding.as_deref(),
436 ctxt.options,
437 ) {
438 Ok(doc) => {
439 ctxt.buf = Vec::new();
440 ctxt.buf.shrink_to_fit();
441 ctxt.last_error = None;
442 Ok(Some(doc))
443 }
444 Err(e) => {
445 ctxt.last_error = Some(e.clone());
446 Err(e)
447 }
448 }
449}
450
451#[doc(alias = "xmlReadIO")]
453pub fn xml_read_io<F>(
454 mut read: F,
455 url: Option<&str>,
456 encoding: Option<&str>,
457 options: i32,
458) -> Result<XmlDoc, XmlError>
459where
460 F: FnMut(&mut [u8]) -> Result<usize, std::io::Error>,
461{
462 let mut buf = Vec::new();
463 let mut tmp = [0u8; 4096];
464 loop {
465 let n = read(&mut tmp).map_err(|e| XmlError::new(XML_ERR_DOCUMENT_START, e.to_string(), 0, 0))?;
466 if n == 0 {
467 break;
468 }
469 buf.extend_from_slice(&tmp[..n]);
470 }
471 xml_read_memory(&buf, url, encoding, options)
472}
473
474#[doc(alias = "xmlCtxtReset")]
476pub fn xml_ctxt_reset(ctxt: &mut XmlParserCtxt) {
477 ctxt.doc = None;
478 ctxt.last_error = None;
479}
480
481struct PushState {
487 doc: XmlDoc,
488 ns_stack: Vec<Vec<(Option<String>, String)>>,
489 stack: Vec<NodeId>,
490 open: Vec<OpenElem>,
491 char_buf: String,
492 line: u32,
493 col: u32,
494 depth: u32,
495 root_closed: bool,
499}
500
501struct OpenElem {
503 qname: String,
505 elem: NodeId,
506}
507
508struct RawAttr {
510 qname: String,
511 value: String,
512 value_off: usize,
513 colon: Option<usize>,
515}
516
517impl RawAttr {
518 fn parts(&self) -> (Option<&str>, &str) {
519 match self.colon {
520 None => (None, self.qname.as_str()),
521 Some(i) => (Some(&self.qname[..i]), &self.qname[i + 1..]),
522 }
523 }
524}
525
526struct Parser<'a> {
527 input: &'a [u8],
528 pos: usize,
529 line: u32,
530 col: u32,
531 options: i32,
532 old10: bool,
533 depth: u32,
534 ns_stack: Vec<Vec<(Option<String>, String)>>,
535 sax: &'a mut dyn SaxHandler,
536 doc: XmlDoc,
537 stack: Vec<NodeId>,
538 char_buf: String,
539 scratch_raw: Vec<RawAttr>,
540 scratch_sax: Vec<SaxAttr>,
541 started: bool,
542 no_tree: bool,
543 recover: bool,
544 entity_budget: usize,
547}
548
549impl<'a> Parser<'a> {
550 fn err(&self, code: i32, msg: impl Into<String>) -> XmlError {
551 XmlError::new(code, msg, self.line, self.col)
552 }
553
554 fn eof(&self) -> bool {
555 self.pos >= self.input.len()
556 }
557
558 fn peek_byte(&self) -> Option<u8> {
559 self.input.get(self.pos).copied()
560 }
561
562 fn starts_with(&self, s: &[u8]) -> bool {
563 self.input[self.pos..].starts_with(s)
564 }
565
566 fn bump_byte(&mut self) -> Option<u8> {
567 let b = self.peek_byte()?;
568 self.pos += 1;
569 if b == b'\n' {
570 self.line += 1;
571 self.col = 1;
572 } else {
573 self.col += 1;
574 }
575 Some(b)
576 }
577
578 fn peek_char(&self) -> Result<Option<char>, XmlError> {
580 let Some(&b0) = self.input.get(self.pos) else {
583 return Ok(None);
584 };
585 if b0 == b'\r' {
586 return Ok(Some('\n'));
587 }
588 if b0 < 0x80 {
589 return Ok(Some(b0 as char));
590 }
591 let rest = &self.input[self.pos..];
592 let head = &rest[..rest.len().min(4)];
596 match std::str::from_utf8(head) {
597 Ok(s) => Ok(s.chars().next()),
598 Err(e) if e.valid_up_to() > 0 => Ok(std::str::from_utf8(&head[..e.valid_up_to()])
601 .ok()
602 .and_then(|s| s.chars().next())),
603 Err(_) => Err(XmlError::new(
604 XML_ERR_INVALID_CHAR,
605 "Invalid UTF-8",
606 self.line,
607 self.col,
608 )),
609 }
610 }
611
612 fn bump_char(&mut self) -> Result<Option<char>, XmlError> {
613 match self.input.get(self.pos) {
616 None => return Ok(None),
617 Some(&b) if b == b'\r' => {
618 self.pos += 1;
619 self.col += 1;
620 if self.input.get(self.pos) == Some(&b'\n') {
621 self.pos += 1;
622 self.line += 1;
623 self.col = 1;
624 }
625 return Ok(Some('\n'));
626 }
627 Some(&b) if b < 0x80 => {
628 self.pos += 1;
629 if b == b'\n' {
630 self.line += 1;
631 self.col = 1;
632 } else {
633 self.col += 1;
634 }
635 return Ok(Some(b as char));
636 }
637 _ => {}
638 }
639 let c = match self.peek_char()? {
640 None => return Ok(None),
641 Some(c) => c,
642 };
643 let n = c.len_utf8();
647 self.pos += n;
648 if c as u32 == 0x0A {
649 self.line += 1;
650 self.col = 1;
651 } else {
652 self.col += n as u32;
655 }
656 Ok(Some(c))
657 }
658
659 fn require_s(&mut self) -> bool {
661 let before = self.pos;
662 let _ = self.skip_s();
663 self.pos > before
664 }
665
666 fn skip_s(&mut self) -> Result<(), XmlError> {
667 while let Some(b) = self.peek_byte() {
670 if b >= 0x80 || !crate::chvalid::xml_is_blank(b as u32) {
671 break;
672 }
673 self.bump_byte();
674 }
675 Ok(())
676 }
677
678 fn expect_byte(&mut self, b: u8, code: i32, msg: &str) -> Result<(), XmlError> {
679 if self.peek_byte() != Some(b) {
680 return Err(self.err(code, msg));
681 }
682 self.bump_byte();
683 Ok(())
684 }
685
686 fn parse_name_span(&mut self) -> Result<(usize, usize), XmlError> {
687 match self.input.get(self.pos) {
690 Some(&b) if b < 0x80 && b != b'\r' => {
691 if !xml_is_name_start_char(b as u32, self.old10) {
692 return Err(self.err(XML_ERR_NAME_REQUIRED, "Name expected"));
693 }
694 }
695 _ => {
696 let c = self
697 .peek_char()?
698 .ok_or_else(|| self.err(XML_ERR_NAME_REQUIRED, "Name expected"))?;
699 if !xml_is_name_start_char(c as u32, self.old10) {
700 return Err(self.err(XML_ERR_NAME_REQUIRED, "Name expected"));
701 }
702 }
703 }
704 let start = self.pos;
709 self.bump_char()?;
710 loop {
711 let Some(b) = self.peek_byte() else { break };
712 if b < 0x80 {
713 if !xml_is_name_char(b as u32, self.old10) {
714 break;
715 }
716 if self.pos - start >= MAX_NAME && (self.options & XML_PARSE_HUGE) == 0 {
717 return Err(self.err(XML_ERR_NAME_REQUIRED, "Name too long"));
718 }
719 self.bump_byte();
720 } else {
721 let Some(c) = self.peek_char()? else { break };
722 if !xml_is_name_char(c as u32, self.old10) {
723 break;
724 }
725 if self.pos - start >= MAX_NAME && (self.options & XML_PARSE_HUGE) == 0 {
726 return Err(self.err(XML_ERR_NAME_REQUIRED, "Name too long"));
727 }
728 self.bump_char()?;
729 }
730 }
731 Ok((start, self.pos))
734 }
735
736 fn parse_name(&mut self) -> Result<String, XmlError> {
739 let (a, b) = self.parse_name_span()?;
740 match std::str::from_utf8(&self.input[a..b]) {
741 Ok(name) => Ok(name.to_string()),
742 Err(_) => Err(self.err(XML_ERR_INVALID_CHAR, "Invalid UTF-8")),
743 }
744 }
745
746 fn split_qname(name: &str) -> Result<(Option<&str>, &str), XmlError> {
747 let mut parts = name.split(':');
748 let a = parts.next().unwrap();
749 match parts.next() {
750 None => Ok((None, a)),
751 Some(b) => {
752 if parts.next().is_some() || a.is_empty() || b.is_empty() {
753 return Err(XmlError::new(
754 XML_NS_ERR_QNAME,
755 format!("Invalid QName {name}"),
756 0,
757 0,
758 ));
759 }
760 Ok((Some(a), b))
761 }
762 }
763 }
764
765 fn lookup_ns(&self, prefix: Option<&str>) -> Option<String> {
766 if prefix == Some("xml") {
767 return Some(XML_NS.into());
768 }
769 if prefix == Some("xmlns") {
770 return Some(XMLNS_NS.into());
771 }
772 for frame in self.ns_stack.iter().rev() {
773 for (p, uri) in frame.iter().rev() {
774 if p.as_deref() == prefix {
775 return Some(uri.clone());
776 }
777 }
778 }
779 None
780 }
781
782 fn uri_has_scheme(uri: &str) -> bool {
783 let bytes = uri.as_bytes();
784 if bytes.is_empty() {
785 return false;
786 }
787 if !bytes[0].is_ascii_alphabetic() {
788 return false;
789 }
790 let mut i = 1;
791 while i < bytes.len() {
792 let b = bytes[i];
793 if b == b':' {
794 return true;
795 }
796 if b.is_ascii_alphanumeric() || b == b'+' || b == b'-' || b == b'.' {
797 i += 1;
798 } else {
799 return false;
800 }
801 }
802 false
803 }
804
805 fn flush_chars(&mut self, parent: Option<NodeId>) -> Result<(), XmlError> {
806 if self.char_buf.is_empty() {
807 return Ok(());
808 }
809 if self.char_buf.len() > MAX_TEXT && (self.options & XML_PARSE_HUGE) == 0 {
810 return Err(self.err(XML_ERR_INVALID_CHAR, "Text too long"));
811 }
812 let skip_blank = (self.options & XML_PARSE_NOBLANKS) != 0
813 && self.char_buf.chars().all(|c| crate::chvalid::xml_is_blank(c as u32));
814 if !skip_blank {
815 self.sax.characters(&self.char_buf);
816 if let Some(p) = parent.filter(|_| !self.no_tree) {
817 let t = self.doc.alloc_unnamed(NodeKind::Text);
818 self.doc.node_mut(t).content = std::mem::take(&mut self.char_buf);
821 self.doc.xml_add_child(p, t);
822 }
823 }
824 self.char_buf.clear();
825 Ok(())
826 }
827
828 fn parse_comment(&mut self, parent: Option<NodeId>) -> Result<(), XmlError> {
829 let mut body = String::new();
831 loop {
832 if self.starts_with(b"-->") {
833 self.pos += 3;
834 self.col += 3;
835 break;
836 }
837 if self.eof() {
838 return Err(self.err(XML_ERR_COMMENT_NOT_FINISHED, "Comment not finished"));
839 }
840 if self.starts_with(b"--") {
841 return Err(self.err(XML_ERR_HYPHEN_IN_COMMENT, "Double hyphen in comment"));
842 }
843 let c = self.bump_char()?.unwrap();
844 if !xml_is_char(c as u32) {
845 return Err(self.err(XML_ERR_INVALID_CHAR, "Invalid character"));
846 }
847 body.push(c);
848 }
849 self.sax.comment(&body);
850 if let Some(p) = parent.filter(|_| !self.no_tree) {
851 let n = self.doc.alloc_unnamed(NodeKind::Comment);
852 self.doc.node_mut(n).content = body;
853 self.doc.xml_add_child(p, n);
854 }
855 Ok(())
856 }
857
858 fn parse_pi(&mut self, parent: Option<NodeId>, xml_decl_ok: bool) -> Result<bool, XmlError> {
859 let target = self.parse_name()?;
861 if target.eq_ignore_ascii_case("xml") {
862 if xml_decl_ok {
863 return self.parse_xml_decl_rest().map(|_| true);
864 }
865 return Err(self.err(XML_ERR_RESERVED_XML_NAME, "Reserved PI target xml"));
866 }
867 if target.contains(':') {
872 self.sax
873 .warning(&format!("colons are forbidden from PI names '{target}'
874"));
875 }
876 let data = if matches!(self.peek_byte(), Some(b) if b < 0x80 && crate::chvalid::xml_is_blank(b as u32)) {
877 self.skip_s()?;
878 let mut d = String::new();
879 loop {
880 if self.starts_with(b"?>") {
881 self.pos += 2;
882 self.col += 2;
883 break;
884 }
885 if self.eof() {
886 return Err(self.err(XML_ERR_PI_NOT_FINISHED, "PI not finished"));
887 }
888 let c = self.bump_char()?.unwrap();
889 if !xml_is_char(c as u32) {
892 return Err(self.err(XML_ERR_INVALID_CHAR, "Invalid character in PI"));
893 }
894 d.push(c);
895 }
896 Some(d)
897 } else {
898 if !self.starts_with(b"?>") {
899 return Err(self.err(XML_ERR_PI_NOT_FINISHED, "PI not finished"));
900 }
901 self.pos += 2;
902 self.col += 2;
903 None
904 };
905 self.sax.processing_instruction(&target, data.as_deref());
906 if let Some(p) = parent.filter(|_| !self.no_tree) {
907 let n = self.doc.alloc(NodeKind::Pi, target);
908 self.doc.node_mut(n).content = data.unwrap_or_default();
909 self.doc.xml_add_child(p, n);
910 }
911 Ok(false)
912 }
913
914 fn parse_xml_decl_rest(&mut self) -> Result<(), XmlError> {
915 self.skip_s()?;
916 if !self.starts_with(b"version") {
918 return Err(self.err(XML_ERR_XMLDECL_NOT_FINISHED, "XML declaration version required"));
919 }
920 self.pos += 7;
921 self.col += 7;
922 self.skip_s()?;
923 self.expect_byte(b'=', XML_ERR_EQUAL_REQUIRED, "'=' required")?;
924 self.skip_s()?;
925 let ver = self.parse_quoted()?;
926 if ver.is_empty()
929 || !ver
930 .chars()
931 .all(|c| c.is_ascii_alphanumeric() || matches!(c, '_' | '.' | ':' | '-'))
932 {
933 return Err(self.err(XML_ERR_XMLDECL_NOT_FINISHED, "Invalid XML version value"));
934 }
935 self.doc.version = ver;
936 let had_s = self.require_s();
939 if self.starts_with(b"encoding") {
940 if !had_s {
941 return Err(self.err(XML_ERR_SPACE_REQUIRED, "Blank needed here"));
942 }
943 self.pos += 8;
944 self.col += 8;
945 self.skip_s()?;
946 self.expect_byte(b'=', XML_ERR_EQUAL_REQUIRED, "'=' required")?;
947 self.skip_s()?;
948 let enc = self.parse_quoted()?;
949 let mut cs = enc.chars();
952 let ok = cs.next().is_some_and(|c| c.is_ascii_alphabetic())
953 && cs.all(|c| c.is_ascii_alphanumeric() || matches!(c, '.' | '_' | '-'));
954 if !ok {
955 return Err(self.err(XML_ERR_ENCODING_NAME, "Invalid XML encoding name"));
956 }
957 self.doc.encoding = Some(enc);
958 self.skip_s()?;
959 }
960 if self.starts_with(b"standalone") {
961 if !had_s {
962 return Err(self.err(XML_ERR_SPACE_REQUIRED, "Blank needed here"));
963 }
964 self.pos += 10;
965 self.col += 10;
966 self.skip_s()?;
967 self.expect_byte(b'=', XML_ERR_EQUAL_REQUIRED, "'=' required")?;
968 self.skip_s()?;
969 let st = self.parse_quoted()?;
970 self.doc.standalone = match st.as_str() {
971 "yes" => Some(true),
972 "no" => Some(false),
973 _ => return Err(self.err(XML_ERR_XMLDECL_NOT_FINISHED, "standalone must be yes or no")),
974 };
975 self.skip_s()?;
976 }
977 if !self.starts_with(b"?>") {
978 return Err(self.err(XML_ERR_XMLDECL_NOT_FINISHED, "XML declaration not finished"));
979 }
980 self.pos += 2;
981 self.col += 2;
982 Ok(())
983 }
984
985 fn parse_quoted(&mut self) -> Result<String, XmlError> {
986 let q = self.peek_byte().ok_or_else(|| self.err(XML_ERR_LITERAL_NOT_FINISHED, "Quote expected"))?;
987 if q != b'\'' && q != b'"' {
988 return Err(self.err(XML_ERR_LITERAL_NOT_FINISHED, "Quote expected"));
989 }
990 self.bump_byte();
991 let mut s = String::new();
992 loop {
993 let c = self.bump_char()?.ok_or_else(|| self.err(XML_ERR_LITERAL_NOT_FINISHED, "Unterminated literal"))?;
994 if c as u8 == q && c.is_ascii() {
995 break;
996 }
997 if !xml_is_char(c as u32) {
1004 return Err(self.err(XML_ERR_INVALID_CHAR, "invalid character in literal"));
1005 }
1006 s.push(c);
1007 }
1008 Ok(s)
1009 }
1010
1011 fn parse_cdata(&mut self, parent: Option<NodeId>) -> Result<(), XmlError> {
1012 let mut body = String::new();
1014 loop {
1015 if self.starts_with(b"]]>") {
1016 self.pos += 3;
1017 self.col += 3;
1018 break;
1019 }
1020 if self.eof() {
1021 return Err(self.err(XML_ERR_CDATA_NOT_FINISHED, "CDATA not finished"));
1022 }
1023 let c = self.bump_char()?.unwrap();
1024 if !xml_is_char(c as u32) {
1027 return Err(self.err(XML_ERR_INVALID_CHAR, "invalid character in CDATA"));
1028 }
1029 body.push(c);
1030 }
1031 if (self.options & XML_PARSE_NOCDATA) != 0 {
1032 self.sax.characters(&body);
1033 if let Some(p) = parent.filter(|_| !self.no_tree) {
1034 let t = self.doc.alloc_unnamed(NodeKind::Text);
1035 self.doc.node_mut(t).content = body;
1036 self.doc.xml_add_child(p, t);
1037 }
1038 } else {
1039 self.sax.cdata_block(&body);
1040 if let Some(p) = parent.filter(|_| !self.no_tree) {
1041 let t = self.doc.alloc_unnamed(NodeKind::CData);
1042 self.doc.node_mut(t).content = body;
1043 self.doc.xml_add_child(p, t);
1044 }
1045 }
1046 Ok(())
1047 }
1048
1049 fn parse_reference(&mut self) -> Result<String, XmlError> {
1050 self.expect_byte(b'&', XML_ERR_ENTITYREF_NO_NAME, "& expected")?;
1051 if self.peek_byte() == Some(b'#') {
1052 self.bump_byte();
1053 let hex = self.peek_byte() == Some(b'x');
1057 if hex {
1058 self.bump_byte();
1059 } else if self.peek_byte() == Some(b'X') {
1060 return Err(self.err(
1061 XML_ERR_INVALID_DEC_CHARREF,
1062 "CharRef: invalid decimal value",
1063 ));
1064 }
1065 let mut digits = String::new();
1066 while let Some(b) = self.peek_byte() {
1067 let ok = if hex {
1068 b.is_ascii_hexdigit()
1069 } else {
1070 b.is_ascii_digit()
1071 };
1072 if !ok {
1073 break;
1074 }
1075 digits.push(b as char);
1076 self.bump_byte();
1077 }
1078 if digits.is_empty() {
1079 return Err(self.err(
1080 if hex { XML_ERR_INVALID_HEX_CHARREF } else { XML_ERR_INVALID_DEC_CHARREF },
1081 "Invalid character reference",
1082 ));
1083 }
1084 self.expect_byte(b';', XML_ERR_ENTITYREF_SEMICOL_MISSING, "';' required")?;
1085 let val = if hex {
1086 u32::from_str_radix(&digits, 16).map_err(|_| {
1087 self.err(XML_ERR_INVALID_HEX_CHARREF, "Invalid hex charref")
1088 })?
1089 } else {
1090 digits.parse::<u32>().map_err(|_| {
1091 self.err(XML_ERR_INVALID_DEC_CHARREF, "Invalid decimal charref")
1092 })?
1093 };
1094 if !xml_is_char(val) {
1095 return Err(self.err(XML_ERR_INVALID_CHARREF, "Invalid character reference"));
1096 }
1097 return Ok(char::from_u32(val).unwrap().to_string());
1098 }
1099 let name = self.parse_name()?;
1100 self.expect_byte(b';', XML_ERR_ENTITYREF_SEMICOL_MISSING, "';' required")?;
1101 match name.as_str() {
1102 "lt" => Ok("<".into()),
1103 "gt" => Ok(">".into()),
1104 "amp" => Ok("&".into()),
1105 "apos" => Ok("'".into()),
1106 "quot" => Ok("\"".into()),
1107 _ => {
1108 let raw = self
1109 .doc
1110 .dtd
1111 .as_ref()
1112 .and_then(|d| d.entities.get(&name))
1113 .cloned();
1114 if let Some(raw) = raw {
1115 return self.expand_entity(&name, &raw, 0);
1119 }
1120 if self.recover {
1121 self.sax
1124 .error(&format!("Entity '{name}' not defined"));
1125 return Ok(format!("&{name};"));
1126 }
1127 Err(self.err(
1128 XML_ERR_UNDECLARED_ENTITY,
1129 format!("Entity '{name}' not defined"),
1130 ))
1131 }
1132 }
1133 }
1134
1135 fn reference_raw_value(&self) -> Option<String> {
1146 let rest = self.input.get((self.pos + 1).min(self.input.len())..)?;
1147 let end = rest.iter().position(|b| *b == b';')?;
1148 let name = std::str::from_utf8(&rest[..end]).ok()?;
1149 self.doc.dtd.as_ref()?.entities.get(name).cloned()
1150 }
1151
1152 fn splice_entity(&mut self, repl: &str, parent: NodeId) -> Result<(), XmlError> {
1163 let wrapped = format!("<rusty-xml-entity>{repl}</rusty-xml-entity>");
1164 let mut null = rusty_xml_sax::NullSax;
1165 let mut sub = Parser {
1166 input: wrapped.as_bytes(),
1167 pos: 0,
1168 line: self.line,
1169 col: self.col,
1170 options: self.options,
1171 old10: self.old10,
1172 depth: self.depth + 1,
1173 ns_stack: Vec::new(),
1174 sax: &mut null,
1175 doc: XmlDoc::with_node_capacity(Some("1.0"), 8),
1176 stack: Vec::new(),
1177 char_buf: String::new(),
1178 scratch_raw: Vec::new(),
1179 scratch_sax: Vec::new(),
1180 started: false,
1181 no_tree: false,
1182 recover: self.recover,
1183 entity_budget: self.entity_budget,
1186 };
1187 sub.doc.dtd = self.doc.dtd.clone();
1188 let r = sub.parse_document();
1189 self.entity_budget = sub.entity_budget;
1190 match r {
1191 Ok(()) => {
1192 if let Some(root) = sub.doc.xml_doc_get_root_element() {
1193 self.doc.xml_copy_children_from(&sub.doc, root, parent);
1194 }
1195 Ok(())
1196 }
1197 Err(e) if e.code == XML_NS_ERR_UNDEFINED_NAMESPACE => {
1198 self.char_buf.push_str(repl);
1199 self.flush_chars(Some(parent))
1200 }
1201 Err(e) => Err(e),
1202 }
1203 }
1204
1205 fn expand_entity(&mut self, name: &str, raw: &str, depth: u32) -> Result<String, XmlError> {
1210 const MAX_ENTITY_DEPTH: u32 = 40;
1211 if depth > MAX_ENTITY_DEPTH {
1212 return Err(self.err(
1213 XML_ERR_UNDECLARED_ENTITY,
1214 format!("Entity '{name}' nested too deeply"),
1215 ));
1216 }
1217 let b = raw.as_bytes();
1218 let mut out = String::with_capacity(raw.len());
1219 let mut i = 0usize;
1220 while i < b.len() {
1221 if b[i] != b'&' {
1222 let start = i;
1223 while i < b.len() && b[i] != b'&' {
1224 i += 1;
1225 }
1226 out.push_str(&raw[start..i]);
1227 continue;
1228 }
1229 let Some(semi) = raw[i..].find(';').map(|k| i + k) else {
1230 out.push('&');
1231 i += 1;
1232 continue;
1233 };
1234 let inner = raw[i + 1..semi].to_string();
1235 if let Some(rest) = inner.strip_prefix('#') {
1236 let (radix, digits) = match rest.strip_prefix(['x', 'X']) {
1237 Some(h) => (16u32, h),
1238 None => (10u32, rest),
1239 };
1240 match u32::from_str_radix(digits, radix).ok().and_then(char::from_u32) {
1241 Some(c) => out.push(c),
1242 None => {
1243 return Err(self.err(
1244 XML_ERR_INVALID_CHAR,
1245 format!("Invalid character reference in entity '{name}'"),
1246 ))
1247 }
1248 }
1249 i = semi + 1;
1250 continue;
1251 }
1252 let replacement: Option<String> = match inner.as_str() {
1253 "lt" => Some("<".into()),
1254 "gt" => Some(">".into()),
1255 "amp" => Some("&".into()),
1256 "apos" => Some("'".into()),
1257 "quot" => Some('"'.to_string()),
1258 other => {
1259 let nested = self
1260 .doc
1261 .dtd
1262 .as_ref()
1263 .and_then(|d| d.entities.get(other))
1264 .cloned();
1265 match nested {
1266 Some(r) => Some(self.expand_entity(other, &r, depth + 1)?),
1267 None => None,
1268 }
1269 }
1270 };
1271 match replacement {
1272 Some(r) => {
1273 if r.len() > self.entity_budget {
1274 return Err(self.err(
1275 XML_ERR_INTERNAL_ERROR,
1276 "Maximum entity amplification exceeded",
1277 ));
1278 }
1279 self.entity_budget -= r.len();
1280 out.push_str(&r);
1281 }
1282 None if self.recover => out.push_str(&raw[i..=semi]),
1283 None => {
1284 return Err(self.err(
1285 XML_ERR_UNDECLARED_ENTITY,
1286 format!("Entity '{inner}' not defined"),
1287 ))
1288 }
1289 }
1290 i = semi + 1;
1291 }
1292 Ok(out)
1293 }
1294
1295 fn parse_att_value(&mut self) -> Result<(String, usize), XmlError> {
1296 let q = self.peek_byte().ok_or_else(|| {
1297 self.err(XML_ERR_ATTRIBUTE_WITHOUT_VALUE, "Attribute value expected")
1298 })?;
1299 if q != b'\'' && q != b'"' {
1300 return Err(self.err(XML_ERR_ATTRIBUTE_WITHOUT_VALUE, "Attribute value expected"));
1301 }
1302 self.bump_byte();
1303 let start = self.pos;
1304 let mut val = String::new();
1305 loop {
1306 {
1309 let rs = self.pos;
1310 let mut i = rs;
1311 while i < self.input.len() {
1312 let b = self.input[i];
1313 if b == q || b == b'<' || b == b'&' || b < 0x20 || b >= 0x80 {
1314 break;
1315 }
1316 i += 1;
1317 }
1318 if i > rs {
1319 if let Ok(run) = std::str::from_utf8(&self.input[rs..i]) {
1320 if val.is_empty() {
1323 val.reserve_exact(i - rs);
1324 }
1325 val.push_str(run);
1326 self.col += (i - rs) as u32;
1327 self.pos = i;
1328 continue;
1329 }
1330 }
1331 }
1332 if self.peek_byte() == Some(q) {
1333 self.bump_byte();
1334 break;
1335 }
1336 if self.eof() {
1337 return Err(self.err(XML_ERR_LITERAL_NOT_FINISHED, "Unterminated attribute"));
1338 }
1339 if self.peek_byte() == Some(b'<') {
1340 return Err(self.err(XML_ERR_LT_IN_ATTRIBUTE, "'<' in attribute value"));
1341 }
1342 if self.peek_byte() == Some(b'&') {
1343 let raw = self.reference_raw_value();
1344 let repl = self.parse_reference()?;
1345 if raw.as_deref().is_some_and(|r| r.contains('<')) {
1349 return Err(self.err(
1350 XML_ERR_LT_IN_ATTRIBUTE,
1351 "'<' in entity is not allowed in attribute values",
1352 ));
1353 }
1354 val.push_str(&repl);
1355 continue;
1356 }
1357 let c = self.bump_char()?.unwrap();
1358 if !xml_is_char(c as u32) {
1365 return Err(self.err(
1366 XML_ERR_INVALID_CHAR,
1367 "invalid character in attribute value",
1368 ));
1369 }
1370 if c == '\n' || c == '\t' {
1372 val.push(' ');
1373 } else {
1374 val.push(c);
1375 }
1376 }
1377 Ok((val, start))
1378 }
1379
1380 fn skip_doctype(&mut self) -> Result<(), XmlError> {
1381 self.skip_s()?;
1383 let name = self.parse_name()?;
1384 self.skip_s()?;
1385 let mut public_id = None;
1386 let mut system_id = None;
1387 if self.starts_with(b"SYSTEM") {
1388 self.pos += 6;
1389 self.col += 6;
1390 if !self.require_s() {
1391 return Err(self.err(XML_ERR_SPACE_REQUIRED, "Space required after 'SYSTEM'"));
1392 }
1393 if !matches!(self.peek_byte(), Some(b'"') | Some(b'\'')) {
1394 return Err(self.err(
1395 XML_ERR_LITERAL_NOT_FINISHED,
1396 "SystemLiteral \" or ' expected",
1397 ));
1398 }
1399 system_id = Some(self.parse_quoted()?);
1400 } else if self.starts_with(b"PUBLIC") {
1401 self.pos += 6;
1402 self.col += 6;
1403 if !self.require_s() {
1404 return Err(self.err(XML_ERR_SPACE_REQUIRED, "Space required after 'PUBLIC'"));
1405 }
1406 let pid = self.parse_quoted()?;
1407 if let Some(bad) = pid.chars().find(|c| !crate::dtd::is_pubid_char(*c)) {
1409 return Err(self.err(
1410 XML_ERR_INVALID_CHAR,
1411 format!("Invalid character 0x{:X} in public identifier", bad as u32),
1412 ));
1413 }
1414 public_id = Some(pid);
1415 if !self.require_s() {
1419 return Err(self.err(
1420 XML_ERR_SPACE_REQUIRED,
1421 "Space required after the Public Identifier",
1422 ));
1423 }
1424 if !matches!(self.peek_byte(), Some(b'"') | Some(b'\'')) {
1425 return Err(self.err(
1426 XML_ERR_LITERAL_NOT_FINISHED,
1427 "SystemLiteral \" or ' expected",
1428 ));
1429 }
1430 system_id = Some(self.parse_quoted()?);
1431 }
1432 self.skip_s()?;
1433 let mut int_subset = None;
1434 if self.peek_byte() == Some(b'[') {
1435 self.bump_byte();
1436 let start = self.pos;
1437 let mut depth = 1i32;
1438 let mut in_quote: Option<u8> = None;
1439 while depth > 0 {
1440 if in_quote.is_none() && self.starts_with(b"<!--") {
1446 match self.input[self.pos..]
1447 .windows(3)
1448 .position(|w| w == b"-->")
1449 {
1450 Some(off) => {
1451 for _ in 0..off + 3 {
1452 self.bump_byte();
1453 }
1454 continue;
1455 }
1456 None => {
1457 return Err(
1458 self.err(XML_ERR_COMMENT_NOT_FINISHED, "Comment not finished")
1459 );
1460 }
1461 }
1462 }
1463 let b = self.bump_byte().ok_or_else(|| {
1464 self.err(XML_ERR_DOCUMENT_END, "Unterminated DOCTYPE")
1465 })?;
1466 if let Some(q) = in_quote {
1467 if b == q {
1468 in_quote = None;
1469 }
1470 continue;
1471 }
1472 match b {
1473 b'\'' | b'"' => in_quote = Some(b),
1474 b'[' => depth += 1,
1475 b']' => depth -= 1,
1476 _ => {}
1477 }
1478 }
1479 int_subset = Some(String::from_utf8_lossy(&self.input[start..self.pos.saturating_sub(1)]).into_owned());
1481 }
1482 self.skip_s()?;
1483 self.expect_byte(b'>', XML_ERR_GT_REQUIRED, "'>' required")?;
1484 let mut dtd = if let Some(ref subset) = int_subset {
1485 match crate::dtd::parse_dtd_subset(subset, self.old10) {
1491 Ok(d) => d,
1492 Err(_) if self.recover => rusty_xml_tree::XmlDtd::default(),
1493 Err(e) => return Err(e),
1494 }
1495 } else {
1496 rusty_xml_tree::XmlDtd::default()
1497 };
1498 dtd.name = Some(name);
1499 dtd.public_id = public_id;
1500 dtd.system_id = system_id;
1501 dtd.int_subset = int_subset;
1502 self.doc.dtd = Some(dtd);
1503 Ok(())
1504 }
1505
1506 fn open_element(&mut self, parent: NodeId) -> Result<Option<OpenElem>, XmlError> {
1513 self.depth += 1;
1514 let cap = if (self.options & XML_PARSE_HUGE) != 0 {
1515 MAX_DEPTH_HUGE
1516 } else {
1517 MAX_DEPTH
1518 };
1519 if self.depth > cap {
1520 return Err(self.err(XML_ERR_INTERNAL_ERROR, "Excessive element nesting"));
1521 }
1522 self.expect_byte(b'<', XML_ERR_LT_REQUIRED, "'<' required")?;
1523 let qname = self.parse_name()?;
1524 let (prefix, local) = Self::split_qname(&qname).map_err(|mut e| {
1525 e.line = self.line;
1526 e.col = self.col;
1527 e
1528 })?;
1529
1530 let mut raw_attrs: Vec<RawAttr> = std::mem::take(&mut self.scratch_raw);
1533 raw_attrs.clear();
1534 loop {
1535 let before_ws = self.pos;
1536 self.skip_s()?;
1537 let had_ws = self.pos > before_ws;
1538 if self.starts_with(b"/>") || self.peek_byte() == Some(b'>') {
1539 break;
1540 }
1541 if !had_ws {
1545 return Err(self.err(
1546 XML_ERR_SPACE_REQUIRED,
1547 "attributes construct error",
1548 ));
1549 }
1550 let an = self.parse_name()?;
1551 self.skip_s()?;
1552 self.expect_byte(b'=', XML_ERR_EQUAL_REQUIRED, "'=' required")?;
1553 self.skip_s()?;
1554 let (value, value_off) = self.parse_att_value()?;
1555 let colon = match Self::split_qname(&an).map_err(|mut e| {
1556 e.line = self.line;
1557 e.col = self.col;
1558 e
1559 })? {
1560 (None, _) => None,
1561 (Some(pfx), _) => Some(pfx.len()),
1562 };
1563 raw_attrs.push(RawAttr {
1564 qname: an,
1565 value,
1566 value_off,
1567 colon,
1568 });
1569 }
1570 let empty = if self.starts_with(b"/>") {
1571 self.pos += 2;
1572 self.col += 2;
1573 true
1574 } else {
1575 self.expect_byte(b'>', XML_ERR_GT_REQUIRED, "'>' required")?;
1576 false
1577 };
1578
1579 let mut ns_frame: Vec<(Option<String>, String)> = Vec::new();
1580 for a in &raw_attrs {
1581 let (ap, al) = a.parts();
1582 if ap.is_none() && al == "xmlns" {
1583 if !a.value.is_empty() && !Self::uri_has_scheme(&a.value) {
1584 let msg = format!("xmlns: URI {} is not absolute\n", a.value);
1585 self.sax.warning(&msg);
1586 }
1587 ns_frame.push((None, a.value.clone()));
1588 } else if ap == Some("xmlns") {
1589 if !a.value.is_empty()
1590 && !Self::uri_has_scheme(&a.value)
1591 && (self.options & XML_PARSE_PEDANTIC) != 0
1592 {
1593 let msg = format!("xmlns:{}: URI {} is not absolute\n", al, a.value);
1594 self.sax.warning(&msg);
1595 }
1596 if al == "xml" {
1599 if a.value != XML_NS {
1600 return Err(self.err(
1601 XML_NS_ERR_UNDEFINED_NAMESPACE,
1602 "xml namespace prefix mapped to wrong URI",
1603 ));
1604 }
1605 } else if a.value == XML_NS {
1606 return Err(self.err(
1607 XML_NS_ERR_UNDEFINED_NAMESPACE,
1608 "xml namespace URI mapped to wrong prefix",
1609 ));
1610 }
1611 if al == "xmlns" {
1612 return Err(self.err(
1613 XML_NS_ERR_UNDEFINED_NAMESPACE,
1614 "redefinition of the xmlns prefix is forbidden",
1615 ));
1616 }
1617 if a.value == XMLNS_NS {
1618 return Err(self.err(
1619 XML_NS_ERR_UNDEFINED_NAMESPACE,
1620 "reuse of the xmlns namespace name is forbidden",
1621 ));
1622 }
1623 if a.value.is_empty() {
1625 return Err(self.err(
1626 XML_NS_ERR_UNDEFINED_NAMESPACE,
1627 "Empty XML namespace is not allowed",
1628 ));
1629 }
1630 ns_frame.push((Some(al.to_string()), a.value.clone()));
1631 }
1632 }
1633 self.ns_stack.push(ns_frame);
1636
1637 let elem_uri = self.lookup_ns(prefix);
1638 if prefix.is_some() && elem_uri.is_none() {
1639 self.sax.error(&format!(
1647 "Namespace prefix {} is not defined",
1648 prefix.unwrap_or_default()
1649 ));
1650 }
1651
1652 let mut seen_keys: std::collections::HashSet<(Option<String>, String)> =
1653 std::collections::HashSet::new();
1654 let mut sax_attrs: Vec<SaxAttr> = std::mem::take(&mut self.scratch_sax);
1655 sax_attrs.clear();
1656 for idx in 0..raw_attrs.len() {
1657 let (ap_owned, al_owned, is_ns, value_off) = {
1660 let a = &mut raw_attrs[idx];
1661 let voff = a.value_off;
1662 match a.colon {
1663 None => {
1666 let is_ns = a.qname == "xmlns";
1667 (None, std::mem::take(&mut a.qname), is_ns, voff)
1668 }
1669 Some(i) => {
1670 let is_ns = &a.qname[..i] == "xmlns";
1671 (
1672 Some(a.qname[..i].to_string()),
1673 a.qname[i + 1..].to_string(),
1674 is_ns,
1675 voff,
1676 )
1677 }
1678 }
1679 };
1680 if is_ns {
1681 continue;
1682 }
1683 let uri = if ap_owned.is_some() {
1684 let u = self.lookup_ns(ap_owned.as_deref());
1685 if u.is_none() && !self.recover {
1686 return Err(self.err(
1687 XML_NS_ERR_UNDEFINED_NAMESPACE,
1688 format!("Undefined namespace prefix {}", ap_owned.clone().unwrap()),
1689 ));
1690 }
1691 u
1692 } else {
1693 None
1694 };
1695 if sax_attrs.len() < 32 {
1701 if sax_attrs
1702 .iter()
1703 .any(|s| s.uri.as_deref() == uri.as_deref() && s.local == al_owned)
1704 {
1705 return Err(self.err(XML_ERR_ATTRIBUTE_REDEFINED, "Attribute redefined"));
1706 }
1707 } else {
1708 if seen_keys.is_empty() {
1709 for a in sax_attrs.iter() {
1710 seen_keys.insert((a.uri.clone(), a.local.clone()));
1711 }
1712 }
1713 if !seen_keys.insert((uri.clone(), al_owned.clone())) {
1714 return Err(self.err(XML_ERR_ATTRIBUTE_REDEFINED, "Attribute redefined"));
1715 }
1716 }
1717 sax_attrs.push(SaxAttr {
1718 local: al_owned,
1719 prefix: ap_owned,
1720 uri,
1721 value: std::mem::take(&mut raw_attrs[idx].value),
1724 value_input_off: Some(value_off),
1725 });
1726 }
1727
1728 let frame: &[(Option<String>, String)] =
1729 self.ns_stack.last().map(Vec::as_slice).unwrap_or(&[]);
1730 self.sax.start_element_ns(
1731 local,
1732 prefix,
1733 elem_uri.as_deref(),
1734 frame,
1735 &sax_attrs,
1736 0,
1737 );
1738
1739 let elem = self.doc.alloc(NodeKind::Element, local);
1740 self.doc.node_mut(elem).prefix = prefix.map(str::to_string);
1741 self.doc.node_mut(elem).ns_uri = elem_uri;
1742 for i in 0..self.ns_stack.last().map_or(0, Vec::len) {
1743 let (p, u) = {
1744 let f = self.ns_stack.last().unwrap();
1745 (f[i].0.clone(), f[i].1.clone())
1746 };
1747 self.doc.push_ns_def(elem, p, u);
1748 }
1749 if self.no_tree {
1750 sax_attrs.clear();
1751 } else {
1752 for a in sax_attrs.drain(..) {
1753 let uri = a.uri;
1754 let aid = self.doc.add_attr_owned(elem, a.local, a.prefix, a.value);
1755 self.doc.node_mut(aid).ns_uri = uri;
1756 }
1757 }
1758 self.doc.xml_add_child(parent, elem);
1759
1760 raw_attrs.clear();
1761 sax_attrs.clear();
1762 self.scratch_raw = raw_attrs;
1763 self.scratch_sax = sax_attrs;
1764
1765 if empty {
1766 let uri = self.doc.node(elem).ns_uri.as_deref();
1767 self.sax.end_element_ns(local, prefix, uri);
1768 self.ns_stack.pop();
1769 self.depth -= 1;
1770 return Ok(None);
1771 }
1772
1773 self.stack.push(elem);
1774 Ok(Some(OpenElem { qname, elem }))
1775 }
1776
1777 fn close_element(&mut self, open: &OpenElem) -> Result<(), XmlError> {
1783 let (prefix, local) = Self::split_qname(&open.qname).map_err(|mut e| {
1784 e.line = self.line;
1785 e.col = self.col;
1786 e
1787 })?;
1788 if !self.starts_with(b"</") {
1789 return Err(self.err(
1790 XML_ERR_TAG_NOT_FINISHED,
1791 format!("Premature end of data in tag {local}"),
1792 ));
1793 }
1794 self.pos += 2;
1795 self.col += 2;
1796 let (ea, eb) = self.parse_name_span()?;
1797 self.skip_s()?;
1798 self.expect_byte(b'>', XML_ERR_GT_REQUIRED, "'>' required")?;
1799 if &self.input[ea..eb] != open.qname.as_bytes() {
1800 let end_name = String::from_utf8_lossy(&self.input[ea..eb]).into_owned();
1801 let qname = &open.qname;
1802 return Err(self.err(
1803 XML_ERR_TAG_NAME_MISMATCH,
1804 format!("Opening and ending tag mismatch: {qname} and {end_name}"),
1805 ));
1806 }
1807 let uri = self.doc.node(open.elem).ns_uri.as_deref();
1808 self.sax.end_element_ns(local, prefix, uri);
1809 self.ns_stack.pop();
1810 self.stack.pop();
1811 self.depth -= 1;
1812 Ok(())
1813 }
1814
1815 fn parse_element(&mut self, parent: NodeId) -> Result<(), XmlError> {
1818 let Some(open) = self.open_element(parent)? else {
1819 return Ok(());
1820 };
1821 self.parse_content(open.elem)?;
1822 self.close_element(&open)
1823 }
1824
1825 fn parse_content(&mut self, parent: NodeId) -> Result<(), XmlError> {
1835 let mut open: Vec<OpenElem> = Vec::new();
1836 self.parse_content_inner(parent, &mut open, false, false)?;
1837 Ok(())
1838 }
1839
1840 fn parse_document(&mut self) -> Result<(), XmlError> {
1841 self.parse_prolog()?;
1842 self.parse_element(NodeId::DOCUMENT)?;
1843 self.parse_epilog()
1844 }
1845
1846 fn resume(
1848 input: &'a [u8],
1849 options: i32,
1850 sax: &'a mut dyn SaxHandler,
1851 st: PushState,
1852 ) -> Self {
1853 let _ = st.root_closed;
1854 Parser {
1855 input,
1856 pos: 0,
1857 line: st.line,
1858 col: st.col,
1859 options,
1860 old10: (options & XML_PARSE_OLD10) != 0,
1861 depth: st.depth,
1862 ns_stack: st.ns_stack,
1863 sax,
1864 doc: st.doc,
1865 stack: st.stack,
1866 char_buf: st.char_buf,
1867 no_tree: (options & XML_PARSE_NO_TREE) != 0,
1868 recover: (options & XML_PARSE_RECOVER) != 0,
1869 entity_budget: input.len().saturating_mul(10).max(1 << 16),
1872 scratch_raw: Vec::new(),
1873 scratch_sax: Vec::new(),
1874 started: true,
1875 }
1876 }
1877
1878 fn suspend(self, open: Vec<OpenElem>, root_closed: bool) -> PushState {
1879 PushState {
1880 root_closed,
1881 doc: self.doc,
1882 ns_stack: self.ns_stack,
1883 stack: self.stack,
1884 open,
1885 char_buf: self.char_buf,
1886 line: self.line,
1887 col: self.col,
1888 depth: self.depth,
1889 }
1890 }
1891
1892 fn incomplete_construct(&self) -> bool {
1899 let r = &self.input[self.pos..];
1900 fn has(h: &[u8], n: &[u8]) -> bool {
1901 h.len() >= n.len() && h.windows(n.len()).any(|w| w == n)
1902 }
1903 fn tag_complete(r: &[u8]) -> bool {
1905 let mut quote: Option<u8> = None;
1906 for &b in &r[1..] {
1907 match quote {
1908 Some(q) if b == q => quote = None,
1909 Some(_) => {}
1910 None => match b {
1911 b'"' | 0x27 => quote = Some(b),
1912 b'>' => return true,
1913 _ => {}
1914 },
1915 }
1916 }
1917 false
1918 }
1919 match r.first() {
1920 Some(b'<') => {
1921 if r.len() < 2 {
1922 return true;
1923 }
1924 if r.starts_with(b"<!--") {
1925 return !has(&r[4..], b"-->");
1926 }
1927 if r.starts_with(b"<![CDATA[") {
1928 return !has(&r[9..], b"]]>");
1929 }
1930 if r.starts_with(b"<?") {
1931 return !has(&r[2..], b"?>");
1932 }
1933 if r[1] == b'!' && r.len() < 9 {
1935 return true;
1936 }
1937 !tag_complete(r)
1938 }
1939 Some(b'&') => !r.contains(&b';'),
1940 Some(b']') => r.len() < 3,
1942 Some(0x0D) => r.len() < 2,
1946 Some(&b0) => {
1950 let need = if b0 < 0x80 {
1951 1
1952 } else if b0 >> 5 == 0b110 {
1953 2
1954 } else if b0 >> 4 == 0b1110 {
1955 3
1956 } else if b0 >> 3 == 0b11110 {
1957 4
1958 } else {
1959 1
1960 };
1961 r.len() < need
1962 }
1963 None => false,
1964 }
1965 }
1966
1967 fn parse_content_inner(
1977 &mut self,
1978 parent: NodeId,
1979 open: &mut Vec<OpenElem>,
1980 stop_at_eof: bool,
1981 stop_when_empty: bool,
1982 ) -> Result<usize, XmlError> {
1983 let outer = parent;
1988 let mut parent = open.last().map(|f| f.elem).unwrap_or(parent);
1989 loop {
1990 let safe = self.pos;
1991 if self.eof() {
1992 if stop_at_eof {
1993 return Ok(safe);
1994 }
1995 self.flush_chars(Some(parent))?;
1996 if let Some(o) = open.last() {
1997 let (_, local) = Self::split_qname(&o.qname).unwrap_or((None, &o.qname));
1998 return Err(self.err(
1999 XML_ERR_TAG_NOT_FINISHED,
2000 format!("Premature end of data in tag {local}"),
2001 ));
2002 }
2003 return Ok(safe);
2004 }
2005 if stop_at_eof && self.incomplete_construct() {
2008 return Ok(safe);
2009 }
2010 if self.starts_with(b"</") {
2011 self.flush_chars(Some(parent))?;
2012 match open.pop() {
2015 Some(o) => {
2016 self.close_element(&o)?;
2017 if stop_when_empty && open.is_empty() {
2021 return Ok(self.pos);
2022 }
2023 parent = open.last().map(|f| f.elem).unwrap_or(outer);
2024 continue;
2025 }
2026 None => return Ok(safe),
2027 }
2028 }
2029 if self.starts_with(b"<!--") {
2030 self.flush_chars(Some(parent))?;
2031 self.pos += 4;
2032 self.col += 4;
2033 self.parse_comment(Some(parent))?;
2034 continue;
2035 }
2036 if self.starts_with(b"<![CDATA[") {
2037 self.flush_chars(Some(parent))?;
2038 self.pos += 9;
2039 self.col += 9;
2040 self.parse_cdata(Some(parent))?;
2041 continue;
2042 }
2043 if self.starts_with(b"<?") {
2044 self.flush_chars(Some(parent))?;
2045 self.pos += 2;
2046 self.col += 2;
2047 self.parse_pi(Some(parent), false)?;
2048 continue;
2049 }
2050 let lead = self.peek_byte();
2051 if lead == Some(b'<') {
2052 self.flush_chars(Some(parent))?;
2053 if let Some(o) = self.open_element(parent)? {
2054 parent = o.elem;
2055 open.push(o);
2056 }
2057 continue;
2058 }
2059 if lead == Some(b'&') {
2060 let is_charref = self.input.get(self.pos + 1) == Some(&b'#');
2073 if is_charref {
2074 let repl = self.parse_reference()?;
2075 self.char_buf.push_str(&repl);
2076 } else {
2077 self.flush_chars(Some(parent))?;
2078 let raw = self.reference_raw_value();
2079 let repl = self.parse_reference()?;
2080 if raw
2094 .as_deref()
2095 .is_some_and(|r| r.contains('<') || r.contains('&'))
2096 && !self.no_tree
2097 {
2098 self.splice_entity(raw.as_deref().unwrap(), parent)?;
2099 } else {
2100 self.char_buf.push_str(&repl);
2101 self.flush_chars(Some(parent))?;
2102 }
2103 }
2104 continue;
2105 }
2106 if self.starts_with(b"]]>") {
2107 return Err(self.err(XML_ERR_MISPLACED_CDATA_END, "Misplaced CDATA end"));
2108 }
2109 {
2113 let start = self.pos;
2114 let mut i = start;
2115 while i < self.input.len() {
2116 let b = self.input[i];
2117 let plain = b == 0x09 || (0x20..0x80).contains(&b);
2118 if !plain || b == b'<' || b == b'&' || b == b']' {
2119 break;
2120 }
2121 i += 1;
2122 }
2123 if i > start {
2124 match std::str::from_utf8(&self.input[start..i]) {
2126 Ok(run) => {
2127 self.char_buf.push_str(run);
2128 self.col += (i - start) as u32;
2129 self.pos = i;
2130 continue;
2131 }
2132 Err(_) => {}
2133 }
2134 }
2135 }
2136 let c = self.bump_char()?.unwrap();
2137 if !xml_is_char(c as u32) {
2138 return Err(self.err(XML_ERR_INVALID_CHAR, "Invalid character"));
2139 }
2140 self.char_buf.push(c);
2141 }
2142 }
2143
2144 fn parse_misc(&mut self, parent: NodeId) -> Result<(), XmlError> {
2145 loop {
2146 self.skip_s()?;
2147 if self.starts_with(b"<!--") {
2148 self.pos += 4;
2149 self.col += 4;
2150 self.parse_comment(Some(parent))?;
2151 continue;
2152 }
2153 if self.starts_with(b"<?") {
2154 self.pos += 2;
2155 self.col += 2;
2156 self.parse_pi(Some(parent), false)?;
2157 continue;
2158 }
2159 break;
2160 }
2161 Ok(())
2162 }
2163
2164 fn parse_prolog(&mut self) -> Result<(), XmlError> {
2168 if self.starts_with(&[0xef, 0xbb, 0xbf]) {
2169 self.pos += 3;
2170 }
2171 self.sax.set_document_locator();
2172 self.sax.start_document();
2173 self.started = true;
2174
2175 if self.starts_with(b"<?xml") {
2177 let save_pos = self.pos;
2178 let save_col = self.col;
2179 let save_line = self.line;
2180 self.pos += 5;
2181 self.col += 5;
2182 match self.peek_byte() {
2183 Some(b) if b < 0x80 && crate::chvalid::xml_is_blank(b as u32) => {
2184 self.parse_xml_decl_rest()?;
2185 }
2186 _ => {
2187 self.pos = save_pos;
2188 self.col = save_col;
2189 self.line = save_line;
2190 self.pos += 2;
2191 self.col += 2;
2192 self.parse_pi(Some(NodeId::DOCUMENT), false)?;
2193 }
2194 }
2195 }
2196
2197 self.parse_misc(NodeId::DOCUMENT)?;
2198 if self.starts_with(b"<!DOCTYPE") {
2199 self.pos += 9;
2200 self.col += 9;
2201 self.skip_doctype()?;
2202 self.parse_misc(NodeId::DOCUMENT)?;
2203 }
2204
2205 if self.peek_byte() != Some(b'<') {
2206 return Err(self.err(XML_ERR_DOCUMENT_EMPTY, "Document is empty"));
2207 }
2208 Ok(())
2209 }
2210
2211 fn parse_epilog(&mut self) -> Result<(), XmlError> {
2213 self.parse_misc(NodeId::DOCUMENT)?;
2214 self.skip_s()?;
2215 if !self.eof() {
2216 return Err(self.err(XML_ERR_EXTRA_CONTENT, "Extra content at the end of the document"));
2217 }
2218 self.sax.end_document();
2219 Ok(())
2220 }
2221}
2222
2223fn parse_doc(
2224 buffer: &[u8],
2225 _url: Option<&str>,
2226 encoding: Option<&str>,
2227 options: i32,
2228 sax: &mut dyn SaxHandler,
2229) -> Result<XmlDoc, XmlError> {
2230 let (converted, enc_name) = crate::encoding::xml_convert_to_utf8_cow(buffer, encoding)?;
2231 parse_utf8(&converted, enc_name.as_deref(), options, sax)
2232}
2233
2234fn parse_utf8(
2235 buffer: &[u8],
2236 enc_name: Option<&str>,
2237 options: i32,
2238 sax: &mut dyn SaxHandler,
2239) -> Result<XmlDoc, XmlError> {
2240 let options = options | XML_PARSE_NONET | XML_PARSE_NO_XXE;
2241 let mut p = Parser {
2242 input: buffer,
2243 pos: 0,
2244 line: 1,
2245 col: 1,
2246 options,
2247 old10: (options & XML_PARSE_OLD10) != 0,
2248 depth: 0,
2249 ns_stack: Vec::new(),
2250 sax,
2251 doc: XmlDoc::with_node_capacity(
2254 Some("1.0"),
2255 if (options & XML_PARSE_NO_TREE) != 0 {
2256 buffer.len() / 32
2260 } else {
2261 buffer.len() / 10
2262 },
2263 ),
2264 stack: Vec::new(),
2265 char_buf: String::new(),
2266 no_tree: (options & XML_PARSE_NO_TREE) != 0,
2267 recover: (options & XML_PARSE_RECOVER) != 0,
2268 entity_budget: buffer.len().saturating_mul(10).max(1 << 16),
2271 scratch_raw: Vec::new(),
2272 scratch_sax: Vec::new(),
2273 started: false,
2274 };
2275 match p.parse_document() {
2276 Ok(()) => {
2277 apply_dtd_defaults(&mut p.doc, buffer.len(), options)?;
2278 if p.doc.encoding.is_none() {
2279 if let Some(n) = enc_name {
2280 if !n.eq_ignore_ascii_case("UTF-8") && !n.eq_ignore_ascii_case("US-ASCII") {
2281 p.doc.encoding = Some(n.to_string());
2282 }
2283 }
2284 }
2285 Ok(p.doc)
2286 }
2287 Err(e) => {
2288 if p.started {
2289 p.sax.end_document();
2290 }
2291 if (options & XML_PARSE_RECOVER) != 0 {
2292 p.sax.error(&e.message);
2295 return Ok(p.doc);
2296 }
2297 Err(e)
2298 }
2299 }
2300}
2301
2302fn apply_dtd_defaults(
2303 doc: &mut XmlDoc,
2304 input_len: usize,
2305 options: i32,
2306) -> Result<(), XmlError> {
2307 if (options & XML_PARSE_DTDATTR) == 0 {
2313 return Ok(());
2314 }
2315 match &doc.dtd {
2319 None => return Ok(()),
2320 Some(d) => {
2321 if !d.attributes.values().any(|a| a.default_value.is_some()) {
2322 return Ok(());
2323 }
2324 }
2325 }
2326 let mut budget = input_len.max(65_536).min(5_000_000);
2336 let dtd = match doc.dtd.clone() {
2337 Some(d) => d,
2338 None => return Ok(()),
2339 };
2340 let mut by_elem: std::collections::HashMap<&str, Vec<(&str, &str)>> =
2344 std::collections::HashMap::new();
2345 for ((elem, aname), ad) in &dtd.attributes {
2346 if let Some(v) = &ad.default_value {
2347 by_elem
2348 .entry(elem.as_str())
2349 .or_default()
2350 .push((aname.as_str(), v.as_str()));
2351 }
2352 }
2353 for list in by_elem.values_mut() {
2358 list.sort_unstable_by(|a, b| a.0.cmp(b.0));
2359 }
2360 let n = doc.len();
2361 for i in 0..n {
2362 let id = NodeId(i as u32);
2363 if doc.kind(id) != NodeKind::Element {
2364 continue;
2365 }
2366 let Some(list) = by_elem.get(doc.name(id)) else {
2367 continue;
2368 };
2369 for (aname, v) in list.iter() {
2370 if doc.xml_get_prop(id, aname).is_none() {
2371 if budget == 0 {
2372 return Err(XmlError::new(
2373 XML_ERR_INTERNAL_ERROR,
2374 "Maximum attribute-default amplification exceeded",
2375 0,
2376 0,
2377 ));
2378 }
2379 budget -= 1;
2380 doc.xml_set_prop(id, aname, v);
2381 }
2382 }
2383 }
2384 Ok(())
2385}
2386
2387#[cfg(test)]
2388mod chvalid_tests {
2389 use crate::xml_is_char;
2390 use std::path::PathBuf;
2391
2392 #[test]
2393 fn xml_is_char_matches_c_bmp_dump() {
2394 let mut p = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
2395 p.pop();
2396 p.pop();
2397 p.push("corpora");
2398 p.push("xmlIsChar-bmp.bin");
2399 if !p.exists() {
2400 return;
2401 }
2402 let dump = std::fs::read(&p).expect("corpora/xmlIsChar-bmp.bin");
2403 assert_eq!(dump.len(), 65536);
2404 for i in 0u32..=0xffff {
2405 let want = dump[i as usize] != 0;
2406 let got = xml_is_char(i);
2407 assert_eq!(got, want, "xml_is_char({i:#x}) = {got}, C dump = {want}");
2408 }
2409 }
2410}