yate/libs/yjingle/yatejabber.h

2664 lines
84 KiB
C++

/**
* yatejabber.h
* Yet Another Jabber Component Protocol Stack
* This file is part of the YATE Project http://YATE.null.ro
*
* Yet Another Telephony Engine - a fully featured software PBX and IVR
* Copyright (C) 2004-2006 Null Team
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __YATEJABBER_H
#define __YATEJABBER_H
#include <xmpputils.h>
#include <xmlparser.h>
/**
* Holds all Telephony Engine related classes.
*/
namespace TelEngine {
class JBEvent; // A Jabber event
class JBStream; // A Jabber stream
class JBComponentStream; // A Jabber Component stream
class JBClientStream; // A Jabber client to server stream
class JBThread; // Base class for private threads
class JBThreadList; // A list of threads
class JBEngine; // A Jabber engine
class JBService; // A Jabber service
class JBPresence; // A Jabber presence service
class JIDResource; // A JID resource
class JIDResourceList; // Resource list
class XMPPUser; // An XMPP user (JID, resources, capabilities etc)
class XMPPUserRoster; // An XMPP user's roster
/**
* This class holds a Jabber stream event. Stream events are raised by streams
* and sent by the engine to the proper service
* @short A Jabber stream event
*/
class YJINGLE_API JBEvent : public RefObject
{
friend class JBStream;
friend class JBClientStream;
public:
/**
* Event type enumeration
*/
enum Type {
// Stream events
Terminated = 1, // Stream terminated. Try to connect
Destroy = 2, // Stream is destroying
Running = 3, // Stream is running (stable state: can send/recv stanzas)
// Result events
WriteFail = 10, // Write failed. m_element is the element, m_id is the id set by the sender
// Stanza events: m_element is always valid
Presence = 20, // m_element is a 'presence' stanza
Message = 30, // m_element is a 'message' stanza
Iq = 50, // m_element is an 'iq' set/get, m_child is it's first child
IqError = 51, // m_element is an 'iq' error, m_child is the 'iq' child if any
IqResult = 52, // m_element is an 'iq' result, m_child is it's first child if any
// Disco: m_child is a 'query' element qualified by DiscoInfo/DiscoItems namespaces
IqDiscoInfoGet = 60,
IqDiscoInfoSet = 61,
IqDiscoInfoRes = 62,
IqDiscoInfoErr = 63,
IqDiscoItemsGet = 64,
IqDiscoItemsSet = 65,
IqDiscoItemsRes = 66,
IqDiscoItemsErr = 67,
// Command: m_child is a 'command' element qualified by Command namespace
IqCommandGet = 70,
IqCommandSet = 71,
IqCommandRes = 72,
IqCommandErr = 73,
// Jingle: m_child is a 'jingle' element qualified by Jingle namespace
IqJingleGet = 80,
IqJingleSet = 81,
IqJingleRes = 82,
IqJingleErr = 83,
// Roster: m_child is a 'query' element qualified by Roster namespace
IqRosterSet = 91,
IqRosterRes = 92,
IqRosterErr = 93,
// Roster update (set or result) received by client streams: m_child is a 'query' element
// qualified by Roster namespace
IqClientRosterUpdate = 150,
// Invalid
Unhandled = 200, // m_element is an unhandled element
Invalid = 500, // m_element is 0
};
/**
* Constructor. Constructs an event from a stream
* @param type Type of this event
* @param stream The stream that generated the event
* @param element Element that generated the event
* @param child Optional type depending element's child
*/
JBEvent(Type type, JBStream* stream, XMLElement* element, XMLElement* child = 0);
/**
* Constructor. Constructs a WriteSuccess/WriteFail event from a stream
* @param type Type of this event
* @param stream The stream that generated the event
* @param element Element that generated the event
* @param senderID Sender's id
*/
JBEvent(Type type, JBStream* stream, XMLElement* element, const String& senderID);
/**
* Destructor. Delete the XML element if valid
*/
virtual ~JBEvent();
/**
* Get the event type
* @return The type of this event as enumeration
*/
inline int type() const
{ return m_type; }
/**
* Get the event name
* @return The name of this event
*/
inline const char* name() const
{ return lookup(type()); }
/**
* Get the element's 'type' attribute if any
* @return The element's 'type' attribute
*/
inline const String& stanzaType() const
{ return m_stanzaType; }
/**
* Get the 'from' attribute of a received stanza
* @return The 'from' attribute
*/
inline const JabberID& from() const
{ return m_from; }
/**
* Get the 'to' attribute of a received stanza
* @return The 'to' attribute
*/
inline const JabberID& to() const
{ return m_to; }
/**
* Get the sender's id for Write... events or the 'id' attribute if the
* event carries a received stanza
* @return The event id
*/
inline const String& id() const
{ return m_id; }
/**
* The stanza's text or termination reason for Terminated/Destroy events
* @return The event's text
*/
inline const String& text() const
{ return m_text; }
/**
* Get the stream that generated this event
* @return The stream that generated this event
*/
inline JBStream* stream() const
{ return m_stream; }
/**
* Get the underlying XMLElement
* @return XMLElement pointer or 0
*/
inline XMLElement* element() const
{ return m_element; }
/**
* Get the first child of the underlying element if any
* @return XMLElement pointer or 0
*/
inline XMLElement* child() const
{ return m_child; }
/**
* Delete the underlying XMLElement(s). Release the ownership.
* The caller is responsable of the returned pointer
* @param del True to delete all xml elements owned by this event
* @return XMLElement pointer if not deleted or 0
*/
inline XMLElement* releaseXML(bool del = false) {
TelEngine::destruct(m_child);
if (del) {
TelEngine::destruct(m_element);
return 0;
}
XMLElement* tmp = m_element;
m_element = 0;
return tmp;
}
/**
* Release the link with the stream to let the stream continue with events
*/
void releaseStream();
/**
* Create an error response from this event if it contains a known type.
* Don't create the error response if this event is carrying a response
* @param type Error type
* @param error The error condition
* @param text Optional text to add to the error element
* @return A valid XMLElement pointer
*/
XMLElement* createError(XMPPError::ErrorType type, XMPPError::Type error, const char* text = 0);
/**
* Get the name of an event type
* @return The name an event type
*/
inline static const char* lookup(int type)
{ return TelEngine::lookup(type,s_type); }
private:
static TokenDict s_type[]; // Event names
JBEvent() {} // Don't use it!
bool init(JBStream* stream, XMLElement* element);
Type m_type; // Type of this event
JBStream* m_stream; // The stream that generated this event
bool m_link; // Stream link state
XMLElement* m_element; // Received XML element, if any
XMLElement* m_child; // The first child element for 'iq' elements
String m_stanzaType; // Stanza's 'type' attribute
JabberID m_from; // Stanza's 'from' attribute
JabberID m_to; // Stanza's 'to' attribute
String m_id; // Sender's id for Write... events
// 'id' attribute if the received stanza has one
String m_text; // The stanza's text or termination reason for
// Terminated/Destroy events
};
/**
* A socket used used to transport data for a Jabber stream
* @short A Jabber streams's socket
*/
class YJINGLE_API JBSocket
{
friend class JBStream;
public:
/**
* Constructor. Build socket for an outgoing stream
* @param engine The Jabber engine
* @param stream The stream owning this socket
* @param address The address used to connect to
* @param port Port used to connect to remote server
*/
JBSocket(JBEngine* engine, JBStream* stream,
const char* address, int port);
/**
* Destructor. Close the socket
*/
inline ~JBSocket()
{ terminate(); }
/**
* Check if the socket is valid
* @return True if the socket is valid.
*/
inline bool valid() const
{ return m_socket && m_socket->valid(); }
/**
* Get the remote peer's address
* @return The remote peer's address
*/
inline const SocketAddr& addr() const
{ return m_address; }
/**
* Get last connect/send/receive error text
* @return Last error text
*/
inline const String& error() const
{ return m_error; }
/**
* Connect the socket
* @param terminated True if false is returned and the socket was terminated
* while connecting
* @param newAddr Optional address to connect to
* @param newPort Optional port to connect to
* @return False on failure
*/
bool connect(bool& terminated, const char* newAddr, int newPort = 0);
/**
* Terminate the socket
* @param shutdown True to shut down, false to asynchronously terminate the socket
*/
void terminate(bool shutdown = false);
/**
* Read data from socket
* @param buffer Destination buffer
* @param len The number of bytes to read. On exit contains the number of
* bytes actually read
* @return False on socket error
*/
bool recv(char* buffer, unsigned int& len);
/**
* Write data to socket
* @param buffer Source buffer
* @param len The number of bytes to send
* @return False on socket error
*/
bool send(const char* buffer, unsigned int& len);
private:
JBEngine* m_engine; // The Jabber engine
JBStream* m_stream; // Stream owning this socket
Socket* m_socket; // The socket
String m_remoteDomain; // Remote domain used to resolve before connecting
SocketAddr m_address; // Remote address
Mutex m_streamMutex; // Lock stream
Mutex m_receiveMutex; // Lock receive
String m_error; // Keep error string from send/receive/connect
};
/**
* Base class for all Jabber streams. Basic stream data processing: send/receive
* XML elements, keep stream state, generate events
* @short A Jabber stream
*/
class YJINGLE_API JBStream : public RefObject
{
friend class JBEngine;
friend class JBEvent;
public:
/**
* Stream state enumeration.
*/
enum State {
Idle = 0, // Stream is waiting to be connected or destroyed
Connecting = 1, // Stream is waiting for the socket to connect
Started = 2, // Stream start tag sent
Securing = 3, // Stream is currently negotiating the TLS
Register = 4, // A new user is currently registering
Auth = 5, // Stream is currently authenticating
Running = 6, // Established. Allow XML stanzas to pass over the stream
Destroy = 7, // Stream is destroying. No more traffic allowed
};
/**
* Values returned by send() methods.
*/
enum Error {
ErrorNone = 0, // No error (stanza enqueued/sent)
ErrorContext, // Invalid stream context (state) or parameters
ErrorPending, // The operation is pending in the stream's queue
ErrorNoSocket, // Unrecoverable socket error. The stream will be terminated
};
/**
* Stream behaviour options
*/
enum Flags {
AutoRestart = 0x0001, // Auto restart stream when down
AllowPlainAuth = 0x0002, // Allow plain password authentication
// If not allowed and this is the only method
// offered by server the stream will be terminated
NoVersion1 = 0x0004, // Don't support RFC 3920 TLS/SASL ...
UseTls = 0x0008, // Use TLS if offered. Internally set if the remote server
// always require encryption
UseSasl = 0x0010, // Use SASL as authentication mechanism (RFC 3920)
// If not set, the deprecated XEP-0078 will be used for authentication
AllowUnsafeSetup = 0x0020, // Allow in-band user account setup on unsecured streams
StreamSecured = 0x0100, // Stream already secured
StreamAuthenticated = 0x0200, // Stream already authenticated
NoRemoteVersion1 = 0x0400, // Remote doesn't support RFC 3920 TLS/SASL ...
};
/**
* Destructor.
* Gracefully close the stream and the socket
*/
virtual ~JBStream();
/**
* Get the type of this stream. See the protocol enumeration of the engine
* @return The type of this stream
*/
inline int type() const
{ return m_type; }
/**
* Get the stream state
* @return The stream state as enumeration.
*/
inline State state() const
{ return m_state; }
/**
* Get the stream direction
* @return True if the stream is an outgoing one
*/
inline bool outgoing() const
{ return m_outgoing; }
/**
* Get the stream's name
* @return The stream's name
*/
inline const String& name() const
{ return m_name; }
/**
* Get the stream id
* @return The stream id
*/
inline const String& id() const
{ return m_id; }
/**
* Get the stream's owner
* @return Pointer to the engine owning this stream
*/
inline JBEngine* engine() const
{ return m_engine; }
/**
* Get the JID of the local side of this stream
* @return The JID of the local side of this stream
*/
inline const JabberID& local() const
{ return m_local; }
/**
* Get the JID of the remote side of this stream
* @return The JID of the remote side of this stream
*/
inline const JabberID& remote() const
{ return m_remote; }
/**
* Get the remote peer's address
* @return The remote peer's address
*/
inline const SocketAddr& addr() const
{ return m_socket.addr(); }
/**
* Check if a given option (or option mask) is set
* @param mask The flag(s) to check
* @return True if set
*/
inline bool flag(int mask) const
{ return 0 != (m_flags & mask); }
/**
* Get the stream mutex
* @return The stream mutex
*/
inline Mutex* streamMutex()
{ return &m_socket.m_streamMutex; }
/**
* Connect the stream. Send stream start tag on success
* This method is thread safe
*/
void connect();
/**
* Read data from socket and pass it to the parser. Terminate stream on
* socket or parser error.
* This method is thread safe
* @return True if data was received
*/
bool receive();
/**
* Send a stanza.
* This method is thread safe
* @param stanza Element to send
* @param senderId Optional sender's id. Used for notification events
* @return The result of posting the stanza
*/
virtual Error sendStanza(XMLElement* stanza, const char* senderId = 0);
/**
* Stream state and data processor. Increase restart counter.
* Restart stream if idle and auto restart.
* Extract an element from parser and construct an event.
* This method is thread safe
* @param time Current time
* @return JBEvent pointer or 0
*/
JBEvent* getEvent(u_int64_t time);
/**
* Terminate stream. Send stream end tag or error.
* Remove pending stanzas without id. Deref stream if destroying.
* This method is thread safe
* @param destroy True to destroy. False to terminate
* @param recvStanza Received stanza, if any
* @param error Termination reason. Set it to NoError to send stream end tag
* @param reason Optional text to be added to the error stanza
* @param send True to send the stream end element
* @param final True if called from destructor
* @param sendError True to send the error element (ignored if error is NoError)
*/
void terminate(bool destroy, XMLElement* recvStanza, XMPPError::Type error, const char* reason,
bool send, bool final = false, bool sendError = true);
/**
* Remove pending stanzas with a given id.
* This method is thread safe
* @param id The id of stanzas to remove
* @param notify True to raise an event for each removed stanza
*/
inline void removePending(const String& id, bool notify = false) {
Lock lock(m_socket.m_streamMutex);
removePending(notify,&id,false);
}
/**
* Get the string representation of this stream
* @return The string representation of this stream
*/
virtual const String& toString() const
{ return name(); }
/**
* Get an object from this stream
* @param name The name of the object to get
*/
virtual void* getObject(const String& name) const;
/**
* Get the name of a stream state
* @param state The requested state number
* @return The name of the requested state
*/
static const char* lookupState(int state);
/**
* Dictionary keeping the flag names
*/
static TokenDict s_flagName[];
protected:
/**
* Internal wait states enumeration. Defines what kind of XML is expected
*/
enum WaitState { // Main state Wait
WaitIdle, // Idle nothing
WaitStart, // Started stream start response
WaitFeatures, // Started stream features
WaitBindRsp, // Started wait for bind response
WaitSessionRsp, // Started wait for session response
WaitTlsRsp, // Started wait response to starttls
WaitChallenge, // Auth iq auth query with auth data sent, wait response
WaitResponse, // Auth iq auth query sent, wait response
WaitAborted, // Auth abort sent, wait confirmation to terminate stream
};
/**
* Constructor. Build an outgoing stream
* @param engine The engine that owns this stream
* @param type Stream type
* @param info Structure containing data used to connect to remote server
* @param localJid Local party's JID
* @param remoteJid Remote party's JID
*/
JBStream(JBEngine* engine, int type, XMPPServerInfo& info,
const JabberID& localJid, const JabberID& remoteJid);
/**
* Default constructor
*/
inline JBStream()
: m_socket(0,0,0,0)
{}
/**
* Close the stream. Release memory
*/
virtual void destroyed();
/**
* Check the 'to' attribute of a received element
* @param xml The received element
* @param respond Action to be taken when if not accepted.
* True to respond with an error, false to just drop it
* @return False to reject it. If the stream is not in Running state,
* it will be terminated
*/
virtual bool checkDestination(XMLElement* xml, bool& respond);
/**
* Get the starting stream element to be sent after stream connected
* @return XMLElement pointer
*/
virtual XMLElement* getStreamStart();
/**
* Get the authentication element to be sent when authentication starts
* @return XMLElement pointer or 0 on failure
*/
virtual XMLElement* getAuthStart();
/**
* Process a received stanza in Running state
* @param xml Valid XMLElement pointer
*/
virtual void processRunning(XMLElement* xml);
/**
* Process a received element in Register state. Descendants MUST consume the data
* @param xml Valid XMLElement pointer
*/
virtual void processRegister(XMLElement* xml);
/**
* Process a received element in Auth state. Descendants MUST consume the data
* @param xml Valid XMLElement pointer
*/
virtual void processAuth(XMLElement* xml);
/**
* Process a received element in Securing state. Descendants MUST consume the data.
* Drop the received element
* @param xml Valid XMLElement pointer
*/
virtual void processSecuring(XMLElement* xml);
/**
* Process a received element in Started state. Descendants MUST consume the data
* @param xml Valid XMLElement pointer
*/
virtual void processStarted(XMLElement* xml);
/**
* Notify descendants when stream state changed to Running
*/
virtual void streamRunning()
{}
/**
* Create an iq event from a received iq stanza
* @param xml Received element
* @param iqType The iq type
* @param error Error type if 0 is returned
* @return JBEvent pointer or 0
*/
JBEvent* getIqEvent(XMLElement* xml, int iqType, XMPPError::Type& error);
/**
* Send declaration and stream start
* @return True on success
*/
bool sendStreamStart();
/**
* Send stream XML elements through the socket
* @param e The element to send
* @param newState The new stream state on success
* @return False if send failed (stream termination was initiated)
*/
bool sendStreamXML(XMLElement* e, State newState);
/**
* Terminate stream on receiving invalid elements
* @param xml Received element
* @param error Termination reason
* @param reason Optional text to be added to the error stanza
*/
void invalidStreamXML(XMLElement* xml, XMPPError::Type error, const char* reason);
/**
* Terminate stream on receiving stanza errors while not running
* @param xml Received element
*/
void errorStreamXML(XMLElement* xml);
/**
* Drop an unexpected or unhandled element
* @param xml Received element
* @param unexpected True if unexpected
*/
void dropXML(XMLElement* xml, bool unexpected = true);
/**
* Change stream's state. Raise a Running event when apropriate
* @param newState the new stream state
*/
void changeState(State newState);
/**
* Clear the remote feature list. Parse the received element to fill it up.
* Terminate the stream on error (such as invalid namespace).
* If false is returned, don't re-use the received element
* @param features Features element to parse
* @return False if the stream is terminated
*/
bool getStreamFeatures(XMLElement* features);
/**
* Start client TLS. Terminate the stream on error
* @return True if TLS was initiated. False on failure: stream termination was initiated
*/
bool startTls();
/**
* Start client registration
* Terminate the stream on error
* @return False if the stream is terminated
*/
bool startRegister();
/**
* Start client authentication. Send first request to authenticate with the server.
* Terminate the stream on error
* @return False if the stream is terminated
*/
bool startAuth();
/**
* Send authentication response. Terminate the stream on error
* @param challenge Received challenge. If non 0 a SASL response is built and sent.
* If 0, a non-SASL response is sent (using handshaking for component and
* XEP-0078 for client streams)
* @return False if the stream is terminated
*/
bool sendAuthResponse(XMLElement* challenge = 0);
/**
* Build SASL authentication response (Plain or Digest MD5 SASL).
* A valid mechanism must be previously set
* @param response Destination string
* @param realm Received realm or 0 to use local jid. If 0, nonce param is ignored
* @param nonce Server nonce if available
*/
void buildSaslResponse(String& response, String* realm = 0,
String* nonce = 0);
/**
* Parse remote's features and pick an authentication mechanism
* to be used when requesting authentication
*/
void setClientAuthMechanism();
/**
* Build a Digest MD5 SASL (RFC 2831) to be sent with authentication responses
* @param dest Destination string
* @param authenticate True if building a Digest MD5 challenge response, false if
* building a Digest MD5 to check a 'success' response
*/
void buildDigestMD5Sasl(String& dest, bool authenticate = true);
/**
* Safely set receive count
* @param value The new value of the receive count
*/
void setRecvCount(int value);
/**
* Start the idle timer if there are no pending stanzas
* @param time The current time in miliseconds
* @return True if started
*/
bool startIdleTimer(u_int64_t time = Time::msecNow());
/**
* Get last event from queue
* @return JBEvent pointer or 0
*/
inline JBEvent* lastEvent() {
ObjList* o = m_events.last();
return o ? static_cast<JBEvent*>(o->get()) : 0;
}
/**
* Stream's name
*/
String m_name;
/**
* The password used for authentication
*/
String m_password;
/**
* Local party feature list
*/
JIDFeatureList m_localFeatures;
/**
* Remote party feature list
*/
JIDFeatureList m_remoteFeatures;
/**
* Stream flags
*/
int m_flags;
/**
* The number of challenge/response exchanges allowed before ending the stream
*/
unsigned int m_challengeCount;
/**
* Internal states
*/
WaitState m_waitState;
/**
* Chosen authentication mechanism
*/
JIDFeatureSasl::Mechanism m_authMech;
/**
* Events queue
*/
ObjList m_events;
/**
* Register a new account
*/
bool m_register;
private:
// Event termination notification
// @param event The notifier. Ignored if it's not m_lastEvent
void eventTerminated(const JBEvent* event);
// Try sending pending stream element if any
// Try to send the first element in pending outgoing stanzas list
// If ErrorNoSocket is returned, stream termination was initiated
Error sendPending();
// Remove pending elements with id if id is not 0
// Remove all elements without id if id is 0
// Set force to true to remove the first element even if partially sent
void removePending(bool notify, const String* id, bool force);
// Called when a setup state was completed
// Set/reset some stream flags and data
void resetStream();
int m_type; // Stream type
State m_state; // Stream state
bool m_outgoing; // Stream direction
unsigned int m_restart; // Remaining restart attempts
unsigned int m_restartMax; // Max restart attempts
u_int64_t m_timeToFillRestart; // Next time to increase the restart counter
u_int64_t m_setupTimeout; // Stream setup timeout (interval allowed between Idle and Running states)
u_int64_t m_idleTimeout; // Connection idle in state Running (send keep alive packet)
String m_id; // Stream id
JabberID m_local; // Local peer's jid
JabberID m_remote; // Remote peer's jid
JBEngine* m_engine; // The owner of this stream
JBSocket m_socket; // The socket used by this stream
XMLParser m_parser; // XML parser
ObjList m_outXML; // Outgoing XML elements
JBEvent* m_lastEvent; // Last generated event
JBEvent* m_terminateEvent; // Destroy/Terminate event
JBEvent* m_startEvent; // Running event
int m_recvCount; // The number of bytes to read: -1: all, 0: nothing 1: 1 byte
XMLElementOut* m_streamXML; // Pending (incomplete) stream element
unsigned int m_declarationSent; // The number of declaration bytes sent
// Auth data
unsigned int m_nonceCount; // Nonce count
String m_nc; // Nonce count string
String m_nonce; // Server nonce
String m_cnonce; // Client nonce
String m_realm; // Client realm
// Register data
unsigned int m_registerId; // Id used when registering a new account
};
/**
* This class holds a Jabber Component stream (implements the Jabber Component Protocol).
* @short A Jabber Component stream
*/
class YJINGLE_API JBComponentStream : public JBStream
{
friend class JBEngine;
public:
/**
* Destructor
*/
virtual ~JBComponentStream()
{}
/**
* Get an object from this stream
* @param name The name of the object to get
* @return Pointer to the object or 0 if not found
*/
virtual void* getObject(const String& name) const;
protected:
/**
* Constructor. Build an outgoing stream
* @param engine The engine that owns this stream
* @param info Structure containing data used to connect to remote server
* @param localJid Local party's JID
* @param remoteJid Remote party's JID
*/
JBComponentStream(JBEngine* engine, XMPPServerInfo& info,
const JabberID& localJid, const JabberID& remoteJid);
/**
* Get the starting stream element to be sent after stream connected
* @return XMLElement pointer
*/
virtual XMLElement* getStreamStart();
/**
* Get the authentication element to be sent when authentication starts
* @return XMLElement pointer
*/
virtual XMLElement* getAuthStart();
/**
* Process a received element in Auth state
* @param xml Valid XMLElement pointer
*/
virtual void processAuth(XMLElement* xml);
/**
* Process a received element in Started state
* @param xml Valid XMLElement pointer
*/
virtual void processStarted(XMLElement* xml);
private:
// Default constructor is private to avoid unwanted use
JBComponentStream() {}
};
/**
* This class holds a Jabber client stream used to connect an user to its server
* @short A Jabber client to server stream
*/
class YJINGLE_API JBClientStream : public JBStream
{
friend class JBEngine;
public:
/**
* Destructor
*/
virtual ~JBClientStream();
/**
* Get the roster of this stream's client
* @return Valid XMPPUserRoster
*/
inline XMPPUserRoster* roster()
{ return m_roster; }
/**
* Get the client's resource
* @return The client's resource
*/
inline JIDResource* getResource()
{ return m_resource; }
/**
* Get an object from this stream
* @param name The name of the object to get
* @return Pointer to the object or 0 if not found
*/
virtual void* getObject(const String& name) const;
/**
* Get a remote user from roster
* @param jid The user's bare jid
* @return Referenced XMPPUser object or 0 if not found
*/
XMPPUser* getRemote(const JabberID& jid);
/**
* Send a stanza. This method is thread safe
* @param stanza Element to send
* @param senderId Optional sender's id. Used for notification events
* @return The result of posting the stanza
*/
virtual Error sendStanza(XMLElement* stanza, const char* senderId = 0);
protected:
/**
* Constructor. Build an outgoing stream
* @param engine The engine that owns this stream
* @param info Structure containing data used to connect to remote server
* @param jid Client's full Jabber ID
* @param params Other stream parameters
*/
JBClientStream(JBEngine* engine, XMPPServerInfo& info, const JabberID& jid,
const NamedList& params);
/**
* Constructor
* @param engine The engine that owns this stream
* @param jid User's JID
* @param password Password used for authentication
* @param address The remote address to connect to
* @param autoRestart True to auto restart the stream
* @param maxRestart The maximum restart attempts allowed
* @param incRestartInterval The interval to increase the restart counter
* @param allowPlainAuth Allow plain text password authentication
* @param outgoing Stream direction
*/
JBClientStream(JBEngine* engine, const JabberID& jid,
const String& password, const SocketAddr& address,
bool autoRestart, unsigned int maxRestart, u_int64_t incRestartInterval,
bool allowPlainAuth = false, bool outgoing = true);
/**
* Notification from parent when steam is authenticated: get roster from server
*/
virtual void streamRunning();
/**
* Process a received stanza in Running state
* @param xml Valid XMLElement pointer
*/
virtual void processRunning(XMLElement* xml);
/**
* Check the 'to' attribute of a received element against the local jid.
* Accept empty or bare/full jid match. Set the 'to' attribute to local jid if empty
* @param xml The received element
* @param respond Action to be taken if not accepted. Always false on exit
* @return False to reject it
*/
virtual bool checkDestination(XMLElement* xml, bool& respond);
private:
// Default constructor is private to avoid unwanted use
JBClientStream() {}
XMPPUserRoster* m_roster; // Client's roster
JIDResource* m_resource; // Client's resource
String m_rosterReqId; // Roster request id
};
/**
* This class holds encapsulates a private library thread
* @short A Jabber thread that can be added to a list of threads
*/
class YJINGLE_API JBThread : public GenObject
{
public:
/**
* Thread type enumeration. Used to do a specific client processing
*/
enum Type {
StreamConnect, // Asynchronously connect a stream's socket
EngineReceive, // Read all streams sockets
EngineProcess, // Get events from sockets and send them to
// registered services
Presence, // Presence service processor
Jingle, // Jingle service processor
Message // Message service processor
};
/**
* Destructor. Remove itself from the owner's list
*/
virtual ~JBThread();
/**
* Get the type of this thread
* @return Thread type as enumeration
*/
inline Type type() const
{ return m_type; }
/**
* Cancel (terminate) this thread
* @param hard Kill the thread the hard way rather than just setting
* an exit check marker
*/
virtual void cancelThread(bool hard = false) = 0;
/**
* Create and start a private thread
* @param type Thread type
* @param list The list owning this thread
* @param client The client to process
* @param sleep Time to sleep if there is nothing to do, zero to use platform default
* @param prio Thread priority, defaults to Normal
* @return False if failed to start the requested thread
*/
static bool start(Type type, JBThreadList* list, void* client, int sleep = 0, int prio = Thread::Normal);
protected:
/**
* Constructor. Append itself to the owner's list
* @param type Thread type
* @param owner The list owning this thread
* @param client The client to process
* @param sleep Time to sleep if there is nothing to do
*/
JBThread(Type type, JBThreadList* owner, void* client, int sleep = 2);
/**
* Process the client
*/
void runClient();
/**
* Get the stream's client
* @return The stream's client
*/
inline void* client()
{ return m_client; }
private:
Type m_type; // Thread type
JBThreadList* m_owner; // List owning this thread
void* m_client; // The client to process
int m_sleep; // Time to sleep if there is nothing to do
};
/**
* This class holds a list of private threads for an object that wants to terminate them on destroy
* @short A list of private threads
*/
class YJINGLE_API JBThreadList
{
friend class JBThread;
public:
/**
* Get the enabler owning this list
* @return The owner of this list
*/
inline DebugEnabler* owner() const
{ return m_owner; }
/**
* Cancel all threads
* This method is thread safe
* @param wait True to wait for the threads to terminate
* @param hard Kill the threads the hard way rather than just setting an exit check marker
*/
void cancelThreads(bool wait = true, bool hard = false);
protected:
/**
* Constructor
* @param owner The owner of this list
*/
JBThreadList(DebugEnabler* owner = 0)
: m_mutex(true,"JBThreadList"),
m_owner(owner), m_cancelling(false)
{ m_threads.setDelete(false); }
/**
* Set the enabler owning this list
* @param dbg The new owner of this list
*/
inline void setOwner(DebugEnabler* dbg)
{ m_owner = dbg; }
private:
Mutex m_mutex; // Lock list operations
DebugEnabler* m_owner; // The owner of this list
ObjList m_threads; // Private threads list
bool m_cancelling; // Cancelling threads operation in progress
};
/**
* This class holds a Jabber engine
* @short A Jabber engine
*/
class YJINGLE_API JBEngine : public DebugEnabler, public Mutex,
public GenObject, public JBThreadList
{
friend class JBStream;
public:
/**
* Jabber protocol type
*/
enum Protocol {
Component = 1, // Use Jabber Component protocol
Client = 2, // Use client streams
};
/**
* Service type enumeration
*/
enum Service {
ServiceJingle = 0, // Receive Jingle events
ServiceIq = 1, // Receive generic Iq events
ServiceMessage = 2, // Receive Message events
ServicePresence = 3, // Receive Presence events
ServiceCommand = 4, // Receive Command events
ServiceDisco = 5, // Receive Disco events
ServiceStream = 6, // Receive stream Terminated or Destroy events
ServiceWriteFail = 7, // Receive WriteFail events
ServiceRoster = 8, // Receive roster events
ServiceCount = 9
};
/**
* Constructor
* @param proto The protocol used by the streams belonging to this engine
*/
JBEngine(Protocol proto);
/**
* Destructor
*/
virtual ~JBEngine();
/**
* Get the Jabber protocol this engine is using
* @return The Jabber protocol as enumeration
*/
inline Protocol protocol() const
{ return m_protocol; }
/**
* Get the default component server
* @return The default component server
*/
inline const JabberID& componentServer() const
{ return m_componentDomain; }
/**
* Set the alternate domain name
* @param domain Name of an acceptable alternate domain
*/
inline void setAlternateDomain(const char* domain = 0)
{ m_alternateDomain = domain; }
/**
* Get the alternate domain name
* @return the alternate domain name
*/
inline const JabberID& getAlternateDomain() const
{ return m_alternateDomain; }
/**
* Get the default resource name.
* @return The default resource name.
*/
inline const String& defaultResource() const
{ return m_defaultResource; }
/**
* Get the stream list
* @return The list of streams belonging to this engine
*/
inline const ObjList& streams() const
{ return m_streams; }
/**
* Cleanup streams. Stop all threads owned by this engine. Release memory
*/
virtual void destruct();
/**
* Initialize the engine's parameters. Start private streams if requested
* @param params Engine's parameters
*/
virtual void initialize(const NamedList& params);
/**
* Terminate all streams
*/
void cleanup();
/**
* Set the default component server to use. The domain must be in the server list.
* Choose the first one from the server list if the given one doesn't exists.
* Do nothing if the protocol is not Component
* @param domain Domain name of the server
*/
void setComponentServer(const char* domain);
/**
* Find a stream by its name. This method is thread safe
* @param name The name of the stream to find
* @return Referenced JBStream pointer or 0
*/
JBStream* findStream(const String& name);
/**
* Get a stream. Create it not found and requested.
* For the component protocol, the jid parameter may contain the domain to find,
* otherwise, the default component will be used.
* This method won't create a client stream. Use @ref createClientStream().
* This method is thread safe
* @param jid Optional jid to use to find or create the stream
* @param create True to create a stream if don't exist. Ignored if the engine's protocol is Client
* @return Referenced JBStream pointer or 0
*/
JBStream* getStream(const JabberID* jid = 0, bool create = true);
/**
* Try to get a stream if stream parameter is 0
* @param stream Stream to check
* @param release Set to true on exit if the caller must deref the stream
* @return True if stream is valid
*/
bool getStream(JBStream*& stream, bool& release);
/**
* Create a new client stream. This method is thread safe
* @param params Stream parameters
* @return Referenced JBClientStream pointer or 0
*/
JBClientStream* createClientStream(NamedList& params);
/**
* Keep calling receive() for each stream until no data is received or the
* thread is terminated
* @return True if data was received
*/
bool receive();
/**
* Get events from the streams owned by this engine and send them to a service.
* Delete them if not processed by a service
* @param time Current time
* @return True if an event was generated by any stream
*/
bool process(u_int64_t time);
/**
* Check if an outgoing stream exists with the same id and remote peer
* @param stream The calling stream
* @return True if found
*/
bool checkDupId(const JBStream* stream);
/**
* Check the 'from' attribute received by a Component stream at startup
* @param stream The calling stream
* @param from The from attribute to check
* @return True if valid
*/
bool checkComponentFrom(JBComponentStream* stream, const char* from);
/**
* Asynchronously call the connect method of the given stream if the stream is idle
* @param stream The stream to connect
*/
virtual void connect(JBStream* stream);
/**
* Check if this engine is exiting
* @return True is terminating
*/
virtual bool exiting() const
{ return false; }
/**
* Setup the transport layer security for a stream
* @param stream The stream requesting the operation
* @return True if stream securing started, false on failure.
*/
virtual bool encryptStream(JBStream* stream);
/**
* Append a server info element to the list
* @param server The object to add
* @param open True to open the stream, if in component mode
*/
void appendServer(XMPPServerInfo* server, bool open);
/**
* Get the identity of the given server
* @param destination The destination buffer
* @param full True to get the full identity
* @param token The search string. If 0 and the component protocol is used,
* the default server will be used
* @param domain True to find by domain name. False to find by address
* @return False if server doesn't exists
*/
bool getServerIdentity(String& destination, bool full, const char* token = 0,
bool domain = true);
/**
* Find server info object
* @param token The search string. If 0 and the Component protocol is used,
* the default component server will be used
* @param domain True to find by domain name. False to find by address
* @return XMPPServerInfo pointer or 0 if not found
*/
XMPPServerInfo* findServerInfo(const char* token, bool domain);
/**
* Attach a service to this engine.
* This method is thread safe
* @param service The service to attach
* @param type Service type
* @param prio Service priority. Set to -1 to use the givent service's priority.
* A lower values indicates a service with higher priority
*/
void attachService(JBService* service, Service type, int prio = -1);
/**
* Remove a service from all event handlers of this engine.
* This method is thread safe
* @param service The service to detach
*/
void detachService(JBService* service);
/**
* Print an XML element to output
* @param xml Element to print
* @param stream Stream requesting the operation
* @param send True if sending, false if receiving
*/
void printXml(const XMLElement& xml, const JBStream* stream, bool send) const;
/**
* Get the name of a protocol
* @return The name of the requested protocol or the default value
*/
inline static const char* lookupProto(int proto, const char* def = 0)
{ return lookup(proto,s_protoName,def); }
/**
* Get the value associated with a protocol name
* @return The value associated with a protocol name
*/
inline static int lookupProto(const char* proto, int def = 0)
{ return lookup(proto,s_protoName,def); }
private:
// Process a Disco... events
bool processDisco(JBEvent* event);
// Process a Command events
bool processCommand(JBEvent* event);
// Pass events to services
bool received(Service service, JBEvent* event);
static TokenDict s_protoName[]; // Protocol names
Protocol m_protocol; // The protocol to use
u_int32_t m_restartUpdateInterval; // Update interval for restart counter of all streams
u_int32_t m_restartCount; // The default restart counter value
u_int64_t m_streamSetupInterval; // Timeout for stream setup
u_int64_t m_streamIdleInterval; // Timeout for stream idle (nothing sent/received in Running state)
int m_printXml; // Print XML data to output
ObjList m_streams; // Streams belonging to this engine
JIDIdentity* m_identity; // Engine's identity
JIDFeatureList m_features; // Engine's features
JabberID m_componentDomain; // Default server domain name
String m_componentAddr; // Default server address
int m_componentCheckFrom; // The behaviour when checking the 'from' attribute for a component stream
// 0: no check 1: local identity 2: remote identity
JabberID m_alternateDomain; // Alternate acceptable domain
String m_defaultResource; // Default name for missing resources
Mutex m_serverMutex; // Lock server info list
ObjList m_server; // Server info list
Mutex m_servicesMutex; // Lock service list
ObjList m_services[ServiceCount]; // Services list
bool m_initialized; // True if already initialized
};
/**
* This class is the base class for a Jabber service who wants
* to get specific protocol data from the Jabber engine
* @short A Jabber service
*/
class YJINGLE_API JBService : public DebugEnabler, public Mutex, public GenObject
{
public:
/**
* Constructor
* @param engine The Jabber engine
* @param name This service's name
* @param params Service's parameters
* @param prio The priority of this service
*/
JBService(JBEngine* engine, const char* name, const NamedList* params, int prio);
/**
* Destructor. Remove from engine
*/
virtual ~JBService();
/**
* Get the Jabber engine
* @return The Jabber engine
*/
inline JBEngine* engine()
{ return m_engine; }
/**
* Get the Jabber engine
* @return The Jabber engine
*/
inline int priority() const
{ return m_priority; }
/**
* Accept an event from the engine. If accepted, the event is enqueued
* and the stream that generated the event is notified on event
* terminated to allow it to process other data.
* This method is thread safe
* @param event The event to accept
* @return False if not accepted, let the engine try another service
*/
bool received(JBEvent* event);
/**
* Initialize the service
* @param params Service's parameters
*/
virtual void initialize(const NamedList& params)
{}
/**
* Remove from engine. Release memory
*/
virtual void destruct();
protected:
/**
* Accept an event from the 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);
/**
* Get an event from queue
* @return JBEvent pointer or 0 if queue is empty
*/
JBEvent* deque();
/**
* True if already initialized
*/
bool m_initialized;
private:
inline JBService() {} // Don't use it !
JBEngine* m_engine; // The Jabber Component engine
int m_priority; // Service priority
ObjList m_events; // Events received from engine
};
/**
* This class is a message receiver service for the Jabber engine
* @short A Jabber message service
*/
class YJINGLE_API JBMessage : public JBService, public JBThreadList
{
public:
/**
* Message type enumeration
*/
enum MsgType {
Chat, // chat
GroupChat, // groupchat
HeadLine, // headline
Normal, // normal
Error, // error
None,
};
/**
* Constructor. Constructs a Jabber message service
* @param engine The Jabber engine
* @param params Service's parameters
* @param prio The priority of this service
*/
inline JBMessage(JBEngine* engine, const NamedList* params, int prio = 0)
: JBService(engine,"jbmsgrecv",params,prio), m_syncProcess(true)
{ JBThreadList::setOwner(this); }
/**
* Destructor. Cancel private thread(s)
*/
virtual ~JBMessage()
{ cancelThreads(); }
/**
* Initialize the service
* @param params Service's parameters
*/
virtual void initialize(const NamedList& params);
/**
* Get a message from queue
* @return JBEvent pointer or 0 if no messages
*/
inline JBEvent* getMessage()
{ return deque(); }
/**
* Message processor. The derived classes must override this method
* to process received messages
* @param event The event to process
*/
virtual void processMessage(JBEvent* event);
/**
* Create a 'message' element
* @param type Message type string
* @param from The 'from' attribute
* @param to The 'to' attribute
* @param id The 'id' attribute
* @param message The message body
* @return A valid XMLElement pointer
*/
static XMLElement* createMessage(const char* type, const char* from,
const char* to, const char* id, const char* message);
/**
* Create a 'message' element
* @param type Message type as enumeration
* @param from The 'from' attribute
* @param to The 'to' attribute
* @param id The 'id' attribute
* @param message The message body
* @return A valid XMLElement pointer
*/
static inline XMLElement* createMessage(MsgType type, const char* from,
const char* to, const char* id, const char* message)
{ return createMessage(lookup(type,s_msg,""),from,to,id,message); }
/**
* Get the type of a 'message' stanza
* @param text The text to check
* @return Message type as enumeration
*/
static inline MsgType msgType(const char* text)
{ return (MsgType)lookup(text,s_msg,None); }
/**
* Get the text from a message type
* @param msg The message type
* @return The associated text or 0
*/
static inline const char* msgText(MsgType msg)
{ return lookup(msg,s_msg,0); }
/**
* Keep the types of 'message' stanzas
*/
static TokenDict s_msg[];
protected:
/**
* Accept an event from the engine and process it if configured to do that
* @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:
bool m_syncProcess; // Process messages on accept
};
/**
* This class is a presence service for Jabber engine. Handle presence stanzas and
* iq query info or items with destination containing a node and a valid domain
* @short A Jabber presence service
*/
class YJINGLE_API JBPresence : public JBService, public JBThreadList
{
friend class XMPPUserRoster;
public:
/**
* Presence type enumeration
*/
enum Presence {
Error = 0, // error
Probe = 1, // probe
Subscribe = 2, // subscribe request
Subscribed = 3, // subscribe accepted
Unavailable = 4, // unavailable
Unsubscribe = 5, // unsubscribe request
Unsubscribed = 6, // unsubscribe accepted
None = 7,
};
/**
* Constructor. Constructs a Jabber Component presence service
* @param engine The Jabber engine
* @param params Service's parameters
* @param prio The priority of this service
*/
JBPresence(JBEngine* engine, const NamedList* params, int prio = 0);
/**
* Destructor
*/
virtual ~JBPresence();
/**
* Get the auto subscribe parameter
* @return The auto subscribe parameter
*/
inline XMPPDirVal autoSubscribe() const
{ return m_autoSubscribe; }
/**
* Check if the unavailable resources must be deleted
* @return The delete unavailable parameter
*/
inline bool delUnavailable() const
{ return m_delUnavailable; }
/**
* Get the 'add on subscribe' flags
* @return The 'add on subscribe' flags
*/
inline XMPPDirVal addOnSubscribe() const
{ return m_addOnSubscribe; }
/**
* Get the 'add on probe' flags
* @return The 'add on probe' flags
*/
inline XMPPDirVal addOnProbe() const
{ return m_addOnProbe; }
/**
* Get the 'add on presence' flags
* @return The 'add on presence' flags
*/
inline XMPPDirVal addOnPresence() const
{ return m_addOnPresence; }
/**
* Check if this service should add new users when receiving presence, probe or subscribe
* @return True if should add a new user when receiving presence, probe or subscribe
*/
inline bool autoRoster() const
{ return m_autoRoster; }
/**
* Check if this service should ignore destination users not in roster
* @return True if non existent destinations should be ignored
*/
inline bool ignoreNonRoster() const
{ return m_ignoreNonRoster; }
/**
* Get the probe interval. Time to send a probe if nothing was received from that user
* @return The probe interval
*/
inline u_int32_t probeInterval()
{ return m_probeInterval; }
/**
* Get the expire after probe interval
* @return The expire after probe interval
*/
inline u_int32_t expireInterval()
{ return m_expireInterval; }
/**
* Initialize the presence service
* @param params Service's parameters
*/
virtual void initialize(const NamedList& params);
/**
* Process an event from the receiving list
* This method is thread safe
* @return False if the list is empty
*/
virtual bool process();
/**
* Check presence timeout
* This method is thread safe
* @param time Current time
*/
virtual void checkTimeout(u_int64_t time);
/**
* Process disco info elements
* @param event The event with the element
*/
virtual void processDisco(JBEvent* event);
/**
* Process a presence error element
* @param event The event with the element
*/
virtual void processError(JBEvent* event);
/**
* Process a presence probe element
* @param event The event with the element
*/
virtual void processProbe(JBEvent* event);
/**
* Process a presence subscribe element
* @param event The event with the element
* @param presence Presence type: Subscribe,Subscribed,Unsubscribe,Unsubscribed
*/
virtual void processSubscribe(JBEvent* event, Presence presence);
/**
* Process a presence unavailable element
* @param event The event with the element
*/
virtual void processUnavailable(JBEvent* event);
/**
* Process a presence element
* @param event The event with the element
*/
virtual void processPresence(JBEvent* event);
/**
* Notify on probe request with users we don't know about
* @param event The event with the element
* @return False to send item-not-found error
*/
virtual bool notifyProbe(JBEvent* event);
/**
* Notify on subscribe event with users we don't know about
* @param event The event with the element
* @param presence Presence type: Subscribe,Subscribed,Unsubscribe,Unsubscribed
* @return False to send item-not-found error
*/
virtual bool notifySubscribe(JBEvent* event, Presence presence);
/**
* Notify on subscribe event
* @param user The user that received the event
* @param presence Presence type: Subscribe,Subscribed,Unsubscribe,Unsubscribed
*/
virtual void notifySubscribe(XMPPUser* user, Presence presence);
/**
* Notify on presence event with users we don't know about or presence unavailable
* received without resource (the remote user is entirely unavailable)
* @param event The event with the element
* @param available The availability of the remote user
* @return False to send item-not-found error
*/
virtual bool notifyPresence(JBEvent* event, bool available);
/**
* Notify on state/capabilities change
* @param user The user that received the event
* @param resource The resource that changet its state or capabilities
*/
virtual void notifyPresence(XMPPUser* user, JIDResource* resource);
/**
* Notify when a new user is added
* Used basically to add a local resource
* @param user The new user
*/
virtual void notifyNewUser(XMPPUser* user);
/**
* Get a roster. Add a new one if requested.
* This method is thread safe
* @param jid The user's jid
* @param add True to add the user if doesn't exists
* @param added Optional parameter to be set if a new user was added
* @return Referenced pointer or 0 if none
*/
XMPPUserRoster* getRoster(const JabberID& jid, bool add, bool* added);
/**
* Get a remote peer of a local one. Add a new one if requested.
* This method is thread safe
* @param local The local peer
* @param remote The remote peer
* @param addLocal True to add the local user if doesn't exists
* @param addedLocal Optional parameter to be set if a new local user was added
* @param addRemote True to add the remote user if doesn't exists
* @param addedRemote Optional parameter to be set if a new remote user was added
* @return Referenced pointer or 0 if none
*/
XMPPUser* getRemoteUser(const JabberID& local, const JabberID& remote,
bool addLocal, bool* addedLocal, bool addRemote, bool* addedRemote);
/**
* Remove a remote peer of a local one
* This method is thread safe
* @param local The local peer
* @param remote The remote peer
*/
void removeRemoteUser(const JabberID& local, const JabberID& remote);
/**
* Check if the given domain is a valid (known) one
* @param domain The domain name to check
* @return True if the given domain is a valid one
*/
bool validDomain(const String& domain);
/**
* Send an element through the given stream.
* If the stream is 0 try to get one from the engine.
* In any case the element is consumed (deleted)
* @param element Element to send
* @param stream The stream to send through
* @return The result of send operation. False if element is 0
*/
bool sendStanza(XMLElement* element, JBStream* stream);
/**
* Create an 'presence' element
* @param from The 'from' attribute
* @param to The 'to' attribute
* @param type Presence type as enumeration
* @return A valid XMLElement pointer
*/
static XMLElement* createPresence(const char* from,
const char* to, Presence type = None);
/**
* Decode an error element
* @param element The XML element
* @param code The 'code' attribute
* @param type The 'type' attribute
* @param error The name of the 'error' child
* @return False if 'element' is 0 or is not a presence one
*/
static bool decodeError(const XMLElement* element,
String& code, String& type, String& error);
/**
* Get the type of a 'presence' stanza as enumeration
* @param text The text to check
* @return Presence type as enumeration
*/
static inline Presence presenceType(const char* text)
{ return (Presence)lookup(text,s_presence,None); }
/**
* Get the text from a presence type
* @param presence The presence type
* @return The associated text or 0
*/
static inline const char* presenceText(Presence presence)
{ return lookup(presence,s_presence,0); }
/**
* Cleanup rosters
*/
void cleanup();
protected:
/**
* Accept an event from the 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);
static TokenDict s_presence[]; // Keep the types of 'presence'
XMPPDirVal m_autoSubscribe; // Auto subscribe state
bool m_delUnavailable; // Delete unavailable user or resource
bool m_autoRoster; // True if this service make an automatically roster management
bool m_ignoreNonRoster; // Ignore all elements whose destination is not in roster
XMPPDirVal m_addOnSubscribe; // Add new user on subscribe request
XMPPDirVal m_addOnProbe; // Add new user on probe request
XMPPDirVal m_addOnPresence; // Add new user on presence
bool m_autoProbe; // Automatically respond to probe requests
u_int32_t m_probeInterval; // Interval to probe a remote user
u_int32_t m_expireInterval; // Expire interval after probe
ObjList m_rosters; // The rosters
JIDIdentity* m_defIdentity; // Default identity
JIDFeatureList m_defFeatures; // Default features
private:
// Wrapper for getRemoteUser() used when receiving presence
// Show a debug message if not found
XMPPUser* recvGetRemoteUser(const char* type, const JabberID& local, const JabberID& remote,
bool addLocal = false, bool* addedLocal = 0,
bool addRemote = false, bool* addedRemote = 0);
void addRoster(XMPPUserRoster* ur);
void removeRoster(XMPPUserRoster* ur);
};
/**
* This class holds a JID resource (name,presence,capabilities)
* @short A JID resource
*/
class YJINGLE_API JIDResource : public RefObject
{
public:
/**
* Resource capabilities enumeration.
*/
enum Capability {
CapChat = 1, // Chat capability
CapAudio = 2, // Jingle capability
};
/**
* Resource presence enumeration
*/
enum Presence {
Unknown = 0, // unknown
Available = 1, // available
Unavailable = 2, // unavailable
};
/**
* Values of the 'show' child of a presence element
*/
enum Show {
ShowAway, // away : Temporarily away
ShowChat, // chat : Actively interested in chatting
ShowDND, // dnd : Busy
ShowXA, // xa : Extended away
ShowNone, // : Missing or no text
};
/**
* Constructor. Set data members
* @param name The resource name
* @param presence The resource presence
* @param capability The resource capability
* @param prio The resource priority
*/
inline JIDResource(const char* name, Presence presence = Unknown,
u_int32_t capability = CapChat, int prio = 0)
: m_name(name), m_presence(presence), m_priority(prio),
m_capability(capability), m_show(ShowNone)
{}
/**
* Destructor
*/
inline virtual ~JIDResource()
{}
/**
* Get the resource name
* @return The resource name
*/
inline const String& name() const
{ return m_name; }
/**
* Set the resource name
* @param name The new name of the resource
*/
inline void setName(const char* name)
{ m_name = name; }
/**
* Get the presence attribute
* @return The presence attribute
*/
inline Presence presence() const
{ return m_presence; }
/**
* Check if the resource is available
* @return True if the resource is available
*/
inline bool available() const
{ return (m_presence == Available); }
/**
* Get the show attribute as enumeration
* @return The show attribute as enumeration
*/
inline Show show() const
{ return m_show; }
/**
* Set the show attribute
* @param s The new show attribute
*/
inline void show(Show s)
{ m_show = s; }
/**
* Get the status of this resource
* @return The status of this resource
*/
inline const String& status() const
{ return m_status; }
/**
* Set the status of this resource
* @param s The new status of this resource
*/
inline void status(const char* s)
{ m_status = s; }
/**
* Get the priority of this resource
* @return The priority of this resource
*/
inline int priority()
{ return m_priority; }
/**
* Set the priority of this resource
* @param value The new priority of this resource
*/
inline void priority(int value)
{ m_priority = value; }
/**
* Get the list of resource features
* @return The resource features
*/
inline JIDFeatureList& features()
{ return m_features; }
/**
* Get the list containing XML elements with additional data describing this resource
* @return The info list
*/
inline ObjList* infoXml()
{ return &m_info; }
/**
* Set the presence information
* @param value True if available, False if not
* @return True if presence changed
*/
bool setPresence(bool value);
/**
* Check if the resource has the required capability
* @param capability The required capability
* @return True if the resource has the required capability
*/
inline bool hasCap(Capability capability) const
{ return (m_capability & capability) != 0; }
/**
* Update resource from a presence element
* @param element A presence element
* @return True if presence or capability changed changed
*/
bool fromXML(XMLElement* element);
/**
* Add capabilities to a presence element
* @param element The target presence element
* @param addInfo True to add the elements from info list
*/
void addTo(XMLElement* element, bool addInfo = true);
/**
* Get the 'show' child of a presence element
* @param element The XML element
* @return The text or 0
*/
static const char* getShow(XMLElement* element);
/**
* Get the 'show' child of a presence element
* @param element The XML element
* @return The text or 0
*/
static const char* getStatus(XMLElement* element);
/**
* Get the type of a 'show' element as enumeration
* @param text The text to check
* @return Show type as enumeration
*/
static inline Show showType(const char* text)
{ return (Show)lookup(text,s_show,ShowNone); }
/**
* Get the text from a show type
* @param show The type to get text for
* @return The associated text or 0
*/
static inline const char* showText(Show show)
{ return lookup(show,s_show,0); }
protected:
static TokenDict s_show[]; // Show texts
private:
String m_name; // Resource name
Presence m_presence; // Resorce presence
int m_priority; // Resource priority
u_int32_t m_capability; // Resource capabilities
Show m_show; // Show attribute
String m_status; // Status attribute
JIDFeatureList m_features; // Resource features
ObjList m_info; // XML elements containing additional info about this resource
};
/**
* This class holds a resource list
* @short A resource list
*/
class YJINGLE_API JIDResourceList : public Mutex
{
friend class XMPPUser;
friend class JBPresence;
public:
/**
* Constructor
*/
inline JIDResourceList()
: Mutex(true,"JIDResourceList")
{}
/**
* Add a resource to the list if a resource with the given name
* doesn't exists
* @param name The resource name
* @return False if the the resource already exists in the list
*/
inline bool add(const String& name)
{ return add(new JIDResource(name)); }
/**
* Add a resource to the list if not already there.
* Destroy the received resource if not added
* @param resource The resource to add
* @return False if the the resource already exists in the list
*/
bool add(JIDResource* resource);
/**
* Remove a resource from the list
* @param resource The resource to remove
* @param del True to delete the resource
*/
inline void remove(JIDResource* resource, bool del = true)
{ Lock lock(this); m_resources.remove(resource,del); }
/**
* Clear the list
*/
inline void clear()
{ Lock lock(this); m_resources.clear(); }
/**
* Get a resource with the given name
* @param name The resource name
* @return A pointer to the resource or 0
*/
JIDResource* get(const String& name);
/**
* Get the first resource from the list
* @return A pointer to the resource or 0
*/
inline JIDResource* getFirst() {
Lock lock(this);
ObjList* obj = m_resources.skipNull();
return obj ? static_cast<JIDResource*>(obj->get()) : 0;
}
/**
* Get the first resource with audio capability
* @param availableOnly True to get only if available
* @return A pointer to the resource or 0
*/
JIDResource* getAudio(bool availableOnly = true);
private:
ObjList m_resources; // The resources list
};
/**
* This class holds a remote XMPP user along with his resources and subscribe state.
* @short An XMPP remote user.
*/
class YJINGLE_API XMPPUser : public RefObject, public Mutex
{
friend class XMPPUserRoster;
friend class JBPresence;
public:
/**
* Create a remote user.
* @param local The local (owner) user peer.
* @param node The node (username) of the remote peer.
* @param domain The domain of the remote peer.
* @param sub The subscription state.
* @param subTo True to force a subscribe request to remote peer if not subscribed.
* @param sendProbe True to probe the new user.
*/
XMPPUser(XMPPUserRoster* local, const char* node, const char* domain,
XMPPDirVal sub, bool subTo = true, bool sendProbe = true);
/**
* Destructor.
* Send unavailable if not already done.
*/
virtual ~XMPPUser();
/**
* Get the jid of this user.
* @return The jid of this user.
*/
inline const JabberID& jid() const
{ return m_jid; }
/**
* Get the roster this user belongs to.
* @return Pointer to the roster this user belongs to.
*/
inline XMPPUserRoster* local() const
{ return m_local; }
/**
* Get the subscription state of this user
* @return The subscription state of this user
*/
inline XMPPDirVal& subscription()
{ return m_subscription; }
/**
* Get the local resource list
* @return The local resource list
*/
inline JIDResourceList& localRes()
{ return m_localRes; }
/**
* Get the remote resource list
* @return The remote resource list
*/
inline JIDResourceList& remoteRes()
{ return m_remoteRes; }
/**
* Add a local resource to the list.
* Send presence if the remote peer is subscribed to the local one.
* This method is thread safe.
* @param resource The resource to add.
* @param send True to send presence from the resource if it is a new one
* @return False if the the resource already exists in the list.
*/
bool addLocalRes(JIDResource* resource, bool send = true);
/**
* Remove a local resource from the list.
* Send unavailable if the remote peer is subscribed to the local one.
* This method is thread safe.
* @param resource The resource to remove.
*/
void removeLocalRes(JIDResource* resource);
/**
* Remove all local resources.
* Send unavailable if the remote peer is subscribed to the local one.
* This method is thread safe.
*/
void clearLocalRes();
/**
* Add a remote resource to the list. This method is thread safe.
* @param resource The resource to add
* @return False if the the resource already exists in the list
*/
bool addRemoteRes(JIDResource* resource);
/**
* Remove a remote resource from the list. This method is thread safe.
* @param resource The resource to remove
*/
void removeRemoteRes(JIDResource* resource);
/**
* Get the first remote resource with audio capability.
* @param local True to request a local resource, false for a remote one.
* @param availableOnly True to get only if available.
* @return A pointer to the resource or 0.
*/
inline JIDResource* getAudio(bool local, bool availableOnly = true) {
return local ? m_localRes.getAudio(availableOnly) :
m_remoteRes.getAudio(availableOnly);
}
/**
* Process received error elements.
* This method is thread safe.
* @param event The event with the element.
*/
void processError(JBEvent* event);
/**
* Process received probe from remote peer.
* This method is thread safe.
* @param event The event with the element.
* @param resName The probed resource if any.
*/
void processProbe(JBEvent* event, const String* resName = 0);
/**
* Process received presence from remote peer.
* This method is thread safe.
* @param event The event with the element.
* @param available The availability of the user.
* @return False if remote user has no more resources available.
*/
bool processPresence(JBEvent* event, bool available);
/**
* Process received subscription from remote peer.
* This method is thread safe.
* @param event The event with the element.
* @param type The subscription type: subscribe/unsubscribe/subscribed/unsubscribed.
*/
void processSubscribe(JBEvent* event, JBPresence::Presence type);
/**
* Probe the remote user.
* This method is thread safe.
* @param stream Optional stream to use to send the request.
* @param time Probe time.
* @return True if send succeedded.
*/
bool probe(JBStream* stream, u_int64_t time = Time::msecNow());
/**
* Send subscription to remote peer.
* This method is thread safe.
* @param type The subscription type: subscribe/unsubscribe/subscribed/unsubscribed.
* @param stream Optional stream to use to send the data.
* @return True if send succeedded.
*/
bool sendSubscribe(JBPresence::Presence type, JBStream* stream);
/**
* Send unavailable to remote peer.
* This method is thread safe.
* @param stream Optional stream to use to send the data.
* @return True if send succeedded.
*/
bool sendUnavailable(JBStream* stream);
/**
* Send presence to remote peer.
* This method is thread safe.
* @param resource The resource to send from.
* @param stream Optional stream to use to send the data.
* @param force True to send even if we've already sent presence from this resource.
* @return True if send succeedded.
*/
bool sendPresence(JIDResource* resource, JBStream* stream = 0, bool force = false);
/**
* Check if this user sent us any presence data for a given interval.
* This method is thread safe.
* @param time Current time.
* @return True if the user timed out.
*/
bool timeout(u_int64_t time);
/**
* Notify the state of a resource.
* This method is thread safe.
* @param remote True for a remote resource: notify the presence engine.
* False for a local resource: send presence to remote user.
* @param name Resource name.
* @param stream Optional stream to use to send the data if remote is false.
* @param force True to send even if we've already sent presence from this resource.
*/
void notifyResource(bool remote, const String& name,
JBStream* stream = 0, bool force = false);
/**
* Notify the state of all resources.
* This method is thread safe.
* @param remote True for remote resources: notify the presence engine.
* False for local resources: send presence to remote user.
* @param stream Optional stream to use to send the data if remote is false.
* @param force True to send even if we've already sent presence from a resource.
*/
void notifyResources(bool remote, JBStream* stream = 0, bool force = false);
protected:
/**
* Update subscription state.
* @param from True for subscription from remote user. False for subscription to the remote user.
* @param value True if subscribed. False is unsubscribed.
* @param stream Optional stream to use to send presence if subscription from remote user changed to true.
*/
void updateSubscription(bool from, bool value, JBStream* stream);
/**
* Update user timeout data.
* @param from True if the update is made on incoming data. False if timeout is made on outgoing probe.
* @param time Current time.
*/
void updateTimeout(bool from, u_int64_t time = Time::msecNow());
private:
XMPPUserRoster* m_local; // Local user
JabberID m_jid; // User's JID
XMPPDirVal m_subscription; // Subscription state
JIDResourceList m_localRes; // Local user's resources
JIDResourceList m_remoteRes; // Remote user's resources
u_int64_t m_nextProbe; // Time to probe
u_int64_t m_expire; // Expire time
};
/**
* This class holds the roster for a local user.
* @short The roster of a local user.
*/
class YJINGLE_API XMPPUserRoster : public RefObject, public Mutex
{
friend class JBPresence;
friend class JBClientStream;
friend class XMPPUser;
public:
/**
* Destructor.
* Remove this roster from engine's queue.
*/
virtual ~XMPPUserRoster();
/**
* Get the local user's jid.
* @return The local user's jid.
*/
const JabberID& jid() const
{ return m_jid; }
/**
* Get the presence engine this user belongs to.
* @return Pointer to the presence engine this user belongs to.
*/
JBPresence* engine()
{ return m_engine; }
/**
* Get the list of available resources belonging to the same user
* @return The list of available resources
*/
inline JIDResourceList& resources()
{ return m_resources; }
/**
* Get the list of remote users
* @return The list of remote users
*/
inline ObjList& users()
{ return m_remote; }
/**
* Get a remote user.
* This method is thread safe.
* @param jid User's jid.
* @param add True to add if not found.
* @param added Optional flag to set if added a new user.
* @return Referenced pointer to the user or 0.
*/
XMPPUser* getUser(const JabberID& jid, bool add = false, bool* added = 0);
/**
* Remove a remote user.
* This method is thread safe.
* @param remote The user to remove.
* @return False if no more users.
*/
bool removeUser(const JabberID& remote);
/**()
* Clear remote user list.
*/
inline void cleanup() {
Lock lock(this);
m_remote.clear();
}
/**
* Check timeout.
* This method is thread safe.
* @param time Current time.
* @return True to remove the roster.
*/
bool timeout(u_int64_t time);
/**
* Create an iq result to respond to disco info. Add user's features and identity
* @param from The from attribute
* @param to The to attribute
* @param id The id attribute
* @return XMLElement pointer
*/
inline XMLElement* createDiscoInfoResult(const char* from, const char* to,
const char* id)
{ return XMPPUtils::createDiscoInfoRes(from,to,id,&m_features,m_identity); }
protected:
/**
* Constructor.
* @param engine Pointer to the presence engine this user belongs to
* @param node User's name
* @param domain User's domain
* @param proto Protocol. Used to create identity
*/
XMPPUserRoster(JBPresence* engine, const char* node, const char* domain,
JBEngine::Protocol proto = JBEngine::Component);
private:
inline void addUser(XMPPUser* u) {
Lock lock(this);
m_remote.append(u);
}
void removeUser(XMPPUser* u) {
Lock lock(this);
m_remote.remove(u,false);
}
JabberID m_jid; // User's bare JID
JIDFeatureList m_features; // Local user's resources
JIDIdentity* m_identity; // JID's identity
ObjList m_remote; // Remote users
JIDResourceList m_resources; // Available resources of the user
JBPresence* m_engine; // Presence engine
};
};
#endif /* __YATEJABBER_H */
/* vi: set ts=8 sw=4 sts=4 noet: */