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
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
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