예제 #1
0
class S5BRecvHandler(object):
    def __init__(self, j):
        self.d = ExpiringDictionary(60)
        self.j = j

    def register_handlers(self):
        '''
        register so that we'll get incomming s5b requests.
        '''
        self.j.stream.set_iq_set_handler('query',BYTESTREAMS_NS,
                                         self.handleSI)
        self.j.idle_loop += self.d.expire

    def handleSI(self, stanza):
        '''
        someone wants to open a stream with us
        '''
        print 'handleSI called'
        b = ByteStreams(stanza.get_query())
        sid = b.sid
        try:
            s5b = self.d.pop(sid)
        except KeyError:
            return False
        else:
            s5b.accept_stream(b, stanza.get_from(), stanza.get_to(), stanza.get_id())
            return True

    def waitfor(self, stanza):
        '''
        add a new stream to those we're looking for.
        '''
        si_ft = SI_FileTransfer(stanza.xpath_eval('si:si',{'si':SI_NS})[0])
        print 'waiting for stream for ', si_ft
        s5b = SOCKS5Bytestream(si_ft, self.j)
        self.d.set_item(si_ft.sid,s5b,timeout_callback=s5b.timed_out)
        return s5b

    __call__ = waitfor
예제 #2
0
class StanzaProcessor:
    """Universal stanza handler/router class.

    Provides facilities to set up custom handlers for various types of stanzas.

    :Ivariables:
        - `lock`: lock object used to synchronize access to the
          `StanzaProcessor` object.
        - `me`: local JID.
        - `peer`: remote stream endpoint JID.
        - `process_all_stanzas`: when `True` then all stanzas received are
          considered local.
        - `initiator`: `True` if local stream endpoint is the initiating entity.
    """

    def __init__(self):
        """Initialize a `StanzaProcessor` object."""
        self.me = None
        self.peer = None
        self.initiator = None
        self.peer_authenticated = False
        self.process_all_stanzas = True
        self._iq_response_handlers = ExpiringDictionary()
        self._iq_get_handlers = {}
        self._iq_set_handlers = {}
        self._message_handlers = []
        self._presence_handlers = []
        self.__logger = logging.getLogger("pyxmpp.Stream")
        self.lock = threading.RLock()

    def process_response(self, response):
        """Examines out the response returned by a stanza handler and sends all
        stanzas provided.

        :Returns:
           - `True`: if `response` is `Stanza`, iterable or `True` (meaning the stanza was processed).
           - `False`: when `response` is `False` or `None`
        :returntype: `bool`
        """

        if response is None or response is False:
            return False

        if isinstance(response, Stanza):
            self.send(response)
            return True

        try:
            response = iter(response)
        except TypeError:
            return bool(response)

        for stanza in response:
            if isinstance(stanza, Stanza):
                self.send(stanza)
        return True

    def process_iq(self, stanza):
        """Process IQ stanza received.

        :Parameters:
            - `stanza`: the stanza received

        If a matching handler is available pass the stanza to it.
        Otherwise ignore it if it is "error" or "result" stanza
        or return "feature-not-implemented" error."""

        sid = stanza.get_id()
        fr = stanza.get_from()

        typ = stanza.get_type()
        if typ in ("result", "error"):
            if fr:
                ufr = fr.as_unicode()
            else:
                ufr = None
            res_handler = err_handler = None
            try:
                res_handler, err_handler = self._iq_response_handlers.pop((sid, ufr))
            except KeyError:
                if fr == self.peer or fr == self.me or fr == self.me.bare():
                    try:
                        res_handler, err_handler = self._iq_response_handlers.pop((sid, None))
                    except KeyError:
                        pass
                if None is res_handler is err_handler:
                    self.__logger.warning("ignoring stanza from %r", fr)
                    self.__logger.warning("I am %r", self.me)
                    self.__logger.warning(self._iq_response_handlers.keys())
                    return False
            if typ == "result":
                response = res_handler(stanza)
            else:
                response = err_handler(stanza)
            self.process_response(response)
            return True

        q = stanza.get_query()
        if not q:
            raise BadRequestProtocolError, "Stanza with no child element"
        el = q.name
        ns = q.ns().getContent()

        if typ == "get":
            if self._iq_get_handlers.has_key((el, ns)):
                response = self._iq_get_handlers[(el, ns)](stanza)
                self.process_response(response)
                return True
            else:
                raise FeatureNotImplementedProtocolError, "Not implemented"
        elif typ == "set":
            if self._iq_set_handlers.has_key((el, ns)):
                response = self._iq_set_handlers[(el, ns)](stanza)
                self.process_response(response)
                return True
            else:
                raise FeatureNotImplementedProtocolError, "Not implemented"
        else:
            raise BadRequestProtocolError, "Unknown IQ stanza type"

    def __try_handlers(self, handler_list, typ, stanza):
        """ Search the handler list for handlers matching
        given stanza type and payload namespace. Run the
        handlers found ordering them by priority until
        the first one which returns `True`.

        :Parameters:
            - `handler_list`: list of available handlers
            - `typ`: stanza type (value of its "type" attribute)
            - `stanza`: the stanza to handle

        :return: result of the last handler or `False` if no
            handler was found."""
        namespaces = []
        if stanza.xmlnode.children:
            c = stanza.xmlnode.children
            while c:
                try:
                    ns = c.ns()
                except libxml2.treeError:
                    ns = None
                if ns is None:
                    c = c.next
                    continue
                ns_uri = ns.getContent()
                if ns_uri not in namespaces:
                    namespaces.append(ns_uri)
                c = c.next
        for handler_entry in handler_list:
            t = handler_entry[1]
            ns = handler_entry[2]
            handler = handler_entry[3]
            if t != typ:
                continue
            if ns is not None and ns not in namespaces:
                continue
            response = handler(stanza)
            if self.process_response(response):
                return True
        return False

    def process_message(self, stanza):
        """Process message stanza.

        Pass it to a handler of the stanza's type and payload namespace.
        If no handler for the actual stanza type succeeds then hadlers
        for type "normal" are used.

        :Parameters:
            - `stanza`: message stanza to be handled
        """

        if not self.initiator and not self.peer_authenticated:
            self.__logger.debug("Ignoring message - peer not authenticated yet")
            return True

        typ = stanza.get_type()
        if self.__try_handlers(self._message_handlers, typ, stanza):
            return True
        if typ != "error":
            return self.__try_handlers(self._message_handlers, "normal", stanza)
        return False

    def process_presence(self, stanza):
        """Process presence stanza.

        Pass it to a handler of the stanza's type and payload namespace.

        :Parameters:
            - `stanza`: presence stanza to be handled
        """

        if not self.initiator and not self.peer_authenticated:
            self.__logger.debug("Ignoring presence - peer not authenticated yet")
            return True

        typ = stanza.get_type()
        if not typ:
            typ = "available"
        return self.__try_handlers(self._presence_handlers, typ, stanza)

    def route_stanza(self, stanza):
        """Process stanza not addressed to us.

        Return "recipient-unavailable" return if it is not
        "error" nor "result" stanza.

        This method should be overriden in derived classes if they
        are supposed to handle stanzas not addressed directly to local
        stream endpoint.

        :Parameters:
            - `stanza`: presence stanza to be processed
        """
        if stanza.get_type() not in ("error", "result"):
            r = stanza.make_error_response("recipient-unavailable")
            self.send(r)
        return True

    def process_stanza(self, stanza):
        """Process stanza received from the stream.

        First "fix" the stanza with `self.fix_in_stanza()`,
        then pass it to `self.route_stanza()` if it is not directed
        to `self.me` and `self.process_all_stanzas` is not True. Otherwise
        stanza is passwd to `self.process_iq()`, `self.process_message()`
        or `self.process_presence()` appropriately.

        :Parameters:
            - `stanza`: the stanza received.

        :returns: `True` when stanza was handled
        """

        self.fix_in_stanza(stanza)
        to = stanza.get_to()

        if not self.process_all_stanzas and to and to != self.me and to.bare() != self.me.bare():
            return self.route_stanza(stanza)

        try:
            if stanza.stanza_type == "iq":
                if self.process_iq(stanza):
                    return True
            elif stanza.stanza_type == "message":
                if self.process_message(stanza):
                    return True
            elif stanza.stanza_type == "presence":
                if self.process_presence(stanza):
                    return True
        except ProtocolError, e:
            typ = stanza.get_type()
            if typ != "error" and (typ != "result" or stanza.stanza_type != "iq"):
                r = stanza.make_error_response(e.xmpp_name)
                self.send(r)
                e.log_reported()
            else:
                e.log_ignored()

        self.__logger.debug("Unhandled %r stanza: %r" % (stanza.stanza_type, stanza.serialize()))
        return False
예제 #3
0
class StanzaProcessor:
    """Universal stanza handler/router class.

    Provides facilities to set up custom handlers for various types of stanzas.

    :Ivariables:
        - `lock`: lock object used to synchronize access to the
          `StanzaProcessor` object.
        - `me`: local JID.
        - `peer`: remote stream endpoint JID.
        - `process_all_stanzas`: when `True` then all stanzas received are
          considered local.
        - `initiator`: `True` if local stream endpoint is the initiating entity.
    """
    def __init__(self):
        """Initialize a `StanzaProcessor` object."""
        self.me = None
        self.peer = None
        self.initiator = None
        self.peer_authenticated = False
        self.process_all_stanzas = True
        self._iq_response_handlers = ExpiringDictionary()
        self._iq_get_handlers = {}
        self._iq_set_handlers = {}
        self._message_handlers = []
        self._presence_handlers = []
        self.__logger = logging.getLogger("pyxmpp.Stream")
        self.lock = threading.RLock()

    def process_response(self, response):
        """Examines out the response returned by a stanza handler and sends all
        stanzas provided.

        :Returns:
           - `True`: if `response` is `Stanza`, iterable or `True` (meaning the stanza was processed).
           - `False`: when `response` is `False` or `None`
        :returntype: `bool`
        """

        if response is None or response is False:
            return False

        if isinstance(response, Stanza):
            self.send(response)
            return True

        try:
            response = iter(response)
        except TypeError:
            return bool(response)

        for stanza in response:
            if isinstance(stanza, Stanza):
                self.send(stanza)
        return True

    def process_iq(self, stanza):
        """Process IQ stanza received.

        :Parameters:
            - `stanza`: the stanza received

        If a matching handler is available pass the stanza to it.
        Otherwise ignore it if it is "error" or "result" stanza
        or return "feature-not-implemented" error."""

        sid = stanza.get_id()
        fr = stanza.get_from()

        typ = stanza.get_type()
        if typ in ("result", "error"):
            if fr:
                ufr = fr.as_unicode()
            else:
                ufr = None
            res_handler = err_handler = None
            try:
                res_handler, err_handler = self._iq_response_handlers.pop(
                    (sid, ufr))
            except KeyError:
                if ((fr == self.peer or fr == self.me
                     or fr == self.me.bare())):
                    try:
                        res_handler, err_handler = self._iq_response_handlers.pop(
                            (sid, None))
                    except KeyError:
                        pass
                if None is res_handler is err_handler:
                    self.__logger.warning('ignoring stanza from %r', fr)
                    self.__logger.warning('I am %r', self.me)
                    self.__logger.warning(self._iq_response_handlers.keys())
                    return False
            if typ == "result":
                response = res_handler(stanza)
            else:
                response = err_handler(stanza)
            self.process_response(response)
            return True

        q = stanza.get_query()
        if not q:
            raise BadRequestProtocolError, "Stanza with no child element"
        el = q.name
        ns = q.ns().getContent()

        if typ == "get":
            if self._iq_get_handlers.has_key((el, ns)):
                response = self._iq_get_handlers[(el, ns)](stanza)
                self.process_response(response)
                return True
            else:
                raise FeatureNotImplementedProtocolError, "Not implemented"
        elif typ == "set":
            if self._iq_set_handlers.has_key((el, ns)):
                response = self._iq_set_handlers[(el, ns)](stanza)
                self.process_response(response)
                return True
            else:
                raise FeatureNotImplementedProtocolError, "Not implemented"
        else:
            raise BadRequestProtocolError, "Unknown IQ stanza type"

    def __try_handlers(self, handler_list, typ, stanza):
        """ Search the handler list for handlers matching
        given stanza type and payload namespace. Run the
        handlers found ordering them by priority until
        the first one which returns `True`.

        :Parameters:
            - `handler_list`: list of available handlers
            - `typ`: stanza type (value of its "type" attribute)
            - `stanza`: the stanza to handle

        :return: result of the last handler or `False` if no
            handler was found."""
        namespaces = []
        if stanza.xmlnode.children:
            c = stanza.xmlnode.children
            while c:
                try:
                    ns = c.ns()
                except libxml2.treeError:
                    ns = None
                if ns is None:
                    c = c.next
                    continue
                ns_uri = ns.getContent()
                if ns_uri not in namespaces:
                    namespaces.append(ns_uri)
                c = c.next
        for handler_entry in handler_list:
            t = handler_entry[1]
            ns = handler_entry[2]
            handler = handler_entry[3]
            if t != typ:
                continue
            if ns is not None and ns not in namespaces:
                continue
            response = handler(stanza)
            if self.process_response(response):
                return True
        return False

    def process_message(self, stanza):
        """Process message stanza.

        Pass it to a handler of the stanza's type and payload namespace.
        If no handler for the actual stanza type succeeds then hadlers
        for type "normal" are used.

        :Parameters:
            - `stanza`: message stanza to be handled
        """

        if not self.initiator and not self.peer_authenticated:
            self.__logger.debug(
                "Ignoring message - peer not authenticated yet")
            return True

        typ = stanza.get_type()
        if self.__try_handlers(self._message_handlers, typ, stanza):
            return True
        if typ != "error":
            return self.__try_handlers(self._message_handlers, "normal",
                                       stanza)
        return False

    def process_presence(self, stanza):
        """Process presence stanza.

        Pass it to a handler of the stanza's type and payload namespace.

        :Parameters:
            - `stanza`: presence stanza to be handled
        """

        if not self.initiator and not self.peer_authenticated:
            self.__logger.debug(
                "Ignoring presence - peer not authenticated yet")
            return True

        typ = stanza.get_type()
        if not typ:
            typ = "available"
        return self.__try_handlers(self._presence_handlers, typ, stanza)

    def route_stanza(self, stanza):
        """Process stanza not addressed to us.

        Return "recipient-unavailable" return if it is not
        "error" nor "result" stanza.

        This method should be overriden in derived classes if they
        are supposed to handle stanzas not addressed directly to local
        stream endpoint.

        :Parameters:
            - `stanza`: presence stanza to be processed
        """
        if stanza.get_type() not in ("error", "result"):
            r = stanza.make_error_response("recipient-unavailable")
            self.send(r)
        return True

    def process_stanza(self, stanza):
        """Process stanza received from the stream.

        First "fix" the stanza with `self.fix_in_stanza()`,
        then pass it to `self.route_stanza()` if it is not directed
        to `self.me` and `self.process_all_stanzas` is not True. Otherwise
        stanza is passwd to `self.process_iq()`, `self.process_message()`
        or `self.process_presence()` appropriately.

        :Parameters:
            - `stanza`: the stanza received.

        :returns: `True` when stanza was handled
        """

        self.fix_in_stanza(stanza)
        to = stanza.get_to()

        if not self.process_all_stanzas and to and to != self.me and to.bare(
        ) != self.me.bare():
            return self.route_stanza(stanza)

        try:
            if stanza.stanza_type == "iq":
                if self.process_iq(stanza):
                    return True
            elif stanza.stanza_type == "message":
                if self.process_message(stanza):
                    return True
            elif stanza.stanza_type == "presence":
                if self.process_presence(stanza):
                    return True
        except ProtocolError, e:
            typ = stanza.get_type()
            if typ != 'error' and (typ != 'result'
                                   or stanza.stanza_type != 'iq'):
                r = stanza.make_error_response(e.xmpp_name)
                self.send(r)
                e.log_reported()
            else:
                e.log_ignored()

        self.__logger.debug("Unhandled %r stanza: %r" %
                            (stanza.stanza_type, stanza.serialize()))
        return False