488 lines
14 KiB
C++
488 lines
14 KiB
C++
/**
|
|
* xmlparser.cpp
|
|
* Yet Another XMPP Stack
|
|
* This file is part of the YATE Project http://YATE.null.ro
|
|
*
|
|
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
|
* Copyright (C) 2004-2006 Null Team
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include <xmlparser.h>
|
|
#include <string.h>
|
|
|
|
using namespace TelEngine;
|
|
|
|
/**
|
|
* XMLElement
|
|
*/
|
|
TokenDict XMLElement::s_names[] = {
|
|
{"stream:stream", StreamStart},
|
|
{"/stream:stream", StreamEnd},
|
|
{"stream:error", StreamError},
|
|
{"stream:features", StreamFeatures},
|
|
{"register", Register},
|
|
{"starttls", Starttls},
|
|
{"handshake", Handshake},
|
|
{"auth", Auth},
|
|
{"challenge", Challenge},
|
|
{"abort", Abort},
|
|
{"aborted", Aborted},
|
|
{"response", Response},
|
|
{"proceed", Proceed},
|
|
{"success", Success},
|
|
{"failure", Failure},
|
|
{"mechanisms", Mechanisms},
|
|
{"mechanism", Mechanism},
|
|
{"session", Session},
|
|
{"iq", Iq},
|
|
{"message", Message},
|
|
{"presence", Presence},
|
|
{"error", Error},
|
|
{"query", Query},
|
|
{"vCard", VCard},
|
|
{"jingle", Jingle},
|
|
{"description", Description},
|
|
{"payload-type", PayloadType},
|
|
{"transport", Transport},
|
|
{"candidate", Candidate},
|
|
{"body", Body},
|
|
{"subject", Subject},
|
|
{"feature", Feature},
|
|
{"bind", Bind},
|
|
{"resource", Resource},
|
|
{"transfer", Transfer},
|
|
{"hold", Hold},
|
|
{"active", Active},
|
|
{"ringing", Ringing},
|
|
{"mute", Mute},
|
|
{"registered", Registered},
|
|
{"remove", Remove},
|
|
{"jid", Jid},
|
|
{"username", Username},
|
|
{"password", Password},
|
|
{"digest", Digest},
|
|
{"required", Required},
|
|
{"dtmf", Dtmf},
|
|
{"dtmf-method", DtmfMethod},
|
|
{"command", Command},
|
|
{"text", Text},
|
|
{"item", Item},
|
|
{"group", Group},
|
|
{"reason", Reason},
|
|
{"content", Content},
|
|
{"parameter", Parameter},
|
|
{"crypto", Crypto},
|
|
{"crypto-required", CryptoRequired},
|
|
{"trying", Trying},
|
|
{"received", Received},
|
|
{"file", File},
|
|
{"offer", Offer},
|
|
{"request", Request},
|
|
{"streamhost", StreamHost},
|
|
{"streamhost-used", StreamHostUsed},
|
|
{0,0}
|
|
};
|
|
|
|
XMLElement::XMLElement()
|
|
: m_type(StreamEnd), m_owner(true), m_element(0)
|
|
{
|
|
m_element = new TiXmlElement(typeName(m_type));
|
|
// XDebug(DebugAll,"XMLElement::XMLElement(%s) [%p]",name(),this);
|
|
}
|
|
|
|
XMLElement::XMLElement(const XMLElement& src)
|
|
: GenObject(),
|
|
m_type(Invalid), m_owner(true), m_element(0)
|
|
{
|
|
TiXmlElement* e = src.get();
|
|
if (!e)
|
|
return;
|
|
m_element = new TiXmlElement(*e);
|
|
setType();
|
|
// XDebug(DebugAll,"XMLElement::XMLElement(%s) [%p]",name(),this);
|
|
}
|
|
|
|
// Partially build this element from another one.
|
|
// Copy name and 'to', 'from', 'type', 'id' attributes
|
|
XMLElement::XMLElement(const XMLElement& src, bool response, bool result)
|
|
: m_type(src.type()), m_owner(true), m_element(0)
|
|
{
|
|
m_element = new TiXmlElement(src.name());
|
|
if (response) {
|
|
setAttributeValid("from",src.getAttribute("to"));
|
|
setAttributeValid("to",src.getAttribute("from"));
|
|
setAttribute("type",result?"result":"error");
|
|
}
|
|
else {
|
|
setAttributeValid("from",src.getAttribute("from"));
|
|
setAttributeValid("to",src.getAttribute("to"));
|
|
setAttributeValid("type",src.getAttribute("type"));
|
|
}
|
|
setAttributeValid("id",src.getAttribute("id"));
|
|
// XDebug(DebugAll,"XMLElement::XMLElement(%s) [%p]",name(),this);
|
|
}
|
|
|
|
XMLElement::XMLElement(const char* name, NamedList* attributes,
|
|
const char* text)
|
|
: m_type(Unknown), m_owner(true), m_element(0)
|
|
{
|
|
m_element = new TiXmlElement(name);
|
|
// Set text
|
|
if (text)
|
|
m_element->LinkEndChild(new TiXmlText(text));
|
|
// Add attributes
|
|
if (attributes) {
|
|
u_int32_t attr_count = attributes->length();
|
|
for (u_int32_t i = 0; i < attr_count; i++) {
|
|
NamedString* ns = attributes->getParam(i);
|
|
if (!ns)
|
|
continue;
|
|
m_element->SetAttribute(ns->name().safe(),ns->safe());
|
|
}
|
|
}
|
|
setType();
|
|
// XDebug(DebugAll,"XMLElement::XMLElement(%s) [%p]",this->name(),this);
|
|
}
|
|
|
|
XMLElement::XMLElement(Type type, NamedList* attributes,
|
|
const char* text)
|
|
: m_type(type), m_owner(true), m_element(0)
|
|
{
|
|
m_element = new TiXmlElement(typeName(m_type));
|
|
// Set text
|
|
if (text)
|
|
m_element->LinkEndChild(new TiXmlText(text));
|
|
// Add attributes
|
|
if (attributes) {
|
|
u_int32_t attr_count = attributes->length();
|
|
for (u_int32_t i = 0; i < attr_count; i++) {
|
|
NamedString* ns = attributes->getParam(i);
|
|
if (!ns)
|
|
continue;
|
|
m_element->SetAttribute(ns->name().safe(),ns->safe());
|
|
}
|
|
}
|
|
// XDebug(DebugAll,"XMLElement::XMLElement(%s) [%p]",name(),this);
|
|
}
|
|
|
|
XMLElement::XMLElement(TiXmlElement* element, bool owner)
|
|
: m_type(Unknown), m_owner(owner), m_element(element)
|
|
{
|
|
setType();
|
|
// XDebug(DebugAll,"XMLElement::XMLElement(%s) owner=%u [%p]",
|
|
// name(),m_owner,this);
|
|
}
|
|
|
|
// Build this XML element from a list containing name, attributes and text.
|
|
XMLElement::XMLElement(NamedList& src, const char* prefix)
|
|
: m_type(Unknown), m_owner(true), m_element(0)
|
|
{
|
|
m_element = new TiXmlElement(src.getValue(prefix));
|
|
DDebug(DebugAll,"XMLElement(%s) src=%s prefix=%s [%p]",
|
|
m_name.c_str(),src.c_str(),prefix,this);
|
|
String pref(String(prefix) + ".");
|
|
// Set text
|
|
const char* text = src.getValue(pref);
|
|
if (text)
|
|
m_element->LinkEndChild(new TiXmlText(text));
|
|
// Add attributes
|
|
unsigned int n = src.count();
|
|
for (unsigned int i = 0; i < n; i++) {
|
|
NamedString* ns = src.getParam(i);
|
|
if (ns && ns->name().startsWith(pref))
|
|
setAttribute(ns->name().substr(pref.length()),*ns);
|
|
}
|
|
setType();
|
|
}
|
|
|
|
XMLElement::~XMLElement()
|
|
{
|
|
if (m_owner && m_element)
|
|
delete m_element;
|
|
// XDebug(DebugAll,"XMLElement::~XMLElement(%s) owner=%u [%p]",
|
|
// m_name.c_str(),m_owner,this);
|
|
}
|
|
|
|
void XMLElement::toString(String& dest, bool unclose) const
|
|
{
|
|
dest.clear();
|
|
if (valid()) {
|
|
TIXML_OSTREAM xmlStr;
|
|
m_element->StreamOut(&xmlStr,unclose);
|
|
dest.assign(xmlStr.c_str(),xmlStr.length());
|
|
}
|
|
}
|
|
|
|
// Put this element's name, text and attributes to a list of parameters
|
|
void XMLElement::toList(NamedList& dest, const char* prefix)
|
|
{
|
|
XDebug(DebugAll,"XMLElement(%s) to list=%s prefix=%s [%p]",
|
|
m_name.c_str(),dest.c_str(),prefix,this);
|
|
dest.addParam(prefix,name());
|
|
String pref(String(prefix) + ".");
|
|
const char* tmp = getText();
|
|
if (tmp)
|
|
dest.addParam(pref,tmp);
|
|
for (const TiXmlAttribute* a = firstAttribute(); a; a = a->Next())
|
|
dest.addParam(pref + a->Name(),a->Value());
|
|
}
|
|
|
|
void XMLElement::setAttribute(const char* name, const char* value)
|
|
{
|
|
if (!(valid() && name && value))
|
|
return;
|
|
m_element->SetAttribute(name,value);
|
|
}
|
|
|
|
const char* XMLElement::getAttribute(const char* name) const
|
|
{
|
|
if (valid() && name)
|
|
return m_element->Attribute(name);
|
|
return 0;
|
|
}
|
|
|
|
// Fill a list with element's attributes
|
|
void XMLElement::getAttributes(NamedList& dest) const
|
|
{
|
|
if (!valid())
|
|
return;
|
|
const TiXmlAttribute* attr = m_element->FirstAttribute();
|
|
for (; attr; attr = attr->Next())
|
|
dest.addParam(attr->Name(),attr->Value());
|
|
}
|
|
|
|
bool XMLElement::hasAttribute(const char* name, const char* value) const
|
|
{
|
|
String tmp;
|
|
if (getAttribute(name,tmp))
|
|
return tmp == value;
|
|
return false;
|
|
}
|
|
|
|
const char* XMLElement::getText() const
|
|
{
|
|
if (valid())
|
|
return m_element->GetText();
|
|
return 0;
|
|
}
|
|
|
|
void XMLElement::addChild(XMLElement* element)
|
|
{
|
|
if (valid() && element) {
|
|
TiXmlElement* tiElement = element->releaseOwnership();
|
|
if (tiElement)
|
|
m_element->LinkEndChild(tiElement);
|
|
}
|
|
TelEngine::destruct(element);
|
|
}
|
|
|
|
// Find the first child element of this one.
|
|
// Remove it from the children list.
|
|
// This element must own its TiXmlElement pointer.
|
|
XMLElement* XMLElement::removeChild(const char* name)
|
|
{
|
|
if (!valid() && m_owner)
|
|
return 0;
|
|
TiXmlElement* element;
|
|
if (name && *name)
|
|
element = ((TiXmlNode*)m_element)->FirstChildElement(name);
|
|
else
|
|
element = ((TiXmlNode*)m_element)->FirstChildElement();
|
|
if (!element)
|
|
return 0;
|
|
m_element->RemoveChild(element,false);
|
|
return new XMLElement(element,true);
|
|
}
|
|
|
|
XMLElement* XMLElement::findFirstChild(const char* name)
|
|
{
|
|
if (!valid())
|
|
return 0;
|
|
TiXmlElement* element;
|
|
if (name && *name)
|
|
element = ((TiXmlNode*)m_element)->FirstChildElement(name);
|
|
else
|
|
element = ((TiXmlNode*)m_element)->FirstChildElement();
|
|
if (element)
|
|
return new XMLElement(element,false);
|
|
return 0;
|
|
}
|
|
|
|
XMLElement* XMLElement::findNextChild(XMLElement* element, const char* name)
|
|
{
|
|
if (!valid()) {
|
|
TelEngine::destruct(element);
|
|
return 0;
|
|
}
|
|
TiXmlElement* tiElement = element ? element->get() : 0;
|
|
XMLElement* result = 0;
|
|
if (tiElement) {
|
|
if (name && *name)
|
|
tiElement = tiElement->NextSiblingElement(name);
|
|
else
|
|
tiElement = tiElement->NextSiblingElement();
|
|
if (tiElement)
|
|
result = new XMLElement(tiElement,false);
|
|
}
|
|
else
|
|
result = findFirstChild(name);
|
|
TelEngine::destruct(element);
|
|
return result;
|
|
}
|
|
|
|
// Get an xml element from a list's parameter
|
|
XMLElement* XMLElement::getXml(NamedList& list, bool stole,
|
|
const char* name, const char* value)
|
|
{
|
|
NamedString* ns = list.getParam(name);
|
|
if (!ns)
|
|
return 0;
|
|
NamedPointer* np = static_cast<NamedPointer*>(ns->getObject("NamedPointer"));
|
|
if (!(np && np->userObject("XMLElement")) || (value && *np != value))
|
|
return 0;
|
|
if (stole)
|
|
return static_cast<XMLElement*>(np->takeData());
|
|
return static_cast<XMLElement*>(np->userData());
|
|
}
|
|
|
|
TiXmlElement* XMLElement::releaseOwnership()
|
|
{
|
|
if (!(m_owner && m_element))
|
|
return 0;
|
|
TiXmlElement* tiElement = m_element;
|
|
m_element = 0;
|
|
m_owner = false;
|
|
return tiElement;
|
|
}
|
|
|
|
/**
|
|
* XMLParser
|
|
*/
|
|
const char* skipBlanks(const char* p)
|
|
{
|
|
for (; *p == ' ' || *p == '\r' || *p == '\n' || *p == '\t'; p++);
|
|
return p;
|
|
}
|
|
|
|
u_int32_t XMLParser::s_maxDataBuffer = XMLPARSER_MAXDATABUFFER;
|
|
TiXmlEncoding XMLParser::s_xmlEncoding = TIXML_ENCODING_UTF8;
|
|
|
|
bool XMLParser::consume(const char* data, u_int32_t len)
|
|
{
|
|
// * Input serialization is assumed to be done by the source
|
|
// * Lock is necessary only when modifying TiXMLDocument
|
|
// 1. Add data to buffer. Adjust stream start tag
|
|
// 2. Call TiXMLDocument::Parse()
|
|
// 3. Check result
|
|
|
|
// 1
|
|
String tmp(data,len);
|
|
m_buffer << tmp;
|
|
// XDebug(DebugAll,"XMLParser::consume. Buffer: '%s'.",m_buffer.c_str());
|
|
if (m_buffer.length() > s_maxDataBuffer) {
|
|
SetError(TIXML_ERROR_BUFFEROVERRUN,0,0,s_xmlEncoding);
|
|
return false;
|
|
}
|
|
// Check for start element.
|
|
// Adjust termination tag in order to pass correct data to the parser
|
|
if (m_findstart) {
|
|
int start = m_buffer.find("stream:stream");
|
|
// Don't process until found: 'stream:stream' ... '>'
|
|
if (start == -1)
|
|
return true;
|
|
int end = m_buffer.find(">",start);
|
|
if (end == -1)
|
|
return true;
|
|
// Check if we received an end stream
|
|
// Search for a '/' before 'stream:stream'
|
|
int i_end = m_buffer.find("/");
|
|
bool b_end = false;
|
|
if (i_end != -1 && i_end < start) {
|
|
const char* pEnd = m_buffer.c_str() + i_end + 1;
|
|
const char* pStart = m_buffer.c_str() + start;
|
|
if (pStart == skipBlanks(pEnd))
|
|
b_end = true;
|
|
}
|
|
if (!b_end) {
|
|
m_findstart = false;
|
|
if (i_end > start && i_end < end) {
|
|
// We found a '/' between 'stream:stream' and '>'
|
|
// Do nothing: The tag is already closed or the element
|
|
// is invalid and the parser will fail
|
|
}
|
|
// Found: insert '/'
|
|
String tmp = m_buffer.substr(end,m_buffer.length() - end);
|
|
m_buffer = m_buffer.substr(0,end) << " /" << tmp;
|
|
}
|
|
// If we received end stream tag before start stream tag
|
|
// The element will be parsed: the upper layer will deal with it
|
|
}
|
|
if (!m_buffer)
|
|
return true;
|
|
lock();
|
|
const char* src = m_buffer.c_str();
|
|
const char* ret = Parse(src,0,s_xmlEncoding);
|
|
unlock();
|
|
// Remove processed data from bufer
|
|
if (ret > src && ret <= src + m_buffer.length())
|
|
m_buffer = m_buffer.substr(ret - src);
|
|
// XDebug(DebugAll,
|
|
// "XMLParser::consume. Data parsed. Buffer: '%s'. Error: '%s'.",
|
|
// m_buffer.c_str(),ErrorDesc());
|
|
// 3
|
|
if (ErrorId() && ErrorId() != TIXML_ERROR_INCOMPLETE)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
XMLElement* XMLParser::extract()
|
|
{
|
|
// Find the first TiXMLElement
|
|
// Check if we received stream end
|
|
// Remove any other object type
|
|
for (;;) {
|
|
TiXmlNode* node = FirstChild();
|
|
if (!node)
|
|
break;
|
|
// Check for XML elements
|
|
if (node->ToElement()) {
|
|
RemoveChild(node,false);
|
|
return new XMLElement(node->ToElement(),true);
|
|
}
|
|
// Check for end stream
|
|
// For Tiny XML '</...>' is an unknown element
|
|
if (node->ToUnknown() &&
|
|
XMLElement::isType(node->Value(),XMLElement::StreamEnd)) {
|
|
RemoveChild(node,true);
|
|
return new XMLElement();
|
|
}
|
|
// Remove non-element
|
|
RemoveChild(node,true);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void XMLParser::reset()
|
|
{
|
|
Lock lock(this);
|
|
TiXmlDocument::Clear();
|
|
m_buffer.clear();
|
|
m_findstart = true;
|
|
}
|
|
|
|
/* vi: set ts=8 sw=4 sts=4 noet: */
|