2009-11-02 16:05:39 +00:00
|
|
|
/**
|
|
|
|
* XML.cpp
|
2014-02-05 11:42:17 +00:00
|
|
|
* This file is part of the YATE Project http://YATE.null.ro
|
2009-11-02 16:05:39 +00:00
|
|
|
*
|
|
|
|
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
2014-02-05 11:42:17 +00:00
|
|
|
* Copyright (C) 2004-2014 Null Team
|
2009-11-02 16:05:39 +00:00
|
|
|
*
|
2013-08-06 13:38:10 +00:00
|
|
|
* This software is distributed under multiple licenses;
|
|
|
|
* see the COPYING file in the main directory for licensing
|
|
|
|
* information for this specific distribution.
|
|
|
|
*
|
|
|
|
* This use of this software may be subject to additional restrictions.
|
|
|
|
* See the LEGAL file in the main directory for details.
|
2009-11-02 16:05:39 +00:00
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
2013-08-06 13:38:10 +00:00
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
2009-11-02 16:05:39 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <yatexml.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
using namespace TelEngine;
|
|
|
|
|
|
|
|
|
|
|
|
const String XmlElement::s_ns = "xmlns";
|
|
|
|
const String XmlElement::s_nsPrefix = "xmlns:";
|
2011-06-03 12:55:03 +00:00
|
|
|
static const String s_type("type");
|
|
|
|
static const String s_name("name");
|
2009-11-02 16:05:39 +00:00
|
|
|
|
|
|
|
|
|
|
|
// Return a replacement char for the given string
|
|
|
|
char replace(const char* str, const XmlEscape* esc)
|
|
|
|
{
|
|
|
|
if (!str)
|
|
|
|
return 0;
|
|
|
|
if (esc) {
|
|
|
|
for (; esc->value; esc++)
|
|
|
|
if (!::strcmp(str,esc->value))
|
|
|
|
return esc->replace;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return a replacement string for the given char
|
|
|
|
const char* replace(char replace, const XmlEscape* esc)
|
|
|
|
{
|
|
|
|
if (esc) {
|
|
|
|
for (; esc->value; esc++)
|
|
|
|
if (replace == esc->replace)
|
|
|
|
return esc->value;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// XmlEscape a string or replace it if found in a list of restrictions
|
|
|
|
static inline void addAuth(String& buf, const String& comp, const String& value,
|
|
|
|
bool esc, const String* auth)
|
|
|
|
{
|
|
|
|
if (auth) {
|
|
|
|
for (; !auth->null(); auth++)
|
|
|
|
if (*auth == comp) {
|
|
|
|
buf << "***";
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (esc)
|
|
|
|
XmlSaxParser::escape(buf,value);
|
|
|
|
else
|
|
|
|
buf << value;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* XmlSaxParser
|
|
|
|
*/
|
2009-11-10 18:44:44 +00:00
|
|
|
const TokenDict XmlSaxParser::s_errorString[] = {
|
2009-11-02 16:05:39 +00:00
|
|
|
{"No error", NoError},
|
|
|
|
{"Error", Unknown},
|
|
|
|
{"Not well formed", NotWellFormed},
|
|
|
|
{"I/O error", IOError},
|
|
|
|
{"Error parsing Element", ElementParse},
|
|
|
|
{"Failed to read Element name", ReadElementName},
|
|
|
|
{"Bad element name", InvalidElementName},
|
|
|
|
{"Error reading Attributes", ReadingAttributes},
|
|
|
|
{"Error reading end tag", ReadingEndTag},
|
|
|
|
{"Error parsing Comment", CommentParse},
|
|
|
|
{"Error parsing Declaration", DeclarationParse},
|
|
|
|
{"Error parsing Definition", DefinitionParse},
|
|
|
|
{"Error parsing CDATA", CDataParse},
|
|
|
|
{"Incomplete", Incomplete},
|
|
|
|
{"Invalid encoding", InvalidEncoding},
|
|
|
|
{"Unsupported encoding", UnsupportedEncoding},
|
|
|
|
{"Unsupported version", UnsupportedVersion},
|
|
|
|
{0,0}
|
|
|
|
};
|
|
|
|
|
2009-11-10 18:44:44 +00:00
|
|
|
const XmlEscape XmlSaxParser::s_escape[] = {
|
2009-11-02 16:05:39 +00:00
|
|
|
{"<", '<'},
|
|
|
|
{">", '>'},
|
|
|
|
{"&", '&'},
|
|
|
|
{""", '\"'},
|
|
|
|
{"'", '\''},
|
|
|
|
{0,0}
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
XmlSaxParser::XmlSaxParser(const char* name)
|
|
|
|
: m_offset(0), m_row(1), m_column(1), m_error(NoError),
|
|
|
|
m_parsed(""), m_unparsed(None)
|
|
|
|
{
|
|
|
|
debugName(name);
|
|
|
|
}
|
|
|
|
|
|
|
|
XmlSaxParser::~XmlSaxParser()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse a given string
|
|
|
|
bool XmlSaxParser::parse(const char* text)
|
|
|
|
{
|
|
|
|
if (TelEngine::null(text))
|
|
|
|
return m_error == NoError;
|
2011-11-11 16:03:07 +00:00
|
|
|
#ifdef XDEBUG
|
|
|
|
String tmp;
|
|
|
|
m_parsed.dump(tmp," ");
|
|
|
|
if (tmp)
|
|
|
|
tmp = " parsed=" + tmp;
|
|
|
|
XDebug(this,DebugAll,"XmlSaxParser::parse(%s) unparsed=%u%s buf=%s [%p]",
|
|
|
|
text,unparsed(),tmp.safe(),m_buf.safe(),this);
|
|
|
|
#endif
|
2009-11-02 16:05:39 +00:00
|
|
|
char car;
|
|
|
|
setError(NoError);
|
|
|
|
String auxData;
|
|
|
|
m_buf << text;
|
|
|
|
if (m_buf.lenUtf8() == -1) {
|
|
|
|
//FIXME this should not be here in case we have a different encoding
|
2010-03-17 15:22:26 +00:00
|
|
|
DDebug(this,DebugNote,"Request to parse invalid utf-8 data [%p]",this);
|
|
|
|
return setError(Incomplete);
|
2009-11-02 16:05:39 +00:00
|
|
|
}
|
|
|
|
if (unparsed()) {
|
|
|
|
if (unparsed() != Text) {
|
|
|
|
if (!auxParse())
|
2011-11-11 16:03:07 +00:00
|
|
|
return false;
|
2009-11-02 16:05:39 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
auxData = m_parsed;
|
|
|
|
resetParsed();
|
|
|
|
setUnparsed(None);
|
|
|
|
}
|
|
|
|
unsigned int len = 0;
|
|
|
|
while (m_buf.at(len) && !error()) {
|
|
|
|
car = m_buf.at(len);
|
|
|
|
if (car != '<' ) { // We have a new child check what it is
|
|
|
|
if (car == '>' || !checkDataChar(car)) {
|
|
|
|
Debug(this,DebugNote,"XML text contains unescaped '%c' character [%p]",
|
|
|
|
car,this);
|
|
|
|
return setError(Unknown);
|
|
|
|
}
|
|
|
|
len++; // Append xml Text
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (len > 0) {
|
|
|
|
auxData << m_buf.substr(0,len);
|
|
|
|
}
|
|
|
|
if (auxData.c_str()) { // We have an end of tag or another child is riseing
|
2012-01-06 10:05:05 +00:00
|
|
|
if (!processText(auxData))
|
2009-11-02 16:05:39 +00:00
|
|
|
return false;
|
|
|
|
m_buf = m_buf.substr(len);
|
|
|
|
len = 0;
|
|
|
|
auxData = "";
|
|
|
|
}
|
2012-01-06 10:05:05 +00:00
|
|
|
char auxCar = m_buf.at(1);
|
|
|
|
if (!auxCar)
|
2009-11-02 16:05:39 +00:00
|
|
|
return setError(Incomplete);
|
|
|
|
if (auxCar == '?') {
|
|
|
|
m_buf = m_buf.substr(2);
|
|
|
|
if (!parseInstruction())
|
|
|
|
return false;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (auxCar == '!') {
|
|
|
|
m_buf = m_buf.substr(2);
|
|
|
|
if (!parseSpecial())
|
|
|
|
return false;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (auxCar == '/') {
|
|
|
|
m_buf = m_buf.substr(2);
|
|
|
|
if (!parseEndTag())
|
|
|
|
return false;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// If we are here mens that we have a element
|
|
|
|
// process an xml element
|
|
|
|
m_buf = m_buf.substr(1);
|
|
|
|
if (!parseElement())
|
|
|
|
return false;
|
|
|
|
}
|
2012-01-06 10:05:05 +00:00
|
|
|
// Incomplete text
|
|
|
|
if ((unparsed() == None || unparsed() == Text) && (auxData || m_buf)) {
|
|
|
|
if (!auxData)
|
|
|
|
m_parsed.assign(m_buf);
|
|
|
|
else {
|
|
|
|
auxData << m_buf;
|
|
|
|
m_parsed.assign(auxData);
|
|
|
|
}
|
2009-11-02 16:05:39 +00:00
|
|
|
m_buf = "";
|
|
|
|
setUnparsed(Text);
|
|
|
|
return setError(Incomplete);
|
|
|
|
}
|
|
|
|
if (error()) {
|
|
|
|
DDebug(this,DebugNote,"Got error while parsing %s [%p]",getError(),this);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
m_buf = "";
|
|
|
|
resetParsed();
|
2012-01-06 10:05:05 +00:00
|
|
|
setUnparsed(None);
|
2009-11-02 16:05:39 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-01-06 10:05:05 +00:00
|
|
|
// Process incomplete text
|
|
|
|
bool XmlSaxParser::completeText()
|
|
|
|
{
|
|
|
|
if (!completed() || unparsed() != Text || error() != Incomplete)
|
|
|
|
return error() == NoError;
|
|
|
|
String tmp = m_parsed;
|
|
|
|
return processText(tmp);
|
|
|
|
}
|
|
|
|
|
2009-11-02 16:05:39 +00:00
|
|
|
// Parse an unfinished xml object
|
|
|
|
bool XmlSaxParser::auxParse()
|
|
|
|
{
|
|
|
|
switch (unparsed()) {
|
|
|
|
case Element:
|
|
|
|
return parseElement();
|
|
|
|
case CData:
|
|
|
|
return parseCData();
|
|
|
|
case Comment:
|
|
|
|
return parseComment();
|
|
|
|
case Declaration:
|
|
|
|
return parseDeclaration();
|
|
|
|
case Instruction:
|
|
|
|
return parseInstruction();
|
|
|
|
case EndTag:
|
|
|
|
return parseEndTag();
|
|
|
|
case Special:
|
|
|
|
return parseSpecial();
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set the error code and destroys a child if error code is not NoError
|
|
|
|
bool XmlSaxParser::setError(Error error, XmlChild* child)
|
|
|
|
{
|
|
|
|
m_error = error;
|
|
|
|
if (child && error)
|
|
|
|
TelEngine::destruct(child);
|
|
|
|
return m_error == XmlSaxParser::NoError;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse an endtag form the main buffer
|
|
|
|
bool XmlSaxParser::parseEndTag()
|
|
|
|
{
|
|
|
|
bool aux = false;
|
|
|
|
String* name = extractName(aux);
|
|
|
|
// We don't check aux flag because we don't look for attributes here
|
|
|
|
if (!name) {
|
|
|
|
if (error() && error() == Incomplete)
|
|
|
|
setUnparsed(EndTag);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!aux || m_buf.at(0) == '/') { // The end tag has attributes or contains / char at the end of name
|
|
|
|
setError(ReadingEndTag);
|
|
|
|
Debug(this,DebugNote,"Got bad end tag </%s/> [%p]",name->c_str(),this);
|
|
|
|
setUnparsed(EndTag);
|
|
|
|
m_buf = *name + m_buf;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
resetError();
|
|
|
|
endElement(*name);
|
|
|
|
if (error()) {
|
|
|
|
setUnparsed(EndTag);
|
|
|
|
m_buf = *name + ">";
|
|
|
|
TelEngine::destruct(name);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
m_buf = m_buf.substr(1);
|
|
|
|
TelEngine::destruct(name);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse an instruction form the main buffer
|
|
|
|
bool XmlSaxParser::parseInstruction()
|
|
|
|
{
|
2011-11-11 16:03:07 +00:00
|
|
|
XDebug(this,DebugAll,"XmlSaxParser::parseInstruction() buf len=%u [%p]",m_buf.length(),this);
|
|
|
|
setUnparsed(Instruction);
|
|
|
|
if (!m_buf.c_str())
|
|
|
|
return setError(Incomplete);
|
2009-11-02 16:05:39 +00:00
|
|
|
// extract the name
|
|
|
|
String name;
|
|
|
|
char c;
|
|
|
|
int len = 0;
|
2011-11-11 16:03:07 +00:00
|
|
|
if (!m_parsed) {
|
|
|
|
bool nameComplete = false;
|
|
|
|
bool endDecl = false;
|
|
|
|
while (0 != (c = m_buf.at(len))) {
|
|
|
|
nameComplete = blank(c);
|
|
|
|
if (!nameComplete) {
|
|
|
|
// Check for instruction end: '?>'
|
|
|
|
if (c == '?') {
|
|
|
|
char next = m_buf.at(len + 1);
|
|
|
|
if (!next)
|
|
|
|
return setError(Incomplete);
|
|
|
|
if (next == '>') {
|
|
|
|
nameComplete = endDecl = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (checkNameCharacter(c)) {
|
|
|
|
len++;
|
|
|
|
continue;
|
|
|
|
}
|
2009-11-02 16:05:39 +00:00
|
|
|
Debug(this,DebugNote,"Instruction name contains bad character '%c' [%p]",c,this);
|
2011-11-11 16:03:07 +00:00
|
|
|
return setError(InvalidElementName);
|
2009-11-02 16:05:39 +00:00
|
|
|
}
|
2011-11-11 16:03:07 +00:00
|
|
|
// Blank found
|
|
|
|
if (len)
|
|
|
|
break;
|
|
|
|
Debug(this,DebugNote,"Instruction with empty name [%p]",this);
|
|
|
|
return setError(InvalidElementName);
|
2009-11-02 16:05:39 +00:00
|
|
|
}
|
2011-11-11 16:03:07 +00:00
|
|
|
if (!len) {
|
|
|
|
if (!endDecl)
|
|
|
|
return setError(Incomplete);
|
|
|
|
// Remove instruction end from buffer
|
|
|
|
m_buf = m_buf.substr(2);
|
2009-11-02 16:05:39 +00:00
|
|
|
Debug(this,DebugNote,"Instruction with empty name [%p]",this);
|
2011-11-11 16:03:07 +00:00
|
|
|
return setError(InvalidElementName);
|
2009-11-02 16:05:39 +00:00
|
|
|
}
|
2011-11-11 16:03:07 +00:00
|
|
|
if (!nameComplete)
|
|
|
|
return setError(Incomplete);
|
2009-11-02 16:05:39 +00:00
|
|
|
name = m_buf.substr(0,len);
|
2011-11-11 16:03:07 +00:00
|
|
|
m_buf = m_buf.substr(!endDecl ? len : len + 2);
|
|
|
|
if (name == YSTRING("xml")) {
|
|
|
|
if (!endDecl)
|
|
|
|
return parseDeclaration();
|
|
|
|
resetParsed();
|
|
|
|
resetError();
|
|
|
|
setUnparsed(None);
|
|
|
|
gotDeclaration(NamedList::empty());
|
|
|
|
return error() == NoError;
|
|
|
|
}
|
|
|
|
// Instruction name can't be xml case insensitive
|
|
|
|
if (name.length() == 3 && name.startsWith("xml",false,true)) {
|
|
|
|
Debug(this,DebugNote,"Instruction name '%s' reserved [%p]",name.c_str(),this);
|
|
|
|
return setError(InvalidElementName);
|
|
|
|
}
|
2009-11-02 16:05:39 +00:00
|
|
|
}
|
2011-11-11 16:03:07 +00:00
|
|
|
else {
|
2009-11-02 16:05:39 +00:00
|
|
|
name = m_parsed;
|
|
|
|
resetParsed();
|
|
|
|
}
|
2011-11-11 16:03:07 +00:00
|
|
|
// Retrieve instruction content
|
2009-11-02 16:05:39 +00:00
|
|
|
skipBlanks();
|
|
|
|
len = 0;
|
2011-11-11 16:03:07 +00:00
|
|
|
while (0 != (c = m_buf.at(len))) {
|
2009-11-02 16:05:39 +00:00
|
|
|
if (c != '?') {
|
2011-11-11 16:03:07 +00:00
|
|
|
if (c == 0x0c) {
|
2009-11-02 16:05:39 +00:00
|
|
|
setError(Unknown);
|
|
|
|
Debug(this,DebugNote,"Xml instruction with unaccepted character '%c' [%p]",
|
|
|
|
c,this);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
len++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
char ch = m_buf.at(len + 1);
|
2011-11-11 16:03:07 +00:00
|
|
|
if (!ch)
|
|
|
|
break;
|
2009-11-02 16:05:39 +00:00
|
|
|
if (ch == '>') { // end of instruction
|
2011-11-11 16:03:07 +00:00
|
|
|
NamedString inst(name,m_buf.substr(0,len));
|
|
|
|
// Parsed instruction: remove instruction end from buffer and reset parsed
|
2009-11-02 16:05:39 +00:00
|
|
|
m_buf = m_buf.substr(len + 2);
|
2011-11-11 16:03:07 +00:00
|
|
|
resetParsed();
|
|
|
|
resetError();
|
|
|
|
setUnparsed(None);
|
|
|
|
gotProcessing(inst);
|
|
|
|
return error() == NoError;
|
2009-11-02 16:05:39 +00:00
|
|
|
}
|
|
|
|
len ++;
|
|
|
|
}
|
|
|
|
// If we are here mens that text has reach his bounds is an error or we need to receive more data
|
2011-11-11 16:03:07 +00:00
|
|
|
m_parsed.assign(name);
|
|
|
|
return setError(Incomplete);
|
2009-11-02 16:05:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Parse a declaration form the main buffer
|
|
|
|
bool XmlSaxParser::parseDeclaration()
|
|
|
|
{
|
2011-11-11 16:03:07 +00:00
|
|
|
XDebug(this,DebugAll,"XmlSaxParser::parseDeclaration() buf len=%u [%p]",m_buf.length(),this);
|
|
|
|
setUnparsed(Declaration);
|
|
|
|
if (!m_buf.c_str())
|
2009-11-02 16:05:39 +00:00
|
|
|
return setError(Incomplete);
|
|
|
|
NamedList dc("xml");
|
|
|
|
if (m_parsed.count()) {
|
|
|
|
dc.copyParams(m_parsed);
|
|
|
|
resetParsed();
|
|
|
|
}
|
|
|
|
char c;
|
|
|
|
skipBlanks();
|
|
|
|
int len = 0;
|
|
|
|
while (m_buf.at(len)) {
|
|
|
|
c = m_buf.at(len);
|
|
|
|
if (c != '?') {
|
|
|
|
skipBlanks();
|
|
|
|
NamedString* s = getAttribute();
|
|
|
|
if (!s) {
|
2011-11-11 16:03:07 +00:00
|
|
|
if (error() == Incomplete)
|
|
|
|
m_parsed = dc;
|
2009-11-02 16:05:39 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
len = 0;
|
|
|
|
if (dc.getParam(s->name())) {
|
|
|
|
Debug(this,DebugNote,"Duplicate attribute '%s' in declaration [%p]",
|
|
|
|
s->name().c_str(),this);
|
2011-11-11 16:03:07 +00:00
|
|
|
TelEngine::destruct(s);
|
2009-11-02 16:05:39 +00:00
|
|
|
return setError(DeclarationParse);
|
|
|
|
}
|
2011-11-11 16:03:07 +00:00
|
|
|
dc.addParam(s);
|
2009-11-02 16:05:39 +00:00
|
|
|
char ch = m_buf.at(len);
|
|
|
|
if (ch && !blank(ch) && ch != '?') {
|
|
|
|
Debug(this,DebugNote,"No blanks between attributes in declaration [%p]",this);
|
|
|
|
return setError(DeclarationParse);
|
|
|
|
}
|
|
|
|
skipBlanks();
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!m_buf.at(++len))
|
|
|
|
break;
|
|
|
|
char ch = m_buf.at(len);
|
|
|
|
if (ch == '>') { // end of declaration
|
2011-11-11 16:03:07 +00:00
|
|
|
// Parsed declaration: remove declaration end from buffer and reset parsed
|
2009-11-02 16:05:39 +00:00
|
|
|
resetError();
|
|
|
|
resetParsed();
|
2011-11-11 16:03:07 +00:00
|
|
|
setUnparsed(None);
|
2009-11-02 16:05:39 +00:00
|
|
|
m_buf = m_buf.substr(len + 1);
|
2011-11-11 16:03:07 +00:00
|
|
|
gotDeclaration(dc);
|
|
|
|
return error() == NoError;
|
2009-11-02 16:05:39 +00:00
|
|
|
}
|
|
|
|
Debug(this,DebugNote,"Invalid declaration ending char '%c' [%p]",ch,this);
|
|
|
|
return setError(DeclarationParse);
|
|
|
|
}
|
|
|
|
m_parsed.copyParams(dc);
|
|
|
|
setError(Incomplete);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse a CData section form the main buffer
|
|
|
|
bool XmlSaxParser::parseCData()
|
|
|
|
{
|
|
|
|
if (!m_buf.c_str()) {
|
|
|
|
setUnparsed(CData);
|
|
|
|
setError(Incomplete);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
String cdata = "";
|
|
|
|
if (m_parsed.c_str()) {
|
|
|
|
cdata = m_parsed;
|
|
|
|
resetParsed();
|
|
|
|
}
|
|
|
|
char c;
|
|
|
|
int len = 0;
|
|
|
|
while (m_buf.at(len)) {
|
|
|
|
c = m_buf.at(len);
|
|
|
|
if (c != ']') {
|
|
|
|
len ++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (m_buf.substr(++len,2) == "]>") { // End of CData section
|
|
|
|
cdata += m_buf.substr(0,len - 1);
|
|
|
|
resetError();
|
|
|
|
gotCdata(cdata);
|
|
|
|
resetParsed();
|
|
|
|
if (error())
|
|
|
|
return false;
|
|
|
|
m_buf = m_buf.substr(len + 2);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
cdata += m_buf;
|
|
|
|
m_buf = "";
|
|
|
|
setUnparsed(CData);
|
|
|
|
int length = cdata.length();
|
|
|
|
m_buf << cdata.substr(length - 2);
|
|
|
|
if (length > 1)
|
|
|
|
m_parsed.assign(cdata.substr(0,length - 2));
|
|
|
|
setError(Incomplete);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Helper method to classify the Xml objects starting with "<!" sequence
|
|
|
|
bool XmlSaxParser::parseSpecial()
|
|
|
|
{
|
|
|
|
if (m_buf.length() < 2) {
|
|
|
|
setUnparsed(Special);
|
|
|
|
return setError(Incomplete);
|
|
|
|
}
|
2011-06-03 12:55:03 +00:00
|
|
|
if (m_buf.startsWith("--")) {
|
2009-11-02 16:05:39 +00:00
|
|
|
m_buf = m_buf.substr(2);
|
|
|
|
if (!parseComment())
|
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (m_buf.length() < 7) {
|
|
|
|
setUnparsed(Special);
|
|
|
|
return setError(Incomplete);
|
|
|
|
}
|
2011-06-03 12:55:03 +00:00
|
|
|
if (m_buf.startsWith("[CDATA[")) {
|
2009-11-02 16:05:39 +00:00
|
|
|
m_buf = m_buf.substr(7);
|
|
|
|
if (!parseCData())
|
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
2011-06-03 12:55:03 +00:00
|
|
|
if (m_buf.startsWith("DOCTYPE")) {
|
2009-11-02 16:05:39 +00:00
|
|
|
m_buf = m_buf.substr(7);
|
|
|
|
if (!parseDoctype())
|
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
Debug(this,DebugNote,"Can't parse unknown special starting with '%s' [%p]",
|
|
|
|
m_buf.c_str(),this);
|
|
|
|
setError(Unknown);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Extract from the given buffer an comment and check if is valid
|
|
|
|
bool XmlSaxParser::parseComment()
|
|
|
|
{
|
|
|
|
String comment;
|
|
|
|
if (m_parsed.c_str()) {
|
|
|
|
comment = m_parsed;
|
|
|
|
resetParsed();
|
|
|
|
}
|
|
|
|
char c;
|
|
|
|
int len = 0;
|
|
|
|
while (m_buf.at(len)) {
|
|
|
|
c = m_buf.at(len);
|
|
|
|
if (c != '-') {
|
|
|
|
if (c == 0x0c) {
|
|
|
|
Debug(this,DebugNote,"Xml comment with unaccepted character '%c' [%p]",c,this);
|
|
|
|
return setError(NotWellFormed);
|
|
|
|
}
|
|
|
|
len++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (m_buf.at(len + 1) == '-' && m_buf.at(len + 2) == '>') { // End of comment
|
|
|
|
comment << m_buf.substr(0,len);
|
|
|
|
m_buf = m_buf.substr(len + 3);
|
|
|
|
#ifdef DEBUG
|
|
|
|
if (comment.at(0) == '-' || comment.at(comment.length() - 1) == '-')
|
|
|
|
DDebug(this,DebugInfo,"Comment starts or ends with '-' character [%p]",this);
|
|
|
|
if (comment.find("--") >= 0)
|
|
|
|
DDebug(this,DebugInfo,"Comment contains '--' char sequence [%p]",this);
|
|
|
|
#endif
|
|
|
|
gotComment(comment);
|
|
|
|
resetParsed();
|
2014-02-05 11:42:17 +00:00
|
|
|
// The comment can apear anywhere sow SaxParser never
|
2009-11-02 16:05:39 +00:00
|
|
|
// sets an error when receive a comment
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
len++;
|
|
|
|
}
|
|
|
|
// If we are here we haven't detect the end of comment
|
|
|
|
comment << m_buf;
|
|
|
|
int length = comment.length();
|
|
|
|
// Keep the last 2 charaters in buffer because if the input buffer ends
|
2014-02-05 11:42:17 +00:00
|
|
|
// between "--" and ">"
|
2009-11-02 16:05:39 +00:00
|
|
|
m_buf = comment.substr(length - 2);
|
|
|
|
setUnparsed(Comment);
|
|
|
|
if (length > 1)
|
|
|
|
m_parsed.assign(comment.substr(0,length - 2));
|
|
|
|
return setError(Incomplete);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse an element form the main buffer
|
|
|
|
bool XmlSaxParser::parseElement()
|
|
|
|
{
|
2012-01-06 10:05:05 +00:00
|
|
|
XDebug(this,DebugAll,"XmlSaxParser::parseElement() buf len=%u [%p]",m_buf.length(),this);
|
2009-11-02 16:05:39 +00:00
|
|
|
if (!m_buf.c_str()) {
|
|
|
|
setUnparsed(Element);
|
|
|
|
return setError(Incomplete);
|
|
|
|
}
|
|
|
|
bool empty = false;
|
|
|
|
if (!m_parsed.c_str()) {
|
|
|
|
String* name = extractName(empty);
|
|
|
|
if (!name) {
|
|
|
|
if (error() == Incomplete)
|
|
|
|
setUnparsed(Element);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
#ifdef XML_STRICT
|
|
|
|
// http://www.w3.org/TR/REC-xml/
|
|
|
|
// Names starting with 'xml' (case insensitive) are reserved
|
|
|
|
if (name->startsWith("xml",false,true)) {
|
|
|
|
Debug(this,DebugNote,"Element tag starts with 'xml' [%p]",this);
|
2011-01-20 14:48:22 +00:00
|
|
|
TelEngine::destruct(name);
|
2009-11-02 16:05:39 +00:00
|
|
|
return setError(ReadElementName);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
m_parsed.assign(*name);
|
|
|
|
TelEngine::destruct(name);
|
|
|
|
}
|
|
|
|
if (empty) { // empty flag means that the element does not have attributes
|
|
|
|
// check if the element is empty
|
|
|
|
bool aux = m_buf.at(0) == '/';
|
|
|
|
if (!processElement(m_parsed,aux))
|
|
|
|
return false;
|
|
|
|
if (aux)
|
|
|
|
m_buf = m_buf.substr(2); // go back where we were
|
|
|
|
else
|
|
|
|
m_buf = m_buf.substr(1); // go back where we were
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
char c;
|
|
|
|
skipBlanks();
|
|
|
|
int len = 0;
|
|
|
|
while (m_buf.at(len)) {
|
|
|
|
c = m_buf.at(len);
|
|
|
|
if (c == '/' || c == '>') { // end of element declaration
|
|
|
|
if (c == '>') {
|
|
|
|
if (!processElement(m_parsed,false))
|
|
|
|
return false;
|
|
|
|
m_buf = m_buf.substr(1);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (!m_buf.at(++len))
|
|
|
|
break;
|
|
|
|
char ch = m_buf.at(len);
|
|
|
|
if (ch != '>') {
|
|
|
|
Debug(this,DebugNote,"Element attribute name contains '/' character [%p]",this);
|
|
|
|
return setError(ReadingAttributes);
|
|
|
|
}
|
|
|
|
if (!processElement(m_parsed,true))
|
|
|
|
return false;
|
|
|
|
m_buf = m_buf.substr(len + 1);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
NamedString* ns = getAttribute();
|
|
|
|
if (!ns) { // Attribute is invalid
|
|
|
|
if (error() == Incomplete)
|
|
|
|
break;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (m_parsed.getParam(ns->name())) {
|
|
|
|
Debug(this,DebugNote,"Duplicate attribute '%s' [%p]",ns->name().c_str(),this);
|
2011-01-24 10:12:37 +00:00
|
|
|
TelEngine::destruct(ns);
|
2009-11-02 16:05:39 +00:00
|
|
|
return setError(NotWellFormed);
|
|
|
|
}
|
|
|
|
XDebug(this,DebugAll,"Parser adding attribute %s='%s' to '%s' [%p]",
|
|
|
|
ns->name().c_str(),ns->c_str(),m_parsed.c_str(),this);
|
|
|
|
m_parsed.setParam(ns);
|
|
|
|
char ch = m_buf.at(len);
|
|
|
|
if (ch && !blank(ch) && (ch != '/' && ch != '>')) {
|
|
|
|
Debug(this,DebugNote,"Element without blanks between attributes [%p]",this);
|
|
|
|
return setError(NotWellFormed);
|
|
|
|
}
|
|
|
|
skipBlanks();
|
|
|
|
}
|
|
|
|
setUnparsed(Element);
|
|
|
|
return setError(Incomplete);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse a doctype form the main buffer
|
|
|
|
bool XmlSaxParser::parseDoctype()
|
|
|
|
{
|
|
|
|
if (!m_buf.c_str()) {
|
|
|
|
setUnparsed(Doctype);
|
|
|
|
setError(Incomplete);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
unsigned int len = 0;
|
|
|
|
skipBlanks();
|
|
|
|
while (m_buf.at(len) && !blank(m_buf.at(len)))
|
|
|
|
len++;
|
|
|
|
// Use a while() to break to the end
|
|
|
|
while (m_buf.at(len)) {
|
|
|
|
while (m_buf.at(len) && blank(m_buf.at(len)))
|
|
|
|
len++;
|
|
|
|
if (len >= m_buf.length())
|
|
|
|
break;
|
|
|
|
if (m_buf[len++] == '[') {
|
|
|
|
while (len < m_buf.length()) {
|
|
|
|
if (m_buf[len] != ']') {
|
|
|
|
len ++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (m_buf.at(++len) != '>')
|
|
|
|
continue;
|
|
|
|
gotDoctype(m_buf.substr(0,len));
|
|
|
|
resetParsed();
|
|
|
|
m_buf = m_buf.substr(len + 1);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
while (len < m_buf.length()) {
|
|
|
|
if (m_buf[len] != '>') {
|
|
|
|
len++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
gotDoctype(m_buf.substr(0,len));
|
|
|
|
resetParsed();
|
|
|
|
m_buf = m_buf.substr(len + 1);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
setUnparsed(Doctype);
|
|
|
|
return setError(Incomplete);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Extract the name of tag
|
|
|
|
String* XmlSaxParser::extractName(bool& empty)
|
|
|
|
{
|
|
|
|
skipBlanks();
|
|
|
|
unsigned int len = 0;
|
|
|
|
bool ok = false;
|
|
|
|
empty = false;
|
|
|
|
while (len < m_buf.length()) {
|
|
|
|
char c = m_buf[len];
|
|
|
|
if (blank(c)) {
|
|
|
|
if (checkFirstNameCharacter(m_buf[0])) {
|
|
|
|
ok = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
Debug(this,DebugNote,"Element tag starting with invalid char %c [%p]",
|
|
|
|
m_buf[0],this);
|
|
|
|
setError(ReadElementName);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (c == '/' || c == '>') { // end of element declaration
|
|
|
|
if (c == '>') {
|
|
|
|
if (checkFirstNameCharacter(m_buf[0])) {
|
|
|
|
empty = true;
|
|
|
|
ok = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
Debug(this,DebugNote,"Element tag starting with invalid char %c [%p]",
|
|
|
|
m_buf[0],this);
|
|
|
|
setError(ReadElementName);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
char ch = m_buf.at(len + 1);
|
|
|
|
if (!ch)
|
|
|
|
break;
|
|
|
|
if (ch != '>') {
|
|
|
|
Debug(this,DebugNote,"Element tag contains '/' character [%p]",this);
|
|
|
|
setError(ReadElementName);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (checkFirstNameCharacter(m_buf[0])) {
|
|
|
|
empty = true;
|
|
|
|
ok = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
Debug(this,DebugNote,"Element tag starting with invalid char %c [%p]",
|
|
|
|
m_buf[0],this);
|
|
|
|
setError(ReadElementName);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (checkNameCharacter(c))
|
|
|
|
len++;
|
|
|
|
else {
|
|
|
|
Debug(this,DebugNote,"Element tag contains invalid char %c [%p]",c,this);
|
|
|
|
setError(ReadElementName);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ok) {
|
|
|
|
String* name = new String(m_buf.substr(0,len));
|
|
|
|
m_buf = m_buf.substr(len);
|
|
|
|
if (!empty) {
|
|
|
|
skipBlanks();
|
|
|
|
empty = (m_buf && m_buf[0] == '>') ||
|
|
|
|
(m_buf.length() > 1 && m_buf[0] == '/' && m_buf[1] == '>');
|
|
|
|
}
|
|
|
|
return name;
|
|
|
|
}
|
|
|
|
setError(Incomplete);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Extract an attribute
|
|
|
|
NamedString* XmlSaxParser::getAttribute()
|
|
|
|
{
|
|
|
|
String name = "";
|
|
|
|
skipBlanks();
|
|
|
|
char c,sep = 0;
|
|
|
|
unsigned int len = 0;
|
|
|
|
|
|
|
|
while (len < m_buf.length()) { // Circle until we find attribute value startup character (["]|['])
|
|
|
|
c = m_buf[len];
|
|
|
|
if (blank(c) || c == '=') {
|
|
|
|
if (!name.c_str())
|
|
|
|
name = m_buf.substr(0,len);
|
|
|
|
len++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (!name.c_str()) {
|
|
|
|
if (!checkNameCharacter(c)) {
|
|
|
|
Debug(this,DebugNote,"Attribute name contains %c character [%p]",c,this);
|
|
|
|
setError(ReadingAttributes);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
len++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (c != '\'' && c != '\"') {
|
|
|
|
Debug(this,DebugNote,"Unenclosed attribute value [%p]",this);
|
|
|
|
setError(ReadingAttributes);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
sep = c;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!sep) {
|
|
|
|
setError(Incomplete);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (!checkFirstNameCharacter(name[0])) {
|
|
|
|
Debug(this,DebugNote,"Attribute name starting with bad character %c [%p]",
|
|
|
|
name.at(0),this);
|
|
|
|
setError(ReadingAttributes);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
int pos = ++len;
|
|
|
|
|
|
|
|
while (len < m_buf.length()) {
|
|
|
|
c = m_buf[len];
|
|
|
|
if (c != sep && !badCharacter(c)) {
|
|
|
|
len ++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (badCharacter(c)) {
|
|
|
|
Debug(this,DebugNote,"Attribute value with unescaped character '%c' [%p]",
|
|
|
|
c,this);
|
|
|
|
setError(ReadingAttributes);
|
|
|
|
return 0;
|
|
|
|
}
|
2011-01-20 14:30:52 +00:00
|
|
|
NamedString* ns = new NamedString(name,m_buf.substr(pos,len - pos));
|
2009-11-02 16:05:39 +00:00
|
|
|
m_buf = m_buf.substr(len + 1);
|
|
|
|
// End of attribute value
|
|
|
|
unEscape(*ns);
|
|
|
|
if (error()) {
|
|
|
|
TelEngine::destruct(ns);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return ns;
|
|
|
|
}
|
|
|
|
|
|
|
|
setError(Incomplete);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reset this parser
|
|
|
|
void XmlSaxParser::reset()
|
|
|
|
{
|
|
|
|
m_offset = 0;
|
|
|
|
m_row = 1;
|
|
|
|
m_column = 1;
|
|
|
|
m_error = NoError;
|
|
|
|
m_buf.clear();
|
|
|
|
resetParsed();
|
|
|
|
m_unparsed = None;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify if the given character is in the range allowed
|
|
|
|
bool XmlSaxParser::checkFirstNameCharacter(unsigned char ch)
|
|
|
|
{
|
|
|
|
return ch == ':' || (ch >= 'A' && ch <= 'Z') || ch == '_' || (ch >= 'a' && ch <= 'z')
|
|
|
|
|| (ch >= 0xc0 && ch <= 0xd6) || (ch >= 0xd8 && ch <= 0xf6) || (ch >= 0xf8);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if the given character is in the range allowed for an xml char
|
|
|
|
bool XmlSaxParser::checkDataChar(unsigned char c)
|
|
|
|
{
|
|
|
|
return c == 0x9 || c == 0xA || c == 0xD || (c >= 0x20);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify if the given character is in the range allowed for a xml name
|
|
|
|
bool XmlSaxParser::checkNameCharacter(unsigned char ch)
|
|
|
|
{
|
|
|
|
return checkFirstNameCharacter(ch) || ch == '-' || ch == '.' || (ch >= '0' && ch <= '9')
|
|
|
|
|| ch == 0xB7;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove blank characters from the beginning of the buffer
|
|
|
|
void XmlSaxParser::skipBlanks()
|
|
|
|
{
|
|
|
|
unsigned int len = 0;
|
|
|
|
while (len < m_buf.length() && blank(m_buf[len]))
|
|
|
|
len++;
|
|
|
|
if (len != 0)
|
|
|
|
m_buf = m_buf.substr(len);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Obtain a char from an ascii decimal char declaration
|
|
|
|
inline unsigned char getDec(String& dec)
|
|
|
|
{
|
|
|
|
if (dec.length() > 6) {
|
|
|
|
DDebug(DebugNote,"Decimal number '%s' too long",dec.c_str());
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
int num = dec.substr(2,dec.length() - 3).toInteger(-1);
|
|
|
|
if (num > 0 && num < 256)
|
|
|
|
return num;
|
|
|
|
DDebug(DebugNote,"Invalid decimal number '%s'",dec.c_str());
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Unescape the given text
|
|
|
|
void XmlSaxParser::unEscape(String& text)
|
|
|
|
{
|
|
|
|
const char* str = text.c_str();
|
|
|
|
if (!str)
|
|
|
|
return;
|
|
|
|
String buf;
|
|
|
|
String aux = "&";
|
|
|
|
unsigned int len = 0;
|
|
|
|
int found = -1;
|
|
|
|
while (str[len]) {
|
|
|
|
if (str[len] == '&' && found < 0) {
|
|
|
|
found = len++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (found < 0) {
|
|
|
|
len++;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (str[len] == '&') {
|
|
|
|
Debug(this,DebugNote,"Unescape. Duplicate '&' in expression [%p]",this);
|
|
|
|
setError(NotWellFormed);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (str[len] != ';')
|
|
|
|
len++;
|
|
|
|
else { // We have a candidate for escaping
|
|
|
|
len += 1; // Append ';' character
|
|
|
|
String aux(str + found,len - found);
|
|
|
|
char re = 0;
|
2009-11-09 19:05:44 +00:00
|
|
|
if (aux.startsWith("&#")) {
|
2013-10-10 14:07:56 +00:00
|
|
|
if (aux.at(2) == 'x') {
|
|
|
|
if (aux.length() > 4 && aux.length() <= 12) {
|
|
|
|
int esc = aux.substr(3,aux.length() - 4).toInteger(-1,16);
|
|
|
|
if (esc != -1) {
|
|
|
|
UChar uc(esc);
|
|
|
|
buf.append(str,found) << uc.c_str();
|
|
|
|
str += len;
|
|
|
|
len = 0;
|
|
|
|
found = -1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else
|
2009-11-02 16:05:39 +00:00
|
|
|
re = getDec(aux);
|
2009-11-09 19:05:44 +00:00
|
|
|
}
|
2009-11-02 16:05:39 +00:00
|
|
|
if (re == '&') {
|
|
|
|
if (str[len] == '#') {
|
|
|
|
aux = String(str + len,4);
|
|
|
|
if (aux == "#60;") {
|
|
|
|
re = '<';
|
|
|
|
len += 4;
|
|
|
|
}
|
|
|
|
if (aux == "#38;") {
|
|
|
|
re = '&';
|
|
|
|
len += 4;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (!re)
|
|
|
|
re = replace(aux,s_escape);
|
|
|
|
if (re) { // We have an valid escape character
|
|
|
|
buf << String(str,found) << re;
|
|
|
|
str += len;
|
|
|
|
len = 0;
|
|
|
|
found = -1;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
Debug(this,DebugNote,"Unescape. No replacement found for '%s' [%p]",
|
|
|
|
String(str + found,len - found).c_str(),this);
|
|
|
|
setError(NotWellFormed);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (found >= 0) {
|
|
|
|
Debug(this,DebugNote,"Unescape. Unexpected end of expression [%p]",this);
|
|
|
|
setError(NotWellFormed);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (len) {
|
|
|
|
if (str != text.c_str()) {
|
|
|
|
buf << String(str,len);
|
|
|
|
text = buf;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
text = buf;
|
|
|
|
}
|
|
|
|
|
2010-11-12 15:59:19 +00:00
|
|
|
// Check if a given string is a valid xml tag name
|
|
|
|
bool XmlSaxParser::validTag(const String& buf)
|
|
|
|
{
|
|
|
|
if (!(buf && checkFirstNameCharacter(buf[0])))
|
|
|
|
return false;
|
|
|
|
for (unsigned int i = 1; i < buf.length(); i++)
|
|
|
|
if (!checkNameCharacter(buf[i]))
|
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2009-11-02 16:05:39 +00:00
|
|
|
// XmlEscape the given text
|
|
|
|
void XmlSaxParser::escape(String& buf, const String& text)
|
|
|
|
{
|
|
|
|
const char* str = text.c_str();
|
|
|
|
if (!str)
|
|
|
|
return;
|
|
|
|
char c;
|
|
|
|
while ((c = *str++)) {
|
|
|
|
const char* rep = replace(c,XmlSaxParser::s_escape);
|
|
|
|
if (!rep) {
|
|
|
|
buf += c;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
buf += rep;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-11-09 13:12:40 +00:00
|
|
|
// Calls gotElement(). Reset parsed if ok
|
2009-11-02 16:05:39 +00:00
|
|
|
bool XmlSaxParser::processElement(NamedList& list, bool empty)
|
|
|
|
{
|
|
|
|
gotElement(list,empty);
|
2009-11-09 13:12:40 +00:00
|
|
|
if (error() == XmlSaxParser::NoError) {
|
|
|
|
resetParsed();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
2009-11-02 16:05:39 +00:00
|
|
|
}
|
|
|
|
|
2012-01-06 10:05:05 +00:00
|
|
|
// Calls gotText() and reset parsed on success
|
|
|
|
bool XmlSaxParser::processText(String& text)
|
|
|
|
{
|
|
|
|
resetError();
|
|
|
|
unEscape(text);
|
|
|
|
if (!error())
|
|
|
|
gotText(text);
|
|
|
|
else
|
|
|
|
setUnparsed(Text);
|
|
|
|
if (!error()) {
|
|
|
|
resetParsed();
|
|
|
|
setUnparsed(None);
|
|
|
|
}
|
|
|
|
return error() == NoError;
|
|
|
|
}
|
|
|
|
|
2009-11-02 16:05:39 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* XmlDomPareser
|
|
|
|
*/
|
|
|
|
XmlDomParser::XmlDomParser(const char* name, bool fragment)
|
|
|
|
: XmlSaxParser(name),
|
2011-01-20 14:10:13 +00:00
|
|
|
m_current(0), m_data(0), m_ownData(true)
|
2009-11-02 16:05:39 +00:00
|
|
|
{
|
|
|
|
if (fragment)
|
|
|
|
m_data = new XmlFragment();
|
|
|
|
else
|
|
|
|
m_data = new XmlDocument();
|
|
|
|
}
|
|
|
|
|
2011-01-20 14:10:13 +00:00
|
|
|
XmlDomParser::XmlDomParser(XmlParent* fragment, bool takeOwnership)
|
|
|
|
: m_current(0), m_data(0), m_ownData(takeOwnership)
|
2009-11-02 16:05:39 +00:00
|
|
|
{
|
2011-01-20 14:10:13 +00:00
|
|
|
m_data = fragment;
|
2009-11-02 16:05:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
XmlDomParser::~XmlDomParser()
|
|
|
|
{
|
2011-01-20 14:10:13 +00:00
|
|
|
if (m_ownData) {
|
|
|
|
reset();
|
|
|
|
if (m_data)
|
|
|
|
delete m_data;
|
|
|
|
}
|
2009-11-02 16:05:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Create a new xml comment and append it in the xml three
|
|
|
|
void XmlDomParser::gotComment(const String& text)
|
|
|
|
{
|
|
|
|
XmlComment* com = new XmlComment(text);
|
|
|
|
if (m_current)
|
|
|
|
setError(m_current->addChild(com),com);
|
|
|
|
else
|
|
|
|
setError(m_data->addChild(com),com);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Append a new xml doctype to main xml parent
|
|
|
|
void XmlDomParser::gotDoctype(const String& doc)
|
|
|
|
{
|
|
|
|
m_data->addChild(new XmlDoctype(doc));
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO implement it see what to do
|
|
|
|
void XmlDomParser::gotProcessing(const NamedString& instr)
|
|
|
|
{
|
2011-11-11 16:03:07 +00:00
|
|
|
DDebug(this,DebugStub,"gotProcessing(%s=%s) not implemented [%p]",
|
|
|
|
instr.name().c_str(),instr.safe(),this);
|
2009-11-02 16:05:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Create a new xml declaration, verifies the version and encoding
|
|
|
|
// and append it in the main xml parent
|
|
|
|
void XmlDomParser::gotDeclaration(const NamedList& decl)
|
|
|
|
{
|
|
|
|
if (m_current) {
|
|
|
|
setError(DeclarationParse);
|
|
|
|
Debug(this,DebugNote,"Received declaration inside element bounds [%p]",this);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
Error err = NoError;
|
|
|
|
while (true) {
|
|
|
|
String* version = decl.getParam("version");
|
|
|
|
if (version) {
|
|
|
|
int ver = version->substr(0,version->find('.')).toInteger();
|
|
|
|
if (ver != 1) {
|
|
|
|
err = UnsupportedVersion;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
String* enc = decl.getParam("encoding");
|
|
|
|
if (enc && !(*enc &= "utf-8")) {
|
|
|
|
err = UnsupportedEncoding;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (err == NoError) {
|
|
|
|
XmlDeclaration* dec = new XmlDeclaration(decl);
|
|
|
|
setError(m_data->addChild(dec),dec);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
setError(err);
|
|
|
|
Debug(this,DebugNote,
|
|
|
|
"Received unacceptable declaration version='%s' encoding='%s' error '%s' [%p]",
|
|
|
|
decl.getValue("version"),decl.getValue("encoding"),getError(),this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create a new xml text and append it in the xml tree
|
|
|
|
void XmlDomParser::gotText(const String& text)
|
|
|
|
{
|
|
|
|
XmlText* tet = new XmlText(text);
|
|
|
|
if (m_current)
|
|
|
|
m_current->addChild(tet);
|
|
|
|
else
|
|
|
|
setError(m_data->addChild(tet),tet);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create a new xml Cdata and append it in the xml tree
|
|
|
|
void XmlDomParser::gotCdata(const String& data)
|
|
|
|
{
|
|
|
|
XmlCData* cdata = new XmlCData(data);
|
|
|
|
if (!m_current) {
|
|
|
|
if (m_data->document()) {
|
|
|
|
Debug(this,DebugNote,"Document got CDATA outside element [%p]",this);
|
|
|
|
setError(NotWellFormed);
|
|
|
|
TelEngine::destruct(cdata);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
setError(m_data->addChild(cdata),cdata);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
setError(m_current->addChild(cdata),cdata);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create a new xml element and append it in the xml tree
|
|
|
|
void XmlDomParser::gotElement(const NamedList& elem, bool empty)
|
|
|
|
{
|
|
|
|
XmlElement* element = 0;
|
|
|
|
if (!m_current) {
|
|
|
|
// If we don't have curent element menns that the main fragment
|
|
|
|
// should hold it
|
|
|
|
element = new XmlElement(elem,empty);
|
|
|
|
setError(m_data->addChild(element),element);
|
2009-11-09 13:12:40 +00:00
|
|
|
if (!empty && error() == XmlSaxParser::NoError)
|
2009-11-02 16:05:39 +00:00
|
|
|
m_current = element;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (empty) {
|
|
|
|
element = new XmlElement(elem,empty);
|
2009-11-09 13:12:40 +00:00
|
|
|
setError(m_current->addChild(element),element);
|
2009-11-02 16:05:39 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
element = new XmlElement(elem,empty,m_current);
|
2009-11-09 13:12:40 +00:00
|
|
|
setError(m_current->addChild(element),element);
|
|
|
|
if (error() == XmlSaxParser::NoError)
|
|
|
|
m_current = element;
|
2009-11-02 16:05:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Verify if is the closeing tag for the current element
|
|
|
|
// Complete th current element and make current the current parent
|
|
|
|
void XmlDomParser::endElement(const String& name)
|
|
|
|
{
|
|
|
|
if (!m_current) {
|
|
|
|
setError(ReadingEndTag);
|
|
|
|
Debug(this,DebugNote,"Unexpected element end tag %s [%p]",name.c_str(),this);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (m_current->getName() != name) {
|
|
|
|
setError(ReadingEndTag);
|
2016-01-08 12:16:15 +00:00
|
|
|
Debug(this,DebugNote,
|
2009-11-02 16:05:39 +00:00
|
|
|
"Received end element for %s, but the expected one is for %s [%p]",
|
|
|
|
name.c_str(),m_current->getName().c_str(),this);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
m_current->setCompleted();
|
|
|
|
XDebug(this,DebugInfo,"End element for %s [%p]",m_current->getName().c_str(),this);
|
|
|
|
m_current = static_cast<XmlElement*>(m_current->getParent());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reset this parser
|
|
|
|
void XmlDomParser::reset()
|
|
|
|
{
|
|
|
|
m_data->reset();
|
|
|
|
m_current = 0;
|
|
|
|
XmlSaxParser::reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* XmlDeclaration
|
|
|
|
*/
|
|
|
|
// Create a new XmlDeclaration from version and encoding
|
|
|
|
XmlDeclaration::XmlDeclaration(const char* version, const char* enc)
|
|
|
|
: m_declaration("")
|
|
|
|
{
|
|
|
|
XDebug(DebugAll,"XmlDeclaration::XmlDeclaration(%s,%s) [%p]",version,enc,this);
|
|
|
|
if (!TelEngine::null(version))
|
|
|
|
m_declaration.addParam("version",version);
|
|
|
|
if (!TelEngine::null(enc))
|
|
|
|
m_declaration.addParam("encoding",enc);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Constructor
|
|
|
|
XmlDeclaration::XmlDeclaration(const NamedList& decl)
|
|
|
|
: m_declaration(decl)
|
|
|
|
{
|
|
|
|
XDebug(DebugAll,"XmlDeclaration::XmlDeclaration(%s) [%p]",m_declaration.c_str(),this);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Copy Constructor
|
|
|
|
XmlDeclaration::XmlDeclaration(const XmlDeclaration& decl)
|
|
|
|
: m_declaration(decl.getDec())
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
// Destructor
|
|
|
|
XmlDeclaration::~XmlDeclaration()
|
|
|
|
{
|
|
|
|
XDebug(DebugAll,"XmlDeclaration::~XmlDeclaration() ( %s| %p )",
|
|
|
|
m_declaration.c_str(),this);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create a String from this Xml Declaration
|
|
|
|
void XmlDeclaration::toString(String& dump, bool esc) const
|
|
|
|
{
|
|
|
|
dump << "<?" << "xml";
|
|
|
|
int n = m_declaration.count();
|
|
|
|
for (int i = 0;i < n;i ++) {
|
|
|
|
NamedString* ns = m_declaration.getParam(i);
|
|
|
|
if (!ns)
|
|
|
|
continue;
|
|
|
|
dump += " ";
|
|
|
|
dump += ns->name();
|
|
|
|
dump << "=\"";
|
|
|
|
if (esc)
|
|
|
|
XmlSaxParser::escape(dump,*ns);
|
|
|
|
else
|
|
|
|
dump += *ns;
|
|
|
|
dump << "\"";
|
|
|
|
}
|
|
|
|
dump << "?>";
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* XmlFragment
|
|
|
|
*/
|
|
|
|
// Constructor
|
|
|
|
XmlFragment::XmlFragment()
|
|
|
|
: m_list()
|
|
|
|
{
|
|
|
|
XDebug(DebugAll,"XmlFragment::XmlFragment() ( %p )",this);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Copy Constructor
|
|
|
|
XmlFragment::XmlFragment(const XmlFragment& orig)
|
|
|
|
{
|
2017-09-26 13:51:18 +00:00
|
|
|
copy(orig);
|
2009-11-02 16:05:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Destructor
|
|
|
|
XmlFragment::~XmlFragment()
|
|
|
|
{
|
|
|
|
m_list.clear();
|
|
|
|
XDebug(DebugAll,"XmlFragment::~XmlFragment() ( %p )",this);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reset. Clear children list
|
|
|
|
void XmlFragment::reset()
|
|
|
|
{
|
|
|
|
m_list.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Append a new child
|
|
|
|
XmlSaxParser::Error XmlFragment::addChild(XmlChild* child)
|
|
|
|
{
|
|
|
|
if (child)
|
|
|
|
m_list.append(child);
|
|
|
|
return XmlSaxParser::NoError;
|
|
|
|
}
|
|
|
|
|
2012-11-22 13:53:51 +00:00
|
|
|
// Remove the first XmlElement from list and returns it if completed
|
|
|
|
XmlElement* XmlFragment::popElement()
|
|
|
|
{
|
|
|
|
for (ObjList* o = m_list.skipNull(); o; o = o->skipNext()) {
|
|
|
|
XmlChild* c = static_cast<XmlChild*>(o->get());
|
|
|
|
XmlElement* x = c->xmlElement();
|
|
|
|
if (x) {
|
|
|
|
if (x->completed()) {
|
|
|
|
o->remove(false);
|
|
|
|
return x;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2009-11-02 16:05:39 +00:00
|
|
|
// Remove a child
|
|
|
|
XmlChild* XmlFragment::removeChild(XmlChild* child, bool delObj)
|
|
|
|
{
|
|
|
|
XmlChild* ch = static_cast<XmlChild*>(m_list.remove(child,delObj));
|
|
|
|
if (ch && ch->xmlElement())
|
|
|
|
ch->xmlElement()->setParent(0);
|
|
|
|
return ch;
|
|
|
|
}
|
|
|
|
|
2017-09-26 13:51:18 +00:00
|
|
|
// Copy other fragment into this one
|
|
|
|
void XmlFragment::copy(const XmlFragment& other, XmlParent* parent)
|
|
|
|
{
|
|
|
|
for (ObjList* o = other.getChildren().skipNull(); o; o = o->skipNext()) {
|
|
|
|
XmlChild* ch = static_cast<XmlChild*>(o->get());
|
|
|
|
if (ch->xmlElement())
|
|
|
|
ch = new XmlElement(*(ch->xmlElement()));
|
|
|
|
else if (ch->xmlCData())
|
|
|
|
ch = new XmlCData(*(ch->xmlCData()));
|
|
|
|
else if (ch->xmlText())
|
|
|
|
ch = new XmlText(*(ch->xmlText()));
|
|
|
|
else if (ch->xmlComment())
|
|
|
|
ch = new XmlComment(*(ch->xmlComment()));
|
|
|
|
else if (ch->xmlDeclaration())
|
|
|
|
ch = new XmlDeclaration(*(ch->xmlDeclaration()));
|
|
|
|
else if (ch->xmlDoctype())
|
|
|
|
ch = new XmlDoctype(*(ch->xmlDoctype()));
|
|
|
|
else
|
|
|
|
continue;
|
|
|
|
ch->setParent(parent);
|
|
|
|
addChild(ch);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-11-02 16:05:39 +00:00
|
|
|
// Create a String from this XmlFragment
|
|
|
|
void XmlFragment::toString(String& dump, bool escape, const String& indent,
|
2009-11-03 11:37:17 +00:00
|
|
|
const String& origIndent, bool completeOnly, const String* auth,
|
2009-11-02 16:05:39 +00:00
|
|
|
const XmlElement* parent) const
|
|
|
|
{
|
|
|
|
ObjList* ob = m_list.skipNull();
|
|
|
|
if (!ob)
|
|
|
|
return;
|
2010-11-15 08:38:01 +00:00
|
|
|
ObjList buffers;
|
2009-11-02 16:05:39 +00:00
|
|
|
for (;ob;ob = ob->skipNext()) {
|
2010-11-15 08:38:01 +00:00
|
|
|
String* s = new String;
|
2009-11-02 16:05:39 +00:00
|
|
|
XmlChild* obj = static_cast<XmlChild*>(ob->get());
|
|
|
|
if (obj->xmlElement())
|
2010-11-15 08:38:01 +00:00
|
|
|
obj->xmlElement()->toString(*s,escape,indent,origIndent,completeOnly,auth);
|
2009-11-02 16:05:39 +00:00
|
|
|
else if (obj->xmlText())
|
2010-11-15 08:38:01 +00:00
|
|
|
obj->xmlText()->toString(*s,escape,indent,auth,parent);
|
2009-11-02 16:05:39 +00:00
|
|
|
else if (obj->xmlCData())
|
2010-11-15 08:38:01 +00:00
|
|
|
obj->xmlCData()->toString(*s,indent);
|
2009-11-02 16:05:39 +00:00
|
|
|
else if (obj->xmlComment())
|
2010-11-15 08:38:01 +00:00
|
|
|
obj->xmlComment()->toString(*s,indent);
|
2009-11-02 16:05:39 +00:00
|
|
|
else if (obj->xmlDeclaration())
|
2010-11-15 08:38:01 +00:00
|
|
|
obj->xmlDeclaration()->toString(*s,escape);
|
2009-11-02 16:05:39 +00:00
|
|
|
else if (obj->xmlDoctype())
|
2010-11-15 08:38:01 +00:00
|
|
|
obj->xmlDoctype()->toString(*s,origIndent);
|
2009-11-02 16:05:39 +00:00
|
|
|
else
|
|
|
|
Debug(DebugStub,"XmlFragment::toString() unhandled element type!");
|
2010-11-15 08:38:01 +00:00
|
|
|
if (!TelEngine::null(s))
|
|
|
|
buffers.append(s);
|
|
|
|
else
|
|
|
|
TelEngine::destruct(s);
|
2009-11-02 16:05:39 +00:00
|
|
|
}
|
2010-11-15 08:38:01 +00:00
|
|
|
dump.append(buffers);
|
2009-11-02 16:05:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Find a completed xml element in a list
|
2011-07-06 10:47:23 +00:00
|
|
|
XmlElement* XmlFragment::findElement(ObjList* list, const String* name, const String* ns,
|
|
|
|
bool noPrefix)
|
2009-11-02 16:05:39 +00:00
|
|
|
{
|
|
|
|
XmlElement* e = 0;
|
|
|
|
for (; list; list = list->skipNext()) {
|
|
|
|
e = (static_cast<XmlChild*>(list->get()))->xmlElement();
|
|
|
|
if (!(e && e->completed()))
|
|
|
|
continue;
|
|
|
|
if (name || ns) {
|
|
|
|
if (!ns) {
|
2011-07-06 10:47:23 +00:00
|
|
|
if (noPrefix) {
|
|
|
|
if (*name == e->unprefixedTag())
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if (*name == e->toString())
|
2009-11-02 16:05:39 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if (name) {
|
|
|
|
const String* t = 0;
|
|
|
|
const String* n = 0;
|
|
|
|
if (e->getTag(t,n) && *t == *name && n && *n == *ns)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
const String* n = e->xmlns();
|
|
|
|
if (n && *n == *ns)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
e = 0;
|
|
|
|
}
|
|
|
|
return e;
|
|
|
|
}
|
|
|
|
|
2018-04-10 10:33:00 +00:00
|
|
|
// Replaces all ${paramname} in fragment's children with the corresponding parameters
|
|
|
|
void XmlFragment::replaceParams(const NamedList& params)
|
|
|
|
{
|
|
|
|
for (ObjList* o = m_list.skipNull(); o; o = o->skipNext())
|
|
|
|
static_cast<XmlChild*>(o->get())->replaceParams(params);
|
|
|
|
}
|
|
|
|
|
2009-11-02 16:05:39 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* XmlDocument
|
|
|
|
*/
|
|
|
|
// Constructor
|
|
|
|
XmlDocument::XmlDocument()
|
|
|
|
: m_root(0)
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Destructor
|
|
|
|
XmlDocument::~XmlDocument()
|
|
|
|
{
|
2011-01-20 16:10:56 +00:00
|
|
|
reset();
|
2009-11-02 16:05:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Append a new child to this document
|
|
|
|
// Set the root to an XML element if not already set. If we already have a completed root
|
|
|
|
// the element will be added to the root, otherwise an error will be returned.
|
|
|
|
// If we don't have a root non xml elements (other then text) will be added the list
|
|
|
|
// of elements before root
|
|
|
|
XmlSaxParser::Error XmlDocument::addChild(XmlChild* child)
|
|
|
|
{
|
|
|
|
if (!child)
|
|
|
|
return XmlSaxParser::NoError;
|
|
|
|
|
|
|
|
XmlElement* element = child->xmlElement();
|
|
|
|
if (!m_root) {
|
|
|
|
if (element) {
|
|
|
|
m_root = element;
|
|
|
|
return XmlSaxParser::NoError;
|
|
|
|
}
|
|
|
|
XmlDeclaration* decl = child->xmlDeclaration();
|
|
|
|
if (decl && declaration()) {
|
|
|
|
DDebug(DebugNote,"XmlDocument. Request to add duplicate declaration [%p]",this);
|
|
|
|
return XmlSaxParser::NotWellFormed;
|
|
|
|
}
|
|
|
|
// Text outside root: ignore empty, raise error otherwise
|
|
|
|
XmlText* text = child->xmlText();
|
|
|
|
if (text) {
|
2013-01-25 14:42:33 +00:00
|
|
|
if (text->onlySpaces()) {
|
|
|
|
m_beforeRoot.addChild(text);
|
2009-11-02 16:05:39 +00:00
|
|
|
return XmlSaxParser::NoError;
|
|
|
|
}
|
|
|
|
Debug(DebugNote,"XmlDocument. Got text outside element [%p]",this);
|
|
|
|
return XmlSaxParser::NotWellFormed;
|
|
|
|
}
|
|
|
|
return m_beforeRoot.addChild(child);
|
|
|
|
}
|
|
|
|
// We have a root
|
|
|
|
if (element) {
|
|
|
|
if (m_root->completed())
|
|
|
|
return m_root->addChild(child);
|
|
|
|
DDebug(DebugStub,"XmlDocument. Request to add xml element child to incomplete root [%p]",this);
|
|
|
|
return XmlSaxParser::NotWellFormed;
|
|
|
|
}
|
2013-01-25 14:42:33 +00:00
|
|
|
if ((child->xmlText() && child->xmlText()->onlySpaces()) || child->xmlComment())
|
|
|
|
return m_afterRoot.addChild(child);
|
2009-11-02 16:05:39 +00:00
|
|
|
// TODO: check what xml we can add after the root or if we can add
|
|
|
|
// anything after an incomplete root
|
|
|
|
Debug(DebugStub,"XmlDocument. Request to add non element while having a root [%p]",this);
|
|
|
|
return XmlSaxParser::NotWellFormed;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Retrieve the document declaration
|
|
|
|
XmlDeclaration* XmlDocument::declaration() const
|
|
|
|
{
|
2013-01-31 14:03:44 +00:00
|
|
|
for (ObjList* o = m_beforeRoot.getChildren().skipNull(); o; o = o->skipNext()) {
|
2009-11-02 16:05:39 +00:00
|
|
|
XmlDeclaration* d = (static_cast<XmlChild*>(o->get()))->xmlDeclaration();
|
|
|
|
if (d)
|
|
|
|
return d;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Obtain root element completed ot not
|
|
|
|
XmlElement* XmlDocument::root(bool completed) const
|
|
|
|
{
|
|
|
|
return (m_root && (m_root->completed() || !completed)) ? m_root : 0;
|
|
|
|
}
|
|
|
|
|
2009-11-03 11:37:17 +00:00
|
|
|
void XmlDocument::toString(String& dump, bool escape, const String& indent, const String& origIndent) const
|
2009-11-02 16:05:39 +00:00
|
|
|
{
|
2009-11-03 11:37:17 +00:00
|
|
|
m_beforeRoot.toString(dump,escape,indent,origIndent);
|
2009-11-02 16:05:39 +00:00
|
|
|
if (m_root) {
|
2009-11-03 11:37:17 +00:00
|
|
|
dump << origIndent;
|
|
|
|
m_root->toString(dump,escape,indent,origIndent);
|
2009-11-02 16:05:39 +00:00
|
|
|
}
|
2013-01-25 14:42:33 +00:00
|
|
|
m_afterRoot.toString(dump,escape,indent,origIndent);
|
2009-11-02 16:05:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Reset this XmlDocument. Destroys root and clear the others xml objects
|
|
|
|
void XmlDocument::reset()
|
|
|
|
{
|
|
|
|
TelEngine::destruct(m_root);
|
|
|
|
m_beforeRoot.clearChildren();
|
2013-01-25 14:42:33 +00:00
|
|
|
m_afterRoot.clearChildren();
|
2009-11-02 16:05:39 +00:00
|
|
|
m_file.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Load this document from data stream and parse it
|
|
|
|
XmlSaxParser::Error XmlDocument::read(Stream& in, int* error)
|
|
|
|
{
|
2011-01-20 14:10:13 +00:00
|
|
|
XmlDomParser parser(static_cast<XmlParent*>(this),false);
|
2009-11-02 16:05:39 +00:00
|
|
|
char buf[8096];
|
2013-02-07 17:10:39 +00:00
|
|
|
bool start = true;
|
2009-11-02 16:05:39 +00:00
|
|
|
while (true) {
|
|
|
|
int rd = in.readData(buf,sizeof(buf) - 1);
|
|
|
|
if (rd > 0) {
|
|
|
|
buf[rd] = 0;
|
2013-02-07 17:10:39 +00:00
|
|
|
const char* text = buf;
|
|
|
|
if (start) {
|
|
|
|
String::stripBOM(text);
|
|
|
|
start = false;
|
|
|
|
}
|
|
|
|
if (parser.parse(text) || parser.error() == XmlSaxParser::Incomplete)
|
2009-11-02 16:05:39 +00:00
|
|
|
continue;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2013-01-25 14:42:33 +00:00
|
|
|
parser.completeText();
|
2011-01-20 14:10:13 +00:00
|
|
|
if (parser.error() != XmlSaxParser::NoError) {
|
2009-11-02 16:05:39 +00:00
|
|
|
DDebug(DebugNote,"XmlDocument error loading stream. Parser error %d '%s' [%p]",
|
2011-01-20 14:10:13 +00:00
|
|
|
parser.error(),parser.getError(),this);
|
|
|
|
return parser.error();
|
2009-11-02 16:05:39 +00:00
|
|
|
}
|
|
|
|
if (in.error()) {
|
|
|
|
if (error)
|
|
|
|
*error = in.error();
|
|
|
|
#ifdef DEBUG
|
|
|
|
String tmp;
|
|
|
|
Thread::errorString(tmp,in.error());
|
|
|
|
Debug(DebugNote,"XmlDocument error loading stream. I/O error %d '%s' [%p]",
|
|
|
|
in.error(),tmp.c_str(),this);
|
|
|
|
#endif
|
|
|
|
return XmlSaxParser::IOError;
|
|
|
|
}
|
|
|
|
return XmlSaxParser::NoError;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write this document to a data stream
|
|
|
|
int XmlDocument::write(Stream& out, bool escape, const String& indent,
|
2009-11-03 11:37:17 +00:00
|
|
|
const String& origIndent, bool completeOnly) const
|
2009-11-02 16:05:39 +00:00
|
|
|
{
|
|
|
|
String dump;
|
2009-11-03 11:37:17 +00:00
|
|
|
m_beforeRoot.toString(dump,escape,indent,origIndent);
|
2009-11-02 16:05:39 +00:00
|
|
|
if (m_root)
|
2009-11-03 11:37:17 +00:00
|
|
|
m_root->toString(dump,escape,indent,origIndent,completeOnly);
|
2013-01-25 14:42:33 +00:00
|
|
|
m_afterRoot.toString(dump,escape,indent,origIndent);
|
2009-11-02 16:05:39 +00:00
|
|
|
return out.writeData(dump);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Load a file and parse it
|
|
|
|
XmlSaxParser::Error XmlDocument::loadFile(const char* file, int* error)
|
|
|
|
{
|
|
|
|
reset();
|
|
|
|
if (TelEngine::null(file))
|
|
|
|
return XmlSaxParser::NoError;
|
|
|
|
m_file = file;
|
|
|
|
File f;
|
|
|
|
if (f.openPath(file))
|
|
|
|
return read(f,error);
|
|
|
|
if (error)
|
|
|
|
*error = f.error();
|
|
|
|
#ifdef DEBUG
|
|
|
|
String tmp;
|
|
|
|
Thread::errorString(tmp,f.error());
|
|
|
|
Debug(DebugNote,"XmlDocument error opening file '%s': %d '%s' [%p]",
|
|
|
|
file,f.error(),tmp.c_str(),this);
|
|
|
|
#endif
|
|
|
|
return XmlSaxParser::IOError;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Save this xml document in a file
|
|
|
|
int XmlDocument::saveFile(const char* file, bool esc, const String& indent,
|
2018-04-12 12:38:59 +00:00
|
|
|
bool completeOnly, const char* eoln) const
|
2009-11-02 16:05:39 +00:00
|
|
|
{
|
|
|
|
if (!file)
|
|
|
|
file = m_file;
|
|
|
|
if (!file)
|
|
|
|
return 0;
|
|
|
|
File f;
|
|
|
|
int err = 0;
|
|
|
|
if (f.openPath(file,true,false,true,false)) {
|
2018-04-12 12:38:59 +00:00
|
|
|
String eol(eoln);
|
|
|
|
if (eoln && !eol)
|
|
|
|
eol = "\r\n";
|
2009-11-02 16:05:39 +00:00
|
|
|
write(f,esc,eol,indent,completeOnly);
|
|
|
|
err = f.error();
|
|
|
|
// Add an empty line
|
2018-04-12 12:38:59 +00:00
|
|
|
if (err >= 0 && eol)
|
2009-11-02 16:05:39 +00:00
|
|
|
f.writeData((void*)eol.c_str(),eol.length());
|
|
|
|
}
|
|
|
|
else
|
|
|
|
err = f.error();
|
|
|
|
if (!err) {
|
|
|
|
XDebug(DebugAll,"XmlDocument saved file '%s' [%p]",file,this);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
|
|
String error;
|
|
|
|
Thread::errorString(error,err);
|
|
|
|
Debug(DebugNote,"Error saving XmlDocument to file '%s'. %d '%s' [%p]",
|
|
|
|
file,err,error.c_str(),this);
|
|
|
|
#endif
|
|
|
|
return f.error();
|
|
|
|
}
|
|
|
|
|
2018-04-10 10:33:00 +00:00
|
|
|
// Replaces all ${paramname} in document's components with the corresponding parameters
|
|
|
|
void XmlDocument::replaceParams(const NamedList& params)
|
|
|
|
{
|
|
|
|
if (m_root)
|
|
|
|
m_root->replaceParams(params);
|
|
|
|
m_beforeRoot.replaceParams(params);
|
|
|
|
m_afterRoot.replaceParams(params);
|
|
|
|
}
|
|
|
|
|
2009-11-02 16:05:39 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* XmlChild
|
|
|
|
*/
|
|
|
|
XmlChild::XmlChild()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* XmlElement
|
|
|
|
*/
|
|
|
|
XmlElement::XmlElement(const NamedList& element, bool empty, XmlParent* parent)
|
|
|
|
: m_element(element), m_prefixed(0),
|
|
|
|
m_parent(0), m_inheritedNs(0),
|
|
|
|
m_empty(empty), m_complete(empty)
|
|
|
|
{
|
|
|
|
XDebug(DebugAll,"XmlElement::XmlElement(%s,%u,%p) [%p]",
|
|
|
|
element.c_str(),empty,parent,this);
|
|
|
|
setPrefixed();
|
|
|
|
setParent(parent);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Copy constructor
|
|
|
|
XmlElement::XmlElement(const XmlElement& el)
|
2017-09-26 13:51:18 +00:00
|
|
|
: m_element(el.getElement()), m_prefixed(0),
|
2009-11-02 16:05:39 +00:00
|
|
|
m_parent(0), m_inheritedNs(0),
|
|
|
|
m_empty(el.empty()), m_complete(el.completed())
|
|
|
|
{
|
|
|
|
setPrefixed();
|
|
|
|
setInheritedNs(&el,true);
|
2017-09-26 13:51:18 +00:00
|
|
|
m_children.copy(el.m_children,this);
|
2009-11-02 16:05:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Create an empty xml element
|
|
|
|
XmlElement::XmlElement(const char* name, bool complete)
|
|
|
|
: m_element(name), m_prefixed(0),
|
|
|
|
m_parent(0), m_inheritedNs(0),
|
|
|
|
m_empty(true), m_complete(complete)
|
|
|
|
{
|
|
|
|
setPrefixed();
|
|
|
|
XDebug(DebugAll,"XmlElement::XmlElement(%s) [%p]",
|
2012-11-22 13:28:32 +00:00
|
|
|
m_element.c_str(),this);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create a new element with a text child
|
|
|
|
XmlElement::XmlElement(const char* name, const char* value, bool complete)
|
|
|
|
: m_element(name), m_prefixed(0),
|
|
|
|
m_parent(0), m_inheritedNs(0),
|
|
|
|
m_empty(true), m_complete(complete)
|
|
|
|
{
|
|
|
|
setPrefixed();
|
|
|
|
addText(value);
|
|
|
|
XDebug(DebugAll,"XmlElement::XmlElement(%s) [%p]",
|
2009-11-02 16:05:39 +00:00
|
|
|
m_element.c_str(),this);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Destructor
|
|
|
|
XmlElement::~XmlElement()
|
|
|
|
{
|
|
|
|
setInheritedNs();
|
2011-01-21 08:57:29 +00:00
|
|
|
TelEngine::destruct(m_prefixed);
|
2009-11-02 16:05:39 +00:00
|
|
|
XDebug(DebugAll,"XmlElement::~XmlElement() ( %s| %p )",
|
|
|
|
m_element.c_str(),this);
|
|
|
|
}
|
|
|
|
|
2013-03-27 15:31:34 +00:00
|
|
|
// Set element's unprefixed tag, don't change namespace prefix
|
|
|
|
void XmlElement::setUnprefixedTag(const String& s)
|
|
|
|
{
|
|
|
|
if (!s || s == unprefixedTag())
|
|
|
|
return;
|
|
|
|
if (TelEngine::null(m_prefixed))
|
|
|
|
m_element.assign(s);
|
|
|
|
else
|
|
|
|
m_element.assign(*m_prefixed + ":" + s);
|
|
|
|
setPrefixed();
|
|
|
|
}
|
|
|
|
|
2009-11-02 16:05:39 +00:00
|
|
|
// Set inherited namespaces from a given element. Reset them anyway
|
|
|
|
void XmlElement::setInheritedNs(const XmlElement* xml, bool inherit)
|
|
|
|
{
|
|
|
|
XDebug(DebugAll,"XmlElement(%s) setInheritedNs(%p,%s) [%p]",
|
|
|
|
tag(),xml,String::boolText(inherit),this);
|
|
|
|
TelEngine::destruct(m_inheritedNs);
|
|
|
|
if (!xml)
|
|
|
|
return;
|
|
|
|
addInheritedNs(xml->attributes());
|
|
|
|
if (!inherit)
|
|
|
|
return;
|
|
|
|
XmlElement* p = xml->parent();
|
|
|
|
bool xmlAdd = (p == 0);
|
|
|
|
while (p) {
|
|
|
|
addInheritedNs(p->attributes());
|
|
|
|
const NamedList* i = p->inheritedNs();
|
|
|
|
p = p->parent();
|
|
|
|
if (!p && i)
|
|
|
|
addInheritedNs(*i);
|
|
|
|
}
|
|
|
|
if (xmlAdd && xml->inheritedNs())
|
|
|
|
addInheritedNs(*xml->inheritedNs());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add inherited namespaces from a list
|
|
|
|
void XmlElement::addInheritedNs(const NamedList& list)
|
|
|
|
{
|
|
|
|
XDebug(DebugAll,"XmlElement(%s) addInheritedNs(%s) [%p]",tag(),list.c_str(),this);
|
|
|
|
unsigned int n = list.count();
|
|
|
|
for (unsigned int i = 0; i < n; i++) {
|
|
|
|
NamedString* ns = list.getParam(i);
|
|
|
|
if (!(ns && isXmlns(ns->name())))
|
|
|
|
continue;
|
|
|
|
// Avoid adding already overridden namespaces
|
|
|
|
if (m_element.getParam(ns->name()))
|
|
|
|
continue;
|
|
|
|
if (m_inheritedNs && m_inheritedNs->getParam(ns->name()))
|
|
|
|
continue;
|
2009-11-09 13:12:40 +00:00
|
|
|
// TODO: Check if attribute names are unique after adding the namespace
|
|
|
|
// See http://www.w3.org/TR/xml-names/ Section 6.3
|
2009-11-02 16:05:39 +00:00
|
|
|
if (!m_inheritedNs)
|
|
|
|
m_inheritedNs = new NamedList("");
|
|
|
|
XDebug(DebugAll,"XmlElement(%s) adding inherited %s=%s [%p]",
|
|
|
|
tag(),ns->name().c_str(),ns->c_str(),this);
|
|
|
|
m_inheritedNs->addParam(ns->name(),*ns);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Obtain the first text of this xml element
|
2013-08-14 13:33:43 +00:00
|
|
|
const String& XmlElement::getText() const
|
2009-11-02 16:05:39 +00:00
|
|
|
{
|
2013-08-14 13:33:43 +00:00
|
|
|
const XmlText* txt = 0;
|
2009-11-02 16:05:39 +00:00
|
|
|
for (ObjList* ob = getChildren().skipNull(); ob && !txt; ob = ob->skipNext())
|
|
|
|
txt = (static_cast<XmlChild*>(ob->get()))->xmlText();
|
|
|
|
return txt ? txt->getText() : String::empty();
|
|
|
|
}
|
|
|
|
|
2013-01-25 14:42:33 +00:00
|
|
|
XmlChild* XmlElement::getFirstChild()
|
|
|
|
{
|
|
|
|
if (!m_children.getChildren().skipNull())
|
|
|
|
return 0;
|
|
|
|
return static_cast<XmlChild*>(m_children.getChildren().skipNull()->get());
|
|
|
|
}
|
|
|
|
|
2013-09-12 08:40:58 +00:00
|
|
|
XmlText* XmlElement::setText(const char* text)
|
|
|
|
{
|
|
|
|
XmlText* txt = 0;
|
|
|
|
for (ObjList* o = getChildren().skipNull(); o; o = o->skipNext()) {
|
|
|
|
txt = (static_cast<XmlChild*>(o->get()))->xmlText();
|
|
|
|
if (txt)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (txt) {
|
|
|
|
if (!text)
|
|
|
|
return static_cast<XmlText*>(removeChild(txt));
|
|
|
|
txt->setText(text);
|
|
|
|
}
|
|
|
|
else if (text) {
|
|
|
|
txt = new XmlText(text);
|
|
|
|
addChild(txt);
|
|
|
|
}
|
|
|
|
return txt;
|
|
|
|
}
|
|
|
|
|
2009-11-02 16:05:39 +00:00
|
|
|
// Add a text child
|
|
|
|
void XmlElement::addText(const char* text)
|
|
|
|
{
|
|
|
|
if (!TelEngine::null(text))
|
|
|
|
addChild(new XmlText(text));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Retrieve the element's tag (without prefix) and namespace
|
|
|
|
bool XmlElement::getTag(const String*& tag, const String*& ns) const
|
|
|
|
{
|
|
|
|
if (!m_prefixed) {
|
|
|
|
tag = &m_element;
|
|
|
|
ns = xmlns();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
// Prefixed element
|
|
|
|
tag = &m_prefixed->name();
|
|
|
|
ns = xmlns();
|
|
|
|
return ns != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Append a new child
|
|
|
|
XmlSaxParser::Error XmlElement::addChild(XmlChild* child)
|
|
|
|
{
|
2014-02-05 11:42:17 +00:00
|
|
|
if (!child)
|
2009-11-02 16:05:39 +00:00
|
|
|
return XmlSaxParser::NoError;
|
2009-11-09 13:12:40 +00:00
|
|
|
// TODO: Check if a child element's attribute names are unique in the new context
|
|
|
|
// See http://www.w3.org/TR/xml-names/ Section 6.3
|
2009-11-02 16:05:39 +00:00
|
|
|
XmlSaxParser::Error err = m_children.addChild(child);
|
|
|
|
if (err == XmlSaxParser::NoError)
|
|
|
|
child->setParent(this);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove a child
|
|
|
|
XmlChild* XmlElement::removeChild(XmlChild* child, bool delObj)
|
|
|
|
{
|
|
|
|
return m_children.removeChild(child,delObj);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set this element's parent. Update inherited namespaces
|
|
|
|
void XmlElement::setParent(XmlParent* parent)
|
|
|
|
{
|
|
|
|
XDebug(DebugAll,"XmlElement(%s) setParent(%p) element=%s [%p]",
|
|
|
|
tag(),parent,String::boolText(parent != 0),this);
|
|
|
|
if (m_parent && m_parent->element()) {
|
|
|
|
// Reset inherited namespaces if the new parent is an element
|
|
|
|
// Otherwise set them from the old parent
|
|
|
|
if (parent && parent->element())
|
|
|
|
setInheritedNs(0);
|
|
|
|
else
|
|
|
|
setInheritedNs(m_parent->element());
|
|
|
|
}
|
|
|
|
m_parent = parent;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Obtain a string from this xml element
|
|
|
|
void XmlElement::toString(String& dump, bool esc, const String& indent,
|
2009-11-03 11:37:17 +00:00
|
|
|
const String& origIndent, bool completeOnly, const String* auth) const
|
2009-11-02 16:05:39 +00:00
|
|
|
{
|
|
|
|
XDebug(DebugAll,"XmlElement(%s) toString(%u,%s,%s,%u,%p) complete=%u [%p]",
|
2009-11-03 11:37:17 +00:00
|
|
|
tag(),esc,indent.c_str(),origIndent.c_str(),completeOnly,auth,m_complete,this);
|
2009-11-02 16:05:39 +00:00
|
|
|
if (!m_complete && completeOnly)
|
|
|
|
return;
|
|
|
|
String auxDump;
|
|
|
|
auxDump << indent << "<" << m_element;
|
|
|
|
int n = m_element.count();
|
|
|
|
for (int i = 0; i < n; i++) {
|
|
|
|
NamedString* ns = m_element.getParam(i);
|
|
|
|
if (!ns)
|
|
|
|
continue;
|
2009-11-17 08:28:46 +00:00
|
|
|
auxDump << " " << ns->name() << "=\"";
|
2009-11-02 16:05:39 +00:00
|
|
|
addAuth(auxDump,ns->name(),*ns,esc,auth);
|
|
|
|
auxDump << "\"";
|
|
|
|
}
|
|
|
|
int m = getChildren().count();
|
|
|
|
if (m_complete && !m)
|
|
|
|
auxDump << "/";
|
|
|
|
auxDump << ">";
|
|
|
|
if (m) {
|
2010-03-02 09:27:10 +00:00
|
|
|
// Avoid adding text on new line when text is the only child
|
|
|
|
XmlText* text = 0;
|
|
|
|
if (m == 1)
|
|
|
|
text = static_cast<XmlChild*>(getChildren().skipNull()->get())->xmlText();
|
|
|
|
if (!text)
|
|
|
|
m_children.toString(auxDump,esc,indent + origIndent,origIndent,completeOnly,auth,this);
|
|
|
|
else
|
|
|
|
text->toString(auxDump,esc,String::empty(),auth,this);
|
2009-11-02 16:05:39 +00:00
|
|
|
if (m_complete)
|
2010-03-02 09:27:10 +00:00
|
|
|
auxDump << (!text ? indent : String::empty()) << "</" << getName() << ">";
|
2009-11-02 16:05:39 +00:00
|
|
|
}
|
|
|
|
dump << auxDump;
|
|
|
|
}
|
|
|
|
|
2010-04-29 13:29:38 +00:00
|
|
|
// Copy element attributes to a list of parameters
|
|
|
|
unsigned int XmlElement::copyAttributes(NamedList& list, const String& prefix) const
|
|
|
|
{
|
|
|
|
unsigned int copy = 0;
|
|
|
|
unsigned int n = m_element.length();
|
|
|
|
for (unsigned int i = 0; i < n; i++) {
|
|
|
|
NamedString* ns = m_element.getParam(i);
|
|
|
|
if (!(ns && ns->name()))
|
|
|
|
continue;
|
|
|
|
list.addParam(prefix + ns->name(),*ns);
|
|
|
|
copy++;
|
|
|
|
}
|
|
|
|
return copy;
|
|
|
|
}
|
|
|
|
|
2013-09-12 08:40:58 +00:00
|
|
|
void XmlElement::setAttributes(NamedList& list, const String& prefix, bool skipPrefix)
|
|
|
|
{
|
|
|
|
if (prefix)
|
|
|
|
m_element.copySubParams(list,prefix,skipPrefix);
|
|
|
|
else
|
|
|
|
m_element.copyParams(list);
|
|
|
|
}
|
|
|
|
|
2009-11-02 16:05:39 +00:00
|
|
|
// Retrieve a namespace attribute. Search in parent or inherited for it
|
|
|
|
String* XmlElement::xmlnsAttribute(const String& name) const
|
|
|
|
{
|
|
|
|
String* tmp = getAttribute(name);
|
|
|
|
if (tmp)
|
|
|
|
return tmp;
|
|
|
|
XmlElement* p = parent();
|
|
|
|
if (p)
|
|
|
|
return p->xmlnsAttribute(name);
|
|
|
|
return m_inheritedNs ? m_inheritedNs->getParam(name) : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set the element's namespace
|
|
|
|
bool XmlElement::setXmlns(const String& name, bool addAttr, const String& value)
|
|
|
|
{
|
|
|
|
const String* cmp = name ? &name : &s_ns;
|
|
|
|
XDebug(DebugAll,"XmlElement(%s)::setXmlns(%s,%u,%s) [%p]",
|
|
|
|
tag(),cmp->c_str(),addAttr,value.c_str(),this);
|
|
|
|
if (*cmp == s_ns) {
|
|
|
|
if (m_prefixed) {
|
|
|
|
m_element.assign(m_prefixed->name());
|
|
|
|
setPrefixed();
|
|
|
|
// TODO: remove children and attributes prefixes
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (!m_prefixed || *m_prefixed != cmp) {
|
|
|
|
if (!m_prefixed)
|
|
|
|
m_element.assign(*cmp + ":" + tag());
|
|
|
|
else
|
|
|
|
m_element.assign(*cmp + ":" + m_prefixed->name());
|
|
|
|
setPrefixed();
|
|
|
|
// TODO: change children and attributes prefixes
|
|
|
|
}
|
|
|
|
if (!(addAttr && value))
|
|
|
|
return true;
|
|
|
|
String attr;
|
|
|
|
if (*cmp == s_ns)
|
|
|
|
attr = s_ns;
|
|
|
|
else
|
|
|
|
attr << s_nsPrefix << *cmp;
|
2009-11-09 13:12:40 +00:00
|
|
|
NamedString* ns = m_element.getParam(attr);
|
|
|
|
if (!ns && m_inheritedNs && m_inheritedNs->getParam(attr))
|
|
|
|
m_inheritedNs->clearParam(attr);
|
|
|
|
// TODO: Check if attribute names are unique after adding the namespace
|
|
|
|
// See http://www.w3.org/TR/xml-names/ Section 6.3
|
|
|
|
if (!ns)
|
|
|
|
m_element.addParam(attr,value);
|
|
|
|
else
|
|
|
|
*ns = value;
|
2009-11-02 16:05:39 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-04-10 10:33:00 +00:00
|
|
|
// Replaces all ${paramname} in element's attributes and children with the
|
|
|
|
// corresponding parameters
|
|
|
|
void XmlElement::replaceParams(const NamedList& params)
|
|
|
|
{
|
|
|
|
m_children.replaceParams(params);
|
|
|
|
for (ObjList* o = m_element.paramList()->skipNull(); o; o = o->skipNext())
|
|
|
|
params.replaceParams(*static_cast<String*>(o->get()));
|
|
|
|
}
|
|
|
|
|
2011-02-18 16:04:07 +00:00
|
|
|
// Build an XML element from a list parameter
|
|
|
|
XmlElement* XmlElement::param2xml(NamedString* param, const String& tag, bool copyXml)
|
|
|
|
{
|
|
|
|
if (!(param && param->name() && tag))
|
|
|
|
return 0;
|
|
|
|
XmlElement* xml = new XmlElement(tag);
|
2011-06-03 12:55:03 +00:00
|
|
|
xml->setAttribute(s_name,param->name());
|
|
|
|
xml->setAttributeValid(YSTRING("value"),*param);
|
2011-02-18 16:04:07 +00:00
|
|
|
NamedPointer* np = YOBJECT(NamedPointer,param);
|
|
|
|
if (!(np && np->userData()))
|
|
|
|
return xml;
|
|
|
|
DataBlock* db = YOBJECT(DataBlock,np->userData());
|
|
|
|
if (db) {
|
2011-06-03 12:55:03 +00:00
|
|
|
xml->setAttribute(s_type,"DataBlock");
|
2011-02-18 16:04:07 +00:00
|
|
|
Base64 b(db->data(),db->length(),false);
|
|
|
|
String tmp;
|
|
|
|
b.encode(tmp);
|
|
|
|
b.clear(false);
|
|
|
|
xml->addText(tmp);
|
|
|
|
return xml;
|
|
|
|
}
|
|
|
|
XmlElement* element = YOBJECT(XmlElement,np->userData());
|
|
|
|
if (element) {
|
2011-06-03 12:55:03 +00:00
|
|
|
xml->setAttribute(s_type,"XmlElement");
|
2011-02-18 16:04:07 +00:00
|
|
|
if (!copyXml) {
|
|
|
|
np->takeData();
|
|
|
|
xml->addChild(element);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
xml->addChild(new XmlElement(*element));
|
|
|
|
return xml;
|
|
|
|
}
|
|
|
|
NamedList* list = YOBJECT(NamedList,np->userData());
|
|
|
|
if (list) {
|
2011-06-03 12:55:03 +00:00
|
|
|
xml->setAttribute(s_type,"NamedList");
|
2011-02-18 16:04:07 +00:00
|
|
|
xml->addText(list->c_str());
|
|
|
|
unsigned int n = list->length();
|
|
|
|
for (unsigned int i = 0; i < n; i++)
|
|
|
|
xml->addChild(param2xml(list->getParam(i),tag,copyXml));
|
|
|
|
return xml;
|
|
|
|
}
|
|
|
|
return xml;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Build a list parameter from xml element
|
|
|
|
NamedString* XmlElement::xml2param(XmlElement* xml, const String* tag, bool copyXml)
|
|
|
|
{
|
2011-06-03 12:55:03 +00:00
|
|
|
const char* name = xml ? xml->attribute(s_name) : 0;
|
2011-02-18 16:04:07 +00:00
|
|
|
if (TelEngine::null(name))
|
|
|
|
return 0;
|
|
|
|
GenObject* gen = 0;
|
2011-06-03 12:55:03 +00:00
|
|
|
String* type = xml->getAttribute(s_type);
|
2011-02-18 16:04:07 +00:00
|
|
|
if (type) {
|
2011-06-03 12:55:03 +00:00
|
|
|
if (*type == YSTRING("DataBlock")) {
|
2011-02-18 16:04:07 +00:00
|
|
|
gen = new DataBlock;
|
|
|
|
const String& text = xml->getText();
|
|
|
|
Base64 b((void*)text.c_str(),text.length(),false);
|
|
|
|
b.decode(*(static_cast<DataBlock*>(gen)));
|
|
|
|
b.clear(false);
|
|
|
|
}
|
2011-06-03 12:55:03 +00:00
|
|
|
else if (*type == YSTRING("XmlElement")) {
|
2011-02-18 16:04:07 +00:00
|
|
|
if (!copyXml)
|
|
|
|
gen = xml->pop();
|
|
|
|
else {
|
|
|
|
XmlElement* tmp = xml->findFirstChild();
|
|
|
|
if (tmp)
|
|
|
|
gen = new XmlElement(*tmp);
|
|
|
|
}
|
|
|
|
}
|
2011-06-03 12:55:03 +00:00
|
|
|
else if (*type == YSTRING("NamedList")) {
|
2011-02-18 16:04:07 +00:00
|
|
|
gen = new NamedList(xml->getText());
|
|
|
|
xml2param(*(static_cast<NamedList*>(gen)),xml,tag,copyXml);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
Debug(DebugStub,"XmlElement::xml2param: unhandled type=%s",type->c_str());
|
|
|
|
}
|
|
|
|
if (!gen)
|
2011-06-03 12:55:03 +00:00
|
|
|
return new NamedString(name,xml->attribute(YSTRING("value")));
|
|
|
|
return new NamedPointer(name,gen,xml->attribute(YSTRING("value")));
|
2011-02-18 16:04:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Build and add list parameters from XML element children
|
|
|
|
void XmlElement::xml2param(NamedList& list, XmlElement* parent, const String* tag,
|
|
|
|
bool copyXml)
|
|
|
|
{
|
|
|
|
if (!parent)
|
|
|
|
return;
|
|
|
|
XmlElement* ch = 0;
|
|
|
|
while (0 != (ch = parent->findNextChild(ch,tag))) {
|
|
|
|
NamedString* ns = xml2param(ch,tag,copyXml);
|
|
|
|
if (ns)
|
|
|
|
list.addParam(ns);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-11-02 16:05:39 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* XmlComment
|
|
|
|
*/
|
|
|
|
// Constructor
|
|
|
|
XmlComment::XmlComment(const String& comm)
|
|
|
|
: m_comment(comm)
|
|
|
|
{
|
|
|
|
XDebug(DebugAll,"XmlComment::XmlComment(const String& comm) ( %s| %p )",
|
|
|
|
m_comment.c_str(),this);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Copy Constructor
|
|
|
|
XmlComment::XmlComment(const XmlComment& comm)
|
|
|
|
: m_comment(comm.getComment())
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
// Destructor
|
|
|
|
XmlComment::~XmlComment()
|
|
|
|
{
|
|
|
|
XDebug(DebugAll,"XmlComment::~XmlComment() ( %s| %p )",
|
|
|
|
m_comment.c_str(),this);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Obtain string representation of this xml comment
|
|
|
|
void XmlComment::toString(String& dump, const String& indent) const
|
|
|
|
{
|
|
|
|
dump << indent << "<!--" << getComment() << "-->";
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* XmlCData
|
|
|
|
*/
|
|
|
|
// Constructor
|
|
|
|
XmlCData::XmlCData(const String& data)
|
|
|
|
: m_data(data)
|
|
|
|
{
|
|
|
|
XDebug(DebugAll,"XmlCData::XmlCData(const String& data) ( %s| %p )",
|
|
|
|
m_data.c_str(),this);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Copy Constructor
|
|
|
|
XmlCData::XmlCData(const XmlCData& data)
|
|
|
|
: m_data(data.getCData())
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
// Destructor
|
|
|
|
XmlCData::~XmlCData()
|
|
|
|
{
|
|
|
|
XDebug(DebugAll,"XmlCData::~XmlCData() ( %s| %p )",
|
|
|
|
m_data.c_str(),this);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Obtain string representation of this xml Cdata
|
|
|
|
void XmlCData::toString(String& dump, const String& indent) const
|
|
|
|
{
|
|
|
|
dump << indent << "<![CDATA[" << getCData() << "]]>";
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* XmlText
|
|
|
|
*/
|
|
|
|
// Constructor
|
|
|
|
XmlText::XmlText(const String& text)
|
|
|
|
: m_text(text)
|
|
|
|
{
|
|
|
|
XDebug(DebugAll,"XmlText::XmlText(%s) [%p]",m_text.c_str(),this);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Copy Constructor
|
|
|
|
XmlText::XmlText(const XmlText& text)
|
|
|
|
: m_text(text.getText())
|
|
|
|
{
|
|
|
|
XDebug(DebugAll,"XmlText::XmlText(%p,%s) [%p]",
|
|
|
|
&text,TelEngine::c_safe(text.getText()),this);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Destructor
|
|
|
|
XmlText::~XmlText()
|
|
|
|
{
|
|
|
|
XDebug(DebugAll,"XmlText::~XmlText [%p]",this);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Obtain string representation of this xml text
|
|
|
|
void XmlText::toString(String& dump, bool esc, const String& indent,
|
|
|
|
const String* auth, const XmlElement* parent) const
|
|
|
|
{
|
|
|
|
dump << indent;
|
|
|
|
if (auth)
|
|
|
|
addAuth(dump,parent ? parent->toString() : String::empty(),m_text,esc,auth);
|
2009-12-03 13:21:37 +00:00
|
|
|
else if (esc)
|
|
|
|
XmlSaxParser::escape(dump,m_text);
|
2009-11-02 16:05:39 +00:00
|
|
|
else
|
|
|
|
dump << m_text;
|
|
|
|
}
|
|
|
|
|
2013-01-25 14:42:33 +00:00
|
|
|
bool XmlText::onlySpaces()
|
|
|
|
{
|
|
|
|
if (!m_text)
|
|
|
|
return true;
|
|
|
|
const char *s = m_text;
|
|
|
|
unsigned int i = 0;
|
|
|
|
for (;i < m_text.length();i++) {
|
|
|
|
if (s[i] == ' ' || s[i] == '\t' || s[i] == '\v' || s[i] == '\f' || s[i] == '\r' || s[i] == '\n')
|
|
|
|
continue;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
2009-11-02 16:05:39 +00:00
|
|
|
|
2018-04-10 10:33:00 +00:00
|
|
|
// Replaces all ${paramname} in text with the corresponding parameters
|
|
|
|
void XmlText::replaceParams(const NamedList& params)
|
|
|
|
{
|
|
|
|
params.replaceParams(m_text);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-11-02 16:05:39 +00:00
|
|
|
/*
|
|
|
|
* XmlDoctype
|
|
|
|
*/
|
|
|
|
// Constructor
|
|
|
|
XmlDoctype::XmlDoctype(const String& doctype)
|
|
|
|
: m_doctype(doctype)
|
|
|
|
{
|
|
|
|
XDebug(DebugAll,"XmlDoctype::XmlDoctype(const String& doctype) ( %s| %p )",
|
|
|
|
m_doctype.c_str(),this);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Copy Constructor
|
|
|
|
XmlDoctype::XmlDoctype(const XmlDoctype& doctype)
|
|
|
|
: m_doctype(doctype.getDoctype())
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
// Destructor
|
|
|
|
XmlDoctype::~XmlDoctype()
|
|
|
|
{
|
|
|
|
XDebug(DebugAll,"XmlDoctype::~XmlDoctype() ( %s| %p )",
|
|
|
|
m_doctype.c_str(),this);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Obtain string representation of this xml doctype
|
|
|
|
void XmlDoctype::toString(String& dump, const String& indent) const
|
|
|
|
{
|
|
|
|
dump << indent << "<!DOCTYPE " << m_doctype << ">";
|
|
|
|
}
|
|
|
|
|
|
|
|
/* vi: set ts=8 sw=4 sts=4 noet: */
|