yate/libs/yjingle/yatejingle.h

992 lines
28 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 JGAudio;
class JGTransport;
class JGSession;
class JGEvent;
class JGEngine;
class JGSentStanza;
// Time to wait before destroing a session after hangup
#define JGSESSION_ENDTIMEOUT 2000
// Time to wait for a response
#define JGSESSION_STANZATIMEOUT 10000
/**
* This class holds a Jingle audio payload description.
* @short A Jingle audio payload.
*/
class YJINGLE_API JGAudio : public RefObject
{
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 bitrate The 'bitrate' attribute.
*/
inline JGAudio(const char* id, const char* name, const char* clockrate,
const char* bitrate = 0)
{ set(id,name,clockrate,bitrate); }
/**
* Copy constructor.
*/
inline JGAudio(const JGAudio& src)
{ set(src.m_id,src.m_name,src.m_clockrate,src.m_bitrate); }
/**
* Constructor.
* Fill this object from an XML element.
* @param element The element to fill from.
*/
inline JGAudio(XMLElement* element)
{ fromXML(element); }
/**
* Destructor.
*/
virtual ~JGAudio()
{}
/**
* Create a 'description' element.
* @return Valid XMLElement pointer.
*/
static XMLElement* createDescription();
/**
* Create a 'payload-type' element from this object.
* @return Valid XMLElement pointer.
*/
XMLElement* toXML();
/**
* Fill this object from a given element.
* @param element The element.
*/
void fromXML(XMLElement* element);
/**
* Create and add a 'payload-type' child to the given element.
* @param description The element.
*/
inline void addTo(XMLElement* description)
{ if (description) description->addChild(toXML()); }
/**
* Set the data.
* @param id The 'id' attribute.
* @param name The 'name' attribute.
* @param clockrate The 'clockrate' attribute.
* @param bitrate The 'bitrate' attribute.
*/
void set(const char* id, const char* name, const char* clockrate,
const char* bitrate = 0);
// Attributes
String m_id;
String m_name;
String m_clockrate;
String m_bitrate;
};
/**
* This class holds a Jingle transport method.
* @short A Jingle transport.
*/
class YJINGLE_API JGTransport : public RefObject
{
public:
/**
* Constructor.
*/
inline JGTransport()
{}
/**
* Copy constructor.
*/
JGTransport(const JGTransport& src);
/**
* Constructor.
* Fill this object from an XML element.
* @param element The element to fill from.
*/
inline JGTransport(XMLElement* element)
{ fromXML(element); }
/**
* Destructor.
*/
virtual ~JGTransport()
{}
/**
* Create a 'transport' element.
* @return Valid XMLElement pointer.
*/
static XMLElement* createTransport();
/**
* Create a 'candidate' element from this object.
* @return Valid XMLElement pointer.
*/
XMLElement* toXML();
/**
* Fill this object from a given element.
* @param element The element.
*/
void fromXML(XMLElement* element);
/**
* Create and add a 'candidate' child to the given element.
* @param transport The element.
*/
inline void addTo(XMLElement* transport)
{ if (transport) transport->addChild(toXML()); }
// Attributes
String m_name;
String m_address;
String m_port;
String m_preference;
String m_username;
String m_protocol;
String m_generation;
String m_password;
String m_type;
String m_network;
};
/**
* This class does the management of a Jingle session.
* @short A Jingle session.
*/
class YJINGLE_API JGSession : public RefObject, public Mutex
{
friend class JGEvent;
friend class JGEngine;
public:
/**
* Session state enumeration.
*/
enum State {
Idle, // Outgoing stream is waiting for
Pending, // Session is pending, session-initiate sent/received
Active, // Session is active, session-accept sent/received
Ending, // Session terminated: Wait for write result
Destroy, // The session will be destroyed
};
/**
* Jingle action enumeration.
*/
enum Action {
ActAccept, // accept
ActInitiate, // initiate
ActModify, // modify
ActRedirect, // redirect
ActReject, // reject
ActTerminate, // terminate
ActTransport, // Used to set/get transport info
ActTransportInfo, // transport-info
ActTransportCandidates, // candidates
ActTransportAccept, // transport-accept
ActContentInfo, // content-info
ActDtmf, // Used to set/get dtmf content-info element
ActDtmfMethod, // Used to set/get dtmf method content-info element
ActCount,
};
/**
* Jingle client type.
*/
enum TransportType {
TransportInfo, // transport-info
TransportCandidates, // candidates
};
/**
* Destructor.
* Send SessionTerminate if Pending or Active.
* Notify the owner of termination. Deref the owner.
*/
virtual ~JGSession();
/**
* Get the session direction.
* @return True if it is an incoming session.
*/
inline bool incoming() const
{ return m_incoming; }
/**
* Get the session id.
* @return The session id.
*/
const String& sid() const
{ return m_sid; }
/**
* Get the local peer's JID.
* @return The local peer's JID.
*/
const JabberID& local() const
{ return m_localJID; }
/**
* Get the remote peer's JID.
* @return The remote peer's JID.
*/
const JabberID& remote() const
{ return m_remoteJID; }
/**
* Get the initiator of this session.
* @return The initiator of this session.
*/
const String& initiator() const
{ return m_incoming ? m_remoteJID : m_localJID; }
/**
* Get the session state.
* @return The session state as enumeration.
*/
inline State state() const
{ return m_state; }
inline const JBComponentStream* stream() const
{ return m_stream; }
/**
* Get the arbitrary user data of this session.
* @return The arbitrary user data of this session.
*/
inline void* jingleConn()
{ return m_private; }
/**
* Set the arbitrary user data of this session.
* @param jingleconn The new arbitrary user data's value.
*/
inline void jingleConn(void* jingleconn)
{ m_private = jingleconn; }
/**
* Send a message to the remote peer.
* This method is thread safe.
* @param message The message to send.
* @return False on socket error.
*/
bool sendMessage(const char* message);
/**
* Send an 'iq' of type 'result' to the remote peer.
* This method is thread safe.
* @param id The element's id attribute.
* @return False if send failed.
*/
bool sendResult(const char* id);
/**
* Send a dtmf character to remote peer.
* This method is thread safe.
* @param dtmf The dtmf character.
* @param buttonUp True to send button-up action. False to send button-down.
* @return False if send failed.
*/
bool sendDtmf(char dtmf, bool buttonUp = true);
/**
* Send a dtmf method to remote peer.
* This method is thread safe.
* @param method The method to send.
* @return False if send failed.
*/
bool sendDtmfMethod(const char* method);
/**
* Deny a dtmf method request from remote peer.
* This method is thread safe.
* @param element The received 'iq' element with the method.
* @return False if send failed.
*/
bool denyDtmfMethod(XMLElement* element);
/**
* Create and sent an 'error' element.
* This method is thread safe.
* @param element The element to respond to.
* @param error The error.
* @param type Error type.
* @param text Optional text to add to the error element.
* @return False if send failed or element is 0.
*/
bool sendError(XMLElement* element, XMPPError::Type error,
XMPPError::ErrorType type = XMPPError::TypeModify,
const char* text = 0);
/**
* Send a 'transport-info' element to the remote peer.
* This method is thread safe.
* @param transport The transport data.
* @return False if send failed.
*/
inline bool requestTransport(JGTransport* transport)
{ return sendTransport(transport,ActTransport); }
/**
* Send a 'transport-accept' element to the remote peer.
* This method is thread safe.
* @param transport Optional transport data to send.
* @return False if send failed.
*/
inline bool acceptTransport(JGTransport* transport = 0)
{ return sendTransport(transport,ActTransportAccept); }
/**
* Accept a session.
* This method is thread safe.
* @param description The media description element to send.
* @return False if send failed.
*/
bool accept(XMLElement* description);
/**
* Send a session terminate element.
* Requirements: .
* This method is thread safe.
* @param reject True to reject.
* @param message Optional message to send before hangup.
* @return False if the requirements are not met.
*/
bool hangup(bool reject = false, const char* message = 0);
/**
* Check if this session is a destination for an event.
* Process it if it is.
* This method is thread safe.
* @param event The event event to process.
* @return False if the event doesn't belong to this session.
*/
bool receive(JBEvent* event);
/**
* Get a Jingle event from the queue.
* This method is thread safe.
* @param time Current time in miliseconds.
* @return JGEvent pointer or 0.
*/
JGEvent* getEvent(u_int64_t time);
/**
* Get the jingle action as enumeration from the given text.
* @param txt Text to check.
* @return The jingle action as enumeration from the given text.
*/
static inline Action action(const char* txt)
{ return (Action)lookup(txt,s_actions,ActCount); }
/**
* Get the text associated with an action.
* @param action The action to find.
* @return Pointer to the text or 0.
*/
static inline const char* actionText(Action action)
{ return lookup(action,s_actions); }
/**
* Check if the given parameter is a valid dtmf character.
* @param dtmf Character to check.
* @return True if the given parameter is a valid dtmf character.
*/
static inline bool isDtmf(char dtmf)
{ return -1 != s_dtmf.find(dtmf); }
/**
* Keeps the dtmf chars.
*/
static String s_dtmf;
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.
*/
JGSession(JGEngine* engine, JBComponentStream* stream,
const String& callerJID, const String& calledJID);
/**
* Constructor.
* Create an incoming session.
* @param engine The engine that owns this session.
* @param event A valid Jabber Jingle event with action session initiate.
*/
JGSession(JGEngine* engine, JBEvent* event);
/**
* Send a bad-request error. Delete event.
* @param event An already generated event.
* @return 0.
*/
JGEvent* badRequest(JGEvent* event);
/**
* Process a received event.
* @param jbev The Jabber Component event to process.
* @param time Current time.
* @return The Jingle event or 0.
*/
JGEvent* processEvent(JBEvent* jbev, u_int64_t time);
/**
* Process received elements in state Pending.
* @param jbev The Jabber Component event to process.
* @param event The jingle event to construct.
* @return The event parameter on success. 0 on failure.
*/
JGEvent* processStatePending(JBEvent* jbev, JGEvent* event);
/**
* Process received elements in state Active.
* @param jbev The Jabber Component event to process.
* @param event The jingle event to construct.
* @return The event parameter on success. 0 on failure.
*/
JGEvent* processStateActive(JBEvent* jbev, JGEvent* event);
/**
* Process received elements in state Idle.
* @param jbev The Jabber Component event to process.
* @param event The jingle event to construct.
* @return The event parameter on success. 0 on failure.
*/
JGEvent* processStateIdle(JBEvent* jbev, JGEvent* event);
/**
* Check if a given event is a valid Jingle one. Send an error if not.
* Set the event's data on success.
* @param event The event to check.
* @return True on success.
*/
bool decodeJingle(JGEvent* event);
/**
* Process a content-info jingle element.
* Set the event's data on success.
* @param event The event to check.
* @return True on success.
*/
bool processContentInfo(JGEvent* event);
/**
* Update media payloads from a Jingle event.
* @param event The event to process.
* @return True on success.
*/
bool updateMedia(JGEvent* event);
/**
* Update transport candidates from a Jingle event.
* @param event The event to process.
* @return True on success.
*/
bool updateTransport(JGEvent* event);
/**
* Set the message text of the the given event.
* @param event The event to process.
* @return The event parameter.
*/
JGEvent* decodeMessage(JGEvent* event);
/**
* Check if a given event is a valid Error one. Send an error if not.
* Set the event's data on success.
* @param event The event to check.
* @return True on success.
*/
bool decodeError(JGEvent* event);
/**
* Send a 'service-unavailable' error to the remote peer.
* @param element The element that generated the error.
* @return True on success.
*/
inline bool sendEServiceUnavailable(XMLElement* element)
{ return sendError(element,XMPPError::SServiceUnavailable); }
/**
* Send a 'bad-request' error to the remote peer.
* @param element The element that generated the error.
* @return True on success.
*/
inline bool sendEBadRequest(XMLElement* element)
{ return sendError(element,XMPPError::SBadRequest); }
/**
* Send a transport related element to the remote peer.
* @param transport Transport data to send.
* @param act The element's type (info, accept, etc).
* @return True on success.
*/
bool sendTransport(JGTransport* transport, Action act);
/**
* Initiate an outgoing call.
* @param media Media description element.
* @param transport Transport description element.
* @return True on success.
*/
bool initiate(XMLElement* media, XMLElement* transport);
/**
* Send an XML element to remote peer.
* @param e The element to send.
* @param addId True to add 'id' attribute.
* @return True on success.
*/
bool sendXML(XMLElement* e, bool addId = true);
/**
* Create an event.
* @param jbev Optional Jabber event that generated the event.
* @return Valid JGEvent pointer.
*/
JGEvent* createEvent(JBEvent* jbev);
/**
* Set last event. Change state. Deref this session if event type is Destroy.
* @param event Event to raise.
* @return Valid JGEvent pointer.
*/
JGEvent* raiseEvent(JGEvent* event);
/**
* Create an 'iq' of type 'set' or 'get' with a 'jingle' child.
* @param action The action of the Jingle stanza.
* @param element1 Optional child element.
* @param element2 Optional child element.
* @return Valid XMLElement pointer.
*/
XMLElement* createJingleSet(Action action,
XMLElement* element1 = 0, XMLElement* element2 = 0);
/**
* Confirm the given element if has to.
* @param element The element to confirm.
*/
void confirmIq(XMLElement* element);
/**
* Selectively confirm an 'iq' (skip confirmation for transport-info).
* @param event The event containing the element to confirm.
*/
void confirmIqSelect(JGEvent* event);
/**
* Terminate notification from an event. Reset the last generated event.
* @param event The notifier.
*/
void eventTerminated(JGEvent* event);
/**
* Check if a received event contains a confirmation of a sent stanza.
* @param jbev The received Jabber Component event.
* @return The confirmed element or 0.
*/
JGSentStanza* isResponse(const JBEvent* jbev);
/**
* Check if any element timed out.
* @return True if timeout.
*/
bool timeout(u_int64_t time);
/**
* Keeps the associations between jingle actions and their text.
*/
static TokenDict s_actions[];
private:
JGSession() {} // Don't use it
void appendSent(XMLElement* element);
// Accept the appropriate events. Called in receive()
// @param retValue The value receive() will return if the message was not accepted
// @return True if the message is accepted. False if not
bool receiveMessage(const JBEvent* event, bool& retValue);
bool receiveResult(const JBEvent* event, bool& retValue);
bool receiveJingle(const JBEvent* event, bool& retValue);
bool receiveDestroy(const JBEvent* event, bool& retValue);
// State info
State m_state; // Session state
TransportType m_transportType; // Remote client type
// Links
JGEngine* m_engine; // The engine that owns this session
JBComponentStream* m_stream; // The stream this session is bound to
// Session info
bool m_incoming; // 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
JGEvent* m_terminateEvent; // Terminate event
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
// Timeout
u_int64_t m_timeout; // Timeout period for Ending state or for sent stanzas
ObjList m_sentStanza; // Sent stanzas' id
};
/**
* This class holds an event generated by a Jingle session.
* @short A Jingle event
*/
class YJINGLE_API JGEvent
{
friend class JGSession;
public:
enum Type {
Jingle, // Actions:
// ActAccept
// ActInitiate
// ActModify
// ActRedirect
// ActReject Never: See Terminated event
// ActTerminate Never: See Terminated event
// ActTransport Transport candidade(s)
// ActTransportInfo Never
// ActTransportCandidates Never
// ActTransportAccept
// ActContentInfo Never
// ActDtmf m_reason is button-up/button-down. m_text is the dtmf
// ActDtmfMethod m_text is the dtmf method: rtp/xmpp
Message, // m_text is the message body
Error,
Unexpected, // Unexpected or invalid element
// 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 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 action as enumeration.
* @return The jingle action as enumeration.
*/
inline JGSession::Action action() const
{ return m_action; }
/**
* Get the audio payloads list.
* @return The audio payloads list.
*/
inline ObjList& audio()
{ return m_audio; }
/**
* Get the transports list.
* @return The transports list.
*/
inline ObjList& transport()
{ return m_transport; }
/**
* Get the id.
* @return The id.
*/
inline const String& id()
{ return m_id; }
/**
* Get the reason.
* @return The reason.
*/
inline const String& reason()
{ return m_reason; }
/**
* Get the text.
* @return The text.
*/
inline const String& text()
{ 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_element = 0;
return tmp;
}
/**
* Check if this event is a final one (Terminated or Destroy).
* @return True if it is.
*/
bool final();
protected:
/**
* Constructor.
* @param type Event type.
* @param session The session that generated this event.
* @param element Optional XML element that generated this event.
*/
JGEvent(Type type, JGSession* session, XMLElement* element = 0);
private:
JGEvent() {} // Don't use it
Type m_type; // The type of this event
JGSession* m_session; // Jingle session that generated this event
XMLElement* m_element; // XML element that generated this event
// Event specific
JGSession::Action m_action; // The action if type is Jingle
ObjList m_audio; // The received audio payloads
ObjList m_transport; // The received transport data
String m_id; // The element's id attribute
String m_reason; // The reason if type is Error or Terminated
String m_text; // The text if type is Error or any other text
};
/**
* This class holds the Jingle engine.
* @short The Jingle engine.
*/
class YJINGLE_API JGEngine : public JBClient, public DebugEnabler, public Mutex
{
friend class JGSession;
public:
/**
* Constructor.
* Constructs a Jingle engine.
* @param jb The JBEngine.
* @param params Engine's parameters.
*/
JGEngine(JBEngine* jb, const NamedList& params);
/**
* Destructor.
* Terminates all active sessions. Delete the XMPP engine.
*/
virtual ~JGEngine();
/**
* Initialize this engine.
* Parameters: None
* @param params Engine's parameters.
*/
void initialize(const NamedList& params);
/**
* Get events from the Jabber engine.
* This method is thread safe.
* @return True if data was received.
*/
bool receive();
/**
* Keep calling receive().
*/
void runReceive();
/**
* Keep calling getEvent() for each session list until no more event is generated.
* Call processEvent if needded.
* @return True if at least one event was generated.
*/
bool process();
/**
* Keep calling process().
*/
void runProcess();
/**
* 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 callerName The local peer's username.
* @param remoteJID The remote peer's JID.
* @param media A valid 'description' XML element.
* @param transport A valid 'transport' XML element.
* @param message Optional message to send before call.
* @return Valid JGSession pointer (referenced) on success.
*/
JGSession* call(const String& callerName, const String& remoteJID,
XMLElement* media, XMLElement* transport, const char* message = 0);
/**
* Default event processor.
* Action: Delete event.
* @param event The event to process.
*/
void defProcessEvent(JGEvent* event);
/**
* Create a session id for an outgoing one.
* @param id Destination string.
*/
void createSessionId(String& id);
protected:
/**
* Process events from the sessions.
* Default action: Delete event.
* Descendants must override this method.
* @param event The event to process.
*/
virtual void processEvent(JGEvent* event);
/**
* Remove a session from the list.
* @param session Session to remove.
*/
void removeSession(JGSession* session);
private:
ObjList m_sessions; // List of sessions
Mutex m_sessionIdMutex; // Session id counter lock
u_int32_t m_sessionId; // Session id counter
};
/**
* This class holds sent stanzas info used for timeout checking.
* @short Timeout info.
*/
class YJINGLE_API JGSentStanza : public RefObject
{
friend class JGSession;
public:
/**
* Constructor.
* @param id The sent stanza's id.
* @param time The sent time.
*/
JGSentStanza(const char* id, u_int64_t time = Time::msecNow())
: m_id(id), m_time(time + JGSESSION_STANZATIMEOUT)
{}
/**
* Destructor.
*/
virtual ~JGSentStanza()
{}
/**
* Check if a received element is an iq result or error with the given id or
* a sent element failed to be written to socket.
* @param jbev The received Jabber Component event.
* @return False if the given element is not a response one or is 0.
*/
bool isResponse(const JBEvent* jbev) {
if (jbev &&
(jbev->type() == JBEvent::IqResult ||
jbev->type() == JBEvent::IqError ||
jbev->type() == JBEvent::WriteFail) &&
m_id == jbev->id())
return true;
return false;
}
/**
* Check if this element timed out.
* @return True if timeout.
*/
inline bool timeout(u_int64_t time) const
{ return time > m_time; }
private:
String m_id; // Sent stanza's id
u_int64_t m_time; // Timeout
};
}
#endif /* __YATEJINGLE_H */
/* vi: set ts=8 sw=4 sts=4 noet: */