yate/libs/yjingle/yatejingle.h

1854 lines
56 KiB
C++

/**
* 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-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 __YATEJINGLE_H
#define __YATEJINGLE_H
#include <yateclass.h>
#include <yatejabber.h>
/**
* 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 YJINGLE_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 channels The 'channels' attribute
* @param synonym The 'synonym' attribute
*/
inline JGRtpMedia(const char* id, const char* name, const char* clockrate,
const char* channels, const char* synonym)
: m_params("")
{ set(id,name,clockrate,channels,synonym); }
/**
* 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(src.m_params)
{ set(src.m_id,src.m_name,src.m_clockrate,src.m_channels,src.m_synonym); }
/**
* Set the data
* @param id The 'id' attribute
* @param name The 'name' attribute
* @param clockrate The 'clockrate' attribute
* @param channels The 'channels' attribute
* @param synonym The 'synonym' attribute
*/
inline void set(const char* id, const char* name, const char* clockrate,
const char* channels, const char* synonym) {
m_id = id;
m_name = name;
m_clockrate = clockrate;
m_channels = channels;
m_synonym = synonym;
}
/**
* 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;
/**
* The number of channels
*/
String m_channels;
/**
* A synonym of this payload's name
*/
String m_synonym;
/**
* List of optional parameters
*/
NamedList m_params;
};
/**
* This class holds a content description's crypto data.
* The tag is kepti in the String component
* @short Content crypto data
*/
class YJINGLE_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);
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 YJINGLE_API JGRtpMediaList : public ObjList
{
public:
/**
* Media type enumeration
*/
enum Media {
MediaMissing = -1,
MediaUnknown = 0,
Audio = 1,
};
/**
* Constructor
* @param m Media type as enumeration
* @param cryptoMandatory True to require media encryption
*/
inline JGRtpMediaList(Media m = MediaMissing, bool cryptoMandatory = false)
: m_media(m), m_cryptoMandatory(cryptoMandatory), m_ready(false)
{}
/**
* 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 payload's id
* @param name The payload's name
* @param clockrate The payload's clockrate
* @param bitrate The payload's bitrate
* @param synonym The payload's synonym
*/
inline void add(const char* id, const char* name, const char* clockrate,
const char* bitrate, const char* synonym)
{ append(new JGRtpMedia(id,name,clockrate,bitrate,synonym)); }
/**
* 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
* @param telEvent True to append a telephone event data payload
* @return Valid XMLElement pointer
*/
XMLElement* toXML(bool telEvent = true) 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 = ",");
/**
* The list of media type names
*/
static TokenDict s_media[];
/**
* The media type
*/
Media m_media;
/**
* Crypto (SRTP) params
*/
bool m_cryptoMandatory;
ObjList m_cryptoLocal;
ObjList m_cryptoRemote;
/**
* Flag indicating wether media was negotiated
*/
bool m_ready;
};
/**
* This class holds a RTP transport candidate
* @short A RTP transport candidate
*/
class 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
*/
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 list of jingle RTP transport candidates
* @short A list of RTP transport candidates
*/
class YJINGLE_API JGRtpCandidates : public ObjList
{
public:
/**
* Enumeration of transport types
*/
enum Type {
Unknown = -1,
RtpIceUdp = 1,
RtpRawUdp = 2,
};
/**
* 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 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 YJINGLE_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 = 2, // Audio: RTP RAW-UDP transport
FileBSBOffer = 3, // File offer: byte stream (SOCKS) transport
FileBSBRequest = 4, // 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 TokenDict s_senders[];
/**
* The list containing the text values for Creator enumeration
*/
static 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 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(const char* jid, const char* addr, int port, const char* zeroConf = 0)
: String(jid),
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_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);
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 YJINGLE_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 termination reasons
*/
enum Reason {
ReasonBusy, // <busy/>
ReasonDecline, // <decline/>
ReasonConn, // <connectivity-error/>
ReasonMedia, // <media-error/>
ReasonTransport, // <unsupported-transports/>
ReasonNoError, // <no-error/>
ReasonOk, // <success/>
ReasonNoApp, // <unsupported-applications/>
ReasonAltSess, // <alternative-session/>
ReasonUnknown, // <general-error/>
ReasonTransfer, // <transferred>
ReasonNone // None of the above
};
/**
* 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
ActInfo, // session-info
ActTransportInfo, // transport-info
ActTransportAccept, // transport-accept
ActTransportReject, // transport-reject
ActTransportReplace, // transport-replace
ActContentAccept, // content-accept
ActContentAdd, // content-add
ActContentModify, // content-modify
ActContentReject, // content-reject
ActContentRemove, // content-remove
ActContentInfo, // content-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,
};
/**
* Destructor
*/
virtual ~JGSession();
/**
* Get the session version
* @return The session version
*/
inline Version version() const
{ return m_version; }
/**
* 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_localJID; }
/**
* Get the remote peer's JID
* @return The remote peer's JID
*/
inline const JabberID& remote() const
{ return m_remoteJID; }
/**
* Get the session state.
* @return The session state as enumeration.
*/
inline State state() const
{ return m_state; }
/**
* Get the stream this session is bound to
* @return The stream this session is bound to
*/
inline const JBStream* stream() const
{ return m_stream; }
/**
* 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; }
/**
* Check if a given XML element is valid jingle one
* @param xml Element to check
* @return The given element if it's a valid jingle element, 0 otherwise
*/
virtual XMLElement* checkJingle(XMLElement* xml)
{ return 0; }
/**
* 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
*/
inline Action getAction(XMLElement* xml)
{ return xml ? lookupAction(xml->getAttribute("type"),m_version) : ActCount; }
/**
* Ask this session to accept an event
* @param event The event to accept
* @param sid The session id if this is a request
* @return True if accepted (the event was enqueued), false if not
*/
bool acceptEvent(JBEvent* event, const String& sid = String::empty());
/**
* Confirm a received element. If the error is NoError a result stanza will be sent.
* Otherwise, an error stanza will be created and sent and the received element is
* consumed (attached to the sent error stanza)
* @param xml The element to confirm
* @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 confirm(XMLElement* xml, XMPPError::Type error = XMPPError::NoError,
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 Termination reason
* @param msg Optional termination message
* @return False if send failed
*/
virtual bool hangup(int reason, const char* msg = 0);
/**
* Create a 'hold' child to be added to a session-info element
* @return Valid XMLElement pointer or 0
*/
virtual XMLElement* createHoldXml()
{ return 0; }
/**
* Create an 'active' child to be added to a session-info element
* @return Valid XMLElement pointer or 0
*/
virtual XMLElement* createActiveXml()
{ 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)
* @return False on failure
*/
bool sendInfo(XMLElement* xml, String* stanzaId = 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);
/**
* Send a message to the remote peer.
* This method is thread safe
* @param msg The message to send
* @return False on socket error
*/
inline bool sendMessage(const char* msg) {
return sendStanza(JBMessage::createMessage(JBMessage::Chat,
m_localJID,m_remoteJID,0,msg),0,false);
}
/**
* 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 TokenDict s_versions[];
/**
* Termination reasons
*/
static TokenDict s_reasons[];
/**
* Session state names
*/
static TokenDict s_states[];
/**
* Action names for version Version0
*/
static TokenDict s_actions0[];
/**
* Action names for version Version1
*/
static TokenDict s_actions1[];
protected:
/**
* Constructor. Create an outgoing session
* @param ver The session version
* @param engine The engine that owns this session
* @param stream The stream this session is bound to
* @param callerJID The caller's full JID
* @param calledJID The called party's full JID
* @param msg Optional message to be sent before session initiate
*/
JGSession(Version ver, JGEngine* engine, JBStream* stream,
const String& callerJID, const String& calledJID,
const char* msg = 0);
/**
* Constructor. Create an incoming session.
* @param ver The session version
* @param engine The engine that owns this session
* @param event A valid Jabber Jingle event with action session initiate
* @param id Session id
*/
JGSession(Version ver, JGEngine* engine, JBEvent* event, 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();
/**
* Enqueue a Jabber engine event.
* This method is thread safe
* @param event The event event to process
*/
void enqueue(JBEvent* event);
/**
* 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
* @return True on success
*/
bool sendStanza(XMLElement* stanza, String* stanzaId = 0, bool confirmation = true,
bool ping = false);
/**
* 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 valid jingle set event. Set the event's data on success
* @param jbev The event to decode
* @return JGEvent pointer or 0
*/
virtual JGEvent* decodeJingle(JBEvent* jbev) = 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 set from a
* jingle set jabber event
* @param ev The event to process
*/
virtual void processJingleSetLastEvent(JBEvent& ev);
/**
* Method called in getEvent() to process a jabber event carrying a response
* @param ev The event to process
* @return False to stop further processing
*/
virtual bool processJabberIqResponse(JBEvent& ev);
/**
* Method called in getEvent() to process a generic jabber iq event
* @param ev The event to process
*/
virtual void processJabberIqEvent(JBEvent& ev);
/**
* 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 info
State m_state; // Session state
u_int64_t m_timeToPing; // Time to send ping (empty session-info)
// Links
JGEngine* m_engine; // The engine that owns this session
JBStream* m_stream; // The stream this session is bound to
// Session info
bool m_outgoing; // Session direction
String m_sid; // Session id
JabberID m_localJID; // Local peer's JID
JabberID m_remoteJID; // Remote peer's JID
// Session data
ObjList m_events; // Incoming events from Jabber engine
JGEvent* m_lastEvent; // Last generated event
bool m_recvTerminate; // Flag indicating whether session-terminate was received
void* m_private; // Arbitrary user data
// Sent stanzas id generation
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
private:
JGSession() {} // Don't use it
};
/**
* A session implementing the old jingle protocol
* @short The version 0 of a jingle session
*/
class YJINGLE_API JGSession0 : public JGSession
{
friend class JGEvent;
friend class JGEngine;
public:
/**
* Destructor
*/
virtual ~JGSession0();
/**
* Check if a given XML element is valid jingle one
* @param xml Element to check
* @return The given element if it's a valid jingle element, 0 otherwise
*/
virtual XMLElement* checkJingle(XMLElement* xml);
/**
* 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 that owns this session
* @param stream The stream this session is bound to
* @param callerJID The caller's full JID
* @param calledJID The called party's full JID
* @param msg Optional message to be sent before session initiate
*/
JGSession0(JGEngine* engine, JBStream* stream,
const String& callerJID, const String& calledJID, const char* msg = 0);
/**
* Constructor. Create an incoming session.
* @param engine The engine that owns this session
* @param event A valid Jabber Jingle event with action session initiate
* @param id Session id
*/
JGSession0(JGEngine* engine, JBEvent* event, 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 valid jingle set event. Set the event's data on success
* @param jbev The event to decode
* @return JGEvent pointer or 0
*/
virtual JGEvent* decodeJingle(JBEvent* jbev);
/**
* 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
};
/**
* A session implementing the Jingle protocol including session transfer and file transfer
* @short The version 1 of a jingle session
*/
class YJINGLE_API JGSession1 : public JGSession
{
friend class JGEvent;
friend class JGEngine;
public:
/**
* Destructor
*/
virtual ~JGSession1();
/**
* Check if a given XML element is valid jingle one
* @param xml Element to check
* @return The given element if it's a valid jingle element, 0 otherwise
*/
virtual XMLElement* checkJingle(XMLElement* xml);
/**
* 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 'hold' child to be added to a session-info element
* @return Valid XMLElement pointer or 0
*/
virtual XMLElement* createHoldXml();
/**
* Create an 'active' child to be added to a session-info element
* @return Valid XMLElement pointer or 0
*/
virtual XMLElement* createActiveXml();
/**
* 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 that owns this session
* @param stream The stream this session is bound to
* @param callerJID The caller's full JID
* @param calledJID The called party's full JID
* @param msg Optional message to be sent before session initiate
*/
JGSession1(JGEngine* engine, JBStream* stream,
const String& callerJID, const String& calledJID, const char* msg = 0);
/**
* Constructor. Create an incoming session.
* @param engine The engine that owns this session
* @param event A valid Jabber Jingle event with action session initiate
* @param id Session id
*/
JGSession1(JGEngine* engine, JBEvent* event, 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 valid jingle set event. Set the event's data on success
* @param jbev The event to decode
* @return JGEvent pointer or 0
*/
virtual JGEvent* decodeJingle(JBEvent* jbev);
/**
* 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);
/**
* @param ev The event to process
*/
virtual void processJabberIqEvent(JBEvent& ev);
};
/**
* This class holds an event generated by a Jingle session
* @short A Jingle event
*/
class YJINGLE_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)
ResultWriteFail, // Response for a sent stanza (failed to send stanza)
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() {
TelEngine::destruct(m_jingle);
XMLElement* tmp = m_element;
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->confirm(element());
return m_session->confirm(releaseXML(),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 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 YJINGLE_API JGEngine : public JBService, public JBThreadList
{
friend class JGSession;
public:
/**
* Constructor. Constructs a Jingle service
* @param engine The Jabber engine
* @param params Service's parameters
* @param prio The priority of this service
*/
JGEngine(JBEngine* engine, const NamedList* params, int prio = 0);
/**
* Destructor. Terminates all active sessions
*/
virtual ~JGEngine();
/**
* 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 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);
/**
* 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 callerName The local peer's username
* @param remoteJID The remote peer's JID
* @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
* @return Valid JGSession pointer (referenced) on success
*/
JGSession* call(JGSession::Version ver, const String& callerName, const String& remoteJID,
const ObjList& contents, XMLElement* extra = 0, const char* msg = 0,
const char* subject = 0);
/**
* 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);
protected:
/**
* Accept an event from the Jabber engine
* @param event The event to accept
* @param processed Set to true on exit to signal that the event was already processed
* @param insert Set to true if accepted to insert on top of the event queue
* @return False if not accepted, let the engine try another service
*/
virtual bool accept(JBEvent* event, bool& processed, bool& insert);
private:
// Create a local session id
void createSessionId(String& id);
ObjList m_sessions; // List of sessions
Mutex m_sessionIdMutex; // Session id counter lock
u_int32_t m_sessionId; // Session id counter
u_int64_t m_stanzaTimeout; // The timeout of a sent stanza
u_int64_t m_pingInterval; // Interval to send ping (empty session-info)
};
/**
* This class holds sent stanzas info used for timeout checking
* @short Send stanza timeout info
*/
class YJINGLE_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
*/
JGSentStanza(const char* id, u_int64_t time, bool notif = false, bool ping = false)
: String(id), m_time(time), m_notify(notif), m_ping(ping)
{}
/**
* 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; }
private:
u_int64_t m_time; // Timeout
bool m_notify; // Notify timeout to sender
bool m_ping; // Sent stanza is a ping one
};
};
#endif /* __YATEJINGLE_H */
/* vi: set ts=8 sw=4 sts=4 noet: */