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 	{"&lt;",   '<'},
100 	{"&gt;",   '>'},
101 	{"&amp;",  '&'},
102 	{"&quot;", '\"'},
103 	{"&apos;", '\''},
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