/** * 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 #include /** * 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(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: */