yate/libs/yjingle/yatejabber.h

1908 lines
60 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;
class JBComponentStream;
class JBServerInfo;
class JBEngine;
class JBClient;
class JBPresence;
class JIDResource;
class JIDResourceList;
class XMPPUser;
class XMPPUserRoster;
/**
* This class holds a Jabber Component stream event.
* @short A Jabber Component event.
*/
class YJINGLE_API JBEvent : public RefObject
{
friend class JBComponentStream;
public:
enum Type {
// Stream events
Terminated = 1, // Stream terminated. Try to connect
Destroy = 2, // Stream is destroying
// Result events
WriteFail = 10, // Write failed
// m_element is the element
// m_id is the id set by the sender
// Stanza events
Presence = 20, // m_element is a 'presence' stanza
Message = 30, // m_element is a 'message' stanza
Iq = 50, // m_element is an unknown 'iq' element
// m_child may be an unexpected element
IqError = 51, // m_element is an 'iq' error
// m_child is the 'error' child if any
IqResult = 52, // m_element is an 'iq' result
IqDiscoGet = 60, // m_element is an 'iq' get
// m_child is a 'query' element qualified by
// XMPPNamespace::DiscoInfo namespace
IqDiscoSet = 61, // m_element is an 'iq' set
// m_child is a 'query' element qualified by
// XMPPNamespace::DiscoInfo namespace
IqDiscoRes = 62, // m_element is an 'iq' result
// m_child is a 'query' element qualified by
// XMPPNamespace::DiscoInfo namespace
IqCommandGet = 70, // m_element is an 'iq' of type get
// m_child is a 'command' element qualified by
// XMPPNamespace::Command namespace
IqCommandSet = 71, // m_element is an 'iq' of type set
// m_child is a 'command' element qualified by
// XMPPNamespace::Command namespace
IqCommandRes = 72, // m_element is an 'iq' result
// m_child is a 'command' element qualified by
// XMPPNamespace::Command namespace
IqJingleGet = 100, // m_element is an 'iq' get
// m_child is a 'jingle' element
IqJingleSet = 101, // m_element is an 'iq' set
// m_child is a 'jingle' element
// Invalid
Unhandled = 200, // m_element is an unhandled element
Invalid = 500, // m_element is 0
};
/**
* Destructor.
* Delete the XML element if valid.
*/
virtual ~JBEvent();
/**
* Get the event type.
* @return the type of this event as enumeration.
*/
inline Type type() const
{ return m_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' data.
* @return The 'from' data.
*/
inline const String& from() const
{ return m_from; }
/**
* Get the 'to' data.
* @return The 'to' data.
*/
inline const String& to() const
{ return m_to; }
/**
* Get the 'id' data.
* @return The 'id' data.
*/
inline const String& id() const
{ return m_id; }
/**
* Get the stream.
* @return The stream.
*/
inline JBComponentStream* 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; }
/**
* Get the underlying XMLElement. Release the ownership.
* The caller is responsable of returned pointer.
* @return XMLElement pointer or 0.
*/
inline XMLElement* releaseXML() {
XMLElement* tmp = m_element;
m_element = 0;
return tmp;
}
/**
* Release the link with the stream to let the stream continue with events.
*/
void releaseStream();
protected:
/**
* Constructor.
* Constructs an internal stream event.
* @param type Type of this event.
* @param stream The stream that generated the event.
*/
JBEvent(Type type, JBComponentStream* stream);
/**
* 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, JBComponentStream* 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, JBComponentStream* stream, XMLElement* element,
const String& senderID);
private:
JBEvent() {} // Don't use it!
bool init(JBComponentStream* stream, XMLElement* element);
Type m_type; // Type of this event
JBComponentStream* 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
String m_from; // Stanza's 'from' attribute
String m_to; // Stanza's 'to' attribute
String m_id; // Sender's id for Write... events
// 'id' attribute if the received stanza has one
};
/**
* This class holds a Jabber Component stream (implements the Jabber Component Protocol).
* @short A Jabber Component stream.
*/
class YJINGLE_API JBComponentStream : public RefObject, public Mutex
{
public:
friend class JBEngine;
public:
/**
* Stream state enumeration.
*/
enum State {
WaitToConnect, // Outgoing stream is waiting for the socket to connect
Started, // Stream start sent
Auth, // Authentication (handshake) sent
Running, // Authenticated. Allow any XML element to pass over the stream
Terminated, // Stream is terminated. Wait to be restarted or destroyed
Destroy, // Stream is destroying. No more traffic allowed
};
/**
* Values returned by send() methods.
*/
enum Error {
ErrorNone = 0, // No error
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.
};
/**
* Destructor.
* Close the stream and the socket.
*/
virtual ~JBComponentStream();
/**
* Get the stream state.
* @return The stream state as enumeration.
*/
inline State state() const
{ return m_state; }
/**
* Get the local name.
* @return The local name.
*/
inline const String& localName() const
{ return m_localName; }
/**
* Get the remote server name.
* @return The remote server name.
*/
inline const String& remoteName() const
{ return m_remoteName; }
/**
* Get the local name.
* @return The local name.
*/
inline const SocketAddr& remoteAddr() const
{ return m_remoteAddr; }
/**
* Get the stream id.
* @return The stream id.
*/
inline const String& id() const
{ return m_id; }
/**
* Get the stream's connection.
* @return The stream connection's pointer.
*/
inline JBEngine* engine() const
{ return m_engine; }
/**
* Connect the stream to the server.
*/
void connect();
/**
* Cleanup the stream. Terminate/Destroy it. Raise the appropriate event.
* This method is thread safe.
* @param destroy True to destroy. False to terminate.
* @param sendEnd True to send stream termination tag.
* @param error Optional XML element to send before termination tag.
* @param sendError True to send the error element.
*/
void terminate(bool destroy, bool sendEnd = false,
XMLElement* error = 0, bool sendError = false);
/**
* Read data from socket and pass it to the parser.
* Raise event on bad XML.
* 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.
*/
Error sendStanza(XMLElement* stanza, const char* senderId = 0);
/**
* Extract an element from parser and construct an event.
* This method is thread safe.
* @param time Current time.
* @return XMPPEvent pointer or 0.
*/
JBEvent* getEvent(u_int64_t time);
/**
* Cancel pending outgoing elements.
* @param raise True to raise WriteFail events.
* @param id Optional 'id' to cancel. 0 to cancel all elements without id.
*/
void cancelPending(bool raise, const String* id = 0);
/**
* Event termination notification.
* @param event The notifier. Must be m_lastEvent.
*/
void eventTerminated(const JBEvent* event);
protected:
/**
* Constructor.
* Constructs an outgoing stream.
* @param engine The engine that owns this stream.
* @param remoteName The remote domain name.
* @param remoteAddr The remote address to connect.
*/
JBComponentStream(JBEngine* engine, const String& remoteName,
const SocketAddr& remoteAddr);
/**
* Send data without passing it through the outgoing queue.
* Change state if operation succeeds. Terminate stream if it fails.
* This method is thread safe.
* @param element The XML element to send.
* @param newState The new state if the operation succeeds.
* @param before Optional XML element to send before element.
* @return False if the write operation fails.
*/
bool sendStreamXML(XMLElement* element, State newState,
XMLElement* before = 0);
/**
* Send a 'stream:error' element. Terminate the stream.
* @param error The XMPP defined condition.
* @param text Optional text to add to the error.
*/
inline void sendStreamError(XMPPError::Type error, const char* text = 0)
{ terminate(false,true,XMPPUtils::createStreamError(error,text)); }
/**
* Send an 'iq' of type 'error'.
* @param stanza Element that generated the error.
* @param eType The error type.
* @param eCond The error condition.
* @param eText Optional text to add to the error stanza.
* @return The result of posting the stanza.
*/
Error sendIqError(XMLElement* stanza, XMPPError::ErrorType eType,
XMPPError::Type eCond, const char* eText = 0);
/**
* Cleanup stream.
* Remove the first element from the outgoing list if partially sent.
* Send stream termination tag if requested.
* Cancel all outgoing elements without id. Destroy the socket.
* This method is thread safe.
* @param endStream True to send stream termination tag.
* @param element Optional element to send before.
*/
void cleanup(bool endStream, XMLElement* element = 0);
/**
* Post an XMLElement in the outgoing queue. Send the first element in the queue.
* This method is thread safe.
* @param element The XMLElementOut to post.
* @return ErrorNone, ErrorContext, ErrorPending, ErrorNoSocket.
*/
Error postXML(XMLElementOut* element);
/**
* Send the first element from the outgoing queue if state is Running.
* Raise WriteFail event if operation fails. Adjust buffer on partial writes.
* @return ErrorNone, ErrorContext, ErrorPending, ErrorNoSocket.
*/
Error sendXML();
/**
* Process the received XML elements. Validate them. Add events.
* @return True if generated any event(s).
*/
bool processIncomingXML();
/**
* Process a received element in state Started.
* @param e The element to process.
* @return True if generated any event(s).
*/
bool processStateStarted(XMLElement* e);
/**
* Process a received element in state Auth.
* @param e The element to process.
* @return True if generated any event(s).
*/
bool processStateAuth(XMLElement* e);
/**
* Process a received element in state Running.
* @param e The element to process.
* @return True if generated any event(s).
*/
bool processStateRunning(XMLElement* e);
/**
* Process a received 'iq' element.
* @param e The element to process.
* @return True if generated any event(s).
*/
bool processIncomingIq(XMLElement* e);
/**
* Add an event to the list.
* @param type Event type.
* @param element Optional XML element.
* @param child Optional child element.
* @return The added event.
*/
JBEvent* addEvent(JBEvent::Type type, XMLElement* element = 0,
XMLElement* child = 0);
/**
* Add a notification event to the list if the element has an id.
* Remove the element from the outgoing queue.
* @param type Event type.
* @param element XMLElementOut element that generated the event.
* @return True if the event was added.
*/
bool addEventNotify(JBEvent::Type type, XMLElementOut* element);
/**
* Actions to take when an invalid element is received.
* Delete element. Send a stream error to remote peer. Raise a Terminated event.
* @param e The invalid element.
* @param type Stream error type.
* @param text Optional text to add to the stream error.
* @return True.
*/
bool invalidElement(XMLElement* e, XMPPError::Type type,
const char* text = 0);
/**
* Actions to take when an unexpected element is received.
* Delete element.
* @param e The unexpected element.
* @return False.
*/
bool unexpectedElement(XMLElement* e);
/**
* Check if a given element is a termination one (stream error or stream end).
* Raise a Terminated event if so.
* If true is returned the element is still valid.
* @param e The element to check.
* @return True if a Terminated event was raised.
*/
bool isStreamEnd(XMLElement* e);
/**
* Read data from socket and pass it to the parser. Terminate on error.
* @param data The destination buffer.
* @param len The maximum number of bytes to read. Bytes read on exit.
* @return True if data was received.
*/
bool readSocket(char* data, u_int32_t& len);
/**
* Write data to socket. Terminate on error.
* @param data The source buffer.
* @param len The buffer length on input. Bytes written on output.
* @return False on socket error. State is Terminated on unrecoverable socket error.
*/
bool writeSocket(const char* data, u_int32_t& len);
private:
JBComponentStream() {} // Don't use it!
// State
State m_state; // Stream state
// Info
String m_id; // Stream id
String m_localName; // Local name received from the remote peer
String m_remoteName; // Remote peer's domain name
SocketAddr m_remoteAddr; // Socket's address
String m_password; // Password used for authentication
// Data
JBEngine* m_engine; // The owner of this stream
Socket* m_socket; // The socket used by this stream
XMLParser m_parser; // XML parser
Mutex m_receiveMutex; // Ensure serialization of received data
ObjList m_outXML; // Outgoing XML elements
ObjList m_events; // Event queue
JBEvent* m_lastEvent; // Last generated event
JBEvent* m_terminateEvent; // Destroy/Terminate event
};
/**
* This class holds info about a component server used by the Jabber engine.
* @short Server info.
*/
class YJINGLE_API JBServerInfo : public RefObject
{
public:
inline JBServerInfo(const char* name, const char* address, int port,
const char* password, const char* identity, const char* fullidentity,
bool roster, bool autoRestart, u_int32_t restartCount)
: m_name(name), m_address(address), m_port(port), m_password(password),
m_identity(identity), m_fullIdentity(fullidentity), m_roster(roster),
m_autoRestart(autoRestart), m_restartCount(restartCount)
{}
virtual ~JBServerInfo() {}
inline const String& address() const
{ return m_address; }
inline const String& name() const
{ return m_name; }
inline const int port() const
{ return m_port; }
inline const String& password() const
{ return m_password; }
inline const String& identity() const
{ return m_identity; }
inline const String& fullIdentity() const
{ return m_fullIdentity; }
inline bool roster() const
{ return m_roster; }
inline bool autoRestart() const
{ return m_autoRestart; }
inline u_int32_t restartCount() const
{ return m_restartCount; }
inline void incRestart()
{ m_restartCount++; }
inline bool getRestart() {
if (!restartCount())
return false;
m_restartCount--;
return true;
}
private:
String m_name; // Domain name
String m_address; // IP address
int m_port; // Port
String m_password; // Authentication data
String m_identity; // Identity. Used for Jabber Component protocol
String m_fullIdentity; // Full identity for this server
bool m_roster; // Keep roster for this server
bool m_autoRestart; // Automatically restart stream
u_int32_t m_restartCount; // Restart counter
};
/**
* This class holds a Jabber engine.
* @short A Jabber engine.
*/
class YJINGLE_API JBEngine : public DebugEnabler, public Mutex, public RefObject
{
friend class JBEvent;
friend class JBComponentStream;
friend class JBClient;
friend class JBPresence;
public:
/**
* Jabber protocol type.
*/
enum Protocol {
Component, // Use Jabber Component protocol
};
/**
* Constructor.
* Constructs a Jabber engine.
*/
JBEngine();
/**
* Destructor.
*/
virtual ~JBEngine();
/**
* Get the Jabber protocol this engine is using.
* @return The Jabber protocol as enumeration.
*/
inline Protocol jabberProtocol() const
{ return Component; }
/**
* Initialize the engine's parameters.
* Parameters:
* stream_restartupdateinterval : int Interval to update (increase) the stream restart counter. Defaults to 15000.
* stream_restartcount : int Max stream restart counter. Defaults to 4.
* xmlparser_maxbuffer : int The maximum allowed xml buffer length. Defaults to 8192.
* @param params Engine's parameters.
*/
void initialize(const NamedList& params);
/**
* Terminate all stream.
*/
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.
* @param domain Domain name of the server.
*/
void setComponentServer(const char* domain);
/**
* Get the default component server.
* @return The default component server.
*/
const String& componentServer()
{ 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 default stream restart count.
* @return The default stream restart count.
*/
inline u_int32_t restartCount() const
{ return m_restartCount; }
/**
* Get the default resource name.
* @return The default resource name.
*/
inline const String& defaultResource() const
{ return m_defaultResource; }
/**
* Check if a sender or receiver of XML elements should print them to output.
* @return True to print XML element to output.
*/
inline bool printXml() const
{ return m_printXml; }
/**
* Set/reset print XML elements to output permission.
* @param print True to allow XML elements printing to output.
*/
inline void printXml(bool print)
{ m_printXml = print; }
/**
* Check if a stream to the given server exists.
* If the stream doesn't exists creates it.
* This method is thread safe.
* @param domain The domain name to check. 0 to use the default server.
* @param create True to create a stream to the specified domain if none exists.
* @return Pointer to a JBComponentStream or 0.
*/
JBComponentStream* getStream(const char* domain = 0, bool create = true);
/**
* Keep calling receive() for each stream until no data is received.
* @return True if data was received.
*/
bool receive();
/**
* Keep calling receive().
*/
void runReceive();
/**
* Get events from the streams owned by this engine.
* This method is thread safe.
* @param time Current time.
* @return JBEvent pointer or 0.
*/
JBEvent* getEvent(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 remoteIdExists(const JBComponentStream* stream);
/**
* Create an SHA1 value from 'id' + 'password'.
* @param sha Destination string.
* @param id First element (stream id).
* @param password The second element (stream password).
*/
void createSHA1(String& sha, const String& id, const String& password);
/**
* Check if a received value is correct.
* @param sha Destination string.
* @param id First element (stream id).
* @param password The second element (stream password).
* @return True if equal.
*/
bool checkSHA1(const String& sha, const String& id, const String& password);
/**
* Called to update time dependent values
* @param time Current time.
*/
virtual void timerTick(u_int64_t time);
/**
* Call the connect method of the given stream.
* @param stream The stream to connect.
* @return False if stream is 0.
*/
virtual bool connect(JBComponentStream* stream);
/**
* Return a non processed event to this engine.
* @param event The returned event.
*/
virtual void returnEvent(JBEvent* event);
/**
* Accept an outgoing stream. If accepted, deliver a password.
* @param remoteAddr The remote address.
* @param password Password to use.
* @return True if accepted. False to terminate the stream.
*/
virtual bool acceptOutgoing(const String& remoteAddr, String& password);
/**
* Deliver a port for an outgoing connection.
* @param remoteAddr The remote address.
* @return True if accepted. False to terminate the stream.
*/
virtual int getPort(const String& remoteAddr);
/**
* Check if the process is terminating.
* @return True if the process is terminating.
*/
virtual bool exiting()
{ return false; }
/**
* Append a servr info element to the list.
* @param server The object to add.
* @param open True to open the stream.
*/
void appendServer(JBServerInfo* server, bool open);
/**
* Find server info object.
* @param token The search string. If 0 the default server will be used.
* @param domain True to find by domain name. False to find by address.
* @return Referenced JBServerInfo pointer or 0.
*/
JBServerInfo* getServer(const char* token = 0, bool domain = true);
/**
* Get the identity of the given server for a component server.
* @param destination The destination.
* @param token The search string. If 0 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, const char* token = 0,
bool domain = true);
/**
* Get the full identity of the given server.
* @param destination The destination.
* @param token The search string. If 0 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 getFullServerIdentity(String& destination, const char* token = 0,
bool domain = true);
/**
* Get the name of the alternate domain - if any
* @return Alternate domain name, empty string if not set
*/
inline const String& getAlternateDomain() const
{ return m_alternateDomain; }
/**
* Check if a stream to a remote server can be restarted.
* If true is returned, the stream restart counter has been decreased.
* @param token The remote server name or address.
* @param domain True to find by domain name. False to find by address.
* @return False if server doesn't exists.
*/
bool getStreamRestart(const char* token, bool domain = true);
protected:
/**
* Process a DiscoInfo event.
* @param event The received event.
* @return True if processed.
*/
bool processDiscoInfo(JBEvent* event);
/**
* Process a Command event.
* @param event The received event.
* @return True if processed.
*/
bool processCommand(JBEvent* event);
/**
* Check if a stream with a given remote address exists.
* @param remoteName The remote address to find.
* @return Stream pointer or 0.
*/
JBComponentStream* findStream(const String& remoteName);
/**
* Remove a stream from the list.
* @param stream The stream to remove.
* @param del Delete flag. If true the stream will be deleted.
*/
void removeStream(JBComponentStream* stream, bool del);
/**
* Add a client to this engine if not in it.
* @param client The client to add.
*/
void addClient(JBClient* client);
/**
* Remove a client to this engine if not in it.
* @param client The client to remove.
*/
void removeClient(JBClient* client);
/**
* Clear the server list.
*/
inline void clearServerList()
{ Lock lock(m_serverMutex); m_server.clear(); }
/**
* Process a message event.
* @param event The event to process. Always a valid message event.
* @return True if the event was processed (kept). False to destroy it.
*/
virtual bool processMessage(JBEvent* event);
private:
void processEventNew(JBEvent* event);
void processEventAuth(JBEvent* event);
bool getServerPassword(String& destination, const char* token = 0,
bool domain = true);
bool getServerPort(int& destination, const char* token = 0,
bool domain = true);
void setPresenceServer(JBPresence* presence);
void unsetPresenceServer(JBPresence* presence);
ObjList m_streams; // Streams belonging to this engine
Mutex m_clientsMutex; // Lock clients list
ObjList m_clients; // XMPP clients list
JBPresence* m_presence; // The presence server
JIDIdentity* m_identity; // Engine's identity
JIDFeatureList m_features; // Engine's features
u_int64_t m_restartUpdateTime; // Time to update the restart counter of all streams
u_int32_t m_restartUpdateInterval; // Update interval for restart counter of all streams
u_int32_t m_restartCount; // The default restart counter value
bool m_printXml; // Print XML data to output
// ID generation data
u_int64_t m_streamID; // Stream id counter
// Server list
String m_componentDomain; // Default server domain name
String m_componentAddr; // Default server address
ObjList m_server; // Server list
Mutex m_serverMutex; // Lock server list
String m_alternateDomain; // Alternate acceptable domain
// Misc
String m_defaultResource; // Default name for missing resources
};
/**
* This class is the base class for a Jabber client who wants
* to deliver protocol specific data to the engine.
* @short An Jabber client.
*/
class YJINGLE_API JBClient : public RefObject
{
public:
/**
* Constructor.
* @param engine The Jabber engine.
*/
JBClient(JBEngine* engine);
/**
* Destructor.
*/
virtual ~JBClient();
/**
* Get the Jabber engine.
* @return The Jabber engine.
*/
JBEngine* engine()
{ return m_engine; }
protected:
JBEngine* m_engine; // The Jabber Component engine
private:
inline JBClient() {} // Don't use it !
};
/**
* This class is the presence server for Jabber.
* @short A Jabber presence server.
*/
class YJINGLE_API JBPresence : public DebugEnabler, public JBClient, public Mutex
{
friend class XMPPUserRoster;
public:
/**
* Presence enumeration.
*/
enum Presence {
Error, // error
Probe, // probe
Subscribe, // subscribe request
Subscribed, // subscribe accepted
Unavailable, // unavailable
Unsubscribe, // unsubscribe request
Unsubscribed, // unsubscribe accepted
None,
};
/**
* Constructor.
* Constructs an Jabber Component presence server.
* @param engine The Jabber Component engine.
* @param params Engine's parameters.
*/
JBPresence(JBEngine* engine, const NamedList& params);
/**
* Destructor.
*/
virtual ~JBPresence();
/**
* Get the auto subscribe parameter.
* @return The auto subscribe parameter.
*/
inline int 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; }
/**
* Check if this server should add new users when receiving subscribe stanzas.
* @return True if should add a new user.
*/
inline bool addOnSubscribe() const
{ return m_addOnSubscribe; }
/**
* Check if this server should add new users when receiving presence probes.
* @return True if should add a new user.
*/
inline bool addOnProbe() const
{ return m_addOnProbe; }
/**
* Check if this server should add new users when receiving presence.
* @return True if should add a new user
*/
inline bool addOnPresence() const
{ return m_addOnPresence; }
/**
* 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 engine.
* @param params Engine's parameters.
*/
void initialize(const NamedList& params);
/**
* Add an event to the list.
* This method is thread safe.
* @param event The event to add.
* @return False if the event is not a Presence one.
*/
bool receive(JBEvent* event);
/**
* Process an event from the receiving list.
* This method is thread safe.
* @return False if the list is empty.
*/
bool process();
/**
* Keep calling process().
*/
void runProcess();
/**
* Process disco info elements.
* @param event The event with the element.
* @param local The local (destination) user.
* @param remote The remote (source) user.
*/
virtual void processDisco(JBEvent* event, const JabberID& local,
const JabberID& remote);
/**
* Process a presence error element.
* @param event The event with the element.
* @param local The local (destination) user.
* @param remote The remote (source) user.
*/
virtual void processError(JBEvent* event, const JabberID& local,
const JabberID& remote);
/**
* Process a presence probe element.
* @param event The event with the element.
* @param local The local (destination) user.
* @param remote The remote (source) user.
*/
virtual void processProbe(JBEvent* event, const JabberID& local,
const JabberID& remote);
/**
* Process a presence subscribe element.
* @param event The event with the element.
* @param presence Presence type: Subscribe,Subscribed,Unsubscribe,Unsubscribed.
* @param local The local (destination) user.
* @param remote The remote (source) user.
*/
virtual void processSubscribe(JBEvent* event, Presence presence,
const JabberID& local, const JabberID& remote);
/**
* Process a presence unavailable element.
* @param event The event with the element.
* @param local The local (destination) user.
* @param remote The remote (source) user.
*/
virtual void processUnavailable(JBEvent* event, const JabberID& local,
const JabberID& remote);
/**
* Process a presence element.
* @param event The event with the element.
* @param local The local (destination) user.
* @param remote The remote (source) user.
*/
virtual void processPresence(JBEvent* event, const JabberID& local,
const JabberID& remote);
/**
* Notify on probe request with users we don't know about.
* @param event The event with the element.
* @param local The local (destination) user.
* @param remote The remote (source) user.
* @return False to send item-not-found error.
*/
virtual bool notifyProbe(JBEvent* event, const JabberID& local,
const JabberID& remote);
/**
* Notify on subscribe event with users we don't know about.
* @param event The event with the element.
* @param local The local (destination) user.
* @param remote The remote (source) user.
* @param presence Presence type: Subscribe,Subscribed,Unsubscribe,Unsubscribed.
* @return False to send item-not-found error.
*/
virtual bool notifySubscribe(JBEvent* event,
const JabberID& local, const JabberID& remote, 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 local The local (destination) user.
* @param remote The remote (source) user.
* @param available The availability of the remote user.
* @return False to send item-not-found error.
*/
virtual bool notifyPresence(JBEvent* event, const JabberID& local,
const JabberID& remote, 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);
/**
* Try to get a stream from Jabber engine 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(JBComponentStream*& stream, bool& release);
/**
* 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, JBComponentStream* stream);
/**
* Send an error. Error type is 'modify'.
* If id is 0 sent element will be of type 'presence'. Otherwise: 'iq'.
* @param type The error.
* @param from The from attribute.
* @param to The to attribute.
* @param element The element that generated the error.
* @param stream Optional stream to use.
* @param id Optional id. If present (even if empty) the error element will be of type 'iq'.
* @return The result of send operation.
*/
bool sendError(XMPPError::Type type, const String& from, const String& to,
XMLElement* element, JBComponentStream* stream = 0, const String* id = 0);
/**
* Check timeout.
* This method is thread safe.
* @param time Current time.
*/
void checkTimeout(u_int64_t time);
/**
* Keep calling checkTimeout().
*/
void runCheckTimeout();
/**
* 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:
/**
* Check if the given jid has a valid domain. Send error if not.
* @param event The event with element.
* @param jid The destination jid.
* @return True if jid has a valid domain.
*/
bool checkDestination(JBEvent* event, const JabberID& jid);
static TokenDict s_presence[]; // Keep the types of 'presence'
int m_autoSubscribe; // Auto subscribe state
bool m_delUnavailable; // Delete unavailable user or resource
bool m_addOnSubscribe; // Add new user on subscribe request
bool m_addOnProbe; // Add new user on probe request
bool 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_events; // Incoming events from Jabber engine
ObjList m_rosters; // The rosters
private:
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.
*/
inline JIDResource(const char* name, Presence presence = Unknown,
u_int32_t capability = CapChat)
: m_name(name), m_presence(presence),
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; }
/**
* 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; }
/**
* 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); }
/**
* 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.
*/
void addTo(XMLElement* element);
/**
* 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
u_int32_t m_capability; // Resource capabilities
Show m_show; // Show attribute
String m_status; // Status attribute
};
/**
* This class holds a resource list.
* @short A resource list.
*/
class YJINGLE_API JIDResourceList : public Mutex
{
friend class XMPPUser;
public:
/**
* Constructor.
*/
inline JIDResourceList()
: Mutex(true)
{}
/**
* 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.
*/
bool add(const String& name);
/**
* Add a resource to the list if a resource with the same doesn't already
* exists. 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)
{ m_resources.remove(resource,del); }
/**
* Clear the list.
*/
inline void clear()
{ 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() {
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;
public:
enum Subscription {
None = 0,
To = 1,
From = 2,
Both = 3,
};
/**
* 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,
Subscription 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.
*/
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; }
/**
* 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.
* @return False if the the resource already exists in the list.
*/
bool addLocalRes(JIDResource* resource);
/**
* 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();
/**
* 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);
}
/**
* Check if the local user is subscribed to the remote one.
* @return True if the local user is subscribed to the remote one.
*/
inline bool subscribedTo() const
{ return (m_subscription & To); }
/**
* Check if the remote user is subscribed to the local one.
* @return True if the remote user is subscribed to the local one.
*/
inline bool subscribedFrom() const
{ return (m_subscription & From); }
/**
* 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.
* @param from The sender's jid.
* @return False if remote user has no more resources available.
*/
bool processPresence(JBEvent* event, bool available, const JabberID& from);
/**
* 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(JBComponentStream* 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, JBComponentStream* 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(JBComponentStream* 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, JBComponentStream* 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,
JBComponentStream* 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, JBComponentStream* stream = 0, bool force = false);
/**
* Get the string associated with a subscription enumeration value.
* @param value The subscription enumeration to get string for.
* @return Pointer to the string associated with the given subscription enumeration or 0 if none.
*/
static inline const char* subscribeText(int value)
{ return lookup(value,s_subscription); }
/**
* Get the subscription enumeration value associated with the given string.
* @param value The subscription string.
* @return the subscription as enumeration.
*/
static inline int subscribeType(const char* value)
{ return lookup(value,s_subscription,None); }
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, JBComponentStream* 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());
/**
* Keep the association between subscription enumeration and strings.
*/
static TokenDict s_subscription[];
private:
XMPPUserRoster* m_local; // Local user
JabberID m_jid; // User's JID
u_int8_t 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 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 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);
protected:
/**
* Constructor.
* @param engine Pointer to the presence engine this user belongs to.
* @param node User's name.
* @param domain User's domain.
*/
XMPPUserRoster(JBPresence* engine, const char* node, const char* domain);
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
ObjList m_remote; // Remote users
JBPresence* m_engine; // Presence engine
};
};
#endif /* __YATEJABBER_H */
/* vi: set ts=8 sw=4 sts=4 noet: */