1 /**
2 * XML.cpp
3 * This file is part of the YATE Project http://YATE.null.ro
4 *
5 * Yet Another Telephony Engine - a fully featured software PBX and IVR
6 * Copyright (C) 2004-2014 Null Team
7 *
8 * This software is distributed under multiple licenses;
9 * see the COPYING file in the main directory for licensing
10 * information for this specific distribution.
11 *
12 * This use of this software may be subject to additional restrictions.
13 * See the LEGAL file in the main directory for details.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 */
19
20 #include <yatexml.h>
21 #include <string.h>
22
23 using namespace TelEngine;
24
25
26 const String XmlElement::s_ns = "xmlns";
27 const String XmlElement::s_nsPrefix = "xmlns:";
28 static const String s_type("type");
29 static const String s_name("name");
30
31
32 // Return a replacement char for the given string
replace(const char * str,const XmlEscape * esc)33 char replace(const char* str, const XmlEscape* esc)
34 {
35 if (!str)
36 return 0;
37 if (esc) {
38 for (; esc->value; esc++)
39 if (!::strcmp(str,esc->value))
40 return esc->replace;
41 }
42 return 0;
43 }
44
45 // Return a replacement string for the given char
replace(char replace,const XmlEscape * esc)46 const char* replace(char replace, const XmlEscape* esc)
47 {
48 if (esc) {
49 for (; esc->value; esc++)
50 if (replace == esc->replace)
51 return esc->value;
52 }
53 return 0;
54 }
55
56 // XmlEscape a string or replace it if found in a list of restrictions
addAuth(String & buf,const String & comp,const String & value,bool esc,const String * auth)57 static inline void addAuth(String& buf, const String& comp, const String& value,
58 bool esc, const String* auth)
59 {
60 if (auth) {
61 for (; !auth->null(); auth++)
62 if (*auth == comp) {
63 buf << "***";
64 return;
65 }
66 }
67 if (esc)
68 XmlSaxParser::escape(buf,value);
69 else
70 buf << value;
71 }
72
73
74 /*
75 * XmlSaxParser
76 */
77 const TokenDict XmlSaxParser::s_errorString[] = {
78 {"No error", NoError},
79 {"Error", Unknown},
80 {"Not well formed", NotWellFormed},
81 {"I/O error", IOError},
82 {"Error parsing Element", ElementParse},
83 {"Failed to read Element name", ReadElementName},
84 {"Bad element name", InvalidElementName},
85 {"Error reading Attributes", ReadingAttributes},
86 {"Error reading end tag", ReadingEndTag},
87 {"Error parsing Comment", CommentParse},
88 {"Error parsing Declaration", DeclarationParse},
89 {"Error parsing Definition", DefinitionParse},
90 {"Error parsing CDATA", CDataParse},
91 {"Incomplete", Incomplete},
92 {"Invalid encoding", InvalidEncoding},
93 {"Unsupported encoding", UnsupportedEncoding},
94 {"Unsupported version", UnsupportedVersion},
95 {0,0}
96 };
97
98 const XmlEscape XmlSaxParser::s_escape[] = {
99 {"<", '<'},
100 {">", '>'},
101 {"&", '&'},
102 {""", '\"'},
103 {"'", '\''},
104 {0,0}
105 };
106
107
XmlSaxParser(const char * name)108 XmlSaxParser::XmlSaxParser(const char* name)
109 : m_offset(0), m_row(1), m_column(1), m_error(NoError),
110 m_parsed(""), m_unparsed(None)
111 {
112 debugName(name);
113 }
114
~XmlSaxParser()115 XmlSaxParser::~XmlSaxParser()
116 {
117 }
118
119 // Parse a given string
parse(const char * text)120 bool XmlSaxParser::parse(const char* text)
121 {
122 if (TelEngine::null(text))
123 return m_error == NoError;
124 #ifdef XDEBUG
125 String tmp;
126 m_parsed.dump(tmp," ");
127 if (tmp)
128 tmp = " parsed=" + tmp;
129 XDebug(this,DebugAll,"XmlSaxParser::parse(%s) unparsed=%u%s buf=%s [%p]",
130 text,unparsed(),tmp.safe(),m_buf.safe(),this);
131 #endif
132 char car;
133 setError(NoError);
134 String auxData;
135 m_buf << text;
136 if (m_buf.lenUtf8() == -1) {
137 //FIXME this should not be here in case we have a different encoding
138 DDebug(this,DebugNote,"Request to parse invalid utf-8 data [%p]",this);
139 return setError(Incomplete);
140 }
141 if (unparsed()) {
142 if (unparsed() != Text) {
143 if (!auxParse())
144 return false;
145 }
146 else
147 auxData = m_parsed;
148 resetParsed();
149 setUnparsed(None);
150 }
151 unsigned int len = 0;
152 while (m_buf.at(len) && !error()) {
153 car = m_buf.at(len);
154 if (car != '<' ) { // We have a new child check what it is
155 if (car == '>' || !checkDataChar(car)) {
156 Debug(this,DebugNote,"XML text contains unescaped '%c' character [%p]",
157 car,this);
158 return setError(Unknown);
159 }
160 len++; // Append xml Text
161 continue;
162 }
163 if (len > 0) {
164 auxData << m_buf.substr(0,len);
165 }
166 if (auxData.c_str()) { // We have an end of tag or another child is riseing
167 if (!processText(auxData))
168 return false;
169 m_buf = m_buf.substr(len);
170 len = 0;
171 auxData = "";
172 }
173 char auxCar = m_buf.at(1);
174 if (!auxCar)
175 return setError(Incomplete);
176 if (auxCar == '?') {
177 m_buf = m_buf.substr(2);
178 if (!parseInstruction())
179 return false;
180 continue;
181 }
182 if (auxCar == '!') {
183 m_buf = m_buf.substr(2);
184 if (!parseSpecial())
185 return false;
186 continue;
187 }
188 if (auxCar == '/') {
189 m_buf = m_buf.substr(2);
190 if (!parseEndTag())
191 return false;
192 continue;
193 }
194 // If we are here mens that we have a element
195 // process an xml element
196 m_buf = m_buf.substr(1);
197 if (!parseElement())
198 return false;
199 }
200 // Incomplete text
201 if ((unparsed() == None || unparsed() == Text) && (auxData || m_buf)) {
202 if (!auxData)
203 m_parsed.assign(m_buf);
204 else {
205 auxData << m_buf;
206 m_parsed.assign(auxData);
207 }
208 m_buf = "";
209 setUnparsed(Text);
210 return setError(Incomplete);
211 }
212 if (error()) {
213 DDebug(this,DebugNote,"Got error while parsing %s [%p]",getError(),this);
214 return false;
215 }
216 m_buf = "";
217 resetParsed();
218 setUnparsed(None);
219 return true;
220 }
221
222 // Process incomplete text
completeText()223 bool XmlSaxParser::completeText()
224 {
225 if (!completed() || unparsed() != Text || error() != Incomplete)
226 return error() == NoError;
227 String tmp = m_parsed;
228 return processText(tmp);
229 }
230
231 // Parse an unfinished xml object
auxParse()232 bool XmlSaxParser::auxParse()
233 {
234 switch (unparsed()) {
235 case Element:
236 return parseElement();
237 case CData:
238 return parseCData();
239 case Comment:
240 return parseComment();
241 case Declaration:
242 return parseDeclaration();
243 case Instruction:
244 return parseInstruction();
245 case EndTag:
246 return parseEndTag();
247 case Special:
248 return parseSpecial();
249 default:
250 return false;
251 }
252 }
253
254 // Set the error code and destroys a child if error code is not NoError
setError(Error error,XmlChild * child)255 bool XmlSaxParser::setError(Error error, XmlChild* child)
256 {
257 m_error = error;
258 if (child && error)
259 TelEngine::destruct(child);
260 return m_error == XmlSaxParser::NoError;
261 }
262
263 // Parse an endtag form the main buffer
parseEndTag()264 bool XmlSaxParser::parseEndTag()
265 {
266 bool aux = false;
267 String* name = extractName(aux);
268 // We don't check aux flag because we don't look for attributes here
269 if (!name) {
270 if (error() && error() == Incomplete)
271 setUnparsed(EndTag);
272 return false;
273 }
274 if (!aux || m_buf.at(0) == '/') { // The end tag has attributes or contains / char at the end of name
275 setError(ReadingEndTag);
276 Debug(this,DebugNote,"Got bad end tag </%s/> [%p]",name->c_str(),this);
277 setUnparsed(EndTag);
278 m_buf = *name + m_buf;
279 return false;
280 }
281 resetError();
282 endElement(*name);
283 if (error()) {
284 setUnparsed(EndTag);
285 m_buf = *name + ">";
286 TelEngine::destruct(name);
287 return false;
288 }
289 m_buf = m_buf.substr(1);
290 TelEngine::destruct(name);
291 return true;
292 }
293
294 // Parse an instruction form the main buffer
parseInstruction()295 bool XmlSaxParser::parseInstruction()
296 {
297 XDebug(this,DebugAll,"XmlSaxParser::parseInstruction() buf len=%u [%p]",m_buf.length(),this);
298 setUnparsed(Instruction);
299 if (!m_buf.c_str())
300 return setError(Incomplete);
301 // extract the name
302 String name;
303 char c;
304 int len = 0;
305 if (!m_parsed) {
306 bool nameComplete = false;
307 bool endDecl = false;
308 while (0 != (c = m_buf.at(len))) {
309 nameComplete = blank(c);
310 if (!nameComplete) {
311 // Check for instruction end: '?>'
312 if (c == '?') {
313 char next = m_buf.at(len + 1);
314 if (!next)
315 return setError(Incomplete);
316 if (next == '>') {
317 nameComplete = endDecl = true;
318 break;
319 }
320 }
321 if (checkNameCharacter(c)) {
322 len++;
323 continue;
324 }
325 Debug(this,DebugNote,"Instruction name contains bad character '%c' [%p]",c,this);
326 return setError(InvalidElementName);
327 }
328 // Blank found
329 if (len)
330 break;
331 Debug(this,DebugNote,"Instruction with empty name [%p]",this);
332 return setError(InvalidElementName);
333 }
334 if (!len) {
335 if (!endDecl)
336 return setError(Incomplete);
337 // Remove instruction end from buffer
338 m_buf = m_buf.substr(2);
339 Debug(this,DebugNote,"Instruction with empty name [%p]",this);
340 return setError(InvalidElementName);
341 }
342 if (!nameComplete)
343 return setError(Incomplete);
344 name = m_buf.substr(0,len);
345 m_buf = m_buf.substr(!endDecl ? len : len + 2);
346 if (name == YSTRING("xml")) {
347 if (!endDecl)
348 return parseDeclaration();
349 resetParsed();
350 resetError();
351 setUnparsed(None);
352 gotDeclaration(NamedList::empty());
353 return error() == NoError;
354 }
355 // Instruction name can't be xml case insensitive
356 if (name.length() == 3 && name.startsWith("xml",false,true)) {
357 Debug(this,DebugNote,"Instruction name '%s' reserved [%p]",name.c_str(),this);
358 return setError(InvalidElementName);
359 }
360 }
361 else {
362 name = m_parsed;
363 resetParsed();
364 }
365 // Retrieve instruction content
366 skipBlanks();
367 len = 0;
368 while (0 != (c = m_buf.at(len))) {
369 if (c != '?') {
370 if (c == 0x0c) {
371 setError(Unknown);
372 Debug(this,DebugNote,"Xml instruction with unaccepted character '%c' [%p]",
373 c,this);
374 return false;
375 }
376 len++;
377 continue;
378 }
379 char ch = m_buf.at(len + 1);
380 if (!ch)
381 break;
382 if (ch == '>') { // end of instruction
383 NamedString inst(name,m_buf.substr(0,len));
384 // Parsed instruction: remove instruction end from buffer and reset parsed
385 m_buf = m_buf.substr(len + 2);
386 resetParsed();
387 resetError();
388 setUnparsed(None);
389 gotProcessing(inst);
390 return error() == NoError;
391 }
392 len ++;
393 }
394 // If we are here mens that text has reach his bounds is an error or we need to receive more data
395 m_parsed.assign(name);
396 return setError(Incomplete);
397 }
398
399 // Parse a declaration form the main buffer
parseDeclaration()400 bool XmlSaxParser::parseDeclaration()
401 {
402 XDebug(this,DebugAll,"XmlSaxParser::parseDeclaration() buf len=%u [%p]",m_buf.length(),this);
403 setUnparsed(Declaration);
404 if (!m_buf.c_str())
405 return setError(Incomplete);
406 NamedList dc("xml");
407 if (m_parsed.count()) {
408 dc.copyParams(m_parsed);
409 resetParsed();
410 }
411 char c;
412 skipBlanks();
413 int len = 0;
414 while (m_buf.at(len)) {
415 c = m_buf.at(len);
416 if (c != '?') {
417 skipBlanks();
418 NamedString* s = getAttribute();
419 if (!s) {
420 if (error() == Incomplete)
421 m_parsed = dc;
422 return false;
423 }
424 len = 0;
425 if (dc.getParam(s->name())) {
426 Debug(this,DebugNote,"Duplicate attribute '%s' in declaration [%p]",
427 s->name().c_str(),this);
428 TelEngine::destruct(s);
429 return setError(DeclarationParse);
430 }
431 dc.addParam(s);
432 char ch = m_buf.at(len);
433 if (ch && !blank(ch) && ch != '?') {
434 Debug(this,DebugNote,"No blanks between attributes in declaration [%p]",this);
435 return setError(DeclarationParse);
436 }
437 skipBlanks();
438 continue;
439 }
440 if (!m_buf.at(++len))
441 break;
442 char ch = m_buf.at(len);
443 if (ch == '>') { // end of declaration
444 // Parsed declaration: remove declaration end from buffer and reset parsed
445 resetError();
446 resetParsed();
447 setUnparsed(None);
448 m_buf = m_buf.substr(len + 1);
449 gotDeclaration(dc);
450 return error() == NoError;
451 }
452 Debug(this,DebugNote,"Invalid declaration ending char '%c' [%p]",ch,this);
453 return setError(DeclarationParse);
454 }
455 m_parsed.copyParams(dc);
456 setError(Incomplete);
457 return false;
458 }
459
460 // Parse a CData section form the main buffer
parseCData()461 bool XmlSaxParser::parseCData()
462 {
463 if (!m_buf.c_str()) {
464 setUnparsed(CData);
465 setError(Incomplete);
466 return false;
467 }
468 String cdata = "";
469 if (m_parsed.c_str()) {
470 cdata = m_parsed;
471 resetParsed();
472 }
473 char c;
474 int len = 0;
475 while (m_buf.at(len)) {
476 c = m_buf.at(len);
477 if (c != ']') {
478 len ++;
479 continue;
480 }
481 if (m_buf.substr(++len,2) == "]>") { // End of CData section
482 cdata += m_buf.substr(0,len - 1);
483 resetError();
484 gotCdata(cdata);
485 resetParsed();
486 if (error())
487 return false;
488 m_buf = m_buf.substr(len + 2);
489 return true;
490 }
491 }
492 cdata += m_buf;
493 m_buf = "";
494 setUnparsed(CData);
495 int length = cdata.length();
496 m_buf << cdata.substr(length - 2);
497 if (length > 1)
498 m_parsed.assign(cdata.substr(0,length - 2));
499 setError(Incomplete);
500 return false;
501 }
502
503 // Helper method to classify the Xml objects starting with "<!" sequence
parseSpecial()504 bool XmlSaxParser::parseSpecial()
505 {
506 if (m_buf.length() < 2) {
507 setUnparsed(Special);
508 return setError(Incomplete);
509 }
510 if (m_buf.startsWith("--")) {
511 m_buf = m_buf.substr(2);
512 if (!parseComment())
513 return false;
514 return true;
515 }
516 if (m_buf.length() < 7) {
517 setUnparsed(Special);
518 return setError(Incomplete);
519 }
520 if (m_buf.startsWith("[CDATA[")) {
521 m_buf = m_buf.substr(7);
522 if (!parseCData())
523 return false;
524 return true;
525 }
526 if (m_buf.startsWith("DOCTYPE")) {
527 m_buf = m_buf.substr(7);
528 if (!parseDoctype())
529 return false;
530 return true;
531 }
532 Debug(this,DebugNote,"Can't parse unknown special starting with '%s' [%p]",
533 m_buf.c_str(),this);
534 setError(Unknown);
535 return false;
536 }
537
538
539 // Extract from the given buffer an comment and check if is valid
parseComment()540 bool XmlSaxParser::parseComment()
541 {
542 String comment;
543 if (m_parsed.c_str()) {
544 comment = m_parsed;
545 resetParsed();
546 }
547 char c;
548 int len = 0;
549 while (m_buf.at(len)) {
550 c = m_buf.at(len);
551 if (c != '-') {
552 if (c == 0x0c) {
553 Debug(this,DebugNote,"Xml comment with unaccepted character '%c' [%p]",c,this);
554 return setError(NotWellFormed);
555 }
556 len++;
557 continue;
558 }
559 if (m_buf.at(len + 1) == '-' && m_buf.at(len + 2) == '>') { // End of comment
560 comment << m_buf.substr(0,len);
561 m_buf = m_buf.substr(len + 3);
562 #ifdef DEBUG
563 if (comment.at(0) == '-' || comment.at(comment.length() - 1) == '-')
564 DDebug(this,DebugInfo,"Comment starts or ends with '-' character [%p]",this);
565 if (comment.find("--") >= 0)
566 DDebug(this,DebugInfo,"Comment contains '--' char sequence [%p]",this);
567 #endif
568 gotComment(comment);
569 resetParsed();
570 // The comment can apear anywhere sow SaxParser never
571 // sets an error when receive a comment
572 return true;
573 }
574 len++;
575 }
576 // If we are here we haven't detect the end of comment
577 comment << m_buf;
578 int length = comment.length();
579 // Keep the last 2 charaters in buffer because if the input buffer ends
580 // between "--" and ">"
581 m_buf = comment.substr(length - 2);
582 setUnparsed(Comment);
583 if (length > 1)
584 m_parsed.assign(comment.substr(0,length - 2));
585 return setError(Incomplete);
586 }
587
588 // Parse an element form the main buffer
parseElement()589 bool XmlSaxParser::parseElement()
590 {
591 XDebug(this,DebugAll,"XmlSaxParser::parseElement() buf len=%u [%p]",m_buf.length(),this);
592 if (!m_buf.c_str()) {
593 setUnparsed(Element);
594 return setError(Incomplete);
595 }
596 bool empty = false;
597 if (!m_parsed.c_str()) {
598 String* name = extractName(empty);
599 if (!name) {
600 if (error() == Incomplete)
601 setUnparsed(Element);
602 return false;
603 }
604 #ifdef XML_STRICT
605 // http://www.w3.org/TR/REC-xml/
606 // Names starting with 'xml' (case insensitive) are reserved
607 if (name->startsWith("xml",false,true)) {
608 Debug(this,DebugNote,"Element tag starts with 'xml' [%p]",this);
609 TelEngine::destruct(name);
610 return setError(ReadElementName);
611 }
612 #endif
613 m_parsed.assign(*name);
614 TelEngine::destruct(name);
615 }
616 if (empty) { // empty flag means that the element does not have attributes
617 // check if the element is empty
618 bool aux = m_buf.at(0) == '/';
619 if (!processElement(m_parsed,aux))
620 return false;
621 if (aux)
622 m_buf = m_buf.substr(2); // go back where we were
623 else
624 m_buf = m_buf.substr(1); // go back where we were
625 return true;
626 }
627 char c;
628 skipBlanks();
629 int len = 0;
630 while (m_buf.at(len)) {
631 c = m_buf.at(len);
632 if (c == '/' || c == '>') { // end of element declaration
633 if (c == '>') {
634 if (!processElement(m_parsed,false))
635 return false;
636 m_buf = m_buf.substr(1);
637 return true;
638 }
639 if (!m_buf.at(++len))
640 break;
641 char ch = m_buf.at(len);
642 if (ch != '>') {
643 Debug(this,DebugNote,"Element attribute name contains '/' character [%p]",this);
644 return setError(ReadingAttributes);
645 }
646 if (!processElement(m_parsed,true))
647 return false;
648 m_buf = m_buf.substr(len + 1);
649 return true;
650 }
651 NamedString* ns = getAttribute();
652 if (!ns) { // Attribute is invalid
653 if (error() == Incomplete)
654 break;
655 return false;
656 }
657 if (m_parsed.getParam(ns->name())) {
658 Debug(this,DebugNote,"Duplicate attribute '%s' [%p]",ns->name().c_str(),this);
659 TelEngine::destruct(ns);
660 return setError(NotWellFormed);
661 }
662 XDebug(this,DebugAll,"Parser adding attribute %s='%s' to '%s' [%p]",
663 ns->name().c_str(),ns->c_str(),m_parsed.c_str(),this);
664 m_parsed.setParam(ns);
665 char ch = m_buf.at(len);
666 if (ch && !blank(ch) && (ch != '/' && ch != '>')) {
667 Debug(this,DebugNote,"Element without blanks between attributes [%p]",this);
668 return setError(NotWellFormed);
669 }
670 skipBlanks();
671 }
672 setUnparsed(Element);
673 return setError(Incomplete);
674 }
675
676 // Parse a doctype form the main buffer
parseDoctype()677 bool XmlSaxParser::parseDoctype()
678 {
679 if (!m_buf.c_str()) {
680 setUnparsed(Doctype);
681 setError(Incomplete);
682 return false;
683 }
684 unsigned int len = 0;
685 skipBlanks();
686 while (m_buf.at(len) && !blank(m_buf.at(len)))
687 len++;
688 // Use a while() to break to the end
689 while (m_buf.at(len)) {
690 while (m_buf.at(len) && blank(m_buf.at(len)))
691 len++;
692 if (len >= m_buf.length())
693 break;
694 if (m_buf[len++] == '[') {
695 while (len < m_buf.length()) {
696 if (m_buf[len] != ']') {
697 len ++;
698 continue;
699 }
700 if (m_buf.at(++len) != '>')
701 continue;
702 gotDoctype(m_buf.substr(0,len));
703 resetParsed();
704 m_buf = m_buf.substr(len + 1);
705 return true;
706 }
707 break;
708 }
709 while (len < m_buf.length()) {
710 if (m_buf[len] != '>') {
711 len++;
712 continue;
713 }
714 gotDoctype(m_buf.substr(0,len));
715 resetParsed();
716 m_buf = m_buf.substr(len + 1);
717 return true;
718 }
719 break;
720 }
721 setUnparsed(Doctype);
722 return setError(Incomplete);
723 }
724
725 // Extract the name of tag
extractName(bool & empty)726 String* XmlSaxParser::extractName(bool& empty)
727 {
728 skipBlanks();
729 unsigned int len = 0;
730 bool ok = false;
731 empty = false;
732 while (len < m_buf.length()) {
733 char c = m_buf[len];
734 if (blank(c)) {
735 if (checkFirstNameCharacter(m_buf[0])) {
736 ok = true;
737 break;
738 }
739 Debug(this,DebugNote,"Element tag starting with invalid char %c [%p]",
740 m_buf[0],this);
741 setError(ReadElementName);
742 return 0;
743 }
744 if (c == '/' || c == '>') { // end of element declaration
745 if (c == '>') {
746 if (checkFirstNameCharacter(m_buf[0])) {
747 empty = true;
748 ok = true;
749 break;
750 }
751 Debug(this,DebugNote,"Element tag starting with invalid char %c [%p]",
752 m_buf[0],this);
753 setError(ReadElementName);
754 return 0;
755 }
756 char ch = m_buf.at(len + 1);
757 if (!ch)
758 break;
759 if (ch != '>') {
760 Debug(this,DebugNote,"Element tag contains '/' character [%p]",this);
761 setError(ReadElementName);
762 return 0;
763 }
764 if (checkFirstNameCharacter(m_buf[0])) {
765 empty = true;
766 ok = true;
767 break;
768 }
769 Debug(this,DebugNote,"Element tag starting with invalid char %c [%p]",
770 m_buf[0],this);
771 setError(ReadElementName);
772 return 0;
773 }
774 if (checkNameCharacter(c))
775 len++;
776 else {
777 Debug(this,DebugNote,"Element tag contains invalid char %c [%p]",c,this);
778 setError(ReadElementName);
779 return 0;
780 }
781 }
782 if (ok) {
783 String* name = new String(m_buf.substr(0,len));
784 m_buf = m_buf.substr(len);
785 if (!empty) {
786 skipBlanks();
787 empty = (m_buf && m_buf[0] == '>') ||
788 (m_buf.length() > 1 && m_buf[0] == '/' && m_buf[1] == '>');
789 }
790 return name;
791 }
792 setError(Incomplete);
793 return 0;
794 }
795
796 // Extract an attribute
getAttribute()797 NamedString* XmlSaxParser::getAttribute()
798 {
799 String name = "";
800 skipBlanks();
801 char c,sep = 0;
802 unsigned int len = 0;
803
804 while (len < m_buf.length()) { // Circle until we find attribute value startup character (["]|['])
805 c = m_buf[len];
806 if (blank(c) || c == '=') {
807 if (!name.c_str())
808 name = m_buf.substr(0,len);
809 len++;
810 continue;
811 }
812 if (!name.c_str()) {
813 if (!checkNameCharacter(c)) {
814 Debug(this,DebugNote,"Attribute name contains %c character [%p]",c,this);
815 setError(ReadingAttributes);
816 return 0;
817 }
818 len++;
819 continue;
820 }
821 if (c != '\'' && c != '\"') {
822 Debug(this,DebugNote,"Unenclosed attribute value [%p]",this);
823 setError(ReadingAttributes);
824 return 0;
825 }
826 sep = c;
827 break;
828 }
829
830 if (!sep) {
831 setError(Incomplete);
832 return 0;
833 }
834 if (!checkFirstNameCharacter(name[0])) {
835 Debug(this,DebugNote,"Attribute name starting with bad character %c [%p]",
836 name.at(0),this);
837 setError(ReadingAttributes);
838 return 0;
839 }
840 int pos = ++len;
841
842 while (len < m_buf.length()) {
843 c = m_buf[len];
844 if (c != sep && !badCharacter(c)) {
845 len ++;
846 continue;
847 }
848 if (badCharacter(c)) {
849 Debug(this,DebugNote,"Attribute value with unescaped character '%c' [%p]",
850 c,this);
851 setError(ReadingAttributes);
852 return 0;
853 }
854 NamedString* ns = new NamedString(name,m_buf.substr(pos,len - pos));
855 m_buf = m_buf.substr(len + 1);
856 // End of attribute value
857 unEscape(*ns);
858 if (error()) {
859 TelEngine::destruct(ns);
860 return 0;
861 }
862 return ns;
863 }
864
865 setError(Incomplete);
866 return 0;
867 }
868
869 // Reset this parser
reset()870 void XmlSaxParser::reset()
871 {
872 m_offset = 0;
873 m_row = 1;
874 m_column = 1;
875 m_error = NoError;
876 m_buf.clear();
877 resetParsed();
878 m_unparsed = None;
879 }
880
881 // Verify if the given character is in the range allowed
checkFirstNameCharacter(unsigned char ch)882 bool XmlSaxParser::checkFirstNameCharacter(unsigned char ch)
883 {
884 return ch == ':' || (ch >= 'A' && ch <= 'Z') || ch == '_' || (ch >= 'a' && ch <= 'z')
885 || (ch >= 0xc0 && ch <= 0xd6) || (ch >= 0xd8 && ch <= 0xf6) || (ch >= 0xf8);
886 }
887
888 // Check if the given character is in the range allowed for an xml char
checkDataChar(unsigned char c)889 bool XmlSaxParser::checkDataChar(unsigned char c)
890 {
891 return c == 0x9 || c == 0xA || c == 0xD || (c >= 0x20);
892 }
893
894 // Verify if the given character is in the range allowed for a xml name
checkNameCharacter(unsigned char ch)895 bool XmlSaxParser::checkNameCharacter(unsigned char ch)
896 {
897 return checkFirstNameCharacter(ch) || ch == '-' || ch == '.' || (ch >= '0' && ch <= '9')
898 || ch == 0xB7;
899 }
900
901 // Remove blank characters from the beginning of the buffer
skipBlanks()902 void XmlSaxParser::skipBlanks()
903 {
904 unsigned int len = 0;
905 while (len < m_buf.length() && blank(m_buf[len]))
906 len++;
907 if (len != 0)
908 m_buf = m_buf.substr(len);
909 }
910
911 // Obtain a char from an ascii decimal char declaration
getDec(String & dec)912 inline unsigned char getDec(String& dec)
913 {
914 if (dec.length() > 6) {
915 DDebug(DebugNote,"Decimal number '%s' too long",dec.c_str());
916 return 0;
917 }
918 int num = dec.substr(2,dec.length() - 3).toInteger(-1);
919 if (num > 0 && num < 256)
920 return num;
921 DDebug(DebugNote,"Invalid decimal number '%s'",dec.c_str());
922 return 0;
923 }
924
925 // Unescape the given text
unEscape(String & text)926 void XmlSaxParser::unEscape(String& text)
927 {
928 const char* str = text.c_str();
929 if (!str)
930 return;
931 String buf;
932 String aux = "&";
933 unsigned int len = 0;
934 int found = -1;
935 while (str[len]) {
936 if (str[len] == '&' && found < 0) {
937 found = len++;
938 continue;
939 }
940 if (found < 0) {
941 len++;
942 continue;
943 }
944 if (str[len] == '&') {
945 Debug(this,DebugNote,"Unescape. Duplicate '&' in expression [%p]",this);
946 setError(NotWellFormed);
947 return;
948 }
949 if (str[len] != ';')
950 len++;
951 else { // We have a candidate for escaping
952 len += 1; // Append ';' character
953 String aux(str + found,len - found);
954 char re = 0;
955 if (aux.startsWith("&#")) {
956 if (aux.at(2) == 'x') {
957 if (aux.length() > 4 && aux.length() <= 12) {
958 int esc = aux.substr(3,aux.length() - 4).toInteger(-1,16);
959 if (esc != -1) {
960 UChar uc(esc);
961 buf.append(str,found) << uc.c_str();
962 str += len;
963 len = 0;
964 found = -1;
965 continue;
966 }
967 }
968 } else
969 re = getDec(aux);
970 }
971 if (re == '&') {
972 if (str[len] == '#') {
973 aux = String(str + len,4);
974 if (aux == "#60;") {
975 re = '<';
976 len += 4;
977 }
978 if (aux == "#38;") {
979 re = '&';
980 len += 4;
981 }
982 }
983 }
984 else if (!re)
985 re = replace(aux,s_escape);
986 if (re) { // We have an valid escape character
987 buf << String(str,found) << re;
988 str += len;
989 len = 0;
990 found = -1;
991 }
992 else {
993 Debug(this,DebugNote,"Unescape. No replacement found for '%s' [%p]",
994 String(str + found,len - found).c_str(),this);
995 setError(NotWellFormed);
996 return;
997 }
998 }
999 }
1000 if (found >= 0) {
1001 Debug(this,DebugNote,"Unescape. Unexpected end of expression [%p]",this);
1002 setError(NotWellFormed);
1003 return;
1004 }
1005 if (len) {
1006 if (str != text.c_str()) {
1007 buf << String(str,len);
1008 text = buf;
1009 }
1010 }
1011 else
1012 text = buf;
1013 }
1014
1015 // Check if a given string is a valid xml tag name
validTag(const String & buf)1016 bool XmlSaxParser::validTag(const String& buf)
1017 {
1018 if (!(buf && checkFirstNameCharacter(buf[0])))
1019 return false;
1020 for (unsigned int i = 1; i < buf.length(); i++)
1021 if (!checkNameCharacter(buf[i]))
1022 return false;
1023 return true;
1024 }
1025
1026 // XmlEscape the given text
escape(String & buf,const String & text)1027 void XmlSaxParser::escape(String& buf, const String& text)
1028 {
1029 const char* str = text.c_str();
1030 if (!str)
1031 return;
1032 char c;
1033 while ((c = *str++)) {
1034 const char* rep = replace(c,XmlSaxParser::s_escape);
1035 if (!rep) {
1036 buf += c;
1037 continue;
1038 }
1039 buf += rep;
1040 }
1041 }
1042
1043 // Calls gotElement(). Reset parsed if ok
processElement(NamedList & list,bool empty)1044 bool XmlSaxParser::processElement(NamedList& list, bool empty)
1045 {
1046 gotElement(list,empty);
1047 if (error() == XmlSaxParser::NoError) {
1048 resetParsed();
1049 return true;
1050 }
1051 return false;
1052 }
1053
1054 // Calls gotText() and reset parsed on success
processText(String & text)1055 bool XmlSaxParser::processText(String& text)
1056 {
1057 resetError();
1058 unEscape(text);
1059 if (!error())
1060 gotText(text);
1061 else
1062 setUnparsed(Text);
1063 if (!error()) {
1064 resetParsed();
1065 setUnparsed(None);
1066 }
1067 return error() == NoError;
1068 }
1069
1070
1071 /*
1072 * XmlDomPareser
1073 */
XmlDomParser(const char * name,bool fragment)1074 XmlDomParser::XmlDomParser(const char* name, bool fragment)
1075 : XmlSaxParser(name),
1076 m_current(0), m_data(0), m_ownData(true)
1077 {
1078 if (fragment)
1079 m_data = new XmlFragment();
1080 else
1081 m_data = new XmlDocument();
1082 }
1083
XmlDomParser(XmlParent * fragment,bool takeOwnership)1084 XmlDomParser::XmlDomParser(XmlParent* fragment, bool takeOwnership)
1085 : m_current(0), m_data(0), m_ownData(takeOwnership)
1086 {
1087 m_data = fragment;
1088 }
1089
~XmlDomParser()1090 XmlDomParser::~XmlDomParser()
1091 {
1092 if (m_ownData) {
1093 reset();
1094 if (m_data)
1095 delete m_data;
1096 }
1097 }
1098
1099 // Create a new xml comment and append it in the xml three
gotComment(const String & text)1100 void XmlDomParser::gotComment(const String& text)
1101 {
1102 XmlComment* com = new XmlComment(text);
1103 if (m_current)
1104 setError(m_current->addChild(com),com);
1105 else
1106 setError(m_data->addChild(com),com);
1107
1108 }
1109
1110 // Append a new xml doctype to main xml parent
gotDoctype(const String & doc)1111 void XmlDomParser::gotDoctype(const String& doc)
1112 {
1113 m_data->addChild(new XmlDoctype(doc));
1114 }
1115
1116 // TODO implement it see what to do
gotProcessing(const NamedString & instr)1117 void XmlDomParser::gotProcessing(const NamedString& instr)
1118 {
1119 DDebug(this,DebugStub,"gotProcessing(%s=%s) not implemented [%p]",
1120 instr.name().c_str(),instr.safe(),this);
1121 }
1122
1123 // Create a new xml declaration, verifies the version and encoding
1124 // and append it in the main xml parent
gotDeclaration(const NamedList & decl)1125 void XmlDomParser::gotDeclaration(const NamedList& decl)
1126 {
1127 if (m_current) {
1128 setError(DeclarationParse);
1129 Debug(this,DebugNote,"Received declaration inside element bounds [%p]",this);
1130 return;
1131 }
1132 Error err = NoError;
1133 while (true) {
1134 String* version = decl.getParam("version");
1135 if (version) {
1136 int ver = version->substr(0,version->find('.')).toInteger();
1137 if (ver != 1) {
1138 err = UnsupportedVersion;
1139 break;
1140 }
1141 }
1142 String* enc = decl.getParam("encoding");
1143 if (enc && !(*enc &= "utf-8")) {
1144 err = UnsupportedEncoding;
1145 break;
1146 }
1147 break;
1148 }
1149 if (err == NoError) {
1150 XmlDeclaration* dec = new XmlDeclaration(decl);
1151 setError(m_data->addChild(dec),dec);
1152 }
1153 else {
1154 setError(err);
1155 Debug(this,DebugNote,
1156 "Received unacceptable declaration version='%s' encoding='%s' error '%s' [%p]",
1157 decl.getValue("version"),decl.getValue("encoding"),getError(),this);
1158 }
1159 }
1160
1161 // Create a new xml text and append it in the xml tree
gotText(const String & text)1162 void XmlDomParser::gotText(const String& text)
1163 {
1164 XmlText* tet = new XmlText(text);
1165 if (m_current)
1166 m_current->addChild(tet);
1167 else
1168 setError(m_data->addChild(tet),tet);
1169 }
1170
1171 // Create a new xml Cdata and append it in the xml tree
gotCdata(const String & data)1172 void XmlDomParser::gotCdata(const String& data)
1173 {
1174 XmlCData* cdata = new XmlCData(data);
1175 if (!m_current) {
1176 if (m_data->document()) {
1177 Debug(this,DebugNote,"Document got CDATA outside element [%p]",this);
1178 setError(NotWellFormed);
1179 TelEngine::destruct(cdata);
1180 return;
1181 }
1182 setError(m_data->addChild(cdata),cdata);
1183 return;
1184 }
1185 setError(m_current->addChild(cdata),cdata);
1186 }
1187
1188 // Create a new xml element and append it in the xml tree
gotElement(const NamedList & elem,bool empty)1189 void XmlDomParser::gotElement(const NamedList& elem, bool empty)
1190 {
1191 XmlElement* element = 0;
1192 if (!m_current) {
1193 // If we don't have curent element menns that the main fragment
1194 // should hold it
1195 element = new XmlElement(elem,empty);
1196 setError(m_data->addChild(element),element);
1197 if (!empty && error() == XmlSaxParser::NoError)
1198 m_current = element;
1199 }
1200 else {
1201 if (empty) {
1202 element = new XmlElement(elem,empty);
1203 setError(m_current->addChild(element),element);
1204 }
1205 else {
1206 element = new XmlElement(elem,empty,m_current);
1207 setError(m_current->addChild(element),element);
1208 if (error() == XmlSaxParser::NoError)
1209 m_current = element;
1210 }
1211 }
1212 }
1213
1214 // Verify if is the closeing tag for the current element
1215 // Complete th current element and make current the current parent
endElement(const String & name)1216 void XmlDomParser::endElement(const String& name)
1217 {
1218 if (!m_current) {
1219 setError(ReadingEndTag);
1220 Debug(this,DebugNote,"Unexpected element end tag %s [%p]",name.c_str(),this);
1221 return;
1222 }
1223 if (m_current->getName() != name) {
1224 setError(ReadingEndTag);
1225 Debug(this,DebugNote,
1226 "Received end element for %s, but the expected one is for %s [%p]",
1227 name.c_str(),m_current->getName().c_str(),this);
1228 return;
1229 }
1230 m_current->setCompleted();
1231 XDebug(this,DebugInfo,"End element for %s [%p]",m_current->getName().c_str(),this);
1232 m_current = static_cast<XmlElement*>(m_current->getParent());
1233 }
1234
1235 // Reset this parser
reset()1236 void XmlDomParser::reset()
1237 {
1238 m_data->reset();
1239 m_current = 0;
1240 XmlSaxParser::reset();
1241 }
1242
1243
1244 /*
1245 * XmlDeclaration
1246 */
1247 // Create a new XmlDeclaration from version and encoding
XmlDeclaration(const char * version,const char * enc)1248 XmlDeclaration::XmlDeclaration(const char* version, const char* enc)
1249 : m_declaration("")
1250 {
1251 XDebug(DebugAll,"XmlDeclaration::XmlDeclaration(%s,%s) [%p]",version,enc,this);
1252 if (!TelEngine::null(version))
1253 m_declaration.addParam("version",version);
1254 if (!TelEngine::null(enc))
1255 m_declaration.addParam("encoding",enc);
1256 }
1257
1258 // Constructor
XmlDeclaration(const NamedList & decl)1259 XmlDeclaration::XmlDeclaration(const NamedList& decl)
1260 : m_declaration(decl)
1261 {
1262 XDebug(DebugAll,"XmlDeclaration::XmlDeclaration(%s) [%p]",m_declaration.c_str(),this);
1263 }
1264
1265 // Copy Constructor
XmlDeclaration(const XmlDeclaration & decl)1266 XmlDeclaration::XmlDeclaration(const XmlDeclaration& decl)
1267 : m_declaration(decl.getDec())
1268 {
1269 }
1270
1271 // Destructor
~XmlDeclaration()1272 XmlDeclaration::~XmlDeclaration()
1273 {
1274 XDebug(DebugAll,"XmlDeclaration::~XmlDeclaration() ( %s| %p )",
1275 m_declaration.c_str(),this);
1276 }
1277
1278 // Create a String from this Xml Declaration
toString(String & dump,bool esc) const1279 void XmlDeclaration::toString(String& dump, bool esc) const
1280 {
1281 dump << "<?" << "xml";
1282 int n = m_declaration.count();
1283 for (int i = 0;i < n;i ++) {
1284 NamedString* ns = m_declaration.getParam(i);
1285 if (!ns)
1286 continue;
1287 dump += " ";
1288 dump += ns->name();
1289 dump << "=\"";
1290 if (esc)
1291 XmlSaxParser::escape(dump,*ns);
1292 else
1293 dump += *ns;
1294 dump << "\"";
1295 }
1296 dump << "?>";
1297 }
1298
1299
1300 /*
1301 * XmlFragment
1302 */
1303 // Constructor
XmlFragment()1304 XmlFragment::XmlFragment()
1305 : m_list()
1306 {
1307 XDebug(DebugAll,"XmlFragment::XmlFragment() ( %p )",this);
1308 }
1309
1310 // Copy Constructor
XmlFragment(const XmlFragment & orig)1311 XmlFragment::XmlFragment(const XmlFragment& orig)
1312 {
1313 copy(orig);
1314 }
1315
1316 // Destructor
~XmlFragment()1317 XmlFragment::~XmlFragment()
1318 {
1319 m_list.clear();
1320 XDebug(DebugAll,"XmlFragment::~XmlFragment() ( %p )",this);
1321 }
1322
1323 // Reset. Clear children list
reset()1324 void XmlFragment::reset()
1325 {
1326 m_list.clear();
1327 }
1328
1329 // Append a new child
addChild(XmlChild * child)1330 XmlSaxParser::Error XmlFragment::addChild(XmlChild* child)
1331 {
1332 if (child)
1333 m_list.append(child);
1334 return XmlSaxParser::NoError;
1335 }
1336
1337 // Remove the first XmlElement from list and returns it if completed
popElement()1338 XmlElement* XmlFragment::popElement()
1339 {
1340 for (ObjList* o = m_list.skipNull(); o; o = o->skipNext()) {
1341 XmlChild* c = static_cast<XmlChild*>(o->get());
1342 XmlElement* x = c->xmlElement();
1343 if (x) {
1344 if (x->completed()) {
1345 o->remove(false);
1346 return x;
1347 }
1348 return 0;
1349 }
1350 }
1351 return 0;
1352 }
1353
1354 // Remove a child
removeChild(XmlChild * child,bool delObj)1355 XmlChild* XmlFragment::removeChild(XmlChild* child, bool delObj)
1356 {
1357 XmlChild* ch = static_cast<XmlChild*>(m_list.remove(child,delObj));
1358 if (ch && ch->xmlElement())
1359 ch->xmlElement()->setParent(0);
1360 return ch;
1361 }
1362
1363 // Copy other fragment into this one
copy(const XmlFragment & other,XmlParent * parent)1364 void XmlFragment::copy(const XmlFragment& other, XmlParent* parent)
1365 {
1366 for (ObjList* o = other.getChildren().skipNull(); o; o = o->skipNext()) {
1367 XmlChild* ch = static_cast<XmlChild*>(o->get());
1368 if (ch->xmlElement())
1369 ch = new XmlElement(*(ch->xmlElement()));
1370 else if (ch->xmlCData())
1371 ch = new XmlCData(*(ch->xmlCData()));
1372 else if (ch->xmlText())
1373 ch = new XmlText(*(ch->xmlText()));
1374 else if (ch->xmlComment())
1375 ch = new XmlComment(*(ch->xmlComment()));
1376 else if (ch->xmlDeclaration())
1377 ch = new XmlDeclaration(*(ch->xmlDeclaration()));
1378 else if (ch->xmlDoctype())
1379 ch = new XmlDoctype(*(ch->xmlDoctype()));
1380 else
1381 continue;
1382 ch->setParent(parent);
1383 addChild(ch);
1384 }
1385 }
1386
1387 // Create a String from this XmlFragment
toString(String & dump,bool escape,const String & indent,const String & origIndent,bool completeOnly,const String * auth,const XmlElement * parent) const1388 void XmlFragment::toString(String& dump, bool escape, const String& indent,
1389 const String& origIndent, bool completeOnly, const String* auth,
1390 const XmlElement* parent) const
1391 {
1392 ObjList* ob = m_list.skipNull();
1393 if (!ob)
1394 return;
1395 ObjList buffers;
1396 for (;ob;ob = ob->skipNext()) {
1397 String* s = new String;
1398 XmlChild* obj = static_cast<XmlChild*>(ob->get());
1399 if (obj->xmlElement())
1400 obj->xmlElement()->toString(*s,escape,indent,origIndent,completeOnly,auth);
1401 else if (obj->xmlText())
1402 obj->xmlText()->toString(*s,escape,indent,auth,parent);
1403 else if (obj->xmlCData())
1404 obj->xmlCData()->toString(*s,indent);
1405 else if (obj->xmlComment())
1406 obj->xmlComment()->toString(*s,indent);
1407 else if (obj->xmlDeclaration())
1408 obj->xmlDeclaration()->toString(*s,escape);
1409 else if (obj->xmlDoctype())
1410 obj->xmlDoctype()->toString(*s,origIndent);
1411 else
1412 Debug(DebugStub,"XmlFragment::toString() unhandled element type!");
1413 if (!TelEngine::null(s))
1414 buffers.append(s);
1415 else
1416 TelEngine::destruct(s);
1417 }
1418 dump.append(buffers);
1419 }
1420
1421 // Find a completed xml element in a list
findElement(ObjList * list,const String * name,const String * ns,bool noPrefix)1422 XmlElement* XmlFragment::findElement(ObjList* list, const String* name, const String* ns,
1423 bool noPrefix)
1424 {
1425 XmlElement* e = 0;
1426 for (; list; list = list->skipNext()) {
1427 e = (static_cast<XmlChild*>(list->get()))->xmlElement();
1428 if (!(e && e->completed()))
1429 continue;
1430 if (name || ns) {
1431 if (!ns) {
1432 if (noPrefix) {
1433 if (*name == e->unprefixedTag())
1434 break;
1435 }
1436 else if (*name == e->toString())
1437 break;
1438 }
1439 else if (name) {
1440 const String* t = 0;
1441 const String* n = 0;
1442 if (e->getTag(t,n) && *t == *name && n && *n == *ns)
1443 break;
1444 }
1445 else {
1446 const String* n = e->xmlns();
1447 if (n && *n == *ns)
1448 break;
1449 }
1450 }
1451 else
1452 break;
1453 e = 0;
1454 }
1455 return e;
1456 }
1457
1458 // Replaces all ${paramname} in fragment's children with the corresponding parameters
replaceParams(const NamedList & params)1459 void XmlFragment::replaceParams(const NamedList& params)
1460 {
1461 for (ObjList* o = m_list.skipNull(); o; o = o->skipNext())
1462 static_cast<XmlChild*>(o->get())->replaceParams(params);
1463 }
1464
1465
1466 /*
1467 * XmlDocument
1468 */
1469 // Constructor
XmlDocument()1470 XmlDocument::XmlDocument()
1471 : m_root(0)
1472 {
1473
1474 }
1475
1476 // Destructor
~XmlDocument()1477 XmlDocument::~XmlDocument()
1478 {
1479 reset();
1480 }
1481
1482 // Append a new child to this document
1483 // Set the root to an XML element if not already set. If we already have a completed root
1484 // the element will be added to the root, otherwise an error will be returned.
1485 // If we don't have a root non xml elements (other then text) will be added the list
1486 // of elements before root
addChild(XmlChild * child)1487 XmlSaxParser::Error XmlDocument::addChild(XmlChild* child)
1488 {
1489 if (!child)
1490 return XmlSaxParser::NoError;
1491
1492 XmlElement* element = child->xmlElement();
1493 if (!m_root) {
1494 if (element) {
1495 m_root = element;
1496 return XmlSaxParser::NoError;
1497 }
1498 XmlDeclaration* decl = child->xmlDeclaration();
1499 if (decl && declaration()) {
1500 DDebug(DebugNote,"XmlDocument. Request to add duplicate declaration [%p]",this);
1501 return XmlSaxParser::NotWellFormed;
1502 }
1503 // Text outside root: ignore empty, raise error otherwise
1504 XmlText* text = child->xmlText();
1505 if (text) {
1506 if (text->onlySpaces()) {
1507 m_beforeRoot.addChild(text);
1508 return XmlSaxParser::NoError;
1509 }
1510 Debug(DebugNote,"XmlDocument. Got text outside element [%p]",this);
1511 return XmlSaxParser::NotWellFormed;
1512 }
1513 return m_beforeRoot.addChild(child);
1514 }
1515 // We have a root
1516 if (element) {
1517 if (m_root->completed())
1518 return m_root->addChild(child);
1519 DDebug(DebugStub,"XmlDocument. Request to add xml element child to incomplete root [%p]",this);
1520 return XmlSaxParser::NotWellFormed;
1521 }
1522 if ((child->xmlText() && child->xmlText()->onlySpaces()) || child->xmlComment())
1523 return m_afterRoot.addChild(child);
1524 // TODO: check what xml we can add after the root or if we can add
1525 // anything after an incomplete root
1526 Debug(DebugStub,"XmlDocument. Request to add non element while having a root [%p]",this);
1527 return XmlSaxParser::NotWellFormed;
1528 }
1529
1530 // Retrieve the document declaration
declaration() const1531 XmlDeclaration* XmlDocument::declaration() const
1532 {
1533 for (ObjList* o = m_beforeRoot.getChildren().skipNull(); o; o = o->skipNext()) {
1534 XmlDeclaration* d = (static_cast<XmlChild*>(o->get()))->xmlDeclaration();
1535 if (d)
1536 return d;
1537 }
1538 return 0;
1539 }
1540
1541 // Obtain root element completed ot not
root(bool completed) const1542 XmlElement* XmlDocument::root(bool completed) const
1543 {
1544 return (m_root && (m_root->completed() || !completed)) ? m_root : 0;
1545 }
1546
toString(String & dump,bool escape,const String & indent,const String & origIndent) const1547 void XmlDocument::toString(String& dump, bool escape, const String& indent, const String& origIndent) const
1548 {
1549 m_beforeRoot.toString(dump,escape,indent,origIndent);
1550 if (m_root) {
1551 dump << origIndent;
1552 m_root->toString(dump,escape,indent,origIndent);
1553 }
1554 m_afterRoot.toString(dump,escape,indent,origIndent);
1555 }
1556
1557 // Reset this XmlDocument. Destroys root and clear the others xml objects
reset()1558 void XmlDocument::reset()
1559 {
1560 TelEngine::destruct(m_root);
1561 m_beforeRoot.clearChildren();
1562 m_afterRoot.clearChildren();
1563 m_file.clear();
1564 }
1565
1566 // Load this document from data stream and parse it
read(Stream & in,int * error)1567 XmlSaxParser::Error XmlDocument::read(Stream& in, int* error)
1568 {
1569 XmlDomParser parser(static_cast<XmlParent*>(this),false);
1570 char buf[8096];
1571 bool start = true;
1572 while (true) {
1573 int rd = in.readData(buf,sizeof(buf) - 1);
1574 if (rd > 0) {
1575 buf[rd] = 0;
1576 const char* text = buf;
1577 if (start) {
1578 String::stripBOM(text);
1579 start = false;
1580 }
1581 if (parser.parse(text) || parser.error() == XmlSaxParser::Incomplete)
1582 continue;
1583 break;
1584 }
1585 break;
1586 }
1587 parser.completeText();
1588 if (parser.error() != XmlSaxParser::NoError) {
1589 DDebug(DebugNote,"XmlDocument error loading stream. Parser error %d '%s' [%p]",
1590 parser.error(),parser.getError(),this);
1591 return parser.error();
1592 }
1593 if (in.error()) {
1594 if (error)
1595 *error = in.error();
1596 #ifdef DEBUG
1597 String tmp;
1598 Thread::errorString(tmp,in.error());
1599 Debug(DebugNote,"XmlDocument error loading stream. I/O error %d '%s' [%p]",
1600 in.error(),tmp.c_str(),this);
1601 #endif
1602 return XmlSaxParser::IOError;
1603 }
1604 return XmlSaxParser::NoError;
1605 }
1606
1607 // Write this document to a data stream
write(Stream & out,bool escape,const String & indent,const String & origIndent,bool completeOnly) const1608 int XmlDocument::write(Stream& out, bool escape, const String& indent,
1609 const String& origIndent, bool completeOnly) const
1610 {
1611 String dump;
1612 m_beforeRoot.toString(dump,escape,indent,origIndent);
1613 if (m_root)
1614 m_root->toString(dump,escape,indent,origIndent,completeOnly);
1615 m_afterRoot.toString(dump,escape,indent,origIndent);
1616 return out.writeData(dump);
1617 }
1618
1619 // Load a file and parse it
loadFile(const char * file,int * error)1620 XmlSaxParser::Error XmlDocument::loadFile(const char* file, int* error)
1621 {
1622 reset();
1623 if (TelEngine::null(file))
1624 return XmlSaxParser::NoError;
1625 m_file = file;
1626 File f;
1627 if (f.openPath(file))
1628 return read(f,error);
1629 if (error)
1630 *error = f.error();
1631 #ifdef DEBUG
1632 String tmp;
1633 Thread::errorString(tmp,f.error());
1634 Debug(DebugNote,"XmlDocument error opening file '%s': %d '%s' [%p]",
1635 file,f.error(),tmp.c_str(),this);
1636 #endif
1637 return XmlSaxParser::IOError;
1638 }
1639
1640 // Save this xml document in a file
saveFile(const char * file,bool esc,const String & indent,bool completeOnly,const char * eoln) const1641 int XmlDocument::saveFile(const char* file, bool esc, const String& indent,
1642 bool completeOnly, const char* eoln) const
1643 {
1644 if (!file)
1645 file = m_file;
1646 if (!file)
1647 return 0;
1648 File f;
1649 int err = 0;
1650 if (f.openPath(file,true,false,true,false)) {
1651 String eol(eoln);
1652 if (eoln && !eol)
1653 eol = "\r\n";
1654 write(f,esc,eol,indent,completeOnly);
1655 err = f.error();
1656 // Add an empty line
1657 if (err >= 0 && eol)
1658 f.writeData((void*)eol.c_str(),eol.length());
1659 }
1660 else
1661 err = f.error();
1662 if (!err) {
1663 XDebug(DebugAll,"XmlDocument saved file '%s' [%p]",file,this);
1664 return 0;
1665 }
1666 #ifdef DEBUG
1667 String error;
1668 Thread::errorString(error,err);
1669 Debug(DebugNote,"Error saving XmlDocument to file '%s'. %d '%s' [%p]",
1670 file,err,error.c_str(),this);
1671 #endif
1672 return f.error();
1673 }
1674
1675 // Replaces all ${paramname} in document's components with the corresponding parameters
replaceParams(const NamedList & params)1676 void XmlDocument::replaceParams(const NamedList& params)
1677 {
1678 if (m_root)
1679 m_root->replaceParams(params);
1680 m_beforeRoot.replaceParams(params);
1681 m_afterRoot.replaceParams(params);
1682 }
1683
1684
1685 /*
1686 * XmlChild
1687 */
XmlChild()1688 XmlChild::XmlChild()
1689 {
1690 }
1691
1692
1693 /*
1694 * XmlElement
1695 */
XmlElement(const NamedList & element,bool empty,XmlParent * parent)1696 XmlElement::XmlElement(const NamedList& element, bool empty, XmlParent* parent)
1697 : m_element(element), m_prefixed(0),
1698 m_parent(0), m_inheritedNs(0),
1699 m_empty(empty), m_complete(empty)
1700 {
1701 XDebug(DebugAll,"XmlElement::XmlElement(%s,%u,%p) [%p]",
1702 element.c_str(),empty,parent,this);
1703 setPrefixed();
1704 setParent(parent);
1705 }
1706
1707 // Copy constructor
XmlElement(const XmlElement & el)1708 XmlElement::XmlElement(const XmlElement& el)
1709 : m_element(el.getElement()), m_prefixed(0),
1710 m_parent(0), m_inheritedNs(0),
1711 m_empty(el.empty()), m_complete(el.completed())
1712 {
1713 setPrefixed();
1714 setInheritedNs(&el,true);
1715 m_children.copy(el.m_children,this);
1716 }
1717
1718 // Create an empty xml element
XmlElement(const char * name,bool complete)1719 XmlElement::XmlElement(const char* name, bool complete)
1720 : m_element(name), m_prefixed(0),
1721 m_parent(0), m_inheritedNs(0),
1722 m_empty(true), m_complete(complete)
1723 {
1724 setPrefixed();
1725 XDebug(DebugAll,"XmlElement::XmlElement(%s) [%p]",
1726 m_element.c_str(),this);
1727 }
1728
1729 // Create a new element with a text child
XmlElement(const char * name,const char * value,bool complete)1730 XmlElement::XmlElement(const char* name, const char* value, bool complete)
1731 : m_element(name), m_prefixed(0),
1732 m_parent(0), m_inheritedNs(0),
1733 m_empty(true), m_complete(complete)
1734 {
1735 setPrefixed();
1736 addText(value);
1737 XDebug(DebugAll,"XmlElement::XmlElement(%s) [%p]",
1738 m_element.c_str(),this);
1739 }
1740
1741 // Destructor
~XmlElement()1742 XmlElement::~XmlElement()
1743 {
1744 setInheritedNs();
1745 TelEngine::destruct(m_prefixed);
1746 XDebug(DebugAll,"XmlElement::~XmlElement() ( %s| %p )",
1747 m_element.c_str(),this);
1748 }
1749
1750 // Set element's unprefixed tag, don't change namespace prefix
setUnprefixedTag(const String & s)1751 void XmlElement::setUnprefixedTag(const String& s)
1752 {
1753 if (!s || s == unprefixedTag())
1754 return;
1755 if (TelEngine::null(m_prefixed))
1756 m_element.assign(s);
1757 else
1758 m_element.assign(*m_prefixed + ":" + s);
1759 setPrefixed();
1760 }
1761
1762 // Set inherited namespaces from a given element. Reset them anyway
setInheritedNs(const XmlElement * xml,bool inherit)1763 void XmlElement::setInheritedNs(const XmlElement* xml, bool inherit)
1764 {
1765 XDebug(DebugAll,"XmlElement(%s) setInheritedNs(%p,%s) [%p]",
1766 tag(),xml,String::boolText(inherit),this);
1767 TelEngine::destruct(m_inheritedNs);
1768 if (!xml)
1769 return;
1770 addInheritedNs(xml->attributes());
1771 if (!inherit)
1772 return;
1773 XmlElement* p = xml->parent();
1774 bool xmlAdd = (p == 0);
1775 while (p) {
1776 addInheritedNs(p->attributes());
1777 const NamedList* i = p->inheritedNs();
1778 p = p->parent();
1779 if (!p && i)
1780 addInheritedNs(*i);
1781 }
1782 if (xmlAdd && xml->inheritedNs())
1783 addInheritedNs(*xml->inheritedNs());
1784 }
1785
1786 // Add inherited namespaces from a list
addInheritedNs(const NamedList & list)1787 void XmlElement::addInheritedNs(const NamedList& list)
1788 {
1789 XDebug(DebugAll,"XmlElement(%s) addInheritedNs(%s) [%p]",tag(),list.c_str(),this);
1790 unsigned int n = list.count();
1791 for (unsigned int i = 0; i < n; i++) {
1792 NamedString* ns = list.getParam(i);
1793 if (!(ns && isXmlns(ns->name())))
1794 continue;
1795 // Avoid adding already overridden namespaces
1796 if (m_element.getParam(ns->name()))
1797 continue;
1798 if (m_inheritedNs && m_inheritedNs->getParam(ns->name()))
1799 continue;
1800 // TODO: Check if attribute names are unique after adding the namespace
1801 // See http://www.w3.org/TR/xml-names/ Section 6.3
1802 if (!m_inheritedNs)
1803 m_inheritedNs = new NamedList("");
1804 XDebug(DebugAll,"XmlElement(%s) adding inherited %s=%s [%p]",
1805 tag(),ns->name().c_str(),ns->c_str(),this);
1806 m_inheritedNs->addParam(ns->name(),*ns);
1807 }
1808 }
1809
1810 // Obtain the first text of this xml element
getText() const1811 const String& XmlElement::getText() const
1812 {
1813 const XmlText* txt = 0;
1814 for (ObjList* ob = getChildren().skipNull(); ob && !txt; ob = ob->skipNext())
1815 txt = (static_cast<XmlChild*>(ob->get()))->xmlText();
1816 return txt ? txt->getText() : String::empty();
1817 }
1818
getFirstChild()1819 XmlChild* XmlElement::getFirstChild()
1820 {
1821 if (!m_children.getChildren().skipNull())
1822 return 0;
1823 return static_cast<XmlChild*>(m_children.getChildren().skipNull()->get());
1824 }
1825
setText(const char * text)1826 XmlText* XmlElement::setText(const char* text)
1827 {
1828 XmlText* txt = 0;
1829 for (ObjList* o = getChildren().skipNull(); o; o = o->skipNext()) {
1830 txt = (static_cast<XmlChild*>(o->get()))->xmlText();
1831 if (txt)
1832 break;
1833 }
1834 if (txt) {
1835 if (!text)
1836 return static_cast<XmlText*>(removeChild(txt));
1837 txt->setText(text);
1838 }
1839 else if (text) {
1840 txt = new XmlText(text);
1841 addChild(txt);
1842 }
1843 return txt;
1844 }
1845
1846 // Add a text child
addText(const char * text)1847 void XmlElement::addText(const char* text)
1848 {
1849 if (!TelEngine::null(text))
1850 addChild(new XmlText(text));
1851 }
1852
1853 // Retrieve the element's tag (without prefix) and namespace
getTag(const String * & tag,const String * & ns) const1854 bool XmlElement::getTag(const String*& tag, const String*& ns) const
1855 {
1856 if (!m_prefixed) {
1857 tag = &m_element;
1858 ns = xmlns();
1859 return true;
1860 }
1861 // Prefixed element
1862 tag = &m_prefixed->name();
1863 ns = xmlns();
1864 return ns != 0;
1865 }
1866
1867 // Append a new child
addChild(XmlChild * child)1868 XmlSaxParser::Error XmlElement::addChild(XmlChild* child)
1869 {
1870 if (!child)
1871 return XmlSaxParser::NoError;
1872 // TODO: Check if a child element's attribute names are unique in the new context
1873 // See http://www.w3.org/TR/xml-names/ Section 6.3
1874 XmlSaxParser::Error err = m_children.addChild(child);
1875 if (err == XmlSaxParser::NoError)
1876 child->setParent(this);
1877 return err;
1878 }
1879
1880 // Remove a child
removeChild(XmlChild * child,bool delObj)1881 XmlChild* XmlElement::removeChild(XmlChild* child, bool delObj)
1882 {
1883 return m_children.removeChild(child,delObj);
1884 }
1885
1886 // Set this element's parent. Update inherited namespaces
setParent(XmlParent * parent)1887 void XmlElement::setParent(XmlParent* parent)
1888 {
1889 XDebug(DebugAll,"XmlElement(%s) setParent(%p) element=%s [%p]",
1890 tag(),parent,String::boolText(parent != 0),this);
1891 if (m_parent && m_parent->element()) {
1892 // Reset inherited namespaces if the new parent is an element
1893 // Otherwise set them from the old parent
1894 if (parent && parent->element())
1895 setInheritedNs(0);
1896 else
1897 setInheritedNs(m_parent->element());
1898 }
1899 m_parent = parent;
1900 }
1901
1902 // Obtain a string from this xml element
toString(String & dump,bool esc,const String & indent,const String & origIndent,bool completeOnly,const String * auth) const1903 void XmlElement::toString(String& dump, bool esc, const String& indent,
1904 const String& origIndent, bool completeOnly, const String* auth) const
1905 {
1906 XDebug(DebugAll,"XmlElement(%s) toString(%u,%s,%s,%u,%p) complete=%u [%p]",
1907 tag(),esc,indent.c_str(),origIndent.c_str(),completeOnly,auth,m_complete,this);
1908 if (!m_complete && completeOnly)
1909 return;
1910 String auxDump;
1911 auxDump << indent << "<" << m_element;
1912 int n = m_element.count();
1913 for (int i = 0; i < n; i++) {
1914 NamedString* ns = m_element.getParam(i);
1915 if (!ns)
1916 continue;
1917 auxDump << " " << ns->name() << "=\"";
1918 addAuth(auxDump,ns->name(),*ns,esc,auth);
1919 auxDump << "\"";
1920 }
1921 int m = getChildren().count();
1922 if (m_complete && !m)
1923 auxDump << "/";
1924 auxDump << ">";
1925 if (m) {
1926 // Avoid adding text on new line when text is the only child
1927 XmlText* text = 0;
1928 if (m == 1)
1929 text = static_cast<XmlChild*>(getChildren().skipNull()->get())->xmlText();
1930 if (!text)
1931 m_children.toString(auxDump,esc,indent + origIndent,origIndent,completeOnly,auth,this);
1932 else
1933 text->toString(auxDump,esc,String::empty(),auth,this);
1934 if (m_complete)
1935 auxDump << (!text ? indent : String::empty()) << "</" << getName() << ">";
1936 }
1937 dump << auxDump;
1938 }
1939
1940 // Copy element attributes to a list of parameters
copyAttributes(NamedList & list,const String & prefix) const1941 unsigned int XmlElement::copyAttributes(NamedList& list, const String& prefix) const
1942 {
1943 unsigned int copy = 0;
1944 unsigned int n = m_element.length();
1945 for (unsigned int i = 0; i < n; i++) {
1946 NamedString* ns = m_element.getParam(i);
1947 if (!(ns && ns->name()))
1948 continue;
1949 list.addParam(prefix + ns->name(),*ns);
1950 copy++;
1951 }
1952 return copy;
1953 }
1954
setAttributes(NamedList & list,const String & prefix,bool skipPrefix)1955 void XmlElement::setAttributes(NamedList& list, const String& prefix, bool skipPrefix)
1956 {
1957 if (prefix)
1958 m_element.copySubParams(list,prefix,skipPrefix);
1959 else
1960 m_element.copyParams(list);
1961 }
1962
1963 // Retrieve a namespace attribute. Search in parent or inherited for it
xmlnsAttribute(const String & name) const1964 String* XmlElement::xmlnsAttribute(const String& name) const
1965 {
1966 String* tmp = getAttribute(name);
1967 if (tmp)
1968 return tmp;
1969 XmlElement* p = parent();
1970 if (p)
1971 return p->xmlnsAttribute(name);
1972 return m_inheritedNs ? m_inheritedNs->getParam(name) : 0;
1973 }
1974
1975 // Set the element's namespace
setXmlns(const String & name,bool addAttr,const String & value)1976 bool XmlElement::setXmlns(const String& name, bool addAttr, const String& value)
1977 {
1978 const String* cmp = name ? &name : &s_ns;
1979 XDebug(DebugAll,"XmlElement(%s)::setXmlns(%s,%u,%s) [%p]",
1980 tag(),cmp->c_str(),addAttr,value.c_str(),this);
1981 if (*cmp == s_ns) {
1982 if (m_prefixed) {
1983 m_element.assign(m_prefixed->name());
1984 setPrefixed();
1985 // TODO: remove children and attributes prefixes
1986 }
1987 }
1988 else if (!m_prefixed || *m_prefixed != cmp) {
1989 if (!m_prefixed)
1990 m_element.assign(*cmp + ":" + tag());
1991 else
1992 m_element.assign(*cmp + ":" + m_prefixed->name());
1993 setPrefixed();
1994 // TODO: change children and attributes prefixes
1995 }
1996 if (!(addAttr && value))
1997 return true;
1998 String attr;
1999 if (*cmp == s_ns)
2000 attr = s_ns;
2001 else
2002 attr << s_nsPrefix << *cmp;
2003 NamedString* ns = m_element.getParam(attr);
2004 if (!ns && m_inheritedNs && m_inheritedNs->getParam(attr))
2005 m_inheritedNs->clearParam(attr);
2006 // TODO: Check if attribute names are unique after adding the namespace
2007 // See http://www.w3.org/TR/xml-names/ Section 6.3
2008 if (!ns)
2009 m_element.addParam(attr,value);
2010 else
2011 *ns = value;
2012 return true;
2013 }
2014
2015 // Replaces all ${paramname} in element's attributes and children with the
2016 // corresponding parameters
replaceParams(const NamedList & params)2017 void XmlElement::replaceParams(const NamedList& params)
2018 {
2019 m_children.replaceParams(params);
2020 for (ObjList* o = m_element.paramList()->skipNull(); o; o = o->skipNext())
2021 params.replaceParams(*static_cast<String*>(o->get()));
2022 }
2023
2024 // Build an XML element from a list parameter
param2xml(NamedString * param,const String & tag,bool copyXml)2025 XmlElement* XmlElement::param2xml(NamedString* param, const String& tag, bool copyXml)
2026 {
2027 if (!(param && param->name() && tag))
2028 return 0;
2029 XmlElement* xml = new XmlElement(tag);
2030 xml->setAttribute(s_name,param->name());
2031 xml->setAttributeValid(YSTRING("value"),*param);
2032 NamedPointer* np = YOBJECT(NamedPointer,param);
2033 if (!(np && np->userData()))
2034 return xml;
2035 DataBlock* db = YOBJECT(DataBlock,np->userData());
2036 if (db) {
2037 xml->setAttribute(s_type,"DataBlock");
2038 Base64 b(db->data(),db->length(),false);
2039 String tmp;
2040 b.encode(tmp);
2041 b.clear(false);
2042 xml->addText(tmp);
2043 return xml;
2044 }
2045 XmlElement* element = YOBJECT(XmlElement,np->userData());
2046 if (element) {
2047 xml->setAttribute(s_type,"XmlElement");
2048 if (!copyXml) {
2049 np->takeData();
2050 xml->addChild(element);
2051 }
2052 else
2053 xml->addChild(new XmlElement(*element));
2054 return xml;
2055 }
2056 NamedList* list = YOBJECT(NamedList,np->userData());
2057 if (list) {
2058 xml->setAttribute(s_type,"NamedList");
2059 xml->addText(list->c_str());
2060 unsigned int n = list->length();
2061 for (unsigned int i = 0; i < n; i++)
2062 xml->addChild(param2xml(list->getParam(i),tag,copyXml));
2063 return xml;
2064 }
2065 return xml;
2066 }
2067
2068 // Build a list parameter from xml element
xml2param(XmlElement * xml,const String * tag,bool copyXml)2069 NamedString* XmlElement::xml2param(XmlElement* xml, const String* tag, bool copyXml)
2070 {
2071 const char* name = xml ? xml->attribute(s_name) : 0;
2072 if (TelEngine::null(name))
2073 return 0;
2074 GenObject* gen = 0;
2075 String* type = xml->getAttribute(s_type);
2076 if (type) {
2077 if (*type == YSTRING("DataBlock")) {
2078 gen = new DataBlock;
2079 const String& text = xml->getText();
2080 Base64 b((void*)text.c_str(),text.length(),false);
2081 b.decode(*(static_cast<DataBlock*>(gen)));
2082 b.clear(false);
2083 }
2084 else if (*type == YSTRING("XmlElement")) {
2085 if (!copyXml)
2086 gen = xml->pop();
2087 else {
2088 XmlElement* tmp = xml->findFirstChild();
2089 if (tmp)
2090 gen = new XmlElement(*tmp);
2091 }
2092 }
2093 else if (*type == YSTRING("NamedList")) {
2094 gen = new NamedList(xml->getText());
2095 xml2param(*(static_cast<NamedList*>(gen)),xml,tag,copyXml);
2096 }
2097 else
2098 Debug(DebugStub,"XmlElement::xml2param: unhandled type=%s",type->c_str());
2099 }
2100 if (!gen)
2101 return new NamedString(name,xml->attribute(YSTRING("value")));
2102 return new NamedPointer(name,gen,xml->attribute(YSTRING("value")));
2103 }
2104
2105 // Build and add list parameters from XML element children
xml2param(NamedList & list,XmlElement * parent,const String * tag,bool copyXml)2106 void XmlElement::xml2param(NamedList& list, XmlElement* parent, const String* tag,
2107 bool copyXml)
2108 {
2109 if (!parent)
2110 return;
2111 XmlElement* ch = 0;
2112 while (0 != (ch = parent->findNextChild(ch,tag))) {
2113 NamedString* ns = xml2param(ch,tag,copyXml);
2114 if (ns)
2115 list.addParam(ns);
2116 }
2117 }
2118
2119
2120 /*
2121 * XmlComment
2122 */
2123 // Constructor
XmlComment(const String & comm)2124 XmlComment::XmlComment(const String& comm)
2125 : m_comment(comm)
2126 {
2127 XDebug(DebugAll,"XmlComment::XmlComment(const String& comm) ( %s| %p )",
2128 m_comment.c_str(),this);
2129 }
2130
2131 // Copy Constructor
XmlComment(const XmlComment & comm)2132 XmlComment::XmlComment(const XmlComment& comm)
2133 : m_comment(comm.getComment())
2134 {
2135 }
2136
2137 // Destructor
~XmlComment()2138 XmlComment::~XmlComment()
2139 {
2140 XDebug(DebugAll,"XmlComment::~XmlComment() ( %s| %p )",
2141 m_comment.c_str(),this);
2142 }
2143
2144 // Obtain string representation of this xml comment
toString(String & dump,const String & indent) const2145 void XmlComment::toString(String& dump, const String& indent) const
2146 {
2147 dump << indent << "<!--" << getComment() << "-->";
2148 }
2149
2150
2151 /*
2152 * XmlCData
2153 */
2154 // Constructor
XmlCData(const String & data)2155 XmlCData::XmlCData(const String& data)
2156 : m_data(data)
2157 {
2158 XDebug(DebugAll,"XmlCData::XmlCData(const String& data) ( %s| %p )",
2159 m_data.c_str(),this);
2160 }
2161
2162 // Copy Constructor
XmlCData(const XmlCData & data)2163 XmlCData::XmlCData(const XmlCData& data)
2164 : m_data(data.getCData())
2165 {
2166 }
2167
2168 // Destructor
~XmlCData()2169 XmlCData::~XmlCData()
2170 {
2171 XDebug(DebugAll,"XmlCData::~XmlCData() ( %s| %p )",
2172 m_data.c_str(),this);
2173 }
2174
2175 // Obtain string representation of this xml Cdata
toString(String & dump,const String & indent) const2176 void XmlCData::toString(String& dump, const String& indent) const
2177 {
2178 dump << indent << "<![CDATA[" << getCData() << "]]>";
2179 }
2180
2181
2182 /*
2183 * XmlText
2184 */
2185 // Constructor
XmlText(const String & text)2186 XmlText::XmlText(const String& text)
2187 : m_text(text)
2188 {
2189 XDebug(DebugAll,"XmlText::XmlText(%s) [%p]",m_text.c_str(),this);
2190 }
2191
2192 // Copy Constructor
XmlText(const XmlText & text)2193 XmlText::XmlText(const XmlText& text)
2194 : m_text(text.getText())
2195 {
2196 XDebug(DebugAll,"XmlText::XmlText(%p,%s) [%p]",
2197 &text,TelEngine::c_safe(text.getText()),this);
2198 }
2199
2200 // Destructor
~XmlText()2201 XmlText::~XmlText()
2202 {
2203 XDebug(DebugAll,"XmlText::~XmlText [%p]",this);
2204 }
2205
2206 // Obtain string representation of this xml text
toString(String & dump,bool esc,const String & indent,const String * auth,const XmlElement * parent) const2207 void XmlText::toString(String& dump, bool esc, const String& indent,
2208 const String* auth, const XmlElement* parent) const
2209 {
2210 dump << indent;
2211 if (auth)
2212 addAuth(dump,parent ? parent->toString() : String::empty(),m_text,esc,auth);
2213 else if (esc)
2214 XmlSaxParser::escape(dump,m_text);
2215 else
2216 dump << m_text;
2217 }
2218
onlySpaces()2219 bool XmlText::onlySpaces()
2220 {
2221 if (!m_text)
2222 return true;
2223 const char *s = m_text;
2224 unsigned int i = 0;
2225 for (;i < m_text.length();i++) {
2226 if (s[i] == ' ' || s[i] == '\t' || s[i] == '\v' || s[i] == '\f' || s[i] == '\r' || s[i] == '\n')
2227 continue;
2228 return false;
2229 }
2230 return true;
2231 }
2232
2233 // Replaces all ${paramname} in text with the corresponding parameters
replaceParams(const NamedList & params)2234 void XmlText::replaceParams(const NamedList& params)
2235 {
2236 params.replaceParams(m_text);
2237 }
2238
2239
2240 /*
2241 * XmlDoctype
2242 */
2243 // Constructor
XmlDoctype(const String & doctype)2244 XmlDoctype::XmlDoctype(const String& doctype)
2245 : m_doctype(doctype)
2246 {
2247 XDebug(DebugAll,"XmlDoctype::XmlDoctype(const String& doctype) ( %s| %p )",
2248 m_doctype.c_str(),this);
2249 }
2250
2251 // Copy Constructor
XmlDoctype(const XmlDoctype & doctype)2252 XmlDoctype::XmlDoctype(const XmlDoctype& doctype)
2253 : m_doctype(doctype.getDoctype())
2254 {
2255 }
2256
2257 // Destructor
~XmlDoctype()2258 XmlDoctype::~XmlDoctype()
2259 {
2260 XDebug(DebugAll,"XmlDoctype::~XmlDoctype() ( %s| %p )",
2261 m_doctype.c_str(),this);
2262 }
2263
2264 // Obtain string representation of this xml doctype
toString(String & dump,const String & indent) const2265 void XmlDoctype::toString(String& dump, const String& indent) const
2266 {
2267 dump << indent << "<!DOCTYPE " << m_doctype << ">";
2268 }
2269
2270 /* vi: set ts=8 sw=4 sts=4 noet: */
2271