Source: libs/yjingle/yatejabber.h


Annotated List
Files
Globals
Hierarchy
Index
/**
 * 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;                           // A Jabber event
class JBStream;                          // A Jabber stream
class JBComponentStream;                 // A Jabber Component stream
class JBClientStream;                    // A Jabber client to server stream
class JBThread;                          // Base class for private threads
class JBThreadList;                      // A list of threads
class JBEngine;                          // A Jabber engine
class JBService;                         // A Jabber service
class JBPresence;                        // A Jabber presence service
class JIDResource;                       // A JID resource
class JIDResourceList;                   // Resource list
class XMPPUser;                          // An XMPP user (JID, resources, capabilities etc)
class XMPPUserRoster;                    // An XMPP user's roster

/**
 * This class holds a Jabber stream event. Stream events are raised by streams
 *  and sent by the engine to the proper service
 * @short A Jabber stream event
 */
class YJINGLE_API JBEvent : public RefObject
{
    friend class JBStream;
    friend class JBClientStream;
public:
    /**
     * Event type enumeration
     */
    enum Type {
	// Stream events
	Terminated              = 1,     // Stream terminated. Try to connect
	Destroy                 = 2,     // Stream is destroying
	Running                 = 3,     // Stream is running (stable state: can send/recv stanzas)
	// Result events
	WriteFail               = 10,    // Write failed. m_element is the element, m_id is the id set by the sender
	// Stanza events: m_element is always valid
	Presence                = 20,    // m_element is a 'presence' stanza
	Message                 = 30,    // m_element is a 'message' stanza
	Iq                      = 50,    // m_element is an 'iq' set/get, m_child is it's first child
	IqError                 = 51,    // m_element is an 'iq' error, m_child is the 'iq' child if any
	IqResult                = 52,    // m_element is an 'iq' result, m_child is it's first child if any
	// Disco: m_child is a 'query' element qualified by DiscoInfo/DiscoItems namespaces
	IqDiscoInfoGet          = 60,
	IqDiscoInfoSet          = 61,
	IqDiscoInfoRes          = 62,
	IqDiscoInfoErr          = 63,
	IqDiscoItemsGet         = 64,
	IqDiscoItemsSet         = 65,
	IqDiscoItemsRes         = 66,
	IqDiscoItemsErr         = 67,
	// Command: m_child is a 'command' element qualified by Command namespace
	IqCommandGet            = 70,
	IqCommandSet            = 71,
	IqCommandRes            = 72,
	IqCommandErr            = 73,
	// Jingle: m_child is a 'jingle' element qualified by Jingle namespace
	IqJingleGet             = 80,
	IqJingleSet             = 81,
	IqJingleRes             = 82,
	IqJingleErr             = 83,
	// Roster: m_child is a 'query' element qualified by Roster namespace
	IqRosterSet             = 91,
	IqRosterRes             = 92,
	IqRosterErr             = 93,
	// Roster update (set or result) received by client streams: m_child is a 'query' element
	//  qualified by Roster namespace
	IqClientRosterUpdate    = 150,
	// Invalid
	Unhandled               = 200,   // m_element is an unhandled element
	Invalid                 = 500,   // m_element is 0
    };

    /**
     * Constructor. Constructs an event from a stream
     * @param type Type of this event
     * @param stream The stream that generated the event
     * @param element Element that generated the event
     * @param child Optional type depending element's child
     */
    JBEvent(Type type, JBStream* stream, XMLElement* element, XMLElement* child = 0);

    /**
     * Constructor. Constructs a WriteSuccess/WriteFail event from a stream
     * @param type Type of this event
     * @param stream The stream that generated the event
     * @param element Element that generated the event
     * @param senderID Sender's id
     */
    JBEvent(Type type, JBStream* stream, XMLElement* element, const String& senderID);

    /**
     * Destructor. Delete the XML element if valid
     */
    virtual ~JBEvent();

    /**
     * Get the event type
     * @return The type of this event as enumeration
     */
    inline int type() const
	{ return m_type; }

    /**
     * Get the event name
     * @return The name of this event
     */
    inline const char* name() const
	{ return lookup(type()); }

    /**
     * Get the element's 'type' attribute if any
     * @return The  element's 'type' attribute
     */
    inline const String& stanzaType() const
	{ return m_stanzaType; }

    /**
     * Get the 'from' attribute of a received stanza
     * @return The 'from' attribute
     */
    inline const JabberID& from() const
	{ return m_from; }

    /**
     * Get the 'to' attribute of a received stanza
     * @return The 'to' attribute
     */
    inline const JabberID& to() const
	{ return m_to; }

    /**
     * Get the sender's id for Write... events or the 'id' attribute if the
     *  event carries a received stanza
     * @return The event id
     */
    inline const String& id() const
	{ return m_id; }

    /**
     * The stanza's text or termination reason for Terminated/Destroy events
     * @return The event's text
     */
    inline const String& text() const
	{ return m_text; }

    /**
     * Get the stream that generated this event
     * @return The stream that generated this event
     */
    inline JBStream* stream() const
	{ return m_stream; }

    /**
     * Get the underlying XMLElement
     * @return XMLElement pointer or 0
     */
    inline XMLElement* element() const
	{ return m_element; }

    /**
     * Get the first child of the underlying element if any
     * @return XMLElement pointer or 0
     */
    inline XMLElement* child() const
	{ return m_child; }

    /**
     * Delete the underlying XMLElement(s). Release the ownership.
     * The caller is responsable of the returned pointer
     * @param del True to delete all xml elements owned by this event
     * @return XMLElement pointer if not deleted or 0
     */
    inline XMLElement* releaseXML(bool del = false) {
	    TelEngine::destruct(m_child);
	    if (del) {
		TelEngine::destruct(m_element);
		return 0;
	    }
	    XMLElement* tmp = m_element;
	    m_element = 0;
	    return tmp;
	}

    /**
     * Release the link with the stream to let the stream continue with events
     */
    void releaseStream();

    /**
     * Create an error response from this event if it contains a known type.
     * Don't create the error response if this event is carrying a response
     * @param type Error type
     * @param error The error condition
     * @param text Optional text to add to the error element
     * @return A valid XMLElement pointer
     */
    XMLElement* createError(XMPPError::ErrorType type, XMPPError::Type error, const char* text = 0);

    /**
     * Get the name of an event type
     * @return The name an event type
     */
    inline static const char* lookup(int type)
	{ return TelEngine::lookup(type,s_type); }

private:
    static TokenDict s_type[];           // Event names
    JBEvent() {}                         // Don't use it!
    bool init(JBStream* stream, XMLElement* element);

    Type m_type;                         // Type of this event
    JBStream* m_stream;                  // The stream that generated this event
    bool m_link;                         // Stream link state
    XMLElement* m_element;               // Received XML element, if any
    XMLElement* m_child;                 // The first child element for 'iq' elements
    String m_stanzaType;                 // Stanza's 'type' attribute
    JabberID m_from;                     // Stanza's 'from' attribute
    JabberID m_to;                       // Stanza's 'to' attribute
    String m_id;                         // Sender's id for Write... events
                                         // 'id' attribute if the received stanza has one
    String m_text;                       // The stanza's text or termination reason for
                                         //  Terminated/Destroy events
};

/**
 * A socket used used to transport data for a Jabber stream
 * @short A Jabber streams's socket
 */
class YJINGLE_API JBSocket
{
    friend class JBStream;
public:
    /**
     * Constructor. Build socket for an outgoing stream
     * @param engine The Jabber engine
     * @param stream The stream owning this socket
     * @param address The address used to connect to
     * @param port Port used to connect to remote server
     */
    JBSocket(JBEngine* engine, JBStream* stream,
	const char* address, int port);

    /**
     * Destructor. Close the socket
     */
    inline ~JBSocket()
	{ terminate(); }

    /**
     * Check if the socket is valid
     * @return True if the socket is valid.
     */
    inline bool valid() const
	{ return m_socket && m_socket->valid(); }

    /**
     * Get the remote peer's address
     * @return The remote peer's address
     */
    inline const SocketAddr& addr() const
	{ return m_address; }

    /**
     * Get last connect/send/receive error text
     * @return Last error text
     */
    inline const String& error() const
	{ return m_error; }

    /**
     * Connect the socket
     * @param terminated True if false is returned and the socket was terminated
     *  while connecting
     * @param newAddr Optional address to connect to
     * @param newPort Optional port to connect to
     * @return False on failure
     */
    bool connect(bool& terminated, const char* newAddr, int newPort = 0);

    /**
     * Terminate the socket
     * @param shutdown True to shut down, false to asynchronously terminate the socket
     */
    void terminate(bool shutdown = false);

    /**
     * Read data from socket
     * @param buffer Destination buffer
     * @param len The number of bytes to read. On exit contains the number of
     *  bytes actually read
     * @return False on socket error
     */
    bool recv(char* buffer, unsigned int& len);

    /**
     * Write data to socket
     * @param buffer Source buffer
     * @param len The number of bytes to send
     * @return False on socket error
     */
    bool send(const char* buffer, unsigned int& len);

private:
    JBEngine* m_engine;                  // The Jabber engine
    JBStream* m_stream;                  // Stream owning this socket
    Socket* m_socket;                    // The socket
    String m_remoteDomain;               // Remote domain used to resolve before connecting
    SocketAddr m_address;                // Remote address
    Mutex m_streamMutex;                 // Lock stream
    Mutex m_receiveMutex;                // Lock receive
    String m_error;                      // Keep error string from send/receive/connect
};

/**
 * Base class for all Jabber streams. Basic stream data processing: send/receive
 *  XML elements, keep stream state, generate events
 * @short A Jabber stream
 */
class YJINGLE_API JBStream : public RefObject
{
    friend class JBEngine;
    friend class JBEvent;
public:
    /**
     * Stream state enumeration.
     */
    enum State {
	Idle        = 0,                 // Stream is waiting to be connected or destroyed
	Connecting  = 1,                 // Stream is waiting for the socket to connect
	Started     = 2,                 // Stream start tag sent
	Securing    = 3,                 // Stream is currently negotiating the TLS
	Register    = 4,                 // A new user is currently registering
	Auth        = 5,                 // Stream is currently authenticating
	Running     = 6,                 // Established. Allow XML stanzas to pass over the stream
	Destroy     = 7,                 // Stream is destroying. No more traffic allowed
    };

    /**
     * Values returned by send() methods.
     */
    enum Error {
	ErrorNone = 0,                   // No error (stanza enqueued/sent)
	ErrorContext,                    // Invalid stream context (state) or parameters
	ErrorPending,                    // The operation is pending in the stream's queue
	ErrorNoSocket,                   // Unrecoverable socket error. The stream will be terminated
    };

    /**
     * Stream behaviour options
     */
    enum Flags {
	AutoRestart     = 0x0001,        // Auto restart stream when down
	AllowPlainAuth  = 0x0002,        // Allow plain password authentication
	                                 //  If not allowed and this is the only method
	                                 //  offered by server the stream will be terminated
	NoVersion1      = 0x0004,        // Don't support RFC 3920 TLS/SASL ...
	UseTls          = 0x0008,        // Use TLS if offered. Internally set if the remote server
	                                 //  always require encryption
	UseSasl         = 0x0010,        // Use SASL as authentication mechanism (RFC 3920)
	                                 //  If not set, the deprecated XEP-0078 will be used for authentication
	AllowUnsafeSetup    = 0x0020,    // Allow in-band user account setup on unsecured streams
	StreamSecured       = 0x0100,    // Stream already secured
	StreamAuthenticated = 0x0200,    // Stream already authenticated
	NoRemoteVersion1    = 0x0400,    // Remote doesn't support RFC 3920 TLS/SASL ...
    };

    /**
     * Destructor.
     * Gracefully close the stream and the socket
     */
    virtual ~JBStream();

    /**
     * Get the type of this stream. See the protocol enumeration of the engine
     * @return The type of this stream
     */
    inline int type() const
	{ return m_type; }

    /**
     * Get the stream state
     * @return The stream state as enumeration.
     */
    inline State state() const
	{ return m_state; }

    /**
     * Get the stream direction
     * @return True if the stream is an outgoing one
     */
    inline bool outgoing() const
	{ return m_outgoing; }

    /**
     * Get the stream's name
     * @return The stream's name
     */
    inline const String& name() const
	{ return m_name; }

    /**
     * Get the stream id
     * @return The stream id
     */
    inline const String& id() const
	{ return m_id; }

    /**
     * Get the stream's owner
     * @return Pointer to the engine owning this stream
     */
    inline JBEngine* engine() const
	{ return m_engine; }

    /**
     * Get the JID of the local side of this stream
     * @return The JID of the local side of this stream
     */
    inline const JabberID& local() const
	{ return m_local; }

    /**
     * Get the JID of the remote side of this stream
     * @return The JID of the remote side of this stream
     */
    inline const JabberID& remote() const
	{ return m_remote; }

    /**
     * Get the remote peer's address
     * @return The remote peer's address
     */
    inline const SocketAddr& addr() const
	{ return m_socket.addr(); }

    /**
     * Check if a given option (or option mask) is set
     * @param mask The flag(s) to check
     * @return True if set
     */
    inline bool flag(int mask) const
	{ return 0 != (m_flags & mask); }

    /**
     * Get the stream mutex
     * @return The stream mutex
     */
    inline Mutex* streamMutex()
	{ return &m_socket.m_streamMutex; }

    /**
     * Connect the stream. Send stream start tag on success
     * This method is thread safe
     */
    void connect();

    /**
     * Read data from socket and pass it to the parser. Terminate stream on
     *  socket or parser error.
     * This method is thread safe
     * @return True if data was received
     */
    bool receive();

    /**
     * Send a stanza.
     * This method is thread safe
     * @param stanza Element to send
     * @param senderId Optional sender's id. Used for notification events
     * @return The result of posting the stanza
     */
    virtual Error sendStanza(XMLElement* stanza, const char* senderId = 0);

    /**
     * Stream state and data processor. Increase restart counter.
     * Restart stream if idle and auto restart.
     * Extract an element from parser and construct an event.
     * This method is thread safe
     * @param time Current time
     * @return JBEvent pointer or 0
     */
    JBEvent* getEvent(u_int64_t time);

    /**
     * Terminate stream. Send stream end tag or error.
     * Remove pending stanzas without id. Deref stream if destroying.
     * This method is thread safe
     * @param destroy True to destroy. False to terminate
     * @param recvStanza Received stanza, if any
     * @param error Termination reason. Set it to NoError to send stream end tag
     * @param reason Optional text to be added to the error stanza
     * @param send True to send the stream end element
     * @param final True if called from destructor
     * @param sendError True to send the error element (ignored if error is NoError)
     */
    void terminate(bool destroy, XMLElement* recvStanza, XMPPError::Type error, const char* reason,
	bool send, bool final = false, bool sendError = true);

    /**
     * Remove pending stanzas with a given id.
     * This method is thread safe
     * @param id The id of stanzas to remove
     * @param notify True to raise an event for each removed stanza
     */
    inline void removePending(const String& id, bool notify = false) {
	    Lock lock(m_socket.m_streamMutex);
	    removePending(notify,&id,false);
	}

    /**
     * Get the string representation of this stream
     * @return The string representation of this stream
     */
    virtual const String& toString() const
	{ return name(); }

    /**
     * Get an object from this stream
     * @param name The name of the object to get
     */
    virtual void* getObject(const String& name) const;

    /**
     * Get the name of a stream state
     * @param state The requested state number
     * @return The name of the requested state
     */
    static const char* lookupState(int state);

    /**
     * Dictionary keeping the flag names
     */
    static TokenDict s_flagName[];

protected:
    /**
     * Internal wait states enumeration. Defines what kind of XML is expected
     */
    enum WaitState {                     // Main state	Wait
	WaitIdle,                        // Idle	nothing
	WaitStart,                       // Started	stream start response
	WaitFeatures,                    // Started	stream features
	WaitBindRsp,                     // Started	wait for bind response
	WaitSessionRsp,                  // Started	wait for session response
	WaitTlsRsp,                      // Started	wait response to starttls
	WaitChallenge,                   // Auth	iq auth query with auth data sent, wait response
	WaitResponse,                    // Auth	iq auth query sent, wait response
	WaitAborted,                     // Auth	abort sent, wait confirmation to terminate stream
    };

    /**
     * Constructor. Build an outgoing stream
     * @param engine The engine that owns this stream
     * @param type Stream type
     * @param info Structure containing data used to connect to remote server
     * @param localJid Local party's JID
     * @param remoteJid Remote party's JID
     */
    JBStream(JBEngine* engine, int type, XMPPServerInfo& info,
	const JabberID& localJid, const JabberID& remoteJid);

    /**
     * Default constructor
     */
    inline JBStream()
	: m_socket(0,0,0,0)
	{}

    /**
     * Close the stream. Release memory
     */
    virtual void destroyed();

    /**
     * Check the 'to' attribute of a received element
     * @param xml The received element
     * @param respond Action to be taken when if not accepted.
     *  True to respond with an error, false to just drop it
     * @return False to reject it. If the stream is not in Running state,
     *  it will be terminated
     */
    virtual bool checkDestination(XMLElement* xml, bool& respond);

    /**
     * Get the starting stream element to be sent after stream connected
     * @return XMLElement pointer
     */
    virtual XMLElement* getStreamStart();

    /**
     * Get the authentication element to be sent when authentication starts
     * @return XMLElement pointer or 0 on failure
     */
    virtual XMLElement* getAuthStart();

    /**
     * Process a received stanza in Running state
     * @param xml Valid XMLElement pointer
     */
    virtual void processRunning(XMLElement* xml);

    /**
     * Process a received element in Register state. Descendants MUST consume the data
     * @param xml Valid XMLElement pointer
     */
    virtual void processRegister(XMLElement* xml);

    /**
     * Process a received element in Auth state. Descendants MUST consume the data
     * @param xml Valid XMLElement pointer
     */
    virtual void processAuth(XMLElement* xml);

    /**
     * Process a received element in Securing state. Descendants MUST consume the data.
     * Drop the received element
     * @param xml Valid XMLElement pointer
     */
    virtual void processSecuring(XMLElement* xml);

    /**
     * Process a received element in Started state. Descendants MUST consume the data
     * @param xml Valid XMLElement pointer
     */
    virtual void processStarted(XMLElement* xml);

    /**
     * Notify descendants when stream state changed to Running
     */
    virtual void streamRunning()
	{}

    /**
     * Create an iq event from a received iq stanza
     * @param xml Received element
     * @param iqType The iq type
     * @param error Error type if 0 is returned
     * @return JBEvent pointer or 0
     */
    JBEvent* getIqEvent(XMLElement* xml, int iqType, XMPPError::Type& error);

    /**
     * Send declaration and stream start
     * @return True on success
     */
    bool sendStreamStart();

    /**
     * Send stream XML elements through the socket
     * @param e The element to send
     * @param newState The new stream state on success
     * @return False if send failed (stream termination was initiated)
     */
    bool sendStreamXML(XMLElement* e, State newState);

    /**
     * Terminate stream on receiving invalid elements
     * @param xml Received element
     * @param error Termination reason
     * @param reason Optional text to be added to the error stanza
     */
    void invalidStreamXML(XMLElement* xml, XMPPError::Type error, const char* reason);

    /**
     * Terminate stream on receiving stanza errors while not running
     * @param xml Received element
     */
    void errorStreamXML(XMLElement* xml);

    /**
     * Drop an unexpected or unhandled element
     * @param xml Received element
     * @param unexpected True if unexpected
     */
    void dropXML(XMLElement* xml, bool unexpected = true);

    /**
     * Change stream's state. Raise a Running event when apropriate
     * @param newState the new stream state
     */
    void changeState(State newState);

    /**
     * Clear the remote feature list. Parse the received element to fill it up.
     * Terminate the stream on error (such as invalid namespace).
     * If false is returned, don't re-use the received element
     * @param features Features element to parse
     * @return False if the stream is terminated
     */
    bool getStreamFeatures(XMLElement* features);

    /**
     * Start client TLS. Terminate the stream on error
     * @return True if TLS was initiated. False on failure: stream termination was initiated
     */
    bool startTls();

    /**
     * Start client registration
     * Terminate the stream on error
     * @return False if the stream is terminated
     */
    bool startRegister();

    /**
     * Start client authentication. Send first request to authenticate with the server.
     * Terminate the stream on error
     * @return False if the stream is terminated
     */
    bool startAuth();

    /**
     * Send authentication response. Terminate the stream on error
     * @param challenge Received challenge. If non 0 a SASL response is built and sent.
     *  If 0, a non-SASL response is sent (using handshaking for component and
     *  XEP-0078 for client streams)
     * @return False if the stream is terminated
     */
    bool sendAuthResponse(XMLElement* challenge = 0);

    /**
     * Build SASL authentication response (Plain or Digest MD5 SASL).
     * A valid mechanism must be previously set
     * @param response Destination string
     * @param realm Received realm or 0 to use local jid. If 0, nonce param is ignored
     * @param nonce Server nonce if available
     */
    void buildSaslResponse(String& response, String* realm = 0,
	String* nonce = 0);

    /**
     * Parse remote's features and pick an authentication mechanism
     *  to be used when requesting authentication
     */
    void setClientAuthMechanism();

    /**
     * Build a Digest MD5 SASL (RFC 2831) to be sent with authentication responses
     * @param dest Destination string
     * @param authenticate True if building a Digest MD5 challenge response, false if
     *  building a Digest MD5 to check a 'success' response
     */
    void buildDigestMD5Sasl(String& dest, bool authenticate = true);

    /**
     * Safely set receive count
     * @param value The new value of the receive count
     */
    void setRecvCount(int value);

    /**
     * Start the idle timer if there are no pending stanzas
     * @param time The current time in miliseconds
     * @return True if started
     */
    bool startIdleTimer(u_int64_t time = Time::msecNow());

    /**
     * Get last event from queue
     * @return JBEvent pointer or 0
     */
    inline JBEvent* lastEvent() {
	    ObjList* o = m_events.last();
	    return o ? static_cast(o->get()) : 0;
	}

    /**
     * Stream's name
     */
    String m_name;

    /**
     * The password used for authentication
     */
    String m_password;

    /**
     * Local party feature list
     */
    JIDFeatureList m_localFeatures;

    /**
     * Remote party feature list
     */
    JIDFeatureList m_remoteFeatures;

    /**
     * Stream flags
     */
    int m_flags;

    /**
     * The number of challenge/response exchanges allowed before ending the stream
     */
    unsigned int m_challengeCount;

    /**
     * Internal states
     */
    WaitState m_waitState;

    /**
     * Chosen authentication mechanism
     */
    JIDFeatureSasl::Mechanism m_authMech;

    /**
     * Events queue
     */
    ObjList m_events;

    /**
     * Register a new account
     */
    bool m_register;

private:
    // Event termination notification
    // @param event The notifier. Ignored if it's not m_lastEvent
    void eventTerminated(const JBEvent* event);
    // Try sending pending stream element if any
    // Try to send the first element in pending outgoing stanzas list
    // If ErrorNoSocket is returned, stream termination was initiated
    Error sendPending();
    // Remove pending elements with id if id is not 0
    // Remove all elements without id if id is 0
    // Set force to true to remove the first element even if partially sent
    void removePending(bool notify, const String* id, bool force);
    // Called when a setup state was completed
    // Set/reset some stream flags and data
    void resetStream();

    int m_type;                          // Stream type
    State m_state;                       // Stream state
    bool m_outgoing;                     // Stream direction
    unsigned int m_restart;              // Remaining restart attempts
    unsigned int m_restartMax;           // Max restart attempts
    u_int64_t m_timeToFillRestart;       // Next time to increase the restart counter
    u_int64_t m_setupTimeout;            // Stream setup timeout (interval allowed between Idle and Running states)
    u_int64_t m_idleTimeout;             // Connection idle in state Running (send keep alive packet)
    String m_id;                         // Stream id
    JabberID m_local;                    // Local peer's jid
    JabberID m_remote;                   // Remote peer's jid
    JBEngine* m_engine;                  // The owner of this stream
    JBSocket m_socket;                   // The socket used by this stream
    XMLParser m_parser;                  // XML parser
    ObjList m_outXML;                    // Outgoing XML elements
    JBEvent* m_lastEvent;                // Last generated event
    JBEvent* m_terminateEvent;           // Destroy/Terminate event
    JBEvent* m_startEvent;               // Running event
    int m_recvCount;                     // The number of bytes to read: -1: all, 0: nothing 1: 1 byte
    XMLElementOut* m_streamXML;          // Pending (incomplete) stream element
    unsigned int m_declarationSent;      // The number of declaration bytes sent
    // Auth data
    unsigned int m_nonceCount;           // Nonce count
    String m_nc;                         // Nonce count string
    String m_nonce;                      // Server nonce
    String m_cnonce;                     // Client nonce
    String m_realm;                      // Client realm
    // Register data
    unsigned int m_registerId;           // Id used when registering a new account
};

/**
 * This class holds a Jabber Component stream (implements the Jabber Component Protocol).
 * @short A Jabber Component stream
 */
class YJINGLE_API JBComponentStream : public JBStream
{
    friend class JBEngine;
public:
    /**
     * Destructor
     */
    virtual ~JBComponentStream()
	{}

    /**
     * Get an object from this stream
     * @param name The name of the object to get
     * @return Pointer to the object or 0 if not found
     */
    virtual void* getObject(const String& name) const;

protected:
    /**
     * Constructor. Build an outgoing stream
     * @param engine The engine that owns this stream
     * @param info Structure containing data used to connect to remote server
     * @param localJid Local party's JID
     * @param remoteJid Remote party's JID
     */
    JBComponentStream(JBEngine* engine, XMPPServerInfo& info,
	const JabberID& localJid, const JabberID& remoteJid);

    /**
     * Get the starting stream element to be sent after stream connected
     * @return XMLElement pointer
     */
    virtual XMLElement* getStreamStart();

    /**
     * Get the authentication element to be sent when authentication starts
     * @return XMLElement pointer
     */
    virtual XMLElement* getAuthStart();

    /**
     * Process a received element in Auth state
     * @param xml Valid XMLElement pointer
     */
    virtual void processAuth(XMLElement* xml);

    /**
     * Process a received element in Started state
     * @param xml Valid XMLElement pointer
     */
    virtual void processStarted(XMLElement* xml);

private:
    // Default constructor is private to avoid unwanted use
    JBComponentStream() {}
};

/**
 * This class holds a Jabber client stream used to connect an user to its server
 * @short A Jabber client to server stream
 */
class YJINGLE_API JBClientStream : public JBStream
{
    friend class JBEngine;
public:
    /**
     * Destructor
     */
    virtual ~JBClientStream();

    /**
     * Get the roster of this stream's client
     * @return Valid XMPPUserRoster
     */
    inline XMPPUserRoster* roster()
	{ return m_roster; }

    /**
     * Get the client's resource
     * @return The client's resource
     */
    inline JIDResource* getResource()
	{ return m_resource; }

    /**
     * Get an object from this stream
     * @param name The name of the object to get
     * @return Pointer to the object or 0 if not found
     */
    virtual void* getObject(const String& name) const;

    /**
     * Get a remote user from roster
     * @param jid The user's bare jid
     * @return Referenced XMPPUser object or 0 if not found
     */
    XMPPUser* getRemote(const JabberID& jid);

    /**
     * Send a stanza. This method is thread safe
     * @param stanza Element to send
     * @param senderId Optional sender's id. Used for notification events
     * @return The result of posting the stanza
     */
    virtual Error sendStanza(XMLElement* stanza, const char* senderId = 0);

protected:
    /**
     * Constructor. Build an outgoing stream
     * @param engine The engine that owns this stream
     * @param info Structure containing data used to connect to remote server
     * @param jid Client's full Jabber ID
     * @param params Other stream parameters
     */
    JBClientStream(JBEngine* engine, XMPPServerInfo& info, const JabberID& jid,
	const NamedList& params);

    /**
     * Constructor
     * @param engine The engine that owns this stream
     * @param jid User's JID
     * @param password Password used for authentication
     * @param address The remote address to connect to
     * @param autoRestart True to auto restart the stream
     * @param maxRestart The maximum restart attempts allowed
     * @param incRestartInterval The interval to increase the restart counter
     * @param allowPlainAuth Allow plain text password authentication
     * @param outgoing Stream direction
     */
    JBClientStream(JBEngine* engine, const JabberID& jid,
	const String& password, const SocketAddr& address,
	bool autoRestart, unsigned int maxRestart, u_int64_t incRestartInterval,
	bool allowPlainAuth = false, bool outgoing = true);

    /**
     * Notification from parent when steam is authenticated: get roster from server
     */
    virtual void streamRunning();

    /**
     * Process a received stanza in Running state
     * @param xml Valid XMLElement pointer
     */
    virtual void processRunning(XMLElement* xml);

    /**
     * Check the 'to' attribute of a received element against the local jid.
     * Accept empty or bare/full jid match. Set the 'to' attribute to local jid if empty
     * @param xml The received element
     * @param respond Action to be taken if not accepted. Always false on exit
     * @return False to reject it
     */
    virtual bool checkDestination(XMLElement* xml, bool& respond);

private:
    // Default constructor is private to avoid unwanted use
    JBClientStream() {}

    XMPPUserRoster* m_roster;            // Client's roster
    JIDResource* m_resource;             // Client's resource
    String m_rosterReqId;                // Roster request id
};


/**
 * This class holds encapsulates a private library thread
 * @short A Jabber thread that can be added to a list of threads
 */
class YJINGLE_API JBThread : public GenObject
{
public:
    /**
     * Thread type enumeration. Used to do a specific client processing
     */
    enum Type {
	StreamConnect,                   // Asynchronously connect a stream's socket
	EngineReceive,                   // Read all streams sockets
	EngineProcess,                   // Get events from sockets and send them to
	                                 //  registered services
	Presence,                        // Presence service processor
	Jingle,                          // Jingle service processor
	Message                          // Message service processor
    };

    /**
     * Destructor. Remove itself from the owner's list
     */
    virtual ~JBThread();

    /**
     * Get the type of this thread
     * @return Thread type as enumeration
     */
    inline Type type() const
	{ return m_type; }

    /**
     * Cancel (terminate) this thread
     * @param hard Kill the thread the hard way rather than just setting
     *  an exit check marker
     */
    virtual void cancelThread(bool hard = false) = 0;

    /**
     * Create and start a private thread
     * @param type Thread type
     * @param list The list owning this thread
     * @param client The client to process
     * @param sleep Time to sleep if there is nothing to do, zero to use platform default
     * @param prio Thread priority, defaults to Normal
     * @return False if failed to start the requested thread
     */
    static bool start(Type type, JBThreadList* list, void* client, int sleep = 0, int prio = Thread::Normal);

protected:
    /**
     * Constructor. Append itself to the owner's list
     * @param type Thread type
     * @param owner The list owning this thread
     * @param client The client to process
     * @param sleep Time to sleep if there is nothing to do
     */
    JBThread(Type type, JBThreadList* owner, void* client, int sleep = 2);

    /**
     * Process the client
     */
    void runClient();

    /**
     * Get the stream's client
     * @return The stream's client
     */
    inline void* client()
	{ return m_client; }

private:
    Type m_type;                         // Thread type
    JBThreadList* m_owner;               // List owning this thread
    void* m_client;                      // The client to process
    int m_sleep;                         // Time to sleep if there is nothing to do
};


/**
 * This class holds a list of private threads for an object that wants to terminate them on destroy
 * @short A list of private threads
 */
class YJINGLE_API JBThreadList
{
    friend class JBThread;
public:
    /**
     * Get the enabler owning this list
     * @return The owner of this list
     */
    inline DebugEnabler* owner() const
	{ return m_owner; }

    /**
     * Cancel all threads
     * This method is thread safe
     * @param wait True to wait for the threads to terminate
     * @param hard Kill the threads the hard way rather than just setting an exit check marker
     */
    void cancelThreads(bool wait = true, bool hard = false);

protected:
    /**
     * Constructor
     * @param owner The owner of this list
     */
    JBThreadList(DebugEnabler* owner = 0)
	: m_mutex(true,"JBThreadList"),
	  m_owner(owner), m_cancelling(false)
	{ m_threads.setDelete(false); }

    /**
     * Set the enabler owning this list
     * @param dbg The new owner of this list
     */
    inline void setOwner(DebugEnabler* dbg)
	{ m_owner = dbg; }

private:
    Mutex m_mutex;                       // Lock list operations
    DebugEnabler* m_owner;               // The owner of this list
    ObjList m_threads;                   // Private threads list
    bool m_cancelling;                   // Cancelling threads operation in progress
};


/**
 * This class holds a Jabber engine
 * @short A Jabber engine
 */
class YJINGLE_API JBEngine : public DebugEnabler, public Mutex,
	public GenObject, public JBThreadList
{
    friend class JBStream;
public:
    /**
     * Jabber protocol type
     */
    enum Protocol {
	Component = 1,                   // Use Jabber Component protocol
	Client    = 2,                   // Use client streams
    };

    /**
     * Service type enumeration
     */
    enum Service {
	ServiceJingle    = 0,            // Receive Jingle events
	ServiceIq        = 1,            // Receive generic Iq events
	ServiceMessage   = 2,            // Receive Message events
	ServicePresence  = 3,            // Receive Presence events
	ServiceCommand   = 4,            // Receive Command events
	ServiceDisco     = 5,            // Receive Disco events
	ServiceStream    = 6,            // Receive stream Terminated or Destroy events
	ServiceWriteFail = 7,            // Receive WriteFail events
	ServiceRoster    = 8,            // Receive roster events
	ServiceCount     = 9
    };

    /**
     * Constructor
     * @param proto The protocol used by the streams belonging to this engine
     */
    JBEngine(Protocol proto);

    /**
     * Destructor
     */
    virtual ~JBEngine();

    /**
     * Get the Jabber protocol this engine is using
     * @return The Jabber protocol as enumeration
     */
    inline Protocol protocol() const
	{ return m_protocol; }

    /**
     * Get the default component server
     * @return The default component server
     */
    inline const JabberID& componentServer() const
	{ return m_componentDomain; }

    /**
     * Set the alternate domain name
     * @param domain Name of an acceptable alternate domain
     */
    inline void setAlternateDomain(const char* domain = 0)
	{ m_alternateDomain = domain; }

    /**
     * Get the alternate domain name
     * @return the alternate domain name
     */
    inline const JabberID& getAlternateDomain() const
	{ return m_alternateDomain; }

    /**
     * Get the default resource name.
     * @return The default resource name.
     */
    inline const String& defaultResource() const
	{ return m_defaultResource; }

    /**
     * Get the stream list
     * @return The list of streams belonging to this engine
     */
    inline const ObjList& streams() const
	{ return m_streams; }

    /**
     * Cleanup streams. Stop all threads owned by this engine. Release memory
     */
    virtual void destruct();

    /**
     * Initialize the engine's parameters. Start private streams if requested
     * @param params Engine's parameters
     */
    virtual void initialize(const NamedList& params);

    /**
     * Terminate all streams
     */
    void cleanup();

    /**
     * Set the default component server to use. The domain must be in the server list.
     * Choose the first one from the server list if the given one doesn't exists.
     * Do nothing if the protocol is not Component
     * @param domain Domain name of the server
     */
    void setComponentServer(const char* domain);

    /**
     * Find a stream by its name. This method is thread safe
     * @param name The name of the stream to find
     * @return Referenced JBStream pointer or 0
     */
    JBStream* findStream(const String& name);

    /**
     * Get a stream. Create it not found and requested.
     * For the component protocol, the jid parameter may contain the domain to find,
     *  otherwise, the default component will be used.
     * This method won't create a client stream. Use @ref createClientStream().
     * This method is thread safe
     * @param jid Optional jid to use to find or create the stream
     * @param create True to create a stream if don't exist. Ignored if the engine's protocol is Client
     * @return Referenced JBStream pointer or 0
     */
    JBStream* getStream(const JabberID* jid = 0, bool create = true);

    /**
     * Try to get a stream if stream parameter is 0
     * @param stream Stream to check
     * @param release Set to true on exit if the caller must deref the stream
     * @return True if stream is valid
     */
    bool getStream(JBStream*& stream, bool& release);

    /**
     * Create a new client stream. This method is thread safe
     * @param params Stream parameters
     * @return Referenced JBClientStream pointer or 0
     */
    JBClientStream* createClientStream(NamedList& params);

    /**
     * Keep calling receive() for each stream until no data is received or the
     *  thread is terminated
     * @return True if data was received
     */
    bool receive();

    /**
     * Get events from the streams owned by this engine and send them to a service.
     * Delete them if not processed by a service
     * @param time Current time
     * @return True if an event was generated by any stream
     */
    bool process(u_int64_t time);

    /**
     * Check if an outgoing stream exists with the same id and remote peer
     * @param stream The calling stream
     * @return True if found
     */
    bool checkDupId(const JBStream* stream);

    /**
     * Check the 'from' attribute received by a Component stream at startup
     * @param stream The calling stream
     * @param from The from attribute to check
     * @return True if valid
     */
    bool checkComponentFrom(JBComponentStream* stream, const char* from);

    /**
     * Asynchronously call the connect method of the given stream if the stream is idle
     * @param stream The stream to connect
     */
    virtual void connect(JBStream* stream);

    /**
     * Check if this engine is exiting
     * @return True is terminating
     */
    virtual bool exiting() const
	{ return false; }

    /**
     * Setup the transport layer security for a stream
     * @param stream The stream requesting the operation
     * @return True if stream securing started, false on failure.
     */
    virtual bool encryptStream(JBStream* stream);

    /**
     * Append a server info element to the list
     * @param server The object to add
     * @param open True to open the stream, if in component mode
     */
    void appendServer(XMPPServerInfo* server, bool open);

    /**
     * Get the identity of the given server
     * @param destination The destination buffer
     * @param full True to get the full identity
     * @param token The search string. If 0 and the component protocol is used,
     *  the default server will be used
     * @param domain True to find by domain name. False to find by address
     * @return False if server doesn't exists
     */
    bool getServerIdentity(String& destination, bool full, const char* token = 0,
	bool domain = true);

    /**
     * Find server info object
     * @param token The search string. If 0 and the Component protocol is used,
     *  the default component server will be used
     * @param domain True to find by domain name. False to find by address
     * @return XMPPServerInfo pointer or 0 if not found
     */
    XMPPServerInfo* findServerInfo(const char* token, bool domain);

    /**
     * Attach a service to this engine.
     * This method is thread safe
     * @param service The service to attach
     * @param type Service type
     * @param prio Service priority. Set to -1 to use the givent service's priority.
     *  A lower values indicates a service with higher priority
     */
    void attachService(JBService* service, Service type, int prio = -1);

    /**
     * Remove a service from all event handlers of this engine.
     * This method is thread safe
     * @param service The service to detach
     */
    void detachService(JBService* service);

    /**
     * Print an XML element to output
     * @param xml Element to print
     * @param stream Stream requesting the operation
     * @param send True if sending, false if receiving
     */
    void printXml(const XMLElement& xml, const JBStream* stream, bool send) const;

    /**
     * Get the name of a protocol
     * @return The name of the requested protocol or the default value
     */
    inline static const char* lookupProto(int proto, const char* def = 0)
	{ return lookup(proto,s_protoName,def); }

    /**
     * Get the value associated with a protocol name
     * @return The value associated with a protocol name
     */
    inline static int lookupProto(const char* proto, int def = 0)
	{ return lookup(proto,s_protoName,def); }

private:
    // Process a Disco... events
    bool processDisco(JBEvent* event);
    // Process a Command events
    bool processCommand(JBEvent* event);
    // Pass events to services
    bool received(Service service, JBEvent* event);
    static TokenDict s_protoName[];      // Protocol names

    Protocol m_protocol;                 // The protocol to use
    u_int32_t m_restartUpdateInterval;   // Update interval for restart counter of all streams
    u_int32_t m_restartCount;            // The default restart counter value
    u_int64_t m_streamSetupInterval;     // Timeout for stream setup
    u_int64_t m_streamIdleInterval;      // Timeout for stream idle (nothing sent/received in Running state)
    int m_printXml;                      // Print XML data to output
    ObjList m_streams;                   // Streams belonging to this engine
    JIDIdentity* m_identity;             // Engine's identity
    JIDFeatureList m_features;           // Engine's features
    JabberID m_componentDomain;          // Default server domain name
    String m_componentAddr;              // Default server address
    int m_componentCheckFrom;            // The behaviour when checking the 'from' attribute for a component stream
                                         // 0: no check 1: local identity 2: remote identity
    JabberID m_alternateDomain;          // Alternate acceptable domain
    String m_defaultResource;            // Default name for missing resources
    Mutex m_serverMutex;                 // Lock server info list
    ObjList m_server;                    // Server info list
    Mutex m_servicesMutex;               // Lock service list
    ObjList m_services[ServiceCount];    // Services list
    bool m_initialized;                  // True if already initialized
};


/**
 * This class is the base class for a Jabber service who wants
 *  to get specific protocol data from the Jabber engine
 * @short A Jabber service
 */
class YJINGLE_API JBService : public DebugEnabler, public Mutex, public GenObject
{
public:
    /**
     * Constructor
     * @param engine The Jabber engine
     * @param name This service's name
     * @param params Service's parameters
     * @param prio The priority of this service
     */
    JBService(JBEngine* engine, const char* name, const NamedList* params, int prio);

    /**
     * Destructor. Remove from engine
     */
    virtual ~JBService();

    /**
     * Get the Jabber engine
     * @return The Jabber engine
     */
    inline JBEngine* engine()
	{ return m_engine; }

    /**
     * Get the Jabber engine
     * @return The Jabber engine
     */
    inline int priority() const
	{ return m_priority; }

    /**
     * Accept an event from the engine. If accepted, the event is enqueued
     *  and the stream that generated the event is notified on event
     *  terminated to allow it to process other data.
     * This method is thread safe
     * @param event The event to accept
     * @return False if not accepted, let the engine try another service
     */
    bool received(JBEvent* event);

    /**
     * Initialize the service
     * @param params Service's parameters
     */
    virtual void initialize(const NamedList& params)
	{}

    /**
     * Remove from engine. Release memory
     */
    virtual void destruct();

protected:
    /**
     * Accept an event from the engine
     * @param event The event to accept
     * @param processed Set to true on exit to signal that the event was
     *  already processed
     * @param insert Set to true if accepted to insert on top of the event queue
     * @return False if not accepted, let the engine try another service
     */
    virtual bool accept(JBEvent* event, bool& processed, bool& insert);

    /**
     * Get an event from queue
     * @return JBEvent pointer or 0 if queue is empty
     */
    JBEvent* deque();

    /**
     * True if already initialized
     */
    bool m_initialized;

private:
    inline JBService() {}                // Don't use it !
    JBEngine* m_engine;                  // The Jabber Component engine
    int m_priority;                      // Service priority
    ObjList m_events;                    // Events received from engine
};


/**
 * This class is a message receiver service for the Jabber engine
 * @short A Jabber message service
 */
class YJINGLE_API JBMessage : public JBService, public JBThreadList
{
public:
    /**
     * Message type enumeration
     */
    enum MsgType {
	Chat,                            // chat
	GroupChat,                       // groupchat
	HeadLine,                        // headline
	Normal,                          // normal
	Error,                           // error
	None,
    };

    /**
     * Constructor. Constructs a Jabber message service
     * @param engine The Jabber engine
     * @param params Service's parameters
     * @param prio The priority of this service
     */
    inline JBMessage(JBEngine* engine, const NamedList* params, int prio = 0)
	: JBService(engine,"jbmsgrecv",params,prio), m_syncProcess(true)
	{ JBThreadList::setOwner(this); }

    /**
     * Destructor. Cancel private thread(s)
     */
    virtual ~JBMessage()
	{ cancelThreads(); }

    /**
     * Initialize the service
     * @param params Service's parameters
     */
    virtual void initialize(const NamedList& params);

    /**
     * Get a message from queue
     * @return JBEvent pointer or 0 if no messages
     */
    inline JBEvent* getMessage()
	{ return deque(); }

    /**
     * Message processor. The derived classes must override this method
     *  to process received messages
     * @param event The event to process
     */
    virtual void processMessage(JBEvent* event);

    /**
     * Create a 'message' element
     * @param type Message type string
     * @param from The 'from' attribute
     * @param to The 'to' attribute
     * @param id The 'id' attribute
     * @param message The message body
     * @return A valid XMLElement pointer
     */
    static XMLElement* createMessage(const char* type, const char* from,
	const char* to, const char* id, const char* message);

    /**
     * Create a 'message' element
     * @param type Message type as enumeration
     * @param from The 'from' attribute
     * @param to The 'to' attribute
     * @param id The 'id' attribute
     * @param message The message body
     * @return A valid XMLElement pointer
     */
    static inline XMLElement* createMessage(MsgType type, const char* from,
	const char* to, const char* id, const char* message)
	{ return createMessage(lookup(type,s_msg,""),from,to,id,message); }

    /**
     * Get the type of a 'message' stanza
     * @param text The text to check
     * @return Message type as enumeration
     */
    static inline MsgType msgType(const char* text)
	{ return (MsgType)lookup(text,s_msg,None); }

    /**
     * Get the text from a message type
     * @param msg The message type
     * @return The associated text or 0
     */
    static inline const char* msgText(MsgType msg)
	{ return lookup(msg,s_msg,0); }

    /**
     * Keep the types of 'message' stanzas
     */
    static TokenDict s_msg[];

protected:
    /**
     * Accept an event from the engine and process it if configured to do that
     * @param event The event to accept
     * @param processed Set to true on exit to signal that the event was already processed
     * @param insert Set to true if accepted to insert on top of the event queue
     * @return False if not accepted, let the engine try another service
     */
    virtual bool accept(JBEvent* event, bool& processed, bool& insert);

private:
    bool m_syncProcess;                  // Process messages on accept
};


/**
 * This class is a presence service for Jabber engine. Handle presence stanzas and
 *  iq query info or items with destination containing a node and a valid domain
 * @short A Jabber presence service
 */
class YJINGLE_API JBPresence : public JBService, public JBThreadList
{
    friend class XMPPUserRoster;
public:
    /**
     * Presence type enumeration
     */
    enum Presence {
	Error         = 0,               // error
	Probe         = 1,               // probe
	Subscribe     = 2,               // subscribe request
	Subscribed    = 3,               // subscribe accepted
	Unavailable   = 4,               // unavailable
	Unsubscribe   = 5,               // unsubscribe request
	Unsubscribed  = 6,               // unsubscribe accepted
	None          = 7,
    };

    /**
     * Constructor. Constructs a Jabber Component presence service
     * @param engine The Jabber engine
     * @param params Service's parameters
     * @param prio The priority of this service
     */
    JBPresence(JBEngine* engine, const NamedList* params, int prio = 0);

    /**
     * Destructor
     */
    virtual ~JBPresence();

    /**
     * Get the auto subscribe parameter
     * @return The auto subscribe parameter
     */
    inline XMPPDirVal autoSubscribe() const
	{ return m_autoSubscribe; }

    /**
     * Check if the unavailable resources must be deleted
     * @return The delete unavailable parameter
     */
    inline bool delUnavailable() const
	{ return m_delUnavailable; }

    /**
     * Get the 'add on subscribe' flags
     * @return The 'add on subscribe' flags
     */
    inline XMPPDirVal addOnSubscribe() const
	{ return m_addOnSubscribe; }

    /**
     * Get the 'add on probe' flags
     * @return The 'add on probe' flags
     */
    inline XMPPDirVal addOnProbe() const
	{ return m_addOnProbe; }

    /**
     * Get the 'add on presence' flags
     * @return The 'add on presence' flags
     */
    inline XMPPDirVal addOnPresence() const
	{ return m_addOnPresence; }

    /**
     * Check if this service should add new users when receiving presence, probe or subscribe
     * @return True if should add a new user when receiving presence, probe or subscribe
     */
    inline bool autoRoster() const
	{ return m_autoRoster; }

    /**
     * Check if this service should ignore destination users not in roster
     * @return True if non existent destinations should be ignored
     */
    inline bool ignoreNonRoster() const
	{ return m_ignoreNonRoster; }

    /**
     * Get the probe interval. Time to send a probe if nothing was received from that user
     * @return The probe interval
     */
    inline u_int32_t probeInterval()
	{ return m_probeInterval; }

    /**
     * Get the expire after probe interval
     * @return The expire after probe interval
     */
    inline u_int32_t expireInterval()
	{ return m_expireInterval; }

    /**
     * Initialize the presence service
     * @param params Service's parameters
     */
    virtual void initialize(const NamedList& params);

    /**
     * Process an event from the receiving list
     * This method is thread safe
     * @return False if the list is empty
     */
    virtual bool process();

    /**
     * Check presence timeout
     * This method is thread safe
     * @param time Current time
     */
    virtual void checkTimeout(u_int64_t time);

    /**
     * Process disco info elements
     * @param event The event with the element
     */
    virtual void processDisco(JBEvent* event);

    /**
     * Process a presence error element
     * @param event The event with the element
     */
    virtual void processError(JBEvent* event);

    /**
     * Process a presence probe element
     * @param event The event with the element
     */
    virtual void processProbe(JBEvent* event);

    /**
     * Process a presence subscribe element
     * @param event The event with the element
     * @param presence Presence type: Subscribe,Subscribed,Unsubscribe,Unsubscribed
     */
    virtual void processSubscribe(JBEvent* event, Presence presence);

    /**
     * Process a presence unavailable element
     * @param event The event with the element
     */
    virtual void processUnavailable(JBEvent* event);

    /**
     * Process a presence element
     * @param event The event with the element
     */
    virtual void processPresence(JBEvent* event);

    /**
     * Notify on probe request with users we don't know about
     * @param event The event with the element
     * @return False to send item-not-found error
     */
    virtual bool notifyProbe(JBEvent* event);

    /**
     * Notify on subscribe event with users we don't know about
     * @param event The event with the element
     * @param presence Presence type: Subscribe,Subscribed,Unsubscribe,Unsubscribed
     * @return False to send item-not-found error
     */
    virtual bool notifySubscribe(JBEvent* event, Presence presence);

    /**
     * Notify on subscribe event
     * @param user The user that received the event
     * @param presence Presence type: Subscribe,Subscribed,Unsubscribe,Unsubscribed
     */
    virtual void notifySubscribe(XMPPUser* user, Presence presence);

    /**
     * Notify on presence event with users we don't know about or presence unavailable
     *  received without resource (the remote user is entirely unavailable)
     * @param event The event with the element
     * @param available The availability of the remote user
     * @return False to send item-not-found error
     */
    virtual bool notifyPresence(JBEvent* event, bool available);

    /**
     * Notify on state/capabilities change
     * @param user The user that received the event
     * @param resource The resource that changet its state or capabilities
     */
    virtual void notifyPresence(XMPPUser* user, JIDResource* resource);

    /**
     * Notify when a new user is added
     * Used basically to add a local resource
     * @param user The new user
     */
    virtual void notifyNewUser(XMPPUser* user);

    /**
     * Get a roster. Add a new one if requested.
     * This method is thread safe
     * @param jid The user's jid
     * @param add True to add the user if doesn't exists
     * @param added Optional parameter to be set if a new user was added
     * @return Referenced pointer or 0 if none
     */
    XMPPUserRoster* getRoster(const JabberID& jid, bool add, bool* added);

    /**
     * Get a remote peer of a local one. Add a new one if requested.
     * This method is thread safe
     * @param local The local peer
     * @param remote The remote peer
     * @param addLocal True to add the local user if doesn't exists
     * @param addedLocal Optional parameter to be set if a new local user was added
     * @param addRemote True to add the remote user if doesn't exists
     * @param addedRemote Optional parameter to be set if a new remote user was added
     * @return Referenced pointer or 0 if none
     */
    XMPPUser* getRemoteUser(const JabberID& local, const JabberID& remote,
	bool addLocal, bool* addedLocal, bool addRemote, bool* addedRemote);

    /**
     * Remove a remote peer of a local one
     * This method is thread safe
     * @param local The local peer
     * @param remote The remote peer
     */
    void removeRemoteUser(const JabberID& local, const JabberID& remote);

    /**
     * Check if the given domain is a valid (known) one
     * @param domain The domain name to check
     * @return True if the given domain is a valid one
     */
    bool validDomain(const String& domain);

    /**
     * Send an element through the given stream.
     * If the stream is 0 try to get one from the engine.
     * In any case the element is consumed (deleted)
     * @param element Element to send
     * @param stream The stream to send through
     * @return The result of send operation. False if element is 0
     */
    bool sendStanza(XMLElement* element, JBStream* stream);

    /**
     * Create an 'presence' element
     * @param from The 'from' attribute
     * @param to The 'to' attribute
     * @param type Presence type as enumeration
     * @return A valid XMLElement pointer
     */
    static XMLElement* createPresence(const char* from,
	const char* to, Presence type = None);

    /**
     * Decode an error element
     * @param element The XML element
     * @param code The 'code' attribute
     * @param type The 'type' attribute
     * @param error The name of the 'error' child
     * @return False if 'element' is 0 or is not a presence one
     */
    static bool decodeError(const XMLElement* element,
	String& code, String& type, String& error);

    /**
     * Get the type of a 'presence' stanza as enumeration
     * @param text The text to check
     * @return Presence type as enumeration
     */
    static inline Presence presenceType(const char* text)
	{ return (Presence)lookup(text,s_presence,None); }

    /**
     * Get the text from a presence type
     * @param presence The presence type
     * @return The associated text or 0
     */
    static inline const char* presenceText(Presence presence)
	{ return lookup(presence,s_presence,0); }

    /**
     * Cleanup rosters
     */
    void cleanup();

protected:
    /**
     * Accept an event from the engine
     * @param event The event to accept
     * @param processed Set to true on exit to signal that the event was already processed
     * @param insert Set to true if accepted to insert on top of the event queue
     * @return False if not accepted, let the engine try another service
     */
    virtual bool accept(JBEvent* event, bool& processed, bool& insert);

    static TokenDict s_presence[];       // Keep the types of 'presence'
    XMPPDirVal m_autoSubscribe;          // Auto subscribe state
    bool m_delUnavailable;               // Delete unavailable user or resource
    bool m_autoRoster;                   // True if this service make an automatically roster management
    bool m_ignoreNonRoster;              // Ignore all elements whose destination is not in roster
    XMPPDirVal m_addOnSubscribe;         // Add new user on subscribe request
    XMPPDirVal m_addOnProbe;             // Add new user on probe request
    XMPPDirVal m_addOnPresence;          // Add new user on presence
    bool m_autoProbe;                    // Automatically respond to probe requests
    u_int32_t m_probeInterval;           // Interval to probe a remote user
    u_int32_t m_expireInterval;          // Expire interval after probe
    ObjList m_rosters;                   // The rosters
    JIDIdentity* m_defIdentity;          // Default identity
    JIDFeatureList m_defFeatures;        // Default features

private:
    // Wrapper for getRemoteUser() used when receiving presence
    // Show a debug message if not found
    XMPPUser* recvGetRemoteUser(const char* type, const JabberID& local, const JabberID& remote,
	bool addLocal = false, bool* addedLocal = 0,
	bool addRemote = false, bool* addedRemote = 0);
    void addRoster(XMPPUserRoster* ur);
    void removeRoster(XMPPUserRoster* ur);
};


/**
 * This class holds a JID resource (name,presence,capabilities)
 * @short A JID resource
 */
class YJINGLE_API JIDResource : public RefObject
{
public:
    /**
     * Resource capabilities enumeration.
     */
    enum Capability {
	CapChat                = 1,      // Chat capability
	CapAudio               = 2,      // Jingle capability
    };

    /**
     * Resource presence enumeration
     */
    enum Presence {
	Unknown                = 0,      // unknown
	Available              = 1,      // available
	Unavailable            = 2,      // unavailable
    };

    /**
     * Values of the 'show' child of a presence element
     */
    enum Show {
	ShowAway,                        // away : Temporarily away
	ShowChat,                        // chat : Actively interested in chatting
	ShowDND,                         // dnd  : Busy
	ShowXA,                          // xa   : Extended away
	ShowNone,                        //      : Missing or no text
    };

    /**
     * Constructor. Set data members
     * @param name The resource name
     * @param presence The resource presence
     * @param capability The resource capability
     * @param prio The resource priority
     */
    inline JIDResource(const char* name, Presence presence = Unknown,
	u_int32_t capability = CapChat, int prio = 0)
	: m_name(name), m_presence(presence), m_priority(prio),
	  m_capability(capability), m_show(ShowNone)
	{}

    /**
     * Destructor
     */
    inline virtual ~JIDResource()
	{}

    /**
     * Get the resource name
     * @return The resource name
     */
    inline const String& name() const
	{ return m_name; }

    /**
     * Set the resource name
     * @param name The new name of the resource
     */
    inline void setName(const char* name)
	{ m_name = name; }

    /**
     * Get the presence attribute
     * @return The presence attribute
     */
    inline Presence presence() const
	{ return m_presence; }

    /**
     * Check if the resource is available
     * @return True if the resource is available
     */
    inline bool available() const
	{ return (m_presence == Available); }

    /**
     * Get the show attribute as enumeration
     * @return The show attribute as enumeration
     */
    inline Show show() const
	{ return m_show; }

    /**
     * Set the show attribute
     * @param s The new show attribute
     */
    inline void show(Show s)
	{ m_show = s; }

    /**
     * Get the status of this resource
     * @return The status of this resource
     */
    inline const String& status() const
	{ return m_status; }

    /**
     * Set the status of this resource
     * @param s The new status of this resource
     */
    inline void status(const char* s)
	{ m_status = s; }

    /**
     * Get the priority of this resource
     * @return The priority of this resource
     */
    inline int priority()
	{ return m_priority; }

    /**
     * Set the priority of this resource
     * @param value The new priority of this resource
     */
    inline void priority(int value)
	{ m_priority = value; }

    /**
     * Get the list of resource features
     * @return The resource features
     */
    inline JIDFeatureList& features()
	{ return m_features; }

    /**
     * Get the list containing XML elements with additional data describing this resource
     * @return The info list
     */
    inline ObjList* infoXml()
	{ return &m_info; }

    /**
     * Set the presence information
     * @param value True if available, False if not
     * @return True if presence changed
     */
    bool setPresence(bool value);

    /**
     * Check if the resource has the required capability
     * @param capability The required capability
     * @return True if the resource has the required capability
     */
    inline bool hasCap(Capability capability) const
	{ return (m_capability & capability) != 0; }

    /**
     * Update resource from a presence element
     * @param element A presence element
     * @return True if presence or capability changed changed
     */
    bool fromXML(XMLElement* element);

    /**
     * Add capabilities to a presence element
     * @param element The target presence element
     * @param addInfo True to add the elements from info list
     */
    void addTo(XMLElement* element, bool addInfo = true);

    /**
     * Get the 'show' child of a presence element
     * @param element The XML element
     * @return The text or 0
     */
    static const char* getShow(XMLElement* element);

    /**
     * Get the 'show' child of a presence element
     * @param element The XML element
     * @return The text or 0
     */
    static const char* getStatus(XMLElement* element);

    /**
     * Get the type of a 'show' element as enumeration
     * @param text The text to check
     * @return Show type as enumeration
     */
    static inline Show showType(const char* text)
	{ return (Show)lookup(text,s_show,ShowNone); }

    /**
     * Get the text from a show type
     * @param show The type to get text for
     * @return The associated text or 0
     */
    static inline const char* showText(Show show)
	{ return lookup(show,s_show,0); }

protected:
    static TokenDict s_show[];           // Show texts

private:
    String m_name;                       // Resource name
    Presence m_presence;                 // Resorce presence
    int m_priority;                      // Resource priority
    u_int32_t m_capability;              // Resource capabilities
    Show m_show;                         // Show attribute
    String m_status;                     // Status attribute
    JIDFeatureList m_features;           // Resource features
    ObjList m_info;                      // XML elements containing additional info about this resource
};


/**
 * This class holds a resource list
 * @short A resource list
 */
class YJINGLE_API JIDResourceList : public Mutex
{
    friend class XMPPUser;
    friend class JBPresence;
public:
    /**
     * Constructor
     */
    inline JIDResourceList()
	: Mutex(true,"JIDResourceList")
	{}

    /**
     * Add a resource to the list if a resource with the given name
     *  doesn't exists
     * @param name The resource name
     * @return False if the the resource already exists in the list
     */
    inline bool add(const String& name)
	{ return add(new JIDResource(name)); }

    /**
     * Add a resource to the list if not already there.
     * Destroy the received resource if not added
     * @param resource The resource to add
     * @return False if the the resource already exists in the list
     */
    bool add(JIDResource* resource);

    /**
     * Remove a resource from the list
     * @param resource The resource to remove
     * @param del True to delete the resource
     */
    inline void remove(JIDResource* resource, bool del = true)
	{ Lock lock(this); m_resources.remove(resource,del); }

    /**
     * Clear the list
     */
    inline void clear()
	{ Lock lock(this); m_resources.clear(); }

    /**
     * Get a resource with the given name
     * @param name The resource name
     * @return A pointer to the resource or 0
     */
    JIDResource* get(const String& name);

    /**
     * Get the first resource from the list
     * @return A pointer to the resource or 0
     */
    inline JIDResource* getFirst() {
	    Lock lock(this);
	    ObjList* obj = m_resources.skipNull();
	    return obj ? static_cast(obj->get()) : 0;
	}

    /**
     * Get the first resource with audio capability
     * @param availableOnly True to get only if available
     * @return A pointer to the resource or 0
     */
    JIDResource* getAudio(bool availableOnly = true);

private:
    ObjList m_resources;                 // The resources list
};


/**
 * This class holds a remote XMPP user along with his resources and subscribe state.
 * @short An XMPP remote user.
 */
class YJINGLE_API XMPPUser : public RefObject, public Mutex
{
    friend class XMPPUserRoster;
    friend class JBPresence;
public:
    /**
     * Create a remote user.
     * @param local The local (owner) user peer.
     * @param node The node (username) of the remote peer.
     * @param domain The domain of the remote peer.
     * @param sub The subscription state.
     * @param subTo True to force a subscribe request to remote peer if not subscribed.
     * @param sendProbe True to probe the new user.
     */
    XMPPUser(XMPPUserRoster* local, const char* node, const char* domain,
	XMPPDirVal sub, bool subTo = true, bool sendProbe = true);

    /**
     * Destructor.
     * Send unavailable if not already done.
     */
    virtual ~XMPPUser();

    /**
     * Get the jid of this user.
     * @return The jid of this user.
     */
    inline const JabberID& jid() const
	{ return m_jid; }

    /**
     * Get the roster this user belongs to.
     * @return Pointer to the roster this user belongs to.
     */
    inline XMPPUserRoster* local() const
	{ return m_local; }

    /**
     * Get the subscription state of this user
     * @return The subscription state of this user
     */
    inline XMPPDirVal& subscription()
	{ return m_subscription; }

    /**
     * Get the local resource list
     * @return The local resource list
     */
    inline JIDResourceList& localRes()
	{ return m_localRes; }

    /**
     * Get the remote resource list
     * @return The remote resource list
     */
    inline JIDResourceList& remoteRes()
	{ return m_remoteRes; }

    /**
     * Add a local resource to the list.
     * Send presence if the remote peer is subscribed to the local one.
     * This method is thread safe.
     * @param resource The resource to add.
     * @param send True to send presence from the resource if it is a new one
     * @return False if the the resource already exists in the list.
     */
    bool addLocalRes(JIDResource* resource, bool send = true);

    /**
     * Remove a local resource from the list.
     * Send unavailable if the remote peer is subscribed to the local one.
     * This method is thread safe.
     * @param resource The resource to remove.
     */
    void removeLocalRes(JIDResource* resource);

    /**
     * Remove all local resources.
     * Send unavailable if the remote peer is subscribed to the local one.
     * This method is thread safe.
     */
    void clearLocalRes();

    /**
     * Add a remote resource to the list. This method is thread safe.
     * @param resource The resource to add
     * @return False if the the resource already exists in the list
     */
    bool addRemoteRes(JIDResource* resource);

    /**
     * Remove a remote resource from the list. This method is thread safe.
     * @param resource The resource to remove
     */
    void removeRemoteRes(JIDResource* resource);

    /**
     * Get the first remote resource with audio capability.
     * @param local True to request a local resource, false for a remote one.
     * @param availableOnly True to get only if available.
     * @return A pointer to the resource or 0.
     */
    inline JIDResource* getAudio(bool local, bool availableOnly = true) {
	    return local ? m_localRes.getAudio(availableOnly) :
		m_remoteRes.getAudio(availableOnly);
	}

    /**
     * Process received error elements.
     * This method is thread safe.
     * @param event The event with the element.
     */
    void processError(JBEvent* event);

    /**
     * Process received probe from remote peer.
     * This method is thread safe.
     * @param event The event with the element.
     * @param resName The probed resource if any.
     */
    void processProbe(JBEvent* event, const String* resName = 0);

    /**
     * Process received presence from remote peer.
     * This method is thread safe.
     * @param event The event with the element.
     * @param available The availability of the user.
     * @return False if remote user has no more resources available.
     */
    bool processPresence(JBEvent* event, bool available);

    /**
     * Process received subscription from remote peer.
     * This method is thread safe.
     * @param event The event with the element.
     * @param type The subscription type: subscribe/unsubscribe/subscribed/unsubscribed.
     */
    void processSubscribe(JBEvent* event, JBPresence::Presence type);

    /**
     * Probe the remote user.
     * This method is thread safe.
     * @param stream Optional stream to use to send the request.
     * @param time Probe time.
     * @return True if send succeedded.
     */
    bool probe(JBStream* stream, u_int64_t time = Time::msecNow());

    /**
     * Send subscription to remote peer.
     * This method is thread safe.
     * @param type The subscription type: subscribe/unsubscribe/subscribed/unsubscribed.
     * @param stream Optional stream to use to send the data.
     * @return True if send succeedded.
     */
    bool sendSubscribe(JBPresence::Presence type, JBStream* stream);

    /**
     * Send unavailable to remote peer.
     * This method is thread safe.
     * @param stream Optional stream to use to send the data.
     * @return True if send succeedded.
     */
    bool sendUnavailable(JBStream* stream);

    /**
     * Send presence to remote peer.
     * This method is thread safe.
     * @param resource The resource to send from.
     * @param stream Optional stream to use to send the data.
     * @param force True to send even if we've already sent presence from this resource.
     * @return True if send succeedded.
     */
    bool sendPresence(JIDResource* resource, JBStream* stream = 0, bool force = false);

    /**
     * Check if this user sent us any presence data for a given interval.
     * This method is thread safe.
     * @param time Current time.
     * @return True if the user timed out.
     */
    bool timeout(u_int64_t time);

    /**
     * Notify the state of a resource.
     * This method is thread safe.
     * @param remote True for a remote resource: notify the presence engine.
     *  False for a local resource: send presence to remote user.
     * @param name Resource name.
     * @param stream Optional stream to use to send the data if remote is false.
     * @param force True to send even if we've already sent presence from this resource.
     */
    void notifyResource(bool remote, const String& name,
	JBStream* stream = 0, bool force = false);

    /**
     * Notify the state of all resources.
     * This method is thread safe.
     * @param remote True for remote resources: notify the presence engine.
     *  False for local resources: send presence to remote user.
     * @param stream Optional stream to use to send the data if remote is false.
     * @param force True to send even if we've already sent presence from a resource.
     */
    void notifyResources(bool remote, JBStream* stream = 0, bool force = false);

protected:
    /**
     * Update subscription state.
     * @param from True for subscription from remote user. False for subscription to the remote user.
     * @param value True if subscribed. False is unsubscribed.
     * @param stream Optional stream to use to send presence if subscription from remote user changed to true.
     */
    void updateSubscription(bool from, bool value, JBStream* stream);

    /**
     * Update user timeout data.
     * @param from True if the update is made on incoming data. False if timeout is made on outgoing probe.
     * @param time Current time.
     */
    void updateTimeout(bool from, u_int64_t time = Time::msecNow());

private:
    XMPPUserRoster* m_local;             // Local user
    JabberID m_jid;                      // User's JID
    XMPPDirVal m_subscription;           // Subscription state
    JIDResourceList m_localRes;          // Local user's resources
    JIDResourceList m_remoteRes;         // Remote user's resources
    u_int64_t m_nextProbe;               // Time to probe
    u_int64_t m_expire;                  // Expire time
};

/**
 * This class holds the roster for a local user.
 * @short The roster of a local user.
 */
class YJINGLE_API XMPPUserRoster : public RefObject, public Mutex
{
    friend class JBPresence;
    friend class JBClientStream;
    friend class XMPPUser;
public:
    /**
     * Destructor.
     * Remove this roster from engine's queue.
     */
    virtual ~XMPPUserRoster();

    /**
     * Get the local user's jid.
     * @return The local user's jid.
     */
    const JabberID& jid() const
	{ return m_jid; }

    /**
     * Get the presence engine this user belongs to.
     * @return Pointer to the presence engine this user belongs to.
     */
    JBPresence* engine()
	{ return m_engine; }

    /**
     * Get the list of available resources belonging to the same user
     * @return The list of available resources
     */
    inline JIDResourceList& resources()
	{ return m_resources; }

    /**
     * Get the list of remote users
     * @return The list of remote users
     */
    inline ObjList& users()
	{ return m_remote; }

    /**
     * Get a remote user.
     * This method is thread safe.
     * @param jid User's jid.
     * @param add True to add if not found.
     * @param added Optional flag to set if added a new user.
     * @return Referenced pointer to the user or 0.
     */
    XMPPUser* getUser(const JabberID& jid, bool add = false, bool* added = 0);

    /**
     * Remove a remote user.
     * This method is thread safe.
     * @param remote The user to remove.
     * @return False if no more users.
     */
    bool removeUser(const JabberID& remote);

    /**()
     * Clear remote user list.
     */
    inline void cleanup() {
	    Lock lock(this);
	    m_remote.clear();
	}

    /**
     * Check timeout.
     * This method is thread safe.
     * @param time Current time.
     * @return True to remove the roster.
     */
    bool timeout(u_int64_t time);

    /**
     * Create an iq result to respond to disco info. Add user's features and identity
     * @param from The from attribute
     * @param to The to attribute
     * @param id The id attribute
     * @return XMLElement pointer
     */
    inline XMLElement* createDiscoInfoResult(const char* from, const char* to,
	const char* id)
	{ return XMPPUtils::createDiscoInfoRes(from,to,id,&m_features,m_identity); }

protected:
    /**
     * Constructor.
     * @param engine Pointer to the presence engine this user belongs to
     * @param node User's name
     * @param domain User's domain
     * @param proto Protocol. Used to create identity
     */
    XMPPUserRoster(JBPresence* engine, const char* node, const char* domain,
	JBEngine::Protocol proto = JBEngine::Component);

private:
    inline void addUser(XMPPUser* u) {
	    Lock lock(this);
	    m_remote.append(u);
	}
    void removeUser(XMPPUser* u) {
	    Lock lock(this);
	    m_remote.remove(u,false);
	}

    JabberID m_jid;                      // User's bare JID
    JIDFeatureList m_features;           // Local user's resources
    JIDIdentity* m_identity;             // JID's identity
    ObjList m_remote;                    // Remote users
    JIDResourceList m_resources;         // Available resources of the user
    JBPresence* m_engine;                // Presence engine
};

};

#endif /* __YATEJABBER_H */

/* vi: set ts=8 sw=4 sts=4 noet: */

Generated by: paulc on bussard on Wed Oct 21 01:57:30 2009, using kdoc 2.0a54.