/** * yatejingle.h * Yet Another Jingle 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-2023 Null Team * * This software is distributed under multiple licenses; * see the COPYING file in the main directory for licensing * information for this specific distribution. * * This use of this software may be subject to additional restrictions. * See the LEGAL file in the main directory for details. * * 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. */ #ifndef __YATEJINGLE_H #define __YATEJINGLE_H #include #include /** * Holds all Telephony Engine related classes. */ namespace TelEngine { class JGRtpMedia; // A Jingle RTP data payload class JGCrypto; // Content crypto data class JGRtpMediaList; // A list of Jingle RTP data payloads class JGRtpCandidate; // A RTP transport candidate class JGRtpCandidates; // A list of RTP transport candidates class JGSessionContent; // A Jingle session content class JGStreamHost; // A Jingle file transfer stream host class JGSession; // A basic Jingle session class JGSession0; // A session implementing the old jingle protocol class JGSession1; // The version 1 of a jingle session class JGEvent; // An event generated by a Jingle session class JGEngine; // The Jingle engine class JGSentStanza; // Sent stanza timeout info /** * This class holds a Jingle data payload description * @short A Jingle data payload */ class YJABBER_API JGRtpMedia : public GenObject { public: /** * Constructor. Fill this object from the given attributes * @param id The 'id' attribute * @param name The 'name' attribute * @param clockrate The 'clockrate' attribute * @param synonym Application synonym for this payload * @param channels Optional 'channels' attribute (the number of channels) * @param pTime Optional "ptime" attribute (packet time) * @param maxPTime Optional "maxptime" attribute (maximum packet time) * @param bitRate Optional "bitrate" attribute */ inline JGRtpMedia(const char* id, const char* name, const char* clockrate, const char* synonym, const char* channels = 0, const char* pTime = 0, const char* maxPTime = 0, const char* bitRate = 0) : m_params("") { set(id,name,clockrate,synonym,channels,pTime,maxPTime,bitRate); } /** * Constructor. Fill this object from an XML element * @param xml The element to fill from */ inline JGRtpMedia(XmlElement* xml) : m_params("") { fromXml(xml); } /** * Copy constructor */ inline JGRtpMedia(const JGRtpMedia& src) : m_params("") { set(src.m_id,src.m_name,src.m_clockrate,src.m_synonym,src.m_channels, src.m_pTime,src.m_maxPTime,src.m_bitRate); m_params = src.m_params; } /** * Set the data * @param id The 'id' attribute * @param name The 'name' attribute * @param clockrate The 'clockrate' attribute * @param synonym Application synonym for this payload * @param channels Optional 'channels' attribute (the number of channels) * @param pTime Optional "ptime" attribute (packet time) * @param maxPTime Optional "maxptime" attribute (maximum packet time) * @param bitRate Optional "bitrate" attribute */ inline void set(const char* id, const char* name, const char* clockrate, const char* synonym = 0, const char* channels = 0, const char* pTime = 0, const char* maxPTime = 0, const char* bitRate = 0) { m_id = id; m_name = name; m_clockrate = clockrate; m_synonym = synonym; m_channels = channels; m_pTime = pTime; m_maxPTime = maxPTime; m_bitRate = bitRate; m_params.clearParams(); } /** * Get the string repreasentation (id) of this payload * @return The string repreasentation (id) of this payload */ virtual const String& toString() const { return m_id; } /** * Create a 'payload-type' element from this object * @return Valid XmlElement pointer */ XmlElement* toXml() const; /** * Fill this object from a given element * @param xml The element */ void fromXml(XmlElement* xml); /** * The numeric id of this payload */ String m_id; /** * The Jingle name of this payload */ String m_name; /** * The clockrate of this payload */ String m_clockrate; /** * A synonym of this payload's name */ String m_synonym; /** * The number of channels */ String m_channels; /** * Packet time */ String m_pTime; /** * Maximum packet time */ String m_maxPTime; /** * Data bit rate */ String m_bitRate; /** * List of optional parameters */ NamedList m_params; }; /** * This class holds a content description's crypto data. * The tag is kept in the String component * @short Content crypto data */ class YJABBER_API JGCrypto : public String { public: /** * Constructor * @param tag The tag parameter * @param suite The crypto-suite paramter * @param key The key-params parameter * @param session The session-param parameter */ inline JGCrypto(const char* tag = "1", const char* suite = 0, const char* key = 0, const char* session = 0) : String(tag), m_suite(suite), m_keyParams(key), m_sessionParams(session) {} /** * Constructor. Build this element from a received element * @param xml The received xml element */ inline JGCrypto(const XmlElement* xml) { fromXml(xml); } /** * Create a 'crypto' element from this object * @return Valid XmlElement pointer */ XmlElement* toXml() const; /** * Build this element from a received element * @param xml The received xml element */ void fromXml(const XmlElement* xml); /** * Build an 'encryption' element from a list of crypto objects * @param list The list of crypto objects * @param required True if encryption is required * @return XmlElement pointer or 0 if the list is empty */ static XmlElement* buildEncryption(const ObjList& list, bool required); /** * Decode an 'encryption' element. Clear the list before starting * @param xml The element to decode * @param list The list to be filled with crypto objects * @param required Variable to be filled with the value of the 'required' attribute */ static void decodeEncryption(const XmlElement* xml, ObjList& list, bool& required); String m_suite; String m_keyParams; String m_sessionParams; }; /** * Hold a list of RTP data payloads * @short A List of Jingle RTP data payloads */ class YJABBER_API JGRtpMediaList : public ObjList { public: /** * Media type enumeration */ enum Media { MediaMissing = -1, MediaUnknown = 0, Audio = 1, }; /** * Constructor * @param m Media type as enumeration * @param cryptoRequired True to require media encryption */ inline JGRtpMediaList(Media m = MediaMissing, bool cryptoRequired = false) : m_media(m), m_bandwidth(0), m_cryptoRequired(cryptoRequired), m_ready(false), m_telEvent(101), m_telEventName("telephone-event") {} /** * Destructor */ inline ~JGRtpMediaList() { TelEngine::destruct(m_bandwidth); } /** * Get the media type of the payloads owned by this list * @return Media type as enumeration */ inline Media media() const { return m_media; } /** * Append a new data payload * @param id The 'id' attribute * @param name The 'name' attribute * @param clockrate The 'clockrate' attribute * @param synonym Optional application synonym for the payload * @param channels Optional 'channels' attribute (the number of channels) * @param pTime Optional "ptime" attribute (packet time) * @param maxPTime Optional "maxptime" attribute (maximum packet time) * @param bitRate Optional "bitrate" attribute */ inline void add(const char* id, const char* name, const char* clockrate, const char* synonym = 0, const char* channels = 0, const char* pTime = 0, const char* maxPTime = 0, const char* bitRate = 0) { append(new JGRtpMedia(id,name,clockrate,synonym,channels,pTime,maxPTime,bitRate)); } /** * Reset the list and data */ void reset(); /** * Set media type and payloads from another list * @param src Media list to copy into this one * @param only Optional list of synonyms to set if found in src. * Copy the whole list if this parameter is empty */ void setMedia(const JGRtpMediaList& src, const String& only = String::empty()); /** * Filter media list preserving only some formats * @param only List of synonyms to preserve, do not filter media if this parameter is empty */ void filterMedia(const String& only); /** * Find a data payload by its id * @param id Identifier of media to find * @return JGRtpMedia pointer or 0 if not found */ JGRtpMedia* findMedia(const String& id); /** * Find a data payload by its synonym * @param value The value to compare with * @return JGRtpMedia pointer or 0 if not found */ JGRtpMedia* findSynonym(const String& value) const; /** * Create a 'description' element and add payload children to it * @return Valid XmlElement pointer */ XmlElement* toXml() const; /** * Fill this list from an XML element's children. Clear before attempting to fill * @param xml The source XML element */ void fromXml(XmlElement* xml); /** * Create a list from data payloads * @param dest Destination string * @param synonym True to create from synonyms, false to create from names * @param sep List item separator * @return False if the list is empty */ bool createList(String& dest, bool synonym, const char* sep = ","); /** * Build and add telephone-event media child to a parent xml element. * Add a second telephone event media child if set * @param xml Parent element * @param name Optional event name. Defaults to set event name */ void addTelEvent(XmlElement* xml, const char* name = 0) const; /** * The list of media type names */ static const TokenDict s_media[]; /** * The media type */ Media m_media; /** * Synchronization source */ String m_ssrc; /** * Optional SDP media bandwith. The name of the string keeps the type ('bwtype') * and its value keeps the actual bandwith */ NamedString* m_bandwidth; /** * Crypto (SRTP) params */ bool m_cryptoRequired; ObjList m_cryptoLocal; ObjList m_cryptoRemote; /** * Flag indicating wether media was negotiated */ bool m_ready; /** * Telephone event payload id */ int m_telEvent; /** * Telephone event payload name */ String m_telEventName; /** * Second telephone event payload name */ String m_telEventName2; }; /** * This class holds a RTP transport candidate * @short A RTP transport candidate */ class YJABBER_API JGRtpCandidate : public String { public: /** * Constructor */ inline JGRtpCandidate(const char* id, const char* component = "1", unsigned int generation = 0, unsigned int net = 0, int prio = 0) : String(id), m_port(0), m_component(component), m_generation(generation), m_network(net), m_priority(prio), m_protocol("udp"), m_type("host") {} /** * Constructor. Build a candidate from received data * @param xml Received xml element * @param container The transport container */ inline JGRtpCandidate(XmlElement* xml, const JGRtpCandidates& container) { fromXml(xml,container); } /** * Create a 'candidate' element from this object using local address/port * @param container The transport container * @return Valid XmlElement pointer if type is a known one */ virtual XmlElement* toXml(const JGRtpCandidates& container) const; /** * Fill this object from a candidate element using remote address/port * @param xml Received xml element * @param container The transport container */ void fromXml(XmlElement* xml, const JGRtpCandidates& container); String m_address; String m_port; String m_component; // Candidate component String m_generation; // Candidate generation String m_network; // NIC card (diagnostic only) String m_priority; // Candidate priority String m_protocol; // The only allowable value is "udp" String m_type; // A Candidate Type as defined in ICE-CORE }; /** * This class holds a RTP transport candidate * @short A RTP transport candidate */ class YJABBER_API JGRtpCandidateP2P : public JGRtpCandidate { YCLASS(JGRtpCandidateP2P,JGRtpCandidate) public: /** * Constructor */ inline JGRtpCandidateP2P() : JGRtpCandidate("") {} /** * Constructor. Build a candidate from received data * @param xml Received xml element * @param container The transport container */ inline JGRtpCandidateP2P(XmlElement* xml, const JGRtpCandidates& container) : JGRtpCandidate("") { fromXml(xml,container); } /** * Create a 'candidate' element from this object using local address/port * @param container The transport container * @return Valid XmlElement pointer if type is a known one */ virtual XmlElement* toXml(const JGRtpCandidates& container) const; /** * Fill this object from a candidate element using remote address/port * @param xml Received xml element * @param container The transport container */ void fromXml(XmlElement* xml, const JGRtpCandidates& container); String m_username; String m_password; }; /** * This class holds a list of jingle RTP transport candidates * @short A list of RTP transport candidates */ class YJABBER_API JGRtpCandidates : public ObjList { public: /** * Enumeration of transport types */ enum Type { Unknown = -1, RtpIceUdp = 1, RtpRawUdp, RtpP2P, RtpGoogleRawUdp, }; /** * Constructor. Fill this object from an XML element * @param t The transport type */ inline JGRtpCandidates(Type t = Unknown) : m_type(t) {} /** * Get the name of this list's type * @return The name of this list's type */ inline const char* typeName() const { return typeName(m_type); } /** * Fill password and ufrag data */ inline void generateIceAuth() { generateIceToken(m_password,true); generateIceToken(m_ufrag,false); } /** * Fill password and ufrag data using old transport restrictions (16 bytes length) */ inline void generateOldIceAuth() { generateOldIceToken(m_password); generateOldIceToken(m_ufrag); } /** * Find a candidate by its component value * @param component The value to search * @return JGRtpCandidate pointer or 0 */ JGRtpCandidate* findByComponent(unsigned int component); /** * Create a 'transport' element from this object. Add candidates * @param addCandidates True to add the candidate children * @param addAuth RtpIceUdp only: add auth data * @return Valid XmlElement pointer */ XmlElement* toXml(bool addCandidates, bool addAuth) const; /** * Fill this object from a given element * @param element The element */ void fromXml(XmlElement* element); /** * Generate a random password or username to be used with ICE-UDP transport * @param dest Destination string * @param pwd True to generate a password, false to generate an username (ufrag) * @param max Maximum number of characters. The maxmimum value is 256. * The minimum value is 22 for password and 4 for username */ static void generateIceToken(String& dest, bool pwd, unsigned int max = 0); /** * Generate a random password or username to be used with old ICE-UDP transport * @param dest Destination string */ static void generateOldIceToken(String& dest); /** * Get the name associated with a list's type * @param t The desired type * @param defVal Default value to return * @return The name associated with a list's type */ static inline const char* typeName(int t, const char* defVal = "unknown") { return TelEngine::lookup(t,s_type,defVal); } /** * The list of type names */ static const TokenDict s_type[]; Type m_type; String m_password; String m_ufrag; }; /** * This class holds a Jingle content negotiated during a session * It can be built from a received xml element and * it can build an xml element from itself * @short A Jingle session content */ class YJABBER_API JGSessionContent : public RefObject { public: /** * Enumeration of content type */ enum Type { Unknown = -1, // Unknown UnknownFileTransfer = -2, // Unknown (unsupported) file transfer content RtpIceUdp = 1, // Audio: RTP ICE-UDP transport RtpRawUdp, // Audio: RTP RAW-UDP transport RtpP2P, // RtpGoogleRawUdp, // FileBSBOffer, // File offer: byte stream (SOCKS) transport FileBSBRequest, // File request: byte stream (SOCKS) transport }; /** * Enumeration values for the 'senders' attribute (required) */ enum Senders { SendUnknown = 0, SendBoth = 1, SendInitiator = 2, SendResponder = 3 }; /** * Enumeration values for the 'creator' attribute (required) */ enum Creator { CreatorUnknown = 0, CreatorInitiator = 1, CreatorResponder = 2 }; /** * Constructor * @param t Content type as enumeration * @param name Content name * @param senders Content senders as enumeration * @param creator Content creator as enumeration * @param disposition Optional content disposition (defauls to 'session' if empty) */ JGSessionContent(Type t, const char* name, Senders senders, Creator creator, const char* disposition = 0); /** * Get the content type * @return Content type as enumeration */ inline Type type() const { return m_type; } /** * Get the senders * @return Senders as enumeration */ inline Senders senders() const { return m_senders; } /** * Get the content creator * @return Content creator as enumeration */ inline Creator creator() const { return m_creator; } /** * Check if this content is a valid audio one: it's media list type is Audio * and the payload list is not empty * @return True if this content can be used for audio purposes */ inline bool isValidAudio() const { return (m_rtpMedia.media() == JGRtpMediaList::Audio) && (0 != m_rtpMedia.skipNull()); } /** * Get the name of this content */ virtual const String& toString() const { return m_name; } /** * Check if the content disposition is session * XEP-0166: true if disposition is missing * @return True if this content should be processed at session level */ inline bool isSession() const { return !m_disposition || m_disposition == "session"; } /** * Check if the content disposition is early media * @return True if this content is an early media one */ inline bool isEarlyMedia() const { return m_disposition == "early-session"; } /** * Set this content's disposition to early media */ inline void setEarlyMedia() { m_disposition = "early-session"; } /** * Build a 'content' XML element from this object * @param minimum Minimum data (only creator and name) * @param addDesc True to add the description child * @param addTrans True to add the transport child * @param addCandidates True to add the transport candidate children * @param addAuth RtpIceUdp only: add auth data * @return Valid XmlElement pointer */ XmlElement* toXml(bool minimum, bool addDesc, bool addTrans, bool addCandidates, bool addAuth) const; /** * Decode 'content' element attributes * @param xml The XML element * @param err The error on failure * @param error Error text to be sent on failure * @return Valid JGSessionContent pointer on success */ static JGSessionContent* fromXml(XmlElement* xml, XMPPError::Type& err, String& error); /** * The list containing the text values for Senders enumeration */ static const TokenDict s_senders[]; /** * The list containing the text values for Creator enumeration */ static const TokenDict s_creator[]; /** * The RTP media description if used */ JGRtpMediaList m_rtpMedia; /** * The RTP local candidates */ JGRtpCandidates m_rtpLocalCandidates; /** * The RTP remote candidates */ JGRtpCandidates m_rtpRemoteCandidates; /** * File info (for file transfer) */ NamedList m_fileTransfer; private: Type m_type; String m_name; Senders m_senders; Creator m_creator; String m_disposition; }; /** * This class holds a file transfer stream host definition * @short A Jingle file transfer stream host */ class JGStreamHost : public String { public: /** * Constructor * @param local Local stream host * @param jid Stream host jid (id) * @param addr Stream host address * @param port Stream host port * @param zeroConf Optional zero conf definition (override address/port) */ JGStreamHost(bool local, const char* jid, const char* addr, int port, const char* zeroConf = 0) : String(jid), m_local(local), m_address(addr), m_port(port), m_zeroConf(zeroConf) {} /** * Copy constructor * @param src Source stream host to copy from */ inline JGStreamHost(const JGStreamHost& src) : String(src), m_local(src.m_local), m_address(src.m_address), m_port(src.m_port), m_zeroConf(src.m_zeroConf) {} /** * Build an XML element from this stream host * @return Valid XmlElement pointer */ XmlElement* toXml(); /** * Build a stream host from an XML element * @param xml The element to build from * @return Valid JGStreamHost pointer or 0 on error */ static JGStreamHost* fromXml(XmlElement* xml); /** * Build a query XML element carrying a list of stream hosts * @param hosts List of JGStreamHost objects * @param sid The query element's sid attribute * @param mode The query element's mode attribute * @return Valid XmlElement pointer */ static XmlElement* buildHosts(const ObjList& hosts, const char* sid, const char* mode = "tcp"); /** * Build a query XML element with a streamhost-used child * @param jid The jid of the stream host used * @return Valid XmlElement pointer */ static XmlElement* buildRsp(const char* jid); bool m_local; String m_address; int m_port; String m_zeroConf; }; /** * This class is a base class for all specific jingle sessions * @short A basic Jingle session */ class YJABBER_API JGSession : public RefObject, public Mutex { friend class JGEvent; friend class JGEngine; public: /** * Jingle session version */ enum Version { Version0 = 0, Version1 = 1, VersionUnknown }; /** * Jingle defined reasons and errors */ enum Reason { ReasonUnknown = 0, // Session termination reason ReasonOk, // success ReasonBusy, // busy ReasonDecline, // decline ReasonCancel, // cancel ReasonExpired, // expired ReasonConn, // connectivity-error ReasonFailApp, // failed-application ReasonFailTransport, // failed-transport ReasonGone, // gone ReasonParams, // incompatible-parameters ReasonMedia, // media-error ReasonTransport, // unsupported-transports ReasonApp, // unsupported-applications ReasonSecurity, // security-error ReasonTimeout, // timeout ReasonGeneral, // general-error ReasonAltSess, // alternative-session // Session transfer (XEP 0251) Transferred, // transferred // RTP session errors (XEP 0167) CryptoRequired, // crypto-required InvalidCrypto, // invalid-crypto }; /** * RTP session info (XEP 0167) */ enum RtpInfo { RtpActive, // active RtpHold, // hold RtpMute, // mute RtpRinging, // ringing }; /** * Session state enumeration */ enum State { Idle = 0, // Outgoing stream is waiting for Pending = 1, // Session is pending, session-initiate sent/received Active = 2, // Session is active, session-accept sent/received Ending = 3, // Session terminated: Wait for write result Destroy = 4, // The session will be destroyed }; /** * Jingle action enumeration */ enum Action { ActAccept, // session-accept ActInitiate, // session-initiate ActTerminate, // session-terminate ActReject, // reject ActInfo, // session-info ActTransportInfo, // transport-info ActTransportAccept, // transport-accept ActTransportReject, // transport-reject ActTransportReplace, // transport-replace ActCandidates, // candidates ActContentAccept, // content-accept ActContentAdd, // content-add ActContentModify, // content-modify ActContentReject, // content-reject ActContentRemove, // content-remove ActContentInfo, // content-info ActDescriptionInfo, // description-info ActTransfer, // session-info: Transfer ActRinging, // session-info: Ringing ActTrying, // session-info: Trying ActReceived, // session-info: Received ActHold, // session-info: Hold ActActive, // session-info: Active ActMute, // session-info: Mute ActDtmf, // session-info: Dtmf ActStreamHost, ActCount, }; /** * Session flags */ enum SessionFlag { FlagNoPing = 0x0001, // Don't send ping FlagRingNsRtp = 0x0002, // Send ringing using rtp namespace instead of rtp info namespace FlagNoOkInitiate = 0x0004, // Don't raise a ResultOk when initiate stanza is confirmed }; /** * Destructor */ virtual ~JGSession(); /** * Get the session version * @return The session version */ inline Version version() const { return m_version; } /** * Retrieve the engine owning this session * @return The engine owning this session */ inline JGEngine* engine() const { return m_engine; } /** * Get the session direction * @return True if it is an outgoing session */ inline bool outgoing() const { return m_outgoing; } /** * Get the session id * @return The session id */ inline const String& sid() const { return m_sid; } /** * Get the local peer's JID * @return The local peer's JID */ inline const JabberID& local() const { return m_local; } /** * Get the remote peer's JID * @return The remote peer's JID */ inline const JabberID& remote() const { return m_remote; } /** * Get the session state. * @return The session state as enumeration. */ inline State state() const { return m_state; } /** * Retrieve session flags * @param mask Mask to retrieve * @return Session flags */ inline int flag(int mask) const { return m_flags & mask; } /** * Replace session flags * @param value The new session flags */ inline void setFlags(int value) { m_flags = value; } /** * Get the arbitrary user data of this session * @return The arbitrary user data of this session */ inline void* userData() { return m_private; } /** * Set the arbitrary user data of this session * @param userdata The new arbitrary user data's value */ inline void userData(void* userdata) { m_private = userdata; } /** * Retrieve the client account used by this session * @return The client account used by this session */ inline const String& line() const { return m_line; } /** * Set the client account used by this session * @param acc The client account used by this session */ inline void line(const String& acc) { m_line = acc; } /** * Get an action (jingle element type) from a jingle element * @param xml Element to check * @return The found action, ActCount if not found or unknown */ Action getAction(XmlElement* xml); /** * Ask this session to accept an incoming xml 'iq' element * @param type Iq type as enumeration * @param from The sender * @param to The recipient * @param id The session id of this is a request (set/get) or the stanza id * @param xml The received element * @return True if accepted (the element was enqueued), false if not */ bool acceptIq(XMPPUtils::IqType type, const JabberID& from, const JabberID& to, const String& id, XmlElement* xml); /** * Confirm (send result) a received element * @param xml The element to confirm * @return False if send failed or element is 0 */ bool confirmResult(XmlElement* xml); /** * Confirm (send error) a received element * @param xml The element to confirm (will be consumed and zeroed) * @param error The error condition * @param text Optional text to add to the error element * @param type Error type * @return False if send failed or element is 0 */ bool confirmError(XmlElement*& xml, XMPPError::Type error, const char* text = 0, XMPPError::ErrorType type = XMPPError::TypeModify); /** * Accept a Pending incoming session. * This method is thread safe * @param contents The list of accepted contents * @param stanzaId Optional string to be filled with sent stanza id (used to track the response) * @return False if send failed */ virtual bool accept(const ObjList& contents, String* stanzaId = 0) { return false; } /** * Close a Pending or Active session * This method is thread safe * @param reason Optional termination reason * @return False if send failed */ virtual bool hangup(XmlElement* reason = 0); /** * Create a RTP info child to be added to a session-info element * @param info The informational tag as enumeration * @return Valid XmlElement pointer or 0 if not supported */ virtual XmlElement* createRtpInfoXml(RtpInfo info) { return 0; } /** * Create a termination reason element * @param reason The reason code * @param text Optional reason text child * @param child Optional additional reason child * @return Valid XmlElement pointer or 0 if not supported */ virtual XmlElement* createReason(int reason, const char* text = 0, XmlElement* child = 0) { return 0; } /** * Create a transfer reason element * @param reason The reason code * @return Valid XmlElement pointer or 0 if not supported */ virtual XmlElement* createTransferReason(int reason) { return 0; } /** * Create a RTP session reason element * @param reason The reason code * @return Valid XmlElement pointer or 0 if not supported */ virtual XmlElement* createRtpSessionReason(int reason) { return 0; } /** * Send a stanza with session content(s) * This method is thread safe * @param action Must be a transport- or content- action * @param contents Non empty list with content(s) to send * @param stanzaId Optional string to be filled with sent * stanza id (used to track the response) * @return False if send failed */ virtual bool sendContent(Action action, const ObjList& contents, String* stanzaId = 0) { return false; } /** * Send a stanza with a session content * This method is thread safe * @param action Must be a transport- or content- action * @param content The content to send * @param stanzaId Optional string to be filled with sent * stanza id (used to track the response) * @return False if send failed */ inline bool sendContent(Action action, const JGSessionContent* content, String* stanzaId = 0) { if (!content) return false; ObjList tmp; tmp.append(content)->setDelete(false); return sendContent(action,tmp,stanzaId); } /** * Send a stanza with stream hosts * This method is thread safe * @param hosts The list of hosts to send * @param stanzaId Optional string to be filled with sent * stanza id (used to track the response) * @return False if send failed */ virtual bool sendStreamHosts(const ObjList& hosts, String* stanzaId = 0) { return false; } /** * Send a stanza with a stream host used. If the jid is empty, send an * item-not-found error response * This method is thread safe * @param jid The stream host to send * @param stanzaId The id of the stanza to confirm * @return False if send failed */ virtual bool sendStreamHostUsed(const char* jid, const char* stanzaId) { return false; } /** * Build SOCKS SHA1 dst.addr used by file transfer * @param buf Destination string */ void buildSocksDstAddr(String& buf); /** * Send a session info element to the remote peer. * This method is thread safe * @param xml The XmlElement carried by the session info element * @param stanzaId Optional string to be filled with sent stanza id (used to track the response) * @param extra Optional extra child for jingle element * @return False on failure */ bool sendInfo(XmlElement* xml, String* stanzaId = 0, XmlElement* extra = 0); /** * Send a dtmf string to remote peer. If the string's length is greater then 1, each * character is added as a 'dtmf' child of the jingle element * @param dtmf The dtmf string * @param msDuration The tone duration in miliseconds. Ignored if 0 * @param stanzaId Optional string to be filled with sent stanza id (used to track the response) * @return False if send failed */ bool sendDtmf(const char* dtmf, unsigned int msDuration = 0, String* stanzaId = 0); /** * Check if the remote party supports a given feature * @param feature The requested feature * @return True if the remote party supports the given feature */ bool hasFeature(XMPPNamespace::Type feature); /** * Build a transfer element * @param transferTo The JID to transfer to * @param transferFrom The transferror's JID * @param sid Optional session id used for attended transfer (empty for unattended transfer) * @return Valid XmlElement pointer */ static XmlElement* buildTransfer(const String& transferTo, const String& transferFrom, const String& sid = String::empty()); /** * Get the session version associated with a text * @param value The version text * @param def Default value to return if not found * @return Session Version value */ static inline Version lookupVersion(const char* value, Version def = VersionUnknown) { return (Version)lookup(value,s_versions,def); } /** * Get the session version name * @param value The version value * @param def Default value to return if not found * @return Session version name or the default value if not found */ static inline const char* lookupVersion(int value, const char* def = "unknown") { return lookup(value,s_versions,def); } /** * Get the termination code associated with a text * @param value The termination text * @param def Default value to return if not found * @return Termination code */ static inline int lookupReason(const char* value, int def = ReasonOk) { return lookup(value,s_reasons,def); } /** * Get the termination code associated with a text * @param value The termination code * @param def Default value to return if not found * @return Termination text */ static inline const char* lookupReason(int value, const char* def = 0) { return lookup(value,s_reasons,def); } /** * Get the name of a session state * @param state The state to find * @return The name of a session state */ static const char* lookupState(int state) { return lookup(state,s_states); } /** * Get the name of an action * @param act The action to find * @param ver Session version to use * @return The name of an action */ static const char* lookupAction(int act, Version ver); /** * Get the action associated with a given string * @param str The action name * @param ver Session version to use * @return The name of an action */ static Action lookupAction(const char* str, Version ver); /** * Session version names */ static const TokenDict s_versions[]; /** * Termination reasons and errors */ static const TokenDict s_reasons[]; /** * RTP session info (XEP 0167) */ static const TokenDict s_rtpInfo[]; /** * Session state names */ static const TokenDict s_states[]; /** * Action names for version Version0 */ static const TokenDict s_actions0[]; /** * Action names for version Version1 */ static const TokenDict s_actions1[]; /** * Session flag names */ static const TokenDict s_flagName[]; protected: /** * Constructor. Create an outgoing session * @param ver The session version * @param engine The engine owning this session * @param caller The caller's full JID * @param called The called party's full JID */ JGSession(Version ver, JGEngine* engine, const JabberID& caller, const JabberID& called); /** * Constructor. Create an incoming session. * @param ver The session version * @param engine The engine owning this session * @param caller The caller's full JID * @param called The called party's full JID * @param xml A valid Jabber Jingle xml with action session initiate * @param id Session id */ JGSession(Version ver, JGEngine* engine, const JabberID& caller, const JabberID& called, XmlElement* xml, const String& id); /** * Build and send the initial message on an outgoing session * @param contents The session contents to be sent with session initiate element * @param extra Optional extra child to be added to the session initiate element * @param subject Optional session subject * @return True on success */ virtual bool initiate(const ObjList& contents, XmlElement* extra, const char* subject = 0) = 0; /** * Get a Jingle event from the queue. * This method is thread safe * @param time Current time in miliseconds * @return JGEvent pointer or 0 */ virtual JGEvent* getEvent(u_int64_t time); /** * Release this session and its memory */ virtual void destroyed(); /** * Send a stanza to the remote peer * @param stanza The stanza to send * @param stanzaId Optional string to be filled with sent stanza id (used to track the response) * @param confirmation True if the stanza needs confirmation (add 'id' attribute) * @param ping True if the stanza is a ping one * @param toutMs Optional stanza timeout interval in milliseconds * @return True on success */ bool sendStanza(XmlElement* stanza, String* stanzaId = 0, bool confirmation = true, bool ping = false, unsigned int toutMs = 0); /** * Send a ping (empty session info) stanza to the remote peer if it's time to do it * @param msecNow The current time * @return True if a ping was succesfully sent */ bool sendPing(u_int64_t msecNow); /** * Decode a jingle element * @param xml The element to decode * @param child The element's first child * @return JGEvent pointer or 0 */ virtual JGEvent* decodeJingle(XmlElement*& xml, XmlElement* child) = 0; /** * Create an 'iq' of type 'set' with a 'jingle' child * @param action The action of the Jingle stanza * @param element1 Optional child element * @param element2 Optional child element * @param element3 Optional child element * @return Valid XmlElement pointer */ virtual XmlElement* createJingle(Action action, XmlElement* element1 = 0, XmlElement* element2 = 0, XmlElement* element3 = 0) = 0; /** * Create a dtmf XML element * @param dtmf The dtmf string * @param msDuration The tone duration in miliseconds. Ignored if 0 * @return Valid XmlElement pointer or 0 */ virtual XmlElement* createDtmf(const char* dtmf, unsigned int msDuration = 0) = 0; /** * Method called in getEvent() to process a last event decoded from a * received jingle element * @param ev The event to process (will be consumed and zeroed) * @return JGEvent pointer or 0 */ virtual JGEvent* processJingleSetEvent(JGEvent*& ev); /** * Method called in getEvent() to process a jabber event carrying a response * @param result True if the element is a result, false if it's an error response * @param xml Xml element to process * @return JGEvent pointer or 0 */ virtual JGEvent* processJabberIqResponse(bool result, XmlElement*& xml); /** * Decode a file transfer element * @param set True if the xml is an iq 'set', false if type is 'get' * @param xml The element to decode * @param child The element's first child * @return JGEvent pointer or 0 */ virtual JGEvent* processFileTransfer(bool set, XmlElement*& xml, XmlElement* child); /** * Terminate notification from an event. Reset the last generated event * @param event Terminated (processed) event */ void eventTerminated(JGEvent* event); /** * Changed session state * @param newState Session new state */ void changeState(State newState); Version m_version; // Session version State m_state; // Session state int m_flags; // Session flags u_int64_t m_timeToPing; // Time to send ping (empty session-info) JGEngine* m_engine; // The engine that owns this session bool m_outgoing; // Session direction String m_sid; // Session id JabberID m_local; // Local peer's JID JabberID m_remote; // Remote peer's JID XmlFragment m_queue; // Incoming, unprocessed, xml elements JGEvent* m_lastEvent; // Last generated event bool m_recvTerminate; // Flag indicating whether session-terminate was received void* m_private; // Arbitrary user data String m_localSid; // Local session id (used to generate element's id) u_int32_t m_stanzaId; // Sent stanza id counter ObjList m_sentStanza; // Sent stanzas' id String m_line; // Session account private: JGSession() {} // Don't use it }; /** * A session implementing the old jingle protocol * @short The version 0 of a jingle session */ class YJABBER_API JGSession0 : public JGSession { friend class JGEvent; friend class JGEngine; public: /** * Destructor */ virtual ~JGSession0(); /** * Accept a Pending incoming session. * This method is thread safe * @param contents The list of accepted contents * @param stanzaId Optional string to be filled with sent stanza id (used to track the response) * @return False if send failed */ virtual bool accept(const ObjList& contents, String* stanzaId = 0); protected: /** * Constructor. Create an outgoing session * @param engine The engine owning this session * @param caller The caller's full JID * @param called The called party's full JID */ JGSession0(JGEngine* engine, const JabberID& caller, const JabberID& called); /** * Constructor. Create an incoming session. * @param engine The engine owning this session * @param caller The caller's full JID * @param called The called party's full JID * @param xml A valid Jabber Jingle xml with action session initiate * @param id Session id */ JGSession0(JGEngine* engine, const JabberID& caller, const JabberID& called, XmlElement* xml, const String& id); /** * Build and send the initial message on an outgoing session * @param contents The session contents to be sent with session initiate element * @param extra Optional extra child to be added to the session initiate element * @param subject Optional session subject * @return True on success */ virtual bool initiate(const ObjList& contents, XmlElement* extra, const char* subject = 0); /** * Send a stanza with session content(s) * This method is thread safe * @param action Must be a transport- action * @param contents Non empty list with content(s) to send * @param stanzaId Optional string to be filled with sent * stanza id (used to track the response) * @return False if send failed */ virtual bool sendContent(Action action, const ObjList& contents, String* stanzaId = 0); /** * Decode a jingle element * @param xml The element to decode * @param child The element's first child * @return JGEvent pointer or 0 */ virtual JGEvent* decodeJingle(XmlElement*& xml, XmlElement* child); /** * Create an 'iq' of type 'set' with a 'jingle' child * @param action The action of the Jingle stanza * @param element1 Optional child element * @param element2 Optional child element * @param element3 Optional child element * @return Valid XmlElement pointer */ virtual XmlElement* createJingle(Action action, XmlElement* element1 = 0, XmlElement* element2 = 0, XmlElement* element3 = 0); /** * Create a dtmf XML element * @param dtmf The dtmf string * @param msDuration The tone duration in miliseconds. Ignored if 0 * @return Valid XmlElement pointer or 0 */ virtual XmlElement* createDtmf(const char* dtmf, unsigned int msDuration = 0); protected: String m_sessContentName; // Content name advertised to upper layer Action m_candidatesAction; // Use candidates/transport-info for candidates }; /** * A session implementing the Jingle protocol including session transfer and file transfer * @short The version 1 of a jingle session */ class YJABBER_API JGSession1 : public JGSession { friend class JGEvent; friend class JGEngine; public: /** * Destructor */ virtual ~JGSession1(); /** * Accept a Pending incoming session. * This method is thread safe * @param contents The list of accepted contents * @param stanzaId Optional string to be filled with sent stanza id (used to track the response) * @return False if send failed */ virtual bool accept(const ObjList& contents, String* stanzaId = 0); /** * Create a RTP info child to be added to a session-info element * @param info The informational tag as enumeration * @return Valid XmlElement pointer or 0 if not supported */ virtual XmlElement* createRtpInfoXml(RtpInfo info); /** * Create a termination reason element * @param reason The reason code * @param text Optional reason text child * @param child Optional additional reason child * @return Valid XmlElement pointer or 0 if not supported */ virtual XmlElement* createReason(int reason, const char* text = 0, XmlElement* child = 0); /** * Create a transfer reason element * @param reason The reason code * @return Valid XmlElement pointer or 0 if not supported */ virtual XmlElement* createTransferReason(int reason); /** * Create a RTP session reason element * @param reason The reason code * @return Valid XmlElement pointer or 0 if not supported */ virtual XmlElement* createRtpSessionReason(int reason); /** * Send a stanza with session content(s) * This method is thread safe * @param action Must be a transport- or content- action * @param contents Non empty list with content(s) to send * @param stanzaId Optional string to be filled with sent * stanza id (used to track the response) * @return False if send failed */ virtual bool sendContent(Action action, const ObjList& contents, String* stanzaId = 0); /** * Send a stanza with stream hosts * This method is thread safe * @param hosts The list of hosts to send * @param stanzaId Optional string to be filled with sent * stanza id (used to track the response) * @return False if send failed */ virtual bool sendStreamHosts(const ObjList& hosts, String* stanzaId = 0); /** * Send a stanza with a stream host used. If the jid is empty, send an * item-not-found error response * This method is thread safe * @param jid The stream host to send * @param stanzaId The id of the stanza to confirm * @return False if send failed */ virtual bool sendStreamHostUsed(const char* jid, const char* stanzaId); protected: /** * Constructor. Create an outgoing session * @param engine The engine owning this session * @param caller The caller's full JID * @param called The called party's full JID */ JGSession1(JGEngine* engine, const JabberID& caller, const JabberID& called); /** * Constructor. Create an incoming session. * @param engine The engine owning this session * @param caller The caller's full JID * @param called The called party's full JID * @param xml A valid Jabber Jingle xml with action session initiate * @param id Session id */ JGSession1(JGEngine* engine, const JabberID& caller, const JabberID& called, XmlElement* xml, const String& id); /** * Build and send the initial message on an outgoing session * @param contents The session contents to be sent with session initiate element * @param extra Optional extra child to be added to the session initiate element * @param subject Optional session subject * @return True on success */ virtual bool initiate(const ObjList& contents, XmlElement* extra, const char* subject = 0); /** * Decode a jingle element * @param xml The element to decode * @param child The element's first child * @return JGEvent pointer or 0 */ virtual JGEvent* decodeJingle(XmlElement*& xml, XmlElement* child); /** * Create an 'iq' of type 'set' with a 'jingle' child * @param action The action of the Jingle stanza * @param element1 Optional child element * @param element2 Optional child element * @param element3 Optional child element * @return Valid XmlElement pointer */ virtual XmlElement* createJingle(Action action, XmlElement* element1 = 0, XmlElement* element2 = 0, XmlElement* element3 = 0); /** * Create a dtmf XML element * @param dtmf The dtmf string * @param msDuration The tone duration in miliseconds. Ignored if 0 * @return Valid XmlElement pointer or 0 */ virtual XmlElement* createDtmf(const char* dtmf, unsigned int msDuration = 0); /** * Decode a file transfer element * @param set True if the xml is an iq 'set', false if type is 'get' * @param xml The element to decode * @param child The element's first child * @return JGEvent pointer or 0 */ virtual JGEvent* processFileTransfer(bool set, XmlElement*& xml, XmlElement* child); }; /** * This class holds an event generated by a Jingle session * @short A Jingle event */ class YJABBER_API JGEvent { friend class JGSession; friend class JGSession0; friend class JGSession1; public: /** * Jingle events enumeration */ enum Type { Jingle, // ResultOk, // Response for a sent stanza (iq with type=result) ResultError, // Response for a sent stanza (iq with type=error) ResultTimeout, // Response for a sent stanza (stanza timeout) // Final Terminated, // m_element is the element that caused the termination // m_reason contains the reason Destroy, // The engine sould delete the event (causing session destruction) }; /** * Destructor. Deref the session. Delete the XML element */ virtual ~JGEvent(); /** * Get the type of this event * @return The type of this event as enumeration */ inline Type type() const { return m_type; } /** * Get the name of this * @return The name of this event */ inline const char* name() { return lookupType(m_type); } /** * Get the session that generated this event * @return The session that generated this event */ inline JGSession* session() const { return m_session; } /** * Get the XML element that generated this event * @return The XML element that generated this event */ inline XmlElement* element() const { return m_element; } /** * Get the Jingle child of the XML element carried by the event * Don't delete it after use: it is owned by the event * @return The Jingle child of the XML element carried by the event */ inline XmlElement* jingle() const { return m_jingle; } /** * Get the jingle action as enumeration * @return The jingle action as enumeration */ inline JGSession::Action action() const { return m_action; } /** * Get the name of an action * @return The name of an action */ inline const char* actionName() const { return m_session ? JGSession::lookupAction(m_action,m_session->version()) : ""; } /** * Get the id * @return The id */ inline const String& id() const { return m_id; } /** * Get the reason * @return The reason */ inline const String& reason() const { return m_reason; } /** * Get the text * @return The text */ inline const String& text() const { return m_text; } /** * Get the XML element that generated this event and set it to 0 * @return The XML element that generated this event */ inline XmlElement* releaseXml() { XmlElement* tmp = m_element; m_jingle = m_element = 0; return tmp; } /** * Check if this event is a final one (Terminated or Destroy) * @return True if it is */ inline bool final() const { return m_type == Terminated || m_type == Destroy; } /** * Confirm the element carryied by this event. See JGSession::confirm() for details * @param error The error condition * @param text Optional text to add to the error element * @param type Error type * @return False if send failed or element is 0 */ inline bool confirmElement(XMPPError::Type error = XMPPError::NoError, const char* text = 0, XMPPError::ErrorType type = XMPPError::TypeModify) { if (m_session && element() && !m_confirmed) { m_confirmed = true; if (error == XMPPError::NoError) return m_session->confirmResult(element()); XmlElement* err = releaseXml(); return m_session->confirmError(err,error,text,type); } return false; } /** * Set the confirmed flag. Use it for action with delayed response */ inline void setConfirmed() { m_confirmed = true; } /** * Set the jingle action as enumeration. Set confirmation flag if * the element don't require it * @param act The jingle action as enumeration */ void setAction(JGSession::Action act); /** * Get the name of an event type * @return The name of an event type */ static inline const char* lookupType(int type) { return lookup(type,s_typeName); } /** * Dictionary with event type names */ static const TokenDict s_typeName[]; /** * The list of session contents if used */ ObjList m_contents; /** * The list of stream hosts if used */ ObjList m_streamHosts; protected: /** * Constructor. Set the id parameter if the element is valid * @param type Event type * @param session The session that generated this event * @param element Optional XML element that generated this event * @param reason Optional reason data * @param text Optional text data */ inline JGEvent(Type type, JGSession* session, XmlElement* element = 0, const char* reason = 0, const char* text = 0) : m_type(type), m_confirmed(true), m_session(0), m_element(element), m_jingle(0), m_action(JGSession::ActCount), m_reason(reason), m_text(text) { init(session); } /** * Constructor. Create a Jingle event. Set the id parameter if the element is valid * @param act The jingle action * @param session The session that generated this event * @param element XML element that generated this event * @param reason Optional reason data * @param text Optional text data */ inline JGEvent(JGSession::Action act, JGSession* session, XmlElement* element, const char* reason = 0, const char* text = 0) : m_type(Jingle), m_confirmed(false), m_session(0), m_element(element), m_jingle(0), m_action(act), m_reason(reason), m_text(text) { init(session); setAction(act); } private: JGEvent() {} // Don't use it void init(JGSession* session); Type m_type; // The type of this event bool m_confirmed; // Flag indicating that element was confirmed JGSession* m_session; // Jingle session that generated this event XmlElement* m_element; // XML element that generated this event XmlElement* m_jingle; // The session child, if present // Event specific JGSession::Action m_action; // The action if type is Jingle String m_id; // The element's id attribute String m_reason; // The reason if type is Error or Terminated String m_text; // Dtmf text }; /** * This class holds a Jingle service for the Jabber engine. Handle jingle stanzas, * stanza write fail events and stream termination events * @short A Jingle engine */ class YJABBER_API JGEngine : public DebugEnabler, public Mutex { friend class JGSession; public: /** * Constructor * @param name Debug name */ JGEngine(const char* name = "jgengine"); /** * Destructor. Terminates all active sessions */ virtual ~JGEngine(); /** * Retrieve the default session flags value * @return The default session flags value */ inline int sessionFlags() const { return m_sessionFlags; } /** * Get the timeout interval of a sent stanza * @return The timeout interval of a sent stanza */ inline u_int64_t stanzaTimeout() const { return m_stanzaTimeout; } /** * Get the timeout interval of a sent stream host stanza * @return The timeout interval of a sent stream host stanza */ inline u_int64_t streamHostTimeout() const { return m_streamHostTimeout; } /** * Get the ping interval used by jingle sessions * @return The interval to ping the remote party of a jingle session */ inline u_int64_t pingInterval() const { return m_pingInterval; } /** * Initialize this service * @param params Service's parameters */ virtual void initialize(const NamedList& params); /** * Send a session's stanza. * This method should be re-implemented * @param session The session requesting the operation * @param stanza The stanza to send. Will be consumed and zeroed * @return True on success */ virtual bool sendStanza(JGSession* session, XmlElement*& stanza); /** * Send a chat message on behalf of a session * @param session The session requesting the operation * @param body Message body * @return True on success */ virtual bool sendMessage(JGSession* session, const char* body); /** * Call getEvent() for each session list until an event is generated or the end is reached * This method is thread safe * @param time Current time in miliseconds * @return The first generated event */ JGEvent* getEvent(u_int64_t time); /** * Make an outgoing call. * 'media' and 'transport' will be invalid on exit. Don't delete them * @param ver The session version to use * @param caller The caller * @param called The called * @param contents The list of session content(s) * @param extra Optional extra child for session initiate element * @param msg Optional message to send before call * @param subject Optional session subject * @param line Optional session account * @param flags Optional session flags to set * @return Valid JGSession pointer (referenced) on success */ JGSession* call(JGSession::Version ver, const JabberID& caller, const JabberID& called, const ObjList& contents, XmlElement* extra = 0, const char* msg = 0, const char* subject = 0, const char* line = 0, int* flags = 0); /** * Ask this engine to accept an incoming xml 'iq' element * @param type Iq type as enumeration * @param from The sender * @param to The recipient * @param id Element id attribute * @param xml The received element * @param line Account receiving the stanza (may be empty) * @param error XMPPError result. This value should be check if false is returned. * Any value different from NoError indicate an invalid element * @param text Error text * @return True if accepted (don't use the given pointer if accepted) */ bool acceptIq(XMPPUtils::IqType type, const JabberID& from, const JabberID& to, const String& id, XmlElement* xml, const char* line, XMPPError::Type& error, String& text); /** * Default event processor. Delete event. * @param event The event to process */ void defProcessEvent(JGEvent* event); /** * Process events from the sessions. Default action: Delete event. * Descendants must override this method to process generated events * @param event The event to process */ virtual void processEvent(JGEvent* event); /** * Decode a comma separated list of flags * @param list The list of flags * @param dict Dictionary to use * @return Found flags */ static int decodeFlags(const String& list, const TokenDict* dict); /** * Encode (append) flags to a comma separated list * @param buf Destination buffer * @param flags Flags to encode * @param dict Dictionary to use */ static void encodeFlags(String& buf, int flags, const TokenDict* dict); private: // Create a local session id void createSessionId(String& id); ObjList m_sessions; // List of sessions u_int32_t m_sessionId; // Session id counter u_int64_t m_stanzaTimeout; // The timeout of a sent stanza u_int64_t m_streamHostTimeout; // The timeout of a sent stream host stanza u_int64_t m_pingInterval; // Interval to send ping (empty session-info) int m_sessionFlags; // Default session flags }; /** * This class holds sent stanzas info used for timeout checking * @short Send stanza timeout info */ class YJABBER_API JGSentStanza : public String { public: /** * Constructor * @param id The sent stanza's id * @param time The sent time * @param notif True to notify stanza timeout or response * @param ping True if the sent stanza is a ping one * @param action Optional sent stanza action */ JGSentStanza(const char* id, u_int64_t time, bool notif = false, bool ping = false, JGSession::Action action = JGSession::ActCount) : String(id), m_time(time), m_notify(notif), m_ping(ping), m_action(action) {} /** * Retrieve stanza timeout * @return Stanza timeout */ inline u_int64_t timeout() const { return m_time; } /** * Check if this element timed out * @return True if timeout */ inline bool timeout(u_int64_t time) const { return time > m_time; } /** * Check if timeout should be notified to sender * @return True to notify */ inline bool notify() const { return m_notify; } /** * Check if the stanza is a ping one * @return True if the stanza is a ping one */ inline bool ping() const { return m_ping; } /** * Get the jingle action as enumeration * @return The jingle action as enumeration */ inline JGSession::Action action() const { return m_action; } private: u_int64_t m_time; // Timeout bool m_notify; // Notify timeout to sender bool m_ping; // Sent stanza is a ping one JGSession::Action m_action; // Sent stanza action }; }; #endif /* __YATEJINGLE_H */ /* vi: set ts=8 sw=4 sts=4 noet: */