/** * xmpputils.h * Yet Another Jabber Component Protocol 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 __XMPPUTILS_H #define __XMPPUTILS_H #include #include #ifdef _WINDOWS #ifdef LIBYJABBER_EXPORTS #define YJABBER_API __declspec(dllexport) #else #ifndef LIBYJABBER_STATIC #define YJABBER_API __declspec(dllimport) #endif #endif #endif /* _WINDOWS */ #ifndef YJABBER_API #define YJABBER_API #endif // Support old RFC 3920 // If not defined RFC 3920bis changes will be used #define RFC3920 /** * Holds all Telephony Engine related classes. */ namespace TelEngine { class StringArray; // A String array class XMPPNamespace; // XMPP namespaces class XMPPError; // XMPP errors class JabberID; // A Jabber ID (JID) class JIDIdentity; // A JID's identity class JIDIdentityList; // A list of JID identities class XMPPFeature; // A feature (stream or JID) class XMPPFeatureSasl; // A SASL feature class XMPPFeatureList; // Feature list class XMPPUtils; // Utilities class XMPPDirVal; // Direction flags class XmlElementOut; // An outgoing xml element /** * This class holds a SRV record returned by a query * The String holds the domain/ip * @short A SRV record */ class YJABBER_API SrvRecord : public String { public: inline SrvRecord(const char* name, int port, int prio, int weight) : String(name), m_port(port), m_priority(prio), m_weight(weight) {} /** * Insert a SrvRecord into a list in the proper location * @param list Destination list * @param rec The item to insert */ static void insert(ObjList& list, SrvRecord* rec); int m_port; int m_priority; int m_weight; }; class YJABBER_API Resolver { public: /** * Make a SRV query * @param query The query content * @param result List of resulting SrvRecord items * @return 0 on success, error code otherwise */ static int srvQuery(const char* query, ObjList& result); }; /** * Implements a String array set from an already allocated * @short A String array */ class YJABBER_API StringArray { public: /** * Constructor * @param array The array * @param len Array length */ inline StringArray(const String* array, unsigned int len) : m_array((String*)array), m_length(len) {} /** * Return the string at a given index (safe) * @param index The index in the array * @return The String at the requested index or an empty one if the index is invalid */ inline const String& at(unsigned int index) const { return index < m_length ? m_array[index] : String::empty(); } /** * Return the string at a given index (unsafe) * @param index The index in the array * @return The String at the requested index */ inline const String& operator[](unsigned int index) const { return m_array[index]; } /** * Lookup for an integer associated with a given String * @param token The String find * @return Token value or 0 if not found */ inline int operator[](const String& token) { unsigned int i = 0; for (; i < m_length; i++) if (m_array[i] == token) return i; return m_length; } protected: String* m_array; unsigned int m_length; private: StringArray() {} }; /** * This class holds the XMPP/Jabber/Jingle namespace enumerations and the associated strings * @short XMPP namespaces */ class YJABBER_API XMPPNamespace : public StringArray { public: /** * Namespace type enumeration */ enum Type { Stream = 0, // http://etherx.jabber.org/streams Client = 1, // jabber:client Server = 2, // jabber:server Dialback = 3, // jabber:server:dialback StreamError = 4, // urn:ietf:params:xml:ns:xmpp-streams StanzaError = 5, // urn:ietf:params:xml:ns:xmpp-stanzas Ping = 6, // urn:xmpp:ping Register = 7, // http://jabber.org/features/iq-register IqRegister = 8, // jabber:iq:register IqPrivate = 9, // jabber:iq:private IqAuth = 10, // jabber:iq:auth IqAuthFeature = 11, // http://jabber.org/features/iq-auth IqVersion = 12, // jabber:iq:version Delay = 13, // urn:xmpp:delay Tls = 14, // urn:ietf:params:xml:ns:xmpp-tls Sasl = 15, // urn:ietf:params:xml:ns:xmpp-sasl Session = 16, // urn:ietf:params:xml:ns:xmpp-session Bind = 17, // urn:ietf:params:xml:ns:xmpp-bind Roster = 18, // jabber:iq:roster DynamicRoster = 19, // jabber:iq:roster-dynamic DiscoInfo = 20, // http://jabber.org/protocol/disco#info DiscoItems = 21, // http://jabber.org/protocol/disco#items EntityCaps = 22, // http://jabber.org/protocol/caps VCard = 23, // vcard-temp SIProfileFileTransfer = 24, // http://jabber.org/protocol/si/profile/file-transfer ByteStreams = 25, // http://jabber.org/protocol/bytestreams Jingle = 26, // urn:xmpp:jingle:1 JingleError = 27, // urn:xmpp:jingle:errors:1 JingleAppsRtp = 28, // urn:xmpp:jingle:apps:rtp:1 JingleAppsRtpError = 29, // urn:xmpp:jingle:apps:rtp:errors:1 JingleAppsRtpInfo = 30, // urn:xmpp:jingle:apps:rtp:info:1 JingleAppsRtpAudio = 31, // urn:xmpp:jingle:apps:rtp:audio JingleAppsFileTransfer = 32, // urn:xmpp:jingle:apps:file-transfer:1 JingleTransportIceUdp = 33, // urn:xmpp:jingle:transports:ice-udp:1 JingleTransportRawUdp = 34, // urn:xmpp:jingle:transports:raw-udp:1 JingleTransportRawUdpInfo = 35, // urn:xmpp:jingle:transports:raw-udp:info:1 JingleTransportByteStreams = 36, // urn:xmpp:jingle:transports:bytestreams:1 JingleTransfer = 37, // urn:xmpp:jingle:transfer:0 JingleDtmf = 38, // urn:xmpp:jingle:dtmf:0 JingleSession = 39, // http://www.google.com/session JingleAudio = 40, // http://www.google.com/session/phone JingleTransport = 41, // http://www.google.com/transport/p2p JingleRtpInfoOld = 42, // urn:xmpp:jingle:apps:rtp:info DtmfOld = 43, // http://jabber.org/protocol/jingle/info/dtmf XOob = 44, // jabber:x:oob Command= 45, // http://jabber.org/protocol/command MsgOffline= 46, // msgoffline Count = 47, }; /** * Constructor */ inline XMPPNamespace() : StringArray(s_array,Count) {} private: static const String s_array[Count]; // Namespace list }; /** * This class holds the XMPP error type, error enumerations and associated strings * @short XMPP errors */ class YJABBER_API XMPPError : public StringArray { public: /** * Error condition enumeration */ enum Type { NoError = 0, BadFormat = 1, // bad-format BadNamespace = 2, // bad-namespace-prefix Conflict = 3, // conflict ConnTimeout = 4, // connection-timeout HostGone = 5, // host-gone HostUnknown = 6, // host-unknown BadAddressing = 7, // improper-addressing Internal = 8, // internal-server-error InvalidFrom = 9, // invalid-from InvalidId = 10, // invalid-id InvalidNamespace = 11, // invalid-namespace InvalidXml = 12, // invalid-xml NotAuth = 13, // not-authorized Policy = 14, // policy-violation RemoteConn = 15, // remote-connection-failed ResConstraint = 16, // resource-constraint RestrictedXml = 17, // restricted-xml SeeOther = 18, // see-other-host Shutdown = 19, // system-shutdown UndefinedCondition = 20, // undefined-condition UnsupportedEnc = 21, // unsupported-encoding UnsupportedStanza = 22, // unsupported-stanza-type UnsupportedVersion = 23, // unsupported-version Xml = 24, // xml-not-well-formed Aborted = 25, // aborted AccountDisabled = 26, // account-disabled CredentialsExpired = 27, // credentials-expired EncryptionRequired = 28, // encryption-required IncorrectEnc = 29, // incorrect-encoding InvalidAuth = 30, // invalid-authzid InvalidMechanism = 31, // invalid-mechanism MalformedRequest = 32, // malformed-request MechanismTooWeak = 33, // mechanism-too-weak NotAuthorized = 34, // not-authorized TempAuthFailure = 35, // temporary-auth-failure TransitionNeeded = 36, // transition-needed ResourceConstraint = 37, // resource-constraint NotAllowed = 38, // not-allowed BadRequest = 39, // bad-request FeatureNotImpl = 40, // feature-not-implemented Forbidden = 41, // forbidden Gone = 42, // gone ItemNotFound = 43, // item-not-found BadJid = 44, // jid-malformed NotAcceptable = 45, // not-acceptable Payment = 46, // payment-required Unavailable = 47, // recipient-unavailable Redirect = 48, // redirect Reg = 49, // registration-required NoRemote = 50, // remote-server-not-found RemoteTimeout = 51, // remote-server-timeout ServiceUnavailable = 52, // service-unavailable Subscription = 53, // subscription-required Request = 54, // unexpected-request SocketError = 55, // Don't send any error or stream end tag to remote party TypeCount = 56 }; /** * Error type enumeration */ enum ErrorType { TypeCancel = TypeCount, // do not retry (the error is unrecoverable) TypeContinue = TypeCount + 1, // proceed (the condition was only a warning) TypeModify = TypeCount + 2, // retry after changing the data sent TypeAuth = TypeCount + 3, // retry after providing credentials TypeWait = TypeCount + 4, // retry after waiting (the error is temporary) Count = TypeCount + 5 }; /** * Constructor */ inline XMPPError() : StringArray(s_array,Count) {} private: static const String s_array[Count]; // Error list }; /** * This class holds a list of XML tags * @short XML known tags array */ class YJABBER_API XmlTag : public StringArray { public: /** * Element tag enumeration */ enum Type { Stream = 0, // stream Error = 1, // error Features = 2, // features Register = 3, // register Starttls = 4, // starttls Auth = 5, // auth Challenge = 6, // challenge Abort = 7, // abort Aborted = 8, // aborted Response = 9, // response Proceed = 10, // proceed Success = 11, // success Failure = 12, // failure Mechanisms = 13, // mechanisms Mechanism = 14, // mechanism Session = 15, // session Iq = 16, // iq Message = 17, // message Presence = 18, // presence Query = 19, // query VCard = 20, // vCard Jingle = 21, // jingle Description = 22, // description PayloadType = 23, // payload-type Transport = 24, // transport Candidate = 25, // candidate Body = 26, // body Subject = 27, // subject Feature = 28, // feature Bind = 29, // bind Resource = 30, // resource Transfer = 31, // transfer Hold = 32, // hold Active = 33, // active Ringing = 34, // ringing Mute = 35, // mute Registered = 36, // registered Remove = 37, // remove Jid = 38, // jid Username = 39, // username Password = 40, // password Digest = 41, // digest Required = 42, // required Optional = 43, // optional Dtmf = 44, // dtmf DtmfMethod = 45, // dtmf-method Command = 46, // command Text = 47, // text Item = 48, // item Group = 49, // group Reason = 50, // reason Content = 51, // content Trying = 52, // trying Received = 53, // received File = 54, // file Offer = 55, // offer Request = 56, // request StreamHost = 57, // streamhost StreamHostUsed = 58, // streamhost-used Ping = 59, // ping Encryption = 60, // encryption Crypto = 61, // crypto Parameter = 62, // parameter Identity = 63, // identity Priority = 64, // priority EntityCapsTag = 65, // c Count = 66 }; /** * Constructor */ inline XmlTag() : StringArray(s_array,Count) {} private: static const String s_array[Count]; // Tag list }; /** * This class holds a Jabber ID * @short A Jabber ID */ class YJABBER_API JabberID : public String { YCLASS(JabberID,String) public: /** * Constructor */ inline JabberID() {} /** * Constructor. Constructs a JID from a given string * @param jid The JID string */ inline JabberID(const char* jid) { set(jid); } /** * Constructor. Constructs a JID from a given string * @param jid The JID string */ inline JabberID(const String& jid) { set(jid); } /** * Constructor. Constructs a JID from a given string * @param jid The JID string */ inline JabberID(const String* jid) { set(TelEngine::c_safe(jid)); } /** * Constructor. Constructs a JID from user, domain, resource * @param node The node * @param domain The domain * @param resource The resource */ inline JabberID(const char* node, const char* domain, const char* resource = 0) { set(node,domain,resource); } /** * Copy constructor * @param src Source Jabber ID */ inline JabberID(const JabberID& src) { *this = src; } /** * Check if this is a valid JID * @return True if this JID is a valid one */ inline bool valid() const { return null() || m_domain; } /** * Get the node part of the JID * @return The node part of the JID */ inline const String& node() const { return m_node; } /** * Get the bare JID: "node@domain" * @return The bare JID */ inline const String& bare() const { return m_bare; } /** * Get the domain part of the JID * @return The domain part of the JID */ inline const String& domain() const { return m_domain; } /** * Set the domain part of the JID. * @param d The new domain part of the JID. */ inline void domain(const char* d) { set(m_node.c_str(),d,m_resource.c_str()); } /** * Get the resource part of the JID * @return The resource part of the JID */ inline const String& resource() const { return m_resource; } /** * Check if this is a full JID * @return True if this is a full JID */ inline bool isFull() const { return m_node && m_domain && m_resource; } /** * Clear content */ inline void clear() { String::clear(); m_node.clear(); m_domain.clear(); m_resource.clear(); m_bare.clear(); } /** * Try to match another JID to this one. If src has a resource compare it too * (case sensitive). Otherwise compare just the bare JID (case insensitive) * @param src The JID to match * @return True if matched */ inline bool match(const JabberID& src) const { return (src.resource().null() || (resource() == src.resource())) && (bare() &= src.bare()); } /** * Assignement operator from JabberID * @param src The JID to copy from * @return This object */ JabberID& operator=(const JabberID& src); /** * Assignement operator from String * @param src The string * @return This object */ inline JabberID& operator=(const String& src) { set(src); return *this; } /** * Assignement operator from String pointer * @param src The string * @return This object */ inline JabberID& operator=(const String* src) { set(TelEngine::c_safe(src)); return *this; } /** * Equality operator. Do a case senitive resource comparison and a case * insensitive bare jid comparison * @param src The JID to compare with * @return True if equal */ inline bool operator==(const JabberID& src) const { return (resource() == src.resource()) && (bare() &= src.bare()); } /** * Equality operator. Build a temporary JID and compare with it * @param src The string to compare with * @return True if equal */ inline bool operator==(const String& src) const { JabberID tmp(src); return operator==(tmp); } /** * Inequality operator * @param src The JID to compare with * @return True if not equal */ inline bool operator!=(const JabberID& src) const { return !operator==(src); } /** * Inequality operator * @param src The string to compare with * @return True if not equal */ inline bool operator!=(const String& src) const { return !operator==(src); } /** * Set the resource part of the JID * @param res The new resource part of the JID */ inline void resource(const char* res) { set(m_node.c_str(),m_domain.c_str(),res); } /** * Set the data * @param jid The JID string to assign */ void set(const char* jid); /** * Set the data * @param node The node * @param domain The domain * @param resource The resource */ void set(const char* node, const char* domain, const char* resource = 0); /** * Get an empty JabberID * @return A global empty JabberID */ static const JabberID& empty(); /** * Check if the given string contains valid characters * @param value The string to check * @return True if value is valid or 0. False if value is a non empty invalid string */ static bool valid(const String& value); /** * Keep the regexp used to check the validity of a string */ static Regexp s_regExpValid; private: void parse(); // Parse the string. Set the data String m_node; // The node part String m_domain; // The domain part String m_resource; // The resource part String m_bare; // The bare JID: node@domain }; /** * This class holds an identity for a JID * See http://xmpp.org/registrar/disco-categories.html for identity categories * and associated types * @short A JID identity */ class YJABBER_API JIDIdentity : public GenObject { YCLASS(JIDIdentity,GenObject) public: /** * Constructor. Build a JID identity * @param c The JID's category * @param t The JID's type * @param name Optional identity (JID) name */ inline JIDIdentity(const char* c, const char* t, const char* name = 0) : m_category(c), m_type(t), m_name(name) {} /** * Constructor. Build a JID identity from xml * @param identity The identity element */ inline JIDIdentity(XmlElement* identity) { fromXml(identity); } /** * Build an XML element from this identity * @return XmlElement pointer or 0 if category or type are empty */ inline XmlElement* toXml() const { if (!(m_category && m_type)) return 0; return createIdentity(m_category,m_type,m_name); } /** * Update this identity from an XML element * @param identity The source element */ void fromXml(XmlElement* identity); /** * Create an 'identity' element * @param category The 'category' attribute * @param type The 'type' attribute * @param name The 'name' attribute * @return A valid XmlElement pointer */ static XmlElement* createIdentity(const char* category, const char* type, const char* name); String m_category; String m_type; String m_name; }; /** * This class holds a list of JID identities * @short A list of JID identities */ class YJABBER_API JIDIdentityList : public ObjList { YCLASS(JIDIdentityList,ObjList) public: /** * Fill an xml element with identities held by this list * @param parent The parent element to fill */ void toXml(XmlElement* parent) const; /** * Add identity children from an xml element * @param parent The element containing the identity children */ void fromXml(XmlElement* parent); }; /** * This class holds an XMPP feature * @short A feature */ class YJABBER_API XMPPFeature : public String { YCLASS(XMPPFeature,GenObject) public: /** * Constructor * @param xml XML element tag as enumeration * @param feature The feature (namespace) index * @param required True if this feature is required */ inline XMPPFeature(int xml, int feature, bool required = false) : m_xml(xml), m_required(required) { setFeature(feature); } /** * Constructor * @param xml XML element tag as enumeration * @param feature The feature name * @param required True if this feature is required */ inline XMPPFeature(int xml, const char* feature, bool required = false) : String(feature), m_xml(xml), m_required(required) {} /** * Constructor. Build from feature index * @param feature The feature */ inline XMPPFeature(int feature) : m_xml(XmlTag::Count), m_required(false) { setFeature(feature); } /** * Constructor. Build from feature name * @param feature The feature */ inline XMPPFeature(const char* feature) : String(feature), m_xml(XmlTag::Count), m_required(false) {} /** * Destructor */ virtual ~XMPPFeature() {} /** * Check if this feature is a required one * @return True if this feature is a required one */ inline bool required() const { return m_required; } /** * Build an xml element from this feature * @param addReq True to add the required/optional child * @return XmlElement pointer or 0 */ virtual XmlElement* build(bool addReq = true); /** * Build a feature element from this one * @return XmlElement pointer */ virtual XmlElement* buildFeature(); /** * Add a required/optional child to an element * @param xml Destination element */ void addReqChild(XmlElement& xml); /** * Build a feature from a stream:features child * @param xml The feature element to parse * @return XMPPFeature pointer or 0 if unknown */ static XMPPFeature* fromStreamFeature(XmlElement& xml); private: void setFeature(int feature); int m_xml; // Element tag as enumeration bool m_required; // Required flag }; /** * This class holds a SASL feature along with authentication mechanisms * @short A SASL feature */ class YJABBER_API XMPPFeatureSasl : public XMPPFeature { YCLASS(XMPPFeatureSasl,XMPPFeature) public: /** * Constructor * @param mech Authentication mechanism(s) * @param required Required flag */ inline XMPPFeatureSasl(int mech, bool required = false) : XMPPFeature(XmlTag::Mechanisms,XMPPNamespace::Sasl,required), m_mechanisms(mech) {} /** * Get the authentication mechanisms * @return The authentication mechanisms used by the JID */ inline int mechanisms() const { return m_mechanisms; } /** * Check if a given mechanism is allowed * @return True if the given mechanism is allowed */ inline bool mechanism(int mech) const { return 0 != (m_mechanisms & mech); } /** * Build an xml element from this feature * @param addReq True to add the required/optional child * @return XmlElement pointer or 0 */ virtual XmlElement* build(bool addReq = true); private: int m_mechanisms; // Authentication mechanisms }; /** * This class holds a list of JID features * @short JID feature list */ class YJABBER_API XMPPFeatureList : public ObjList { YCLASS(XMPPFeatureList,ObjList) public: /** * Add a feature to the list * @param xml XML element tag as enumeration * @param feature The feature to add as enumeration * @param required True if this feature is required * @return False if the given feature already exists */ inline bool add(int xml, int feature, bool required = false) { if (get(feature)) return false; append(new XMPPFeature(xml,feature,required)); return true; } /** * Add a feature to the list * @param feature The feature to add as enumeration * @return False if the given feature already exists */ inline bool add(int feature) { if (get(feature)) return false; append(new XMPPFeature(feature)); return true; } /** * Add a feature to the list. Destroy the received parameter if already in the list * @param feature The feature to add * @return False if the given feature already exists */ inline bool add(XMPPFeature* feature) { if (!feature || get(*feature)) { TelEngine::destruct(feature); return false; } append(feature); return true; } /** * Clear data */ inline void reset() { clear(); m_identities.clear(); m_entityCapsHash.clear(); } /** * Move a list of features to this list. Don't check duplicates * @param list The source list */ void add(XMPPFeatureList& list); /** * Re-build this list from stream features * @param xml The features element to parse */ void fromStreamFeatures(XmlElement& xml); /** * Re-build this list from disco info responses * @param xml The element to parse */ void fromDiscoInfo(XmlElement& xml); /** * Remove a feature from the list * @param feature The feature to remove */ inline void remove(int feature) { ObjList::remove(get(feature),true); } /** * Get a feature from the list * @param feature The feature to get * @return Pointer to the feature or 0 if it doesn't exists */ XMPPFeature* get(int feature); /** * Get a feature from the list * @param feature The feature name to find * @return Pointer to the feature or 0 if it doesn't exists */ inline XMPPFeature* get(const String& feature) { ObjList* o = find(feature); return o ? static_cast(o->get()) : 0; } /** * Build stream features from this list * @return XmlElement pointer */ XmlElement* buildStreamFeatures(); /** * Build an iq query disco info result from this list * @param from The 'from' attribute * @param to The 'to' attribute * @param id The 'id' attribute * @param node Optional 'node' attribute * @param cap Optional capability to be set as 'node' suffix * @return XmlElement pointer */ XmlElement* buildDiscoInfo(const char* from, const char* to, const char* id, const char* node = 0, const char* cap = 0); /** * Add this list to an xml element * @param xml Destination element */ void add(XmlElement& xml); /** * Update the entity capabilities hash */ void updateEntityCaps(); JIDIdentityList m_identities; String m_entityCapsHash; // SHA-1 entity caps as defined in XEP 0115 }; /** * This class is a general XMPP utilities * @short General XMPP utilities */ class YJABBER_API XMPPUtils { public: /** * Presence type enumeration */ enum Presence { Probe, // probe Subscribe, // subscribe request Subscribed, // subscribe accepted Unavailable, // unavailable Unsubscribe, // unsubscribe request Unsubscribed, // unsubscribe accepted PresenceError, // error PresenceNone }; /** * Message type enumeration */ enum MsgType { Chat, // chat GroupChat, // groupchat HeadLine, // headline Normal, // normal MsgError, // error MsgNone }; /** * Iq type enumeration */ enum IqType { IqSet, // set IqGet, // get IqResult, // result IqError, // error IqCount }; /** * Command action enumeration */ enum CommandAction { CommExecute, CommCancel, CommPrev, CommNext, CommComplete, }; /** * Command status enumeration */ enum CommandStatus { CommExecuting, CommCompleted, CommCancelled, }; /** * Authentication methods */ enum AuthMethod { AuthNone = 0x00, // No authentication mechanism AuthSHA1 = 0x01, // SHA1 digest AuthMD5 = 0x02, // MD5 digest AuthPlain = 0x04, // Plain text password AuthDialback = 0x08, // Dialback authentication }; /** * Check if an xml element has type 'result' or 'error' * @param xml The element to check * @return True if the element is a response one */ static inline bool isResponse(const XmlElement& xml) { String* tmp = xml.getAttribute("type"); return tmp && (*tmp == "result" || *tmp == "error"); } /** * Create an XML element * @param name Element's name * @param text Optional text for the element * @return A valid XmlElement pointer */ static inline XmlElement* createElement(const char* name, const char* text = 0) { XmlElement* xml = new XmlElement(String(name),true); if (!TelEngine::null(text)) xml->addText(text); return xml; } /** * Create an XML element * @param type Element's type * @param text Optional text for the element * @return A valid XmlElement pointer */ static inline XmlElement* createElement(int type, const char* text = 0) { return createElement(s_tag[type],text); } /** * Create an XML element with an 'xmlns' attribute * @param name Element's name * @param ns Optional 'xmlns' attribute as enumeration * @param text Optional text for the element * @return A valid XmlElement pointer */ static inline XmlElement* createElement(const char* name, int ns, const char* text = 0) { XmlElement* xml = createElement(name,text); setXmlns(*xml,String::empty(),true,ns); return xml; } /** * Create an XML element with an 'xmlns' attribute * @param type Element's type * @param ns 'xmlns' attribute as enumeration * @param text Optional text for the element * @return A valid XmlElement pointer */ static inline XmlElement* createElement(int type, int ns, const char* text = 0) { return createElement(s_tag[type],ns,text); } /** * Partially build an XML element from another one. * Copy tag 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 */ static XmlElement* createElement(const XmlElement& src, bool response, bool result); /** * Create an 'iq' element * @param type Iq type as enumeration * @param from The 'from' attribute * @param to The 'to' attribute * @param id The 'id' attribute * @return A valid XmlElement pointer */ static XmlElement* createIq(IqType type, const char* from = 0, const char* to = 0, const char* id = 0); /** * Create an 'iq' result element * @param from The 'from' attribute * @param to The 'to' attribute * @param id The 'id' attribute * @param child Optional element child (will be consumed) * @return A valid XmlElement pointer */ static inline XmlElement* createIqResult(const char* from, const char* to, const char* id, XmlElement* child = 0) { XmlElement* xml = createIq(IqResult,from,to,id); if (child) xml->addChild(child); return xml; } /** * Create an 'iq' error from a received element. Consume the received element. * Add the given element to the error stanza if the 'id' attribute is missing * @param from The 'from' attribute * @param to The 'to' attribute * @param xml Received element * @param type Error type * @param error The error * @param text Optional text to add to the error element * @return A valid XmlElement pointer or 0 if xml */ static XmlElement* createIqError(const char* from, const char* to, XmlElement*& xml, int type, int error, const char* text = 0); /** * Create an 'iq' element with a 'vcard' child * @param get True to set the iq's type to 'get', false to set it to 'set' * @param from The 'from' attribute * @param to The 'to' attribute * @param id The 'id' attribute * @return A valid XmlElement pointer */ static XmlElement* createVCard(bool get, const char* from, const char* to, const char* id); /** * Create a 'command' element * @param action The command action * @param node The command * @param sessionId Optional session ID for the command * @return A valid XmlElement pointer */ static XmlElement* createCommand(CommandAction action, const char* node, const char* sessionId = 0); /** * Create a disco info/items 'iq' element with a 'query' child * @param info True to create a query info request. False to create a query items request * @param req True to create a request (type=get), false to create a response (type=result) * @param from The 'from' attribute * @param to The 'to' attribute * @param id The 'id' attribute * @param node Optional 'node' attribute * @param cap Optional capability to be set as 'node' suffix * @return A valid XmlElement pointer */ static XmlElement* createIqDisco(bool info, bool req, const char* from, const char* to, const char* id, const char* node = 0, const char* cap = 0); /** * Create a version 'iq' result as defined in XEP-0092 * @param from The 'from' attribute * @param to The 'to' attribute * @param id The 'id' attribute * @param name Program name * @param version Program version * @param os Optional operating system * @return A valid XmlElement pointer */ static XmlElement* createIqVersionRes(const char* from, const char* to, const char* id, const char* name, const char* version, const char* os = 0); /** * Create a 'error' element * @param type Error type * @param error The error * @param text Optional text to add to the error element * @return A valid XmlElement pointer */ static XmlElement* createError(int type, int error, const char* text = 0); /** * Create an error from a received element. Consume the received element. * Reverse 'to' and 'from' attributes * @param xml Received element * @param type Error type * @param error The error * @param text Optional text to add to the error element * @return A valid XmlElement pointer or 0 if xml is 0 */ static XmlElement* createError(XmlElement* xml, int type, int error, const char* text = 0); /** * Create a 'stream:error' element * @param error The XMPP defined condition * @param text Optional text to add to the error * @return A valid XmlElement pointer */ static XmlElement* createStreamError(int error, const char* text = 0); /** * Build a register query element * @param type Iq type as enumeration * @param from The 'from' attribute * @param to The 'to' attribute * @param id The 'id' attribute * @param child1 Optional child of query element * @param child2 Optional child of query element * @param child3 Optional child of query element * @return Valid XmlElement pointer */ static XmlElement* createRegisterQuery(IqType type, const char* from, const char* to, const char* id, XmlElement* child1 = 0, XmlElement* child2 = 0, XmlElement* child3 = 0); /** * Build an register query element used to create/set username/password * @param from The 'from' attribute * @param to The 'to' attribute * @param id The 'id' attribute * @param username The username * @param password The password * @return Valid XmlElement pointer */ static inline XmlElement* createRegisterQuery(const char* from, const char* to, const char* id, const char* username, const char* password) { return createRegisterQuery(XMPPUtils::IqSet,from,to,id, createElement(XmlTag::Username,username), createElement(XmlTag::Password,password)); } /** * Create a failure element * @param ns Element namespace * @param error Optional error * @return XmlElement pointer */ static inline XmlElement* createFailure(XMPPNamespace::Type ns, XMPPError::Type error = XMPPError::NoError) { XmlElement* xml = createElement(XmlTag::Failure,ns); if (error != XMPPError::NoError) xml->addChild(new XmlElement(s_error[error])); return xml; } /** * Create an 'x' jabber:x:oob url element as described in XEP-0066 * @param url The URL * @param desc Optional description * @return XmlElement pointer */ static inline XmlElement* createXOobUrl(const char* url, const char* desc = 0) { XmlElement* xml = createElement("x",XMPPNamespace::XOob); xml->addChild(createElement("url",url)); if (desc) xml->addChild(createElement("desc",desc)); return xml; } /** * Create a 'delay' element as defined in XEP-0203 * @param timeSec The time to encode (in seconds) * @param from Optional 'from' attribute * @param fractions Optional second fractions * @param text Optional xml element text * @return XmlElement pointer */ static XmlElement* createDelay(unsigned int timeSec, const char* from = 0, unsigned int fractions = 0, const char* text = 0); /** * Check if an element has a child with 'remove' tag * @param xml The element to check * @return True if the element has a child with 'remove' tag */ static inline bool remove(XmlElement& xml) { return 0 != findFirstChild(xml,XmlTag::Remove); } /** * Check if an element has a child with 'required' tag * @param xml The element to check * @return True if the element has a child with 'required' tag */ static inline bool required(XmlElement& xml) { return 0 != findFirstChild(xml,XmlTag::Required); } /** * Check if an element has a child with 'priority' tag * @param xml The element to check * @param defVal Default value to return if not found or invalid integer * @return Element priority */ static int priority(XmlElement& xml, int defVal = 0); /** * Add a 'priority' child to an element * @param xml The element to set * @param prio Priority text */ static inline void setPriority(XmlElement& xml, const char* prio) { xml.addChild(createElement(XmlTag::Priority,prio)); } /** * Get an element's namespace * @param xml Element * @return Element namespace as enumeration */ static inline int xmlns(XmlElement& xml) { String* x = xml.xmlns(); return x ? s_ns[*x] : XMPPNamespace::Count; } /** * Check if the given element has a given default namespace * @param xml Element to check * @param ns Namespace value to check * @return True if the given element has the requested default namespace */ static inline bool hasDefaultXmlns(const XmlElement& xml, int ns) { String* s = xml.xmlnsAttribute(XmlElement::s_ns); return s && *s == s_ns[ns]; } /** * Check if the given element has a given namespace * @param xml Element to check * @param ns Namespace value to check * @return True if the given element is in the requested namespace */ static inline bool hasXmlns(const XmlElement& xml, int ns) { return xml.hasXmlns(s_ns[ns]); } /** * Set an element's namespace * @param xml Element * @param name Namespace attribute name * @param addAttr True to add the namespace attribute value * @param ns Namespace value as enumeration * @return True on success */ static inline bool setXmlns(XmlElement& xml, const String& name = String::empty(), bool addAttr = false, int ns = XMPPNamespace::Count) { if (ns < XMPPNamespace::Count) return xml.setXmlns(name,addAttr,s_ns[ns]); return xml.setXmlns(name); } /** * Set the 'stream' namespace to an element * @param xml Element * @return True on success */ static inline bool setStreamXmlns(XmlElement& xml) { return setXmlns(xml,"stream",true,XMPPNamespace::Stream); } /** * Set the 'db' namespace to an element * @param xml Element * @return True on success */ static inline bool setDbXmlns(XmlElement& xml) { return setXmlns(xml,"db",true,XMPPNamespace::Dialback); } /** * Find an element's first child element in a given namespace * @param xml Element * @param t Optional element tag as enumeration * @param ns Optional element namespace as enumeration * @return XmlElement pointer or 0 if not found */ static XmlElement* findFirstChild(const XmlElement& xml, int t = XmlTag::Count, int ns = XMPPNamespace::Count); /** * Find an element's next child element * @param xml Element * @param start Starting child * @param t Optional element tag as enumeration * @param ns Optional element namespace as enumeration * @return XmlElement pointer or 0 if not found */ static XmlElement* findNextChild(const XmlElement& xml, XmlElement* start, int t = XmlTag::Count, int ns = XMPPNamespace::Count); /** * Decode a stream error or stanza error * @param xml The element * @param error The error condition * @param text The stanza's error or error text */ static void decodeError(XmlElement* xml, String& error, String& text); /** * Decode a stream or stanza error condition element * @param xml The element * @param stanza True if the condition must be a stanza error, false to * decode a stream error * @param error The error condition * @param text The stanza's error or error text */ static void decodeError(XmlElement* xml, bool stanza, String& error, String& text); /** * Encode EPOCH time given in seconds to a date/time profile as defined in * XEP-0082 and XML Schema Part 2: Datatypes Second Edition * @param buf Destination string * @param timeSec The time to encode (in seconds) * @param fractions Optional second fractions */ static void encodeDateTimeSec(String& buf, unsigned int timeSec, unsigned int fractions = 0); /** * Decode a date/time profile as defined in XEP-0082 * and XML Schema Part 2: Datatypes Second Edition to EPOCH time * @param time The date/time string * @param fractions Pointer to integer to be filled with second fractions, if present * @return The decoded time in seconds, -1 on error */ static unsigned int decodeDateTimeSec(const String& time, unsigned int* fractions = 0); /** * Print an XmlElement to a string * @param xmlStr The destination string * @param xml The xml to print * @param verbose True to print XML data on multiple lines */ static void print(String& xmlStr, XmlChild& xml, bool verbose); /** * Put an element's name, text and attributes to a list of parameters * @param xml The element * @param dest Destination list * @param prefix Prefix to add to parameters */ static void toList(XmlElement& xml, NamedList& dest, const char* prefix); /** * Split a string at a delimiter character and fills a named list with its parts * Skip empty parts * @param dest The destination NamedList * @param src Pointer to the string * @param sep The delimiter * @param nameFirst True to add the parts as name and index as value. * False to do the other way */ static bool split(NamedList& dest, const char* src, const char sep, bool nameFirst); /** * Decode a comma separated list of flags and put them into an integer mask * @param src Source string * @param dict Dictionary containing flag names and values * @return The mask of found flags */ static int decodeFlags(const String& src, const TokenDict* dict); /** * Encode a mask of flags to a comma separated list of names * @param dest Destination string * @param src Source mask * @param dict Dictionary containing flag names and values */ static void buildFlags(String& dest, int src, const TokenDict* dict); /** * Add child elements from a list to a destination element * @param dest Destination XmlElement * @param list A list containing XML elements * @return True if at least one child was added */ static bool addChidren(XmlElement* dest, ObjList& list); /** * Create a 'c' entity capability element as defined in XEP 0115 * @param hash The 'ver' attribute * @param node The 'node' attribute * @return XmlElement pointer or 0 on failure */ static XmlElement* createEntityCaps(const String& hash, const char* node); /** * Create a 'c' entity capability element as defined by GTalk * @return A valid XmlElement pointer */ static XmlElement* createEntityCapsGTalkV1(); /** * Create an 'presence' element * @param from The 'from' attribute * @param to The 'to' attribute * @param type Presence type as enumeration * @return A valid XmlElement pointer */ static XmlElement* createPresence(const char* from, const char* to, Presence type = PresenceNone); /** * Create a 'message' element * @param type Message type string * @param from The 'from' attribute * @param to The 'to' attribute * @param id The 'id' attribute * @param body The message body * @return A valid XmlElement pointer */ static XmlElement* createMessage(const char* type, const char* from, const char* to, const char* id, const char* body); /** * Create a 'message' element * @param type Message type as enumeration * @param from The 'from' attribute * @param to The 'to' attribute * @param id The 'id' attribute * @param body The message body * @return A valid XmlElement pointer */ static inline XmlElement* createMessage(MsgType type, const char* from, const char* to, const char* id, const char* body) { return createMessage(msgText(type),from,to,id,body); } /** * Build a dialback 'db:result' xml element used to send a dialback key * @param from The sender * @param to The recipient * @param key The dialback key * @return XmlElement pointer */ static XmlElement* createDialbackKey(const char* from, const char* to, const char* key); /** * Build a dialback 'db:result' xml element used to send a dialback key response * @param from The sender * @param to The recipient * @param valid True if valid, false if invalid * @return XmlElement pointer */ static XmlElement* createDialbackResult(const char* from, const char* to, bool valid); /** * Build a dialback 'db:verify' xml element * @param from The sender * @param to The recipient * @param id The 'id' attribute (stream id) * @param key The dialback key * @return XmlElement pointer */ static XmlElement* createDialbackVerify(const char* from, const char* to, const char* id, const char* key); /** * Build a dialback 'db:verify' response xml element * @param from The sender * @param to The recipient * @param id The 'id' attribute (stream id) * @param valid True if valid, false if invalid * @return XmlElement pointer */ static XmlElement* createDialbackVerifyRsp(const char* from, const char* to, const char* id, bool valid); /** * Build a 'subject' xml element * @param subject Element text * @return XmlElement pointer */ static inline XmlElement* createSubject(const char* subject) { return createElement(XmlTag::Subject,subject); } /** * Get an element's subject (the text of the first 'subject' child) * @param xml The element * @return Element subject or an empty string */ static inline const String& subject(XmlElement& xml) { XmlElement* s = findFirstChild(xml,XmlTag::Subject); return s ? s->getText() : String::empty(); } /** * Build a 'body' xml element * @param body Element text * @param ns Optional namespace * @return XmlElement pointer */ static inline XmlElement* createBody(const char* body, int ns = XMPPNamespace::Count) { return createElement(XmlTag::Body,ns,body); } /** * Retrieve the text of an element's body child * @param xml The element * @param ns Optional body namespace to match (default: match parent's namespace) * @return Body or empty string */ static const String& body(XmlElement& xml, int ns = XMPPNamespace::Count); /** * Build a name/value parameter xml element * @param name The 'name' attribute * @param value The value parameter * @param tag Optional element tag (defaults to 'parameter') * @return XmlElement pointer */ static inline XmlElement* createParameter(const char* name, const char* value, const char* tag = "parameter") { XmlElement* tmp = new XmlElement(tag); tmp->setAttributeValid("name",name); tmp->setAttributeValid("value",value); return tmp; } /** * Build a name/value parameter xml element * @param pair The name/value pair * @param tag Optional element tag (defaults to 'parameter') * @return XmlElement pointer */ static inline XmlElement* createParameter(const NamedString& pair, const char* tag = "parameter") { return createParameter(pair.name(),pair,tag); } /** * Get an element's namespace * @param xml The element * @return The namespace integer value as XMPPNamespace value */ static inline int ns(const XmlElement& xml) { String* xmlns = xml.xmlns(); return xmlns ? s_ns[*xmlns] : XMPPNamespace::Count; } /** * Get an XML tag enumeration value associated with an element's tag * @param xml The element to check * @return Xml tag as enumeration */ static inline int tag(const XmlElement& xml) { return s_tag[xml.getTag()]; } /** * Get an XML element's tag and namespace * @param xml The element to check * @param tag Element tag as enumeration * @param ns Element namespace as enumeration * @return True if data was succesfully retrieved */ static inline bool getTag(const XmlElement& xml, int& tag, int& ns) { const String* t = 0; const String* n = 0; if (!xml.getTag(t,n)) return false; tag = s_tag[*t]; ns = n ? s_ns[*n] : XMPPNamespace::Count; return tag != XmlTag::Count; } /** * Check if an xml element has a given tag (without prefix) and namespace * @param xml The element to check * @param tag Tag to check * @param ns Namespace to check * @return True if the element has the requested tag and namespace */ static inline bool isTag(const XmlElement& xml, int tag, int ns) { int t,n; return getTag(xml,t,n) && tag == t && n == ns; } /** * Check if an xml element has a given tag (without prefix) * @param xml The element to check * @param tag Tag to check * @return True if the element has the requested tag */ static inline bool isUnprefTag(const XmlElement& xml, int tag) { return xml.unprefixedTag() == s_tag[tag]; } /** * Check if a given element is a stanza one ('iq', 'message' or 'presence') * @param xml The element to check * @return True if the element is a stanza */ static inline bool isStanza(const XmlElement& xml) { int t,n; return getTag(xml,t,n) && (t == XmlTag::Iq || t == XmlTag::Presence || t == XmlTag::Message); } /** * Retrieve an xml element from a NamedPointer. * Release NamedPointer ownership if found * @param gen The object to be processed * @return XmlElement pointer or 0 */ static XmlElement* getXml(GenObject* gen); /** * Parse a string to an XmlElement * @param data XML data to parse * @return XmlElement pointer or 0 if the string is an invalid xml or contains more * then one element */ static XmlElement* getXml(const String& data); /** * Retrieve an xml element from a list parameter. * Clear the given parameter from list if an XmlElement is found * Try to build (parse) from an extra parameter if not found * @param list The list of parameters * @param param The name of the parameter with the xml element * @param extra Optional parameter containing xml string data * @return XmlElement pointer or 0 */ static XmlElement* getXml(NamedList& list, const char* param = "xml", const char* extra = "data"); /** * Retrieve a presence xml element from a list parameter. * Clear the given parameter from list if an XmlElement is found. * Try to build (parse) from an extra parameter if not found. * Build a presence stanza from parameters if an element is not found * @param list The list of parameters * @param param The name of the parameter with the xml element * @param extra Optional parameter containing xml string data * @param type Presence type to build * @param build True to build a message stanza if an element is not found * @return XmlElement pointer or 0 */ static XmlElement* getPresenceXml(NamedList& list, const char* param = "xml", const char* extra = "data", Presence type = PresenceNone, bool build = true); /** * Retrieve a chat (message) xml element from a list parameter. * Clear the given parameter from list if an XmlElement is found. * Try to build (parse) from an extra parameter if not found. * Build a message stanza from parameters if an element is not found * @param list The list of parameters * @param param The name of the parameter with the xml element * @param extra Optional parameter containing xml string data * @param build True to build a message stanza if an element is not found * @return XmlElement pointer or 0 */ static XmlElement* getChatXml(NamedList& list, const char* param = "xml", const char* extra = "data", bool build = true); /** * Get the type of a 'presence' stanza as enumeration * @param text The text to check * @return Presence type as enumeration */ static inline Presence presenceType(const char* text) { return (Presence)lookup(text,s_presence,PresenceNone); } /** * Get the text from a presence type * @param presence The presence type * @return The associated text or 0 */ static inline const char* presenceText(Presence presence) { return lookup(presence,s_presence,0); } /** * Get the type of a 'message' stanza * @param text The text to check * @return Message type as enumeration */ static inline MsgType msgType(const char* text) { return (MsgType)lookup(text,s_msg,TelEngine::null(text) ? Normal : MsgNone); } /** * Get the text from a message type * @param msg The message type * @return The associated text or 0 */ static inline const char* msgText(MsgType msg) { return lookup(msg,s_msg,0); } /** * Get the type of an 'iq' stanza as enumeration * @param text The text to check * @return Iq type as enumeration */ static inline IqType iqType(const char* text) { return (IqType)lookup(text,s_iq,IqCount); } /** * Get the authentication method associated with a given text * @param text The text to check * @param defVal Default value to return if not found * @return Authentication method */ static inline int authMeth(const char* text, int defVal = AuthNone) { return lookup(text,s_authMeth,defVal); } /** * Namespaces */ static XMPPNamespace s_ns; /** * Errors */ static XMPPError s_error; /** * XML tags */ static XmlTag s_tag; /** * Keep the types of 'presence' stanzas */ static const TokenDict s_presence[]; /** * Keep the types of 'message' stanzas */ static const TokenDict s_msg[]; /** * Keep the types of 'iq' stanzas */ static const TokenDict s_iq[]; /** * Keep the command actions */ static const TokenDict s_commandAction[]; /** * Keep the command status */ static const TokenDict s_commandStatus[]; /** * Authentication methods names */ static const TokenDict s_authMeth[]; }; /** * This class holds a direction flags (such as subscription states) * @short Direction flags */ class YJABBER_API XMPPDirVal { public: /** * Direction flags enumeration */ enum Direction { None = 0x00, To = 0x01, From = 0x02, PendingIn = 0x10, PendingOut = 0x20, // Masks Both = 0x03, Pending = 0x30 }; /** * Constructor * @param flags Flag(s) to set */ inline XMPPDirVal(int flags = None) : m_value(flags) {} /** * Constructor * @param flags Comma separated list of flags */ inline XMPPDirVal(const String& flags) : m_value(0) { replace(flags); } /** * Replace all flags * @param flag The new value of the flags */ inline void replace(int flag) { m_value = flag; } /** * Replace all flags from a list * @param flags Comma separated list of flags */ inline void replace(const String& flags) { m_value = XMPPUtils::decodeFlags(flags,s_names); } /** * Build a string representation of this object * @param buf Destination string * @param full True to add all flags, false to ignore pending flags */ void toString(String& buf, bool full) const; /** * Build a subscription state string representation of this object * @param buf Destination string */ void toSubscription(String& buf) const; /** * Set one or more flags * @param flag Flag(s) to set */ inline void set(int flag) { m_value |= flag; } /** * Reset one or more flags * @param flag Flag(s) to reset */ inline void reset(int flag) { m_value &= ~flag; } /** * Check if a given bit mask is set * @param mask Bit mask to check * @return True if the given bit mask is set */ inline bool test(int mask) const { return (m_value & mask) != 0; } /** * Check if the 'To' flag is set * @return True if the 'To' flag is set */ inline bool to() const { return test(To); } /** * Check if the 'From' flag is set * @return True if the 'From' flag is set */ inline bool from() const { return test(From); } /** * Cast operator */ inline operator int() { return m_value; } /** * Keep the flag names */ static const TokenDict s_names[]; private: int m_value; // The value }; /** * This class holds an XML element to be sent through a stream * @short An outgoing XML element */ class YJABBER_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 const 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 unsigned int 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(unsigned int& 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(unsigned int 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,true,String::empty(),String::empty(),!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 unsigned int 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 /* __XMPPUTILS_H */ /* vi: set ts=8 sw=4 sts=4 noet: */