Ejemplo n.º 1
0
    def __roster_result(self, iq):
        """Process roster request success.

        :Parameters:
            - `iq`: IQ result stanza received in reply to the roster request.
        :Types:
            - `iq`: `pyxmpp.Iq`"""
        q = iq.get_query()
        if q:
            self.state_changed.acquire()
            self.roster = Roster(q)
            self.state_changed.notify()
            self.state_changed.release()
            self.roster_updated()
        else:
            raise ClientError("Roster retrieval failed")
Ejemplo n.º 2
0
    def __roster_result(self,iq):
        """Process roster request success.

        :Parameters:
            - `iq`: IQ result stanza received in reply to the roster request.
        :Types:
            - `iq`: `pyxmpp.Iq`"""
        q=iq.get_query()
        if q:
            self.state_changed.acquire()
            self.roster=Roster(q)
            self.state_changed.notify()
            self.state_changed.release()
            self.roster_updated()
        else:
            raise ClientError("Roster retrieval failed")
Ejemplo n.º 3
0
class Client:
    """Base class for an XMPP-IM client.

    This class does not provide any JSF extensions to the XMPP protocol,
    including legacy authentication methods.

    :Ivariables:
        - `jid`: configured JID of the client (current actual JID
          is avialable as `self.stream.jid`).
        - `password`: authentication password.
        - `server`: server to use if non-standard and not discoverable
          by SRV lookups.
        - `port`: port number on the server to use if non-standard and not
          discoverable by SRV lookups.
        - `auth_methods`: methods allowed for stream authentication. SASL
          mechanism names should be preceded with "sasl:" prefix.
        - `keepalive`: keepalive interval for the stream or 0 when keepalive is
          disabled.
        - `stream`: current stream when the client is connected,
          `None` otherwise.
        - `roster`: user's roster or `None` if the roster is not yet retrieved.
        - `session_established`: `True` when an IM session is established.
        - `lock`: lock for synchronizing `Client` attributes access.
        - `state_changed`: condition notified the the object state changes
          (stream becomes connected, session established etc.).
        - `interface_providers`: list of object providing interfaces that
          could be used by the Client object. Initialized to [`self`] by
          the constructor if not set earlier. Put objects providing 
          `IPresenceHandlersProvider`, `IMessageHandlersProvider`,
          `IIqHandlersProvider` or `IStanzaHandlersProvider` into this list.
    :Types:
        - `jid`: `pyxmpp.JID`
        - `password`: `unicode`
        - `server`: `unicode`
        - `port`: `int`
        - `auth_methods`: `list` of `str`
        - `keepalive`: `int`
        - `stream`: `pyxmpp.ClientStream`
        - `roster`: `pyxmpp.Roster`
        - `session_established`: `bool`
        - `lock`: `threading.RLock`
        - `state_changed`: `threading.Condition`
        - `interface_providers`: `list`
    """
    def __init__(self,
                 jid=None,
                 password=None,
                 server=None,
                 port=5222,
                 auth_methods=("sasl:DIGEST-MD5", ),
                 tls_settings=None,
                 keepalive=0):
        """Initialize a Client object.

        :Parameters:
            - `jid`: user full JID for the connection.
            - `password`: user password.
            - `server`: server to use. If not given then address will be derived form the JID.
            - `port`: port number to use. If not given then address will be derived form the JID.
            - `auth_methods`: sallowed authentication methods. SASL authentication mechanisms
              in the list should be prefixed with "sasl:" string.
            - `tls_settings`: settings for StartTLS -- `TLSSettings` instance.
            - `keepalive`: keepalive output interval. 0 to disable.
        :Types:
            - `jid`: `pyxmpp.JID`
            - `password`: `unicode`
            - `server`: `unicode`
            - `port`: `int`
            - `auth_methods`: sequence of `str`
            - `tls_settings`: `pyxmpp.TLSSettings`
            - `keepalive`: `int`
        """
        self.jid = jid
        self.password = password
        self.server = server
        self.port = port
        self.auth_methods = list(auth_methods)
        self.tls_settings = tls_settings
        self.keepalive = keepalive
        self.stream = None
        self.lock = threading.RLock()
        self.state_changed = threading.Condition(self.lock)
        self.session_established = False
        self.roster = None
        self.stream_class = ClientStream
        if not hasattr(self, "interface_providers"):
            self.interface_providers = [self]
        self.__logger = logging.getLogger("pyxmpp.Client")

# public methods

    def connect(self, register=False):
        """Connect to the server and set up the stream.

        Set `self.stream` and notify `self.state_changed` when connection
        succeeds."""
        if not self.jid:
            raise ClientError, "Cannot connect: no or bad JID given"
        self.lock.acquire()
        try:
            stream = self.stream
            self.stream = None
            if stream:
                stream.close()

            self.__logger.debug("Creating client stream: %r, auth_methods=%r" %
                                (self.stream_class, self.auth_methods))
            stream = self.stream_class(jid=self.jid,
                                       password=self.password,
                                       server=self.server,
                                       port=self.port,
                                       auth_methods=self.auth_methods,
                                       tls_settings=self.tls_settings,
                                       keepalive=self.keepalive,
                                       owner=self)
            stream.process_stream_error = self.stream_error
            self.stream_created(stream)
            stream.state_change = self.__stream_state_change
            stream.connect()
            self.stream = stream
            self.state_changed.notify()
            self.state_changed.release()
        except:
            self.stream = None
            self.state_changed.release()
            raise

    def get_stream(self):
        """Get the connected stream object.

        :return: stream object or `None` if the client is not connected.
        :returntype: `pyxmpp.ClientStream`"""
        self.lock.acquire()
        stream = self.stream
        self.lock.release()
        return stream

    def disconnect(self):
        """Disconnect from the server."""
        stream = self.get_stream()
        if stream:
            stream.disconnect()

    def request_session(self):
        """Request an IM session."""
        stream = self.get_stream()
        if not stream.version:
            need_session = False
        elif not stream.features:
            need_session = False
        else:
            ctxt = stream.doc_in.xpathNewContext()
            ctxt.setContextNode(stream.features)
            ctxt.xpathRegisterNs("sess", "urn:ietf:params:xml:ns:xmpp-session")
            # jabberd2 hack
            ctxt.xpathRegisterNs(
                "jsess", "http://jabberd.jabberstudio.org/ns/session/1.0")
            sess_n = None
            try:
                sess_n = ctxt.xpathEval("sess:session or jsess:session")
            finally:
                ctxt.xpathFreeContext()
            if sess_n:
                need_session = True
            else:
                need_session = False

        if not need_session:
            self.state_changed.acquire()
            self.session_established = 1
            self.state_changed.notify()
            self.state_changed.release()
            self._session_started()
        else:
            iq = Iq(stanza_type="set")
            iq.new_query("urn:ietf:params:xml:ns:xmpp-session", "session")
            stream.set_response_handlers(iq, self.__session_result,
                                         self.__session_error,
                                         self.__session_timeout)
            stream.send(iq)

    def request_roster(self):
        """Request the user's roster."""
        stream = self.get_stream()
        iq = Iq(stanza_type="get")
        iq.new_query("jabber:iq:roster")
        stream.set_response_handlers(iq, self.__roster_result,
                                     self.__roster_error,
                                     self.__roster_timeout)
        stream.set_iq_set_handler("query", "jabber:iq:roster",
                                  self.__roster_push)
        stream.send(iq)

    def get_socket(self):
        """Get the socket object of the active connection.

        :return: socket used by the stream.
        :returntype: `socket.socket`"""
        return self.stream.socket

    def loop(self, timeout=1):
        """Simple "main loop" for the client.

        By default just call the `pyxmpp.Stream.loop_iter` method of
        `self.stream`, which handles stream input and `self.idle` for some
        "housekeeping" work until the stream is closed.

        This usually will be replaced by something more sophisticated. E.g.
        handling of other input sources."""
        while 1:
            stream = self.get_stream()
            if not stream:
                break
            act = stream.loop_iter(timeout)
            if not act:
                self.idle()

# private methods

    def __session_timeout(self):
        """Process session request time out.

        :raise FatalClientError:"""
        raise FatalClientError("Timeout while tryin to establish a session")

    def __session_error(self, iq):
        """Process session request failure.

        :Parameters:
            - `iq`: IQ error stanza received as result of the session request.
        :Types:
            - `iq`: `pyxmpp.Iq`

        :raise FatalClientError:"""
        err = iq.get_error()
        msg = err.get_message()
        raise FatalClientError("Failed to establish a session: " + msg)

    def __session_result(self, _unused):
        """Process session request success.

        :Parameters:
            - `_unused`: IQ result stanza received in reply to the session request.
        :Types:
            - `_unused`: `pyxmpp.Iq`"""
        self.state_changed.acquire()
        self.session_established = True
        self.state_changed.notify()
        self.state_changed.release()
        self._session_started()

    def _session_started(self):
        """Called when session is started.
        
        Activates objects from `self.interface_provides` by installing
        their stanza handlers, etc."""
        for ob in self.interface_providers:
            if IPresenceHandlersProvider.providedBy(ob):
                for handler_data in ob.get_presence_handlers():
                    self.stream.set_presence_handler(*handler_data)
            if IMessageHandlersProvider.providedBy(ob):
                for handler_data in ob.get_message_handlers():
                    self.stream.set_message_handler(*handler_data)
            if IIqHandlersProvider.providedBy(ob):
                for handler_data in ob.get_iq_get_handlers():
                    self.stream.set_iq_get_handler(*handler_data)
                for handler_data in ob.get_iq_set_handlers():
                    self.stream.set_iq_set_handler(*handler_data)
        self.session_started()

    def __roster_timeout(self):
        """Process roster request time out.

        :raise ClientError:"""
        raise ClientError("Timeout while tryin to retrieve roster")

    def __roster_error(self, iq):
        """Process roster request failure.

        :Parameters:
            - `iq`: IQ error stanza received as result of the roster request.
        :Types:
            - `iq`: `pyxmpp.Iq`

        :raise ClientError:"""
        err = iq.get_error()
        msg = err.get_message()
        raise ClientError("Roster retrieval failed: " + msg)

    def __roster_result(self, iq):
        """Process roster request success.

        :Parameters:
            - `iq`: IQ result stanza received in reply to the roster request.
        :Types:
            - `iq`: `pyxmpp.Iq`"""
        q = iq.get_query()
        if q:
            self.state_changed.acquire()
            self.roster = Roster(q)
            self.state_changed.notify()
            self.state_changed.release()
            self.roster_updated()
        else:
            raise ClientError("Roster retrieval failed")

    def __roster_push(self, iq):
        """Process a "roster push" (change notification) received.

        :Parameters:
            - `iq`: IQ result stanza received.
        :Types:
            - `iq`: `pyxmpp.Iq`"""
        fr = iq.get_from()
        if fr and fr != self.jid and fr != self.jid.bare():
            resp = iq.make_error_response("forbidden")
            self.stream.send(resp)
            self.__logger.warning("Got roster update from wrong source")
            return
        if not self.roster:
            raise ClientError("Roster update, but no roster")
        q = iq.get_query()
        item = self.roster.update(q)
        if item:
            self.roster_updated(item)
        resp = iq.make_result_response()
        self.stream.send(resp)

    def __stream_state_change(self, state, arg):
        """Handle stream state changes.

        Call apopriate methods of self.

        :Parameters:
            - `state`: the new state.
            - `arg`: state change argument.
        :Types:
            - `state`: `str`"""
        self.stream_state_changed(state, arg)
        if state == "fully connected":
            self.connected()
        elif state == "authorized":
            self.authorized()
        elif state == "disconnected":
            self.state_changed.acquire()
            try:
                if self.stream:
                    self.stream.close()
                self.stream_closed(self.stream)
                self.stream = None
                self.state_changed.notify()
            finally:
                self.state_changed.release()
            self.disconnected()

# Method to override

    def idle(self):
        """Do some "housekeeping" work like cache expiration or timeout
        handling. Should be called periodically from the application main
        loop. May be overriden in derived classes."""
        stream = self.get_stream()
        if stream:
            stream.idle()

    def stream_created(self, stream):
        """Handle stream creation event. May be overriden in derived classes.
        This one does nothing.

        :Parameters:
            - `stream`: the new stream.
        :Types:
            - `stream`: `pyxmpp.ClientStream`"""
        pass

    def stream_closed(self, stream):
        """Handle stream closure event. May be overriden in derived classes.
        This one does nothing.

        :Parameters:
            - `stream`: the new stream.
        :Types:
            - `stream`: `pyxmpp.ClientStream`"""
        pass

    def session_started(self):
        """Handle session started event. May be overriden in derived classes.
        This one requests the user's roster and sends the initial presence."""
        self.request_roster()
        p = Presence()
        self.stream.send(p)

    def stream_error(self, err):
        """Handle stream error received. May be overriden in derived classes.
        This one passes an error messages to logging facilities.

        :Parameters:
            - `err`: the error element received.
        :Types:
            - `err`: `pyxmpp.error.StreamErrorNode`"""
        self.__logger.error("Stream error: condition: %s %r" %
                            (err.get_condition().name, err.serialize()))

    def roster_updated(self, item=None):
        """Handle roster update event. May be overriden in derived classes.
        This one does nothing.

        :Parameters:
            - `item`: the roster item changed or `None` if whole roster was
              received.
        :Types:
            - `item`: `pyxmpp.RosterItem`"""
        pass

    def stream_state_changed(self, state, arg):
        """Handle any stream state change. May be overriden in derived classes.
        This one does nothing.

        :Parameters:
            - `state`: the new state.
            - `arg`: state change argument.
        :Types:
            - `state`: `str`"""
        pass

    def connected(self):
        """Handle "connected" event. May be overriden in derived classes.
        This one does nothing."""
        pass

    def authenticated(self):
        """Handle "authenticated" event. May be overriden in derived classes.
        This one does nothing."""
        pass

    def authorized(self):
        """Handle "authorized" event. May be overriden in derived classes.
        This one requests an IM session."""
        self.request_session()

    def disconnected(self):
        """Handle "disconnected" event. May be overriden in derived classes.
        This one does nothing."""
        pass
Ejemplo n.º 4
0
class Client:
    """Base class for an XMPP-IM client.

    This class does not provide any JSF extensions to the XMPP protocol,
    including legacy authentication methods.

    :Ivariables:
        - `jid`: configured JID of the client (current actual JID
          is avialable as `self.stream.jid`).
        - `password`: authentication password.
        - `server`: server to use if non-standard and not discoverable
          by SRV lookups.
        - `port`: port number on the server to use if non-standard and not
          discoverable by SRV lookups.
        - `auth_methods`: methods allowed for stream authentication. SASL
          mechanism names should be preceded with "sasl:" prefix.
        - `keepalive`: keepalive interval for the stream or 0 when keepalive is
          disabled.
        - `stream`: current stream when the client is connected,
          `None` otherwise.
        - `roster`: user's roster or `None` if the roster is not yet retrieved.
        - `session_established`: `True` when an IM session is established.
        - `lock`: lock for synchronizing `Client` attributes access.
        - `state_changed`: condition notified the the object state changes
          (stream becomes connected, session established etc.).
        - `interface_providers`: list of object providing interfaces that
          could be used by the Client object. Initialized to [`self`] by
          the constructor if not set earlier. Put objects providing 
          `IPresenceHandlersProvider`, `IMessageHandlersProvider`,
          `IIqHandlersProvider` or `IStanzaHandlersProvider` into this list.
    :Types:
        - `jid`: `pyxmpp.JID`
        - `password`: `unicode`
        - `server`: `unicode`
        - `port`: `int`
        - `auth_methods`: `list` of `str`
        - `keepalive`: `int`
        - `stream`: `pyxmpp.ClientStream`
        - `roster`: `pyxmpp.Roster`
        - `session_established`: `bool`
        - `lock`: `threading.RLock`
        - `state_changed`: `threading.Condition`
        - `interface_providers`: `list`
    """
    def __init__(self,jid=None,password=None,server=None,port=5222,
            auth_methods=("sasl:DIGEST-MD5",),
            tls_settings=None,keepalive=0):
        """Initialize a Client object.

        :Parameters:
            - `jid`: user full JID for the connection.
            - `password`: user password.
            - `server`: server to use. If not given then address will be derived form the JID.
            - `port`: port number to use. If not given then address will be derived form the JID.
            - `auth_methods`: sallowed authentication methods. SASL authentication mechanisms
              in the list should be prefixed with "sasl:" string.
            - `tls_settings`: settings for StartTLS -- `TLSSettings` instance.
            - `keepalive`: keepalive output interval. 0 to disable.
        :Types:
            - `jid`: `pyxmpp.JID`
            - `password`: `unicode`
            - `server`: `unicode`
            - `port`: `int`
            - `auth_methods`: sequence of `str`
            - `tls_settings`: `pyxmpp.TLSSettings`
            - `keepalive`: `int`
        """
        self.jid=jid
        self.password=password
        self.server=server
        self.port=port
        self.auth_methods=list(auth_methods)
        self.tls_settings=tls_settings
        self.keepalive=keepalive
        self.stream=None
        self.lock=threading.RLock()
        self.state_changed=threading.Condition(self.lock)
        self.session_established=False
        self.roster=None
        self.stream_class=ClientStream
        if not hasattr(self, "interface_providers"):
            self.interface_providers = [self]
        self.__logger=logging.getLogger("pyxmpp.Client")

# public methods

    def connect(self, register = False):
        """Connect to the server and set up the stream.

        Set `self.stream` and notify `self.state_changed` when connection
        succeeds."""
        if not self.jid:
            raise ClientError, "Cannot connect: no or bad JID given"
        self.lock.acquire()
        try:
            stream = self.stream
            self.stream = None
            if stream:
                stream.close()

            self.__logger.debug("Creating client stream: %r, auth_methods=%r"
                    % (self.stream_class, self.auth_methods))
            stream=self.stream_class(jid = self.jid,
                    password = self.password,
                    server = self.server,
                    port = self.port,
                    auth_methods = self.auth_methods,
                    tls_settings = self.tls_settings,
                    keepalive = self.keepalive,
                    owner = self)
            stream.process_stream_error = self.stream_error
            self.stream_created(stream)
            stream.state_change = self.__stream_state_change
            stream.connect()
            self.stream = stream
            self.state_changed.notify()
            self.state_changed.release()
        except:
            self.stream = None
            self.state_changed.release()
            raise

    def get_stream(self):
        """Get the connected stream object.

        :return: stream object or `None` if the client is not connected.
        :returntype: `pyxmpp.ClientStream`"""
        self.lock.acquire()
        stream=self.stream
        self.lock.release()
        return stream

    def disconnect(self):
        """Disconnect from the server."""
        stream=self.get_stream()
        if stream:
            stream.disconnect()

    def request_session(self):
        """Request an IM session."""
        stream=self.get_stream()
        if not stream.version:
            need_session=False
        elif not stream.features:
            need_session=False
        else:
            ctxt = stream.doc_in.xpathNewContext()
            ctxt.setContextNode(stream.features)
            ctxt.xpathRegisterNs("sess","urn:ietf:params:xml:ns:xmpp-session")
            # jabberd2 hack
            ctxt.xpathRegisterNs("jsess","http://jabberd.jabberstudio.org/ns/session/1.0")
            sess_n=None
            try:
                sess_n=ctxt.xpathEval("sess:session or jsess:session")
            finally:
                ctxt.xpathFreeContext()
            if sess_n:
                need_session=True
            else:
                need_session=False

        if not need_session:
            self.state_changed.acquire()
            self.session_established=1
            self.state_changed.notify()
            self.state_changed.release()
            self._session_started()
        else:
            iq=Iq(stanza_type="set")
            iq.new_query("urn:ietf:params:xml:ns:xmpp-session","session")
            stream.set_response_handlers(iq,
                self.__session_result,self.__session_error,self.__session_timeout)
            stream.send(iq)

    def request_roster(self):
        """Request the user's roster."""
        stream=self.get_stream()
        iq=Iq(stanza_type="get")
        iq.new_query("jabber:iq:roster")
        stream.set_response_handlers(iq,
            self.__roster_result,self.__roster_error,self.__roster_timeout)
        stream.set_iq_set_handler("query","jabber:iq:roster",self.__roster_push)
        stream.send(iq)

    def get_socket(self):
        """Get the socket object of the active connection.

        :return: socket used by the stream.
        :returntype: `socket.socket`"""
        return self.stream.socket

    def loop(self,timeout=1):
        """Simple "main loop" for the client.

        By default just call the `pyxmpp.Stream.loop_iter` method of
        `self.stream`, which handles stream input and `self.idle` for some
        "housekeeping" work until the stream is closed.

        This usually will be replaced by something more sophisticated. E.g.
        handling of other input sources."""
        while 1:
            stream=self.get_stream()
            if not stream:
                break
            act=stream.loop_iter(timeout)
            if not act:
                self.idle()

# private methods

    def __session_timeout(self):
        """Process session request time out.

        :raise FatalClientError:"""
        raise FatalClientError("Timeout while tryin to establish a session")

    def __session_error(self,iq):
        """Process session request failure.

        :Parameters:
            - `iq`: IQ error stanza received as result of the session request.
        :Types:
            - `iq`: `pyxmpp.Iq`

        :raise FatalClientError:"""
        err=iq.get_error()
        msg=err.get_message()
        raise FatalClientError("Failed to establish a session: "+msg)

    def __session_result(self, _unused):
        """Process session request success.

        :Parameters:
            - `_unused`: IQ result stanza received in reply to the session request.
        :Types:
            - `_unused`: `pyxmpp.Iq`"""
        self.state_changed.acquire()
        self.session_established=True
        self.state_changed.notify()
        self.state_changed.release()
        self._session_started()

    def _session_started(self):
        """Called when session is started.
        
        Activates objects from `self.interface_provides` by installing
        their stanza handlers, etc."""
        for ob in self.interface_providers:
            if IPresenceHandlersProvider.providedBy(ob):
                for handler_data in ob.get_presence_handlers():
                    self.stream.set_presence_handler(*handler_data)
            if IMessageHandlersProvider.providedBy(ob):
                for handler_data in ob.get_message_handlers():
                    self.stream.set_message_handler(*handler_data)
            if IIqHandlersProvider.providedBy(ob):
                for handler_data in ob.get_iq_get_handlers():
                    self.stream.set_iq_get_handler(*handler_data)
                for handler_data in ob.get_iq_set_handlers():
                    self.stream.set_iq_set_handler(*handler_data)
        self.session_started()

    def __roster_timeout(self):
        """Process roster request time out.

        :raise ClientError:"""
        raise ClientError("Timeout while tryin to retrieve roster")

    def __roster_error(self,iq):
        """Process roster request failure.

        :Parameters:
            - `iq`: IQ error stanza received as result of the roster request.
        :Types:
            - `iq`: `pyxmpp.Iq`

        :raise ClientError:"""
        err=iq.get_error()
        msg=err.get_message()
        raise ClientError("Roster retrieval failed: "+msg)

    def __roster_result(self,iq):
        """Process roster request success.

        :Parameters:
            - `iq`: IQ result stanza received in reply to the roster request.
        :Types:
            - `iq`: `pyxmpp.Iq`"""
        q=iq.get_query()
        if q:
            self.state_changed.acquire()
            self.roster=Roster(q)
            self.state_changed.notify()
            self.state_changed.release()
            self.roster_updated()
        else:
            raise ClientError("Roster retrieval failed")

    def __roster_push(self,iq):
        """Process a "roster push" (change notification) received.

        :Parameters:
            - `iq`: IQ result stanza received.
        :Types:
            - `iq`: `pyxmpp.Iq`"""
        fr=iq.get_from()
        if fr and fr != self.jid and fr != self.jid.bare():
            resp=iq.make_error_response("forbidden")
            self.stream.send(resp)
            self.__logger.warning("Got roster update from wrong source")
            return
        if not self.roster:
            raise ClientError("Roster update, but no roster")
        q=iq.get_query()
        item=self.roster.update(q)
        if item:
            self.roster_updated(item)
        resp=iq.make_result_response()
        self.stream.send(resp)

    def __stream_state_change(self,state,arg):
        """Handle stream state changes.

        Call apopriate methods of self.

        :Parameters:
            - `state`: the new state.
            - `arg`: state change argument.
        :Types:
            - `state`: `str`"""
        self.stream_state_changed(state,arg)
        if state=="fully connected":
            self.connected()
        elif state=="authorized":
            self.authorized()
        elif state=="disconnected":
            self.state_changed.acquire()
            try:
                if self.stream:
                    self.stream.close()
                self.stream_closed(self.stream)
                self.stream=None
                self.state_changed.notify()
            finally:
                self.state_changed.release()
            self.disconnected()

# Method to override
    def idle(self):
        """Do some "housekeeping" work like cache expiration or timeout
        handling. Should be called periodically from the application main
        loop. May be overriden in derived classes."""
        stream=self.get_stream()
        if stream:
            stream.idle()

    def stream_created(self,stream):
        """Handle stream creation event. May be overriden in derived classes.
        This one does nothing.

        :Parameters:
            - `stream`: the new stream.
        :Types:
            - `stream`: `pyxmpp.ClientStream`"""
        pass

    def stream_closed(self,stream):
        """Handle stream closure event. May be overriden in derived classes.
        This one does nothing.

        :Parameters:
            - `stream`: the new stream.
        :Types:
            - `stream`: `pyxmpp.ClientStream`"""
        pass

    def session_started(self):
        """Handle session started event. May be overriden in derived classes.
        This one requests the user's roster and sends the initial presence."""
        self.request_roster()
        p=Presence()
        self.stream.send(p)

    def stream_error(self,err):
        """Handle stream error received. May be overriden in derived classes.
        This one passes an error messages to logging facilities.

        :Parameters:
            - `err`: the error element received.
        :Types:
            - `err`: `pyxmpp.error.StreamErrorNode`"""
        self.__logger.error("Stream error: condition: %s %r"
                % (err.get_condition().name,err.serialize()))

    def roster_updated(self,item=None):
        """Handle roster update event. May be overriden in derived classes.
        This one does nothing.

        :Parameters:
            - `item`: the roster item changed or `None` if whole roster was
              received.
        :Types:
            - `item`: `pyxmpp.RosterItem`"""
        pass

    def stream_state_changed(self,state,arg):
        """Handle any stream state change. May be overriden in derived classes.
        This one does nothing.

        :Parameters:
            - `state`: the new state.
            - `arg`: state change argument.
        :Types:
            - `state`: `str`"""
        pass

    def connected(self):
        """Handle "connected" event. May be overriden in derived classes.
        This one does nothing."""
        pass

    def authenticated(self):
        """Handle "authenticated" event. May be overriden in derived classes.
        This one does nothing."""
        pass

    def authorized(self):
        """Handle "authorized" event. May be overriden in derived classes.
        This one requests an IM session."""
        self.request_session()

    def disconnected(self):
        """Handle "disconnected" event. May be overriden in derived classes.
        This one does nothing."""
        pass