/** * xmlparser.h * 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. */ #ifndef __XMLPARSER_H #define __XMLPARSER_H #include #include #ifdef _WINDOWS #ifdef LIBYJINGLE_EXPORTS #define YJINGLE_API __declspec(dllexport) #else #ifndef LIBYJINGLE_STATIC #define YJINGLE_API __declspec(dllimport) #endif #endif #endif /* _WINDOWS */ #ifndef YJINGLE_API #define YJINGLE_API #endif /** * Holds all Telephony Engine related classes. */ namespace TelEngine { #define XMLPARSER_MAXDATABUFFER 8192 // Default max data buffer class XMLElement; class XMLParser; class XMLElementOut; /** * This class holds an XML element * @short An XML element */ class YJINGLE_API XMLElement : public GenObject { friend class XMLParser; public: /** * Element type as enumeration */ enum Type { // *** Stream related elements StreamStart, // stream:stream StreamEnd, // /stream:stream StreamError, // stream::error StreamFeatures, // stream::features 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 // *** Stanzas Iq, // iq Message, // message Presence, // presence // *** Stanza children Error, // error Query, // query VCard, // vCard Jingle, // session // Description Description, // description PayloadType, // payload-type // Transport Transport, // transport Candidate, // candidate // Message Body, // body Subject, // subject // Resources Feature, // feature Bind, // bind Resource, // resource // Jingle session info Transfer, // transfer Hold, // hold Active, // active Ringing, // ringing Mute, // mute // Miscellaneous Registered, // registered Remove, // remove Jid, // jid Username, // username Password, // password Digest, // digest Required, // required Dtmf, // dtmf DtmfMethod, // dtmf-method Command, // command Text, // text Item, // item Group, // group Reason, // reason Content, // content Parameter, // parameter Crypto, // crypto CryptoRequired, // crypto-required Trying, // trying Received, // received File, // file Offer, // offer Request, // request StreamHost, // streamhost StreamHostUsed, // streamhost-used Unknown, // Any text Invalid, // m_element is 0 }; /** * Constructor. * Constructs a StreamEnd element */ XMLElement(); /** * Copy constructor * @param src Source element */ XMLElement(const XMLElement& src); /** * Constructor. Partially build this element from another one. * Copy name and 'to', 'from', 'type', 'id' attributes * @param src Source element * @param response True to reverse 'to' and 'from' attributes * @param result True to set type to "result", false to set it to "error". * Ignored if response is false */ XMLElement(const XMLElement& src, bool response, bool result); /** * Constructor. * Constructs an XML element with a TiXmlElement element with the given name. * Used for outgoing elements * @param name The element's name * @param attributes Optional list of attributes * @param text Optional text for the XML element */ XMLElement(const char* name, NamedList* attributes = 0, const char* text = 0); /** * Constructor. * Constructs an XML element with a TiXmlElement element with the given type's name. * Used for outgoing elements * @param type The element's type * @param attributes Optional list of attributes * @param text Optional text for the XML element */ XMLElement(Type type, NamedList* attributes = 0, const char* text = 0); /** * Constructor. * Build this XML element from a list containing name, attributes and text. * Element's name must be a parameter whose name must be equal to prefix. * Element's text must be a parameter whose name is prefix followed by a dot. * The list of attributes will be built from parameters starting with prefix.attributename * @param src The list containing data used to build this XML element * @param prefix The prefix used to search the list of parameters */ XMLElement(NamedList& src, const char* prefix); /** * Destructor. Deletes the underlying TiXmlElement if owned */ virtual ~XMLElement(); /** * Get the type of this object * @return The type of this object as enumeration */ inline Type type() const { return m_type; } /** * Get the TiXmlElement's name * @return The name of the TiXmlElement object or 0 */ inline const char* name() const { return valid() ? m_element->Value() : 0; } /** * Check if the TiXmlElement's name is the given text * @param text Text to compare with * @return False if text is 0 or not equal to name */ inline bool nameIs(const char* text) const { return (text && name() && (0 == ::strcmp(name(),text))); } /** * Get the validity of this object * @return True if m_element is non null */ inline bool valid() const { return m_element != 0; } /** * Change the type of this object * @param t The new type of this object */ inline void changeType(Type t) { m_type = t; } /** * Put the element in a buffer * @param dest Destination string * @param unclose True to leave the tag unclosed */ void toString(String& dest, bool unclose = false) const; /** * Put this element's name, text and attributes to a list of parameters * @param dest Destination list * @param prefix Prefix to add to parameters */ void toList(NamedList& dest, const char* prefix); /** * Set the value of an existing attribute or adds a new one * @param name Attribute's name * @param value Attribute's value */ void setAttribute(const char* name, const char* value); /** * Set the value of an existing attribute or adds a new one if the value's length is not 0 * @param name Attribute's name * @param value Attribute's value */ inline void setAttributeValid(const char* name, const String& value) { if (value) setAttribute(name,value); } /** * Set the value of an existing attribute or adds a new one from an integer * @param name Attribute's name * @param value Attribute's value */ inline void setAttribute(const char* name, int value) { String s(value); setAttribute(name,s); } /** * Get the value of an attribute * @param name Attribute's name * @return Attribute's value. May be 0 if doesn't exists or empty */ const char* getAttribute(const char* name) const; /** * Get the value of an attribute * @param name Attribute's name * @param value Destination string * @return True if attribute with the given name exists and is not empty */ inline bool getAttribute(const char* name, String& value) const { value = getAttribute(name); return 0 != value.length(); } /** * Fill a list with element's attributes * @param dest The destination list */ void getAttributes(NamedList& dest) const; /** * Check if an attribute with the given name and value exists * @param name Attribute's name * @param value Attribute's value * @return True/False */ bool hasAttribute(const char* name, const char* value) const; /** * Get the text of this XML element * @return Pointer to the text of this XML element or 0 */ const char* getText() const; /** * Add a child to this object. Release the received element * @param element XMLElement to add */ void addChild(XMLElement* element); /** * Find the first child element of this one. * Remove it from the children list. * If an element is returned, it owns the TiXmlElement pointer. * This element must own its TiXmlElement pointer. * @param name Optional name of the child * @return Pointer to an XMLElement or 0 if not found */ XMLElement* removeChild(const char* name = 0); /** * Find the first child element of this one. * Remove it from the children list. * If an element is returned, it owns the TiXmlElement pointer. * This element must own its TiXmlElement pointer. * @param type Child's type to find * @return Pointer to an XMLElement or 0 if not found */ inline XMLElement* removeChild(Type type) { return removeChild(typeName(type)); } /** * Find the first child element of this one. * If an element is returned, it is a newly allocated one, not owning its TiXmlElement pointer * @param name Optional name of the child * @return Pointer to an XMLElement or 0 if not found */ XMLElement* findFirstChild(const char* name = 0); /** * Find the first child element of the given type. * If an element is returned, it is a newly allocated one, not owning its TiXmlElement pointer * @param type Child's type to find * @return Pointer to an XMLElement or 0 if not found */ inline XMLElement* findFirstChild(Type type) { return findFirstChild(typeName(type)); } /** * Check if this element has a given child * @param name Optional name of the child (check for the first one if 0) * @return True if this element has the desired child */ inline bool hasChild(const char* name) { XMLElement* tmp = findFirstChild(name); bool ok = (0 != tmp); TelEngine::destruct(tmp); return ok; } /** * Check if this element has a given child * @param type Child's type to find * @return True if this element has the desired child */ inline bool hasChild(Type type) { return hasChild(typeName(type)); } /** * Find the next child element. Delete the starting element if not 0. * If an element is returned, it is a newly allocated one, not owning its TiXmlElement pointer * @param element Starting XMLElement. O to find from the beginning * @param name Optional name of the child * @return Pointer to an XMLElement or 0 if not found */ XMLElement* findNextChild(XMLElement* element, const char* name = 0); /** * Find the next child element of the given type. Delete the starting element if not 0. * If an element is returned, it is a newly allocated one, not owning its TiXmlElement pointer * @param element Starting XMLElement. O to find from the beginning * @param type Child's type to find * @return Pointer to an XMLElement or 0 if not found */ inline XMLElement* findNextChild(XMLElement* element, Type type) { return findNextChild(element,typeName(type)); } /** * Find the first attribute * @return Pointer to the first attribute or 0 */ inline const TiXmlAttribute* firstAttribute() const { return valid() ? m_element->FirstAttribute() : 0; } /** * Get the name associated with the given type * @param type Element type as enumeration * @return Pointer to the name or 0 */ static inline const char* typeName(Type type) { return lookup(type,s_names); } /** * Check if the given text is equal to the one associated with the given type * @param txt Text to compare * @param type Element type as enumeration * @return True if txt equals the text associated with the given type */ static inline bool isType(const char* txt, Type type) { const char* s = typeName(type); return (txt && s && (0 == ::strcmp(txt,s))); } /** * Get a pointer to this object */ virtual void* getObject(const String& name) const { if (name == "XMLElement") return (void*)this; return GenObject::getObject(name); } /** * Release memory */ virtual const String& toString() const { return m_name; } /** * Release memory */ virtual void destruct() { if (m_owner && m_element) delete m_element; m_element = 0; GenObject::destruct(); } /** * Get an xml element from a list's parameter * @param list The list to be searched for the given parameter * @param stole True to release parameter ownership (defaults to false) * @param name Parameter name (defaults to 'xml') * @param value Optional parameter value to check * @return XMLElement pointer or 0. If a valid pointer is returned and * stole is true the caller will own the pointer */ static XMLElement* getXml(NamedList& list, bool stole = false, const char* name = "xml", const char* value = 0); /** * Associations between XML element name and type */ static TokenDict s_names[]; protected: /** * Constructor. * Constructs an XML element from a TiXmlElement. * Used to extract elements from parser and access the children. * When extracting elements from parser the object will own the TiXmlElement. * When accessing the children, the object will not own the TiXmlElement * @param element Pointer to a valid TiXmlElement * @param owner Owner flag */ XMLElement(TiXmlElement* element, bool owner); /** * Get the underlying TiXmlElement * @return The underlying TiXmlElement object or 0 */ inline TiXmlElement* get() const { return m_element; } /** * Release the ownership of the underlying TiXmlElement * and returns it if the object owns it * @return The underlying TiXmlElement object or 0 if not owned or 0 */ TiXmlElement* releaseOwnership(); private: // Set this object's type from m_element's name inline void setType() { m_name = name(); m_type = (Type)lookup(name(),s_names,Unknown); } Type m_type; // Element's type bool m_owner; // Owner flag. If true, this object owns the XML element String m_name; // The name of this element TiXmlElement* m_element; // The underlying XML element }; /** * This class is responsable of parsing incoming data. * Keeps the resulting XML elements and the input buffer * @short An XML parser */ class YJINGLE_API XMLParser : public TiXmlDocument, public Mutex { public: /** * Constructor. * Constructs an XML parser */ inline XMLParser() : TiXmlDocument(), Mutex(true), m_findstart(true) {} /** * Destructor */ virtual ~XMLParser() {} /** * Add data to buffer. Parse the buffer. * On success, the already parsed data is removed from buffer. * This method is thread safe * @param data Pointer to the data to consume * @param len Data length * @return True on successfully parsed */ bool consume(const char* data, u_int32_t len); /** * Extract the first XML element from document. * Remove non-element children of the document (e.g. declaration). * This method is thread safe * @return Pointer to an XMLElement or 0 if the document is empty */ XMLElement* extract(); /** * Get the buffer length (incomplete data) * @return The number of bytes belonging to an incomplete XML element */ inline unsigned int bufLen() const { return m_buffer.length(); } /** * Get a copy of the parser's buffer * @param dest Destination string */ inline void getBuffer(String& dest) const { dest = m_buffer; } /** * Clear the parser's input buffer and already parsed elements. Reset data */ void reset(); /** * The maximum allowed buffer length */ static u_int32_t s_maxDataBuffer; /** * The XML encoding */ static TiXmlEncoding s_xmlEncoding; private: String m_buffer; // Input data buffer bool m_findstart; // Search for stream start tag or not }; /** * This class holds an XML element to be sent through a stream * @short An outgoing XML element */ class YJINGLE_API XMLElementOut : public RefObject { public: /** * Constructor * @param element The XML element * @param senderID Optional sender id * @param unclose True to not close the tag when building the buffer */ inline XMLElementOut(XMLElement* element, const char* senderID = 0, bool unclose = false) : m_element(element), m_offset(0), m_id(senderID), m_unclose(unclose), m_sent(false) {} /** * Destructor * Delete m_element if not 0 */ virtual ~XMLElementOut() { TelEngine::destruct(m_element); } /** * Get the underlying element * @return The underlying element */ inline XMLElement* element() const { return m_element; } /** * Check if this element was (partially) sent * @return True if an attempt to send this element was already done */ inline bool sent() const { return m_sent; } /** * Get the data buffer * @return The data buffer */ inline String& buffer() { return m_buffer; } /** * Get the id member * @return The id member */ inline const String& id() const { return m_id; } /** * Get the remainig byte count to send * @return The unsent number of bytes */ inline u_int32_t dataCount() { return m_buffer.length() - m_offset; } /** * Get the remainig data to send. Set the buffer if not already set * @param nCount The number of unsent bytes * @return Pointer to the remaining data or 0 */ inline const char* getData(u_int32_t& nCount) { if (!m_buffer) prepareToSend(); nCount = dataCount(); return m_buffer.c_str() + m_offset; } /** * Increase the offset with nCount bytes. Set the sent flag * @param nCount The number of bytes sent */ inline void dataSent(u_int32_t nCount) { m_sent = true; m_offset += nCount; if (m_offset > m_buffer.length()) m_offset = m_buffer.length(); } /** * Release the ownership of m_element * The caller is responsable of returned pointer * @return XMLElement pointer or 0 */ inline XMLElement* release() { XMLElement* e = m_element; m_element = 0; return e; } /** * Fill a buffer with the XML element to send * @param buffer The buffer to fill */ inline void toBuffer(String& buffer) { if (m_element) m_element->toString(buffer,m_unclose); } /** * Fill the buffer with the XML element to send */ inline void prepareToSend() { toBuffer(m_buffer); } private: XMLElement* m_element; // The XML element String m_buffer; // Data to send u_int32_t m_offset; // Offset to send String m_id; // Sender's id bool m_unclose; // Close or not the element's tag bool m_sent; // Sent flag (true if an attempt to send was done) }; }; #endif /* __XMLPARSER_H */ /* vi: set ts=8 sw=4 sts=4 noet: */