yate/libs/yjingle/xmlparser.cpp

330 lines
8.7 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},
{"handshake", Handshake},
{"iq", Iq},
{"message", Message},
{"presence", Presence},
{"error", Error},
{"query", Query},
{"session", Jingle},
{"description", Description},
{"payload-type", PayloadType},
{"transport", Transport},
{"candidate", Candidate},
{"body", Body},
{"feature", Feature},
{"bind", Bind},
{"resource", Resource},
{"dtmf", Dtmf},
{"dtmf-method", DtmfMethod},
{"command", Command},
{0,0}
};
XMLElement::XMLElement()
: m_type(StreamEnd), m_owner(true), m_element(0)
{
m_element = new TiXmlElement(typeName(m_type));
// XDebug(DebugAll,"XMLElement::XMLElement [%p]. Name: '%s'",this,name());
}
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().c_str(),ns->c_str());
}
}
setType();
// XDebug(DebugAll,"XMLElement::XMLElement [%p]. Name: '%s'",this,name);
}
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().c_str(),ns->c_str());
}
}
// XDebug(DebugAll,"XMLElement::XMLElement [%p]. Name: '%s'",this,name());
}
XMLElement::XMLElement(TiXmlElement* element, bool owner)
: m_type(Unknown), m_owner(owner), m_element(element)
{
setType();
// XDDebug(DebugAll,"XMLElement::XMLElement [%p]. Name: '%s'",this,name());
}
XMLElement::~XMLElement()
{
if (m_owner && m_element)
delete m_element;
// XDebug(DebugAll,"XMLElement::~XMLElement [%p]. Name: '%s'",this,name());
}
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());
}
}
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)
{
if (valid() && name)
return m_element->Attribute(name);
return 0;
}
bool XMLElement::hasAttribute(const char* name, const char* value)
{
String tmp;
if (getAttribute(name,tmp))
return tmp == value;
return false;
}
const char* XMLElement::getText()
{
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);
}
}
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(const XMLElement* element, const char* name)
{
if (!valid())
return 0;
TiXmlElement* tiElement = element->get();
if (!(element && tiElement))
return findFirstChild(name);
for (;;) {
if (name && *name)
tiElement = tiElement->NextSiblingElement(name);
else
tiElement = tiElement->NextSiblingElement();
if (!tiElement)
break;
return new XMLElement(tiElement,false);
}
return 0;
}
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;
}
else
// 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: */