Пример #1
0
 def disco_get_info(self,node,iq):
     to=iq.get_to()
     try:
         network=self.config.get_network(to)
     except KeyError:
         return iq.make_error_response("recipient-unavailable")
     if to.node is None and to.resource is None:
         di=DiscoInfo()
         if node is None:
             di.add_feature("jabber:iq:version")
             di.add_feature("jabber:iq:register")
             di.add_feature(MUC_NS)
             if network.name:
                 name=network.name
             else:
                 name="IRC gateway"
             DiscoIdentity(di,name,"conference","irc")
             DiscoIdentity(di,name,"conference","text")
             DiscoIdentity(di,name,"gateway","x-irc")
         return di
     elif len(to.node)>1 and to.node[0] in u"&#+!" and to.resource is None:
         di=DiscoInfo()
         di.add_feature(MUC_NS)
         if network.name:
             name="%s channel on %s IRC network" % (to.node,network.name)
         else:
             name="%s IRC channel" % (to.node,)
         DiscoIdentity(di, name, "conference", "text")
         DiscoIdentity(di, name, "conference", "irc")
         return di
     return iq.make_error_response("feature-not-implemented")
Пример #2
0
    def __init__(self,
                 jid=None,
                 password=None,
                 server=None,
                 port=5222,
                 auth_methods=("sasl:DIGEST-MD5", "digest"),
                 tls_settings=None,
                 keepalive=0,
                 disco_name=u"pyxmpp based Jabber client",
                 disco_category=u"client",
                 disco_type=u"pc"):
        """Initialize a JabberClient 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.
            - `disco_name`: name of the client identity in the disco#info
              replies.
            - `disco_category`: category of the client identity in the disco#info
              replies. The default of u'client' should be the right choice in
              most cases.
            - `disco_type`: type of the client identity in the disco#info
              replies. Use `the types registered by Jabber Registrar <http://www.jabber.org/registrar/disco-categories.html>`__
        :Types:
            - `jid`: `pyxmpp.JID`
            - `password`: `unicode`
            - `server`: `unicode`
            - `port`: `int`
            - `auth_methods`: sequence of `str`
            - `tls_settings`: `pyxmpp.TLSSettings`
            - `keepalive`: `int`
            - `disco_name`: `unicode`
            - `disco_category`: `unicode`
            - `disco_type`: `unicode`
        """

        Client.__init__(self, jid, password, server, port, auth_methods,
                        tls_settings, keepalive)
        self.stream_class = LegacyClientStream
        self.disco_items = DiscoItems()
        self.disco_info = DiscoInfo()
        self.disco_identity = DiscoIdentity(self.disco_info, disco_name,
                                            disco_category, disco_type)
        self.register_feature(u"dnssrv")
        self.register_feature(u"stringprep")
        self.register_feature(u"urn:ietf:params:xml:ns:xmpp-sasl#c2s")
        self.cache = CacheSuite(max_items=1000)
        self.__logger = logging.getLogger("pyxmpp.jabber.JabberClient")
Пример #3
0
Файл: disco.py Проект: dax/jcl
 def handle(self, stanza, lang_class, node, disco_obj, data):
     """Implement discovery get_info on an account node"""
     self.__logger.debug("account_disco_get_info")
     disco_info = DiscoInfo(node)
     disco_info.add_feature(vcard.VCARD_NS)
     disco_info.add_feature("jabber:iq:last")
     disco_info.add_feature("jabber:iq:register")
     return [disco_info]
Пример #4
0
    def __init__(self, jid=None, secret=None, server=None, port=5347,
            disco_name=u"PyXMPP based component", disco_category=u"x-service",
            disco_type=u"x-unknown", keepalive=0):
        """Initialize a `Component` object.

        :Parameters:
            - `jid`: component JID (should contain only the domain part).
            - `secret`: the authentication secret.
            - `server`: server name or address the component should connect.
            - `port`: port number on the server where the component should connect.
            - `disco_name`: disco identity name to be used in the
              disco#info responses.
            - `disco_category`: disco identity category to be used in the
              disco#info responses.  Use `the categories registered by Jabber Registrar <http://www.jabber.org/registrar/disco-categories.html>`__
            - `disco_type`: disco identity type to be used in the component's
              disco#info responses.  Use `the types registered by Jabber Registrar <http://www.jabber.org/registrar/disco-categories.html>`__
            - `keepalive`: keepalive interval for the stream.

        :Types:
            - `jid`:  `pyxmpp.JID`
            - `secret`: `unicode`
            - `server`: `str` or `unicode`
            - `port`: `int`
            - `disco_name`: `unicode`
            - `disco_category`: `unicode`
            - `disco_type`: `unicode`
            - `keepalive`: `int`"""
        self.jid=jid
        self.secret=secret
        self.server=server
        self.port=port
        self.keepalive=keepalive
        self.stream=None
        self.lock=threading.RLock()
        self.state_changed=threading.Condition(self.lock)
        self.stream_class=ComponentStream
        self.disco_items=DiscoItems()
        self.disco_info=DiscoInfo()
        self.disco_identity=DiscoIdentity(self.disco_info,
                            disco_name, disco_category, disco_type)
        self.register_feature("stringprep")
        self.__logger=logging.getLogger("pyxmpp.jabberd.Component")
Пример #5
0
    def __init__(
        self,
        jid=None,
        secret=None,
        server=None,
        port=5347,
        disco_name=u"PyXMPP based component",
        disco_category=u"x-service",
        disco_type=u"x-unknown",
        keepalive=0,
    ):
        """Initialize a `Component` object.

        :Parameters:
            - `jid`: component JID (should contain only the domain part).
            - `secret`: the authentication secret.
            - `server`: server name or address the component should connect.
            - `port`: port number on the server where the component should connect.
            - `disco_name`: disco identity name to be used in the
              disco#info responses.
            - `disco_category`: disco identity category to be used in the
              disco#info responses.  Use `the categories registered by Jabber Registrar <http://www.jabber.org/registrar/disco-categories.html>`__
            - `disco_type`: disco identity type to be used in the component's
              disco#info responses.  Use `the types registered by Jabber Registrar <http://www.jabber.org/registrar/disco-categories.html>`__
            - `keepalive`: keepalive interval for the stream.

        :Types:
            - `jid`:  `pyxmpp.JID`
            - `secret`: `unicode`
            - `server`: `str` or `unicode`
            - `port`: `int`
            - `disco_name`: `unicode`
            - `disco_category`: `unicode`
            - `disco_type`: `unicode`
            - `keepalive`: `int`"""
        self.jid = jid
        self.secret = secret
        self.server = server
        self.port = port
        self.keepalive = keepalive
        self.stream = None
        self.lock = threading.RLock()
        self.state_changed = threading.Condition(self.lock)
        self.stream_class = ComponentStream
        self.disco_items = DiscoItems()
        self.disco_info = DiscoInfo()
        self.disco_identity = DiscoIdentity(self.disco_info, disco_name, disco_category, disco_type)
        self.register_feature("stringprep")
        self.__logger = logging.getLogger("pyxmpp.jabberd.Component")
Пример #6
0
    def __init__(self,jid=None, password=None, server=None, port=5222,
            auth_methods=("sasl:DIGEST-MD5","digest"),
            tls_settings=None, keepalive=0,
            disco_name=u"pyxmpp based Jabber client", disco_category=u"client",
            disco_type=u"pc"):
        """Initialize a JabberClient 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.
            - `disco_name`: name of the client identity in the disco#info
              replies.
            - `disco_category`: category of the client identity in the disco#info
              replies. The default of u'client' should be the right choice in
              most cases.
            - `disco_type`: type of the client identity in the disco#info
              replies. Use `the types registered by Jabber Registrar <http://www.jabber.org/registrar/disco-categories.html>`__
        :Types:
            - `jid`: `pyxmpp.JID`
            - `password`: `unicode`
            - `server`: `unicode`
            - `port`: `int`
            - `auth_methods`: sequence of `str`
            - `tls_settings`: `pyxmpp.TLSSettings`
            - `keepalive`: `int`
            - `disco_name`: `unicode`
            - `disco_category`: `unicode`
            - `disco_type`: `unicode`
        """

        Client.__init__(self,jid,password,server,port,auth_methods,tls_settings,keepalive)
        self.stream_class = LegacyClientStream
        self.disco_items=DiscoItems()
        self.disco_info=DiscoInfo()
        self.disco_identity=DiscoIdentity(self.disco_info,
                            disco_name, disco_category, disco_type)
        self.register_feature(u"dnssrv")
        self.register_feature(u"stringprep")
        self.register_feature(u"urn:ietf:params:xml:ns:xmpp-sasl#c2s")
        self.cache = CacheSuite(max_items = 1000)
        self.__logger = logging.getLogger("pyxmpp.jabber.JabberClient")
Пример #7
0
class JabberClient(Client):
    """Base class for a Jabber client.

    :Ivariables:
        - `disco_items`: default Disco#items reply for a query to an empty node.
        - `disco_info`: default Disco#info reply for a query to an empty node --
          provides information about the client and its supported fetures.
        - `disco_identity`: default identity of the default `disco_info`.
        - `register`: when `True` than registration will be started instead of authentication.
    :Types:
        - `disco_items`: `DiscoItems`
        - `disco_info`: `DiscoInfo`
        - `register`: `bool`
    """
    def __init__(self,
                 jid=None,
                 password=None,
                 server=None,
                 port=5222,
                 auth_methods=("sasl:DIGEST-MD5", "digest"),
                 tls_settings=None,
                 keepalive=0,
                 disco_name=u"pyxmpp based Jabber client",
                 disco_category=u"client",
                 disco_type=u"pc"):
        """Initialize a JabberClient 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.
            - `disco_name`: name of the client identity in the disco#info
              replies.
            - `disco_category`: category of the client identity in the disco#info
              replies. The default of u'client' should be the right choice in
              most cases.
            - `disco_type`: type of the client identity in the disco#info
              replies. Use `the types registered by Jabber Registrar <http://www.jabber.org/registrar/disco-categories.html>`__
        :Types:
            - `jid`: `pyxmpp.JID`
            - `password`: `unicode`
            - `server`: `unicode`
            - `port`: `int`
            - `auth_methods`: sequence of `str`
            - `tls_settings`: `pyxmpp.TLSSettings`
            - `keepalive`: `int`
            - `disco_name`: `unicode`
            - `disco_category`: `unicode`
            - `disco_type`: `unicode`
        """

        Client.__init__(self, jid, password, server, port, auth_methods,
                        tls_settings, keepalive)
        self.stream_class = LegacyClientStream
        self.disco_items = DiscoItems()
        self.disco_info = DiscoInfo()
        self.disco_identity = DiscoIdentity(self.disco_info, disco_name,
                                            disco_category, disco_type)
        self.register_feature(u"dnssrv")
        self.register_feature(u"stringprep")
        self.register_feature(u"urn:ietf:params:xml:ns:xmpp-sasl#c2s")
        self.cache = CacheSuite(max_items=1000)
        self.__logger = logging.getLogger("pyxmpp.jabber.JabberClient")

# 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. Additionally, initialize Disco items and info of the client.
        """
        Client.connect(self, register)
        if register:
            self.stream.registration_callback = self.process_registration_form

    def register_feature(self, feature_name):
        """Register a feature to be announced by Service Discovery.

        :Parameters:
            - `feature_name`: feature namespace or name.
        :Types:
            - `feature_name`: `unicode`"""
        self.disco_info.add_feature(feature_name)

    def unregister_feature(self, feature_name):
        """Unregister a feature to be announced by Service Discovery.

        :Parameters:
            - `feature_name`: feature namespace or name.
        :Types:
            - `feature_name`: `unicode`"""
        self.disco_info.remove_feature(feature_name)

    def submit_registration_form(self, form):
        """Submit a registration form

        :Parameters:
            - `form`: the form to submit
        :Types:
            - `form`: `pyxmpp.jabber.dataforms.Form`"""
        self.stream.submit_registration_form(form)

# private methods

    def __disco_info(self, iq):
        """Handle a disco#info request.

        `self.disco_get_info` method will be used to prepare the query response.

        :Parameters:
            - `iq`: the IQ stanza received.
        :Types:
            - `iq`: `pyxmpp.iq.Iq`"""
        q = iq.get_query()
        if q.hasProp("node"):
            node = from_utf8(q.prop("node"))
        else:
            node = None
        info = self.disco_get_info(node, iq)
        if isinstance(info, DiscoInfo):
            resp = iq.make_result_response()
            self.__logger.debug(
                "Disco-info query: %s preparing response: %s with reply: %s" %
                (iq.serialize(), resp.serialize(), info.xmlnode.serialize()))
            resp.set_content(info.xmlnode.copyNode(1))
        elif isinstance(info, Stanza):
            resp = info
        else:
            resp = iq.make_error_response("item-not-found")
        self.__logger.debug("Disco-info response: %s" % (resp.serialize(), ))
        self.stream.send(resp)

    def __disco_items(self, iq):
        """Handle a disco#items request.

        `self.disco_get_items` method will be used to prepare the query response.

        :Parameters:
            - `iq`: the IQ stanza received.
        :Types:
            - `iq`: `pyxmpp.iq.Iq`"""
        q = iq.get_query()
        if q.hasProp("node"):
            node = from_utf8(q.prop("node"))
        else:
            node = None
        items = self.disco_get_items(node, iq)
        if isinstance(items, DiscoItems):
            resp = iq.make_result_response()
            self.__logger.debug(
                "Disco-items query: %s preparing response: %s with reply: %s" %
                (iq.serialize(), resp.serialize(), items.xmlnode.serialize()))
            resp.set_content(items.xmlnode.copyNode(1))
        elif isinstance(items, Stanza):
            resp = items
        else:
            resp = iq.make_error_response("item-not-found")
        self.__logger.debug("Disco-items response: %s" % (resp.serialize(), ))
        self.stream.send(resp)

    def _session_started(self):
        """Called when session is started.
        
        Activates objects from `self.interface_provides` by installing
        their disco features."""
        Client._session_started(self)
        for ob in self.interface_providers:
            if IFeaturesProvider.providedBy(ob):
                for ns in ob.get_features():
                    self.register_feature(ns)

# methods to override

    def authorized(self):
        """Handle "authorized" event. May be overriden in derived classes.
        By default: request an IM session and setup Disco handlers."""
        Client.authorized(self)
        self.stream.set_iq_get_handler("query", DISCO_ITEMS_NS,
                                       self.__disco_items)
        self.stream.set_iq_get_handler("query", DISCO_INFO_NS,
                                       self.__disco_info)
        disco.register_disco_cache_fetchers(self.cache, self.stream)

    def disco_get_info(self, node, iq):
        """Return Disco#info data for a node.

        :Parameters:
            - `node`: the node queried.
            - `iq`: the request stanza received.
        :Types:
            - `node`: `unicode`
            - `iq`: `pyxmpp.iq.Iq`

        :return: self.disco_info if `node` is empty or `None` otherwise.
        :returntype: `DiscoInfo`"""
        to = iq.get_to()
        if to and to != self.jid:
            return iq.make_error_response("recipient-unavailable")
        if not node and self.disco_info:
            return self.disco_info
        return None

    def disco_get_items(self, node, iq):
        """Return Disco#items data for a node.

        :Parameters:
            - `node`: the node queried.
            - `iq`: the request stanza received.
        :Types:
            - `node`: `unicode`
            - `iq`: `pyxmpp.iq.Iq`

        :return: self.disco_info if `node` is empty or `None` otherwise.
        :returntype: `DiscoInfo`"""
        to = iq.get_to()
        if to and to != self.jid:
            return iq.make_error_response("recipient-unavailable")
        if not node and self.disco_items:
            return self.disco_items
        return None

    def process_registration_form(self, stanza, form):
        """Fill-in the registration form provided by the server.

        This default implementation fills-in "username" and "passwords"
        fields only and instantly submits the form.

        :Parameters:
            - `stanza`: the stanza received.
            - `form`: the registration form.
        :Types:
            - `stanza`: `pyxmpp.iq.Iq`
            - `form`: `pyxmpp.jabber.dataforms.Form`
        """
        _unused = stanza
        self.__logger.debug(
            u"default registration callback started. auto-filling-in the form..."
        )
        if not 'FORM_TYPE' in form or 'jabber:iq:register' not in form[
                'FORM_TYPE'].values:
            raise RuntimeError, "Unknown form type: %r %r" % (
                form, form['FORM_TYPE'])
        for field in form:
            if field.name == u"username":
                self.__logger.debug(u"Setting username to %r" %
                                    (self.jid.node, ))
                field.value = self.jid.node
            elif field.name == u"password":
                self.__logger.debug_s(
                    u"Setting password to %r.decode('rot13')" %
                    (self.password.encode('rot13'), ))
                field.value = self.password
            elif field.required:
                self.__logger.debug(u"Unknown required field: %r" %
                                    (field.name, ))
                raise RuntimeError, "Unsupported required registration form field %r" % (
                    field.name, )
            else:
                self.__logger.debug(u"Unknown field: %r" % (field.name, ))
        self.submit_registration_form(form)
Пример #8
0
class JabberClient(Client):
    """Base class for a Jabber client.

    :Ivariables:
        - `disco_items`: default Disco#items reply for a query to an empty node.
        - `disco_info`: default Disco#info reply for a query to an empty node --
          provides information about the client and its supported fetures.
        - `disco_identity`: default identity of the default `disco_info`.
        - `register`: when `True` than registration will be started instead of authentication.
    :Types:
        - `disco_items`: `DiscoItems`
        - `disco_info`: `DiscoInfo`
        - `register`: `bool`
    """
    def __init__(self,jid=None, password=None, server=None, port=5222,
            auth_methods=("sasl:DIGEST-MD5","digest"),
            tls_settings=None, keepalive=0,
            disco_name=u"pyxmpp based Jabber client", disco_category=u"client",
            disco_type=u"pc"):
        """Initialize a JabberClient 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.
            - `disco_name`: name of the client identity in the disco#info
              replies.
            - `disco_category`: category of the client identity in the disco#info
              replies. The default of u'client' should be the right choice in
              most cases.
            - `disco_type`: type of the client identity in the disco#info
              replies. Use `the types registered by Jabber Registrar <http://www.jabber.org/registrar/disco-categories.html>`__
        :Types:
            - `jid`: `pyxmpp.JID`
            - `password`: `unicode`
            - `server`: `unicode`
            - `port`: `int`
            - `auth_methods`: sequence of `str`
            - `tls_settings`: `pyxmpp.TLSSettings`
            - `keepalive`: `int`
            - `disco_name`: `unicode`
            - `disco_category`: `unicode`
            - `disco_type`: `unicode`
        """

        Client.__init__(self,jid,password,server,port,auth_methods,tls_settings,keepalive)
        self.stream_class = LegacyClientStream
        self.disco_items=DiscoItems()
        self.disco_info=DiscoInfo()
        self.disco_identity=DiscoIdentity(self.disco_info,
                            disco_name, disco_category, disco_type)
        self.register_feature(u"dnssrv")
        self.register_feature(u"stringprep")
        self.register_feature(u"urn:ietf:params:xml:ns:xmpp-sasl#c2s")
        self.cache = CacheSuite(max_items = 1000)
        self.__logger = logging.getLogger("pyxmpp.jabber.JabberClient")

# 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. Additionally, initialize Disco items and info of the client.
        """
        Client.connect(self, register)
        if register:
            self.stream.registration_callback = self.process_registration_form

    def register_feature(self, feature_name):
        """Register a feature to be announced by Service Discovery.

        :Parameters:
            - `feature_name`: feature namespace or name.
        :Types:
            - `feature_name`: `unicode`"""
        self.disco_info.add_feature(feature_name)

    def unregister_feature(self, feature_name):
        """Unregister a feature to be announced by Service Discovery.

        :Parameters:
            - `feature_name`: feature namespace or name.
        :Types:
            - `feature_name`: `unicode`"""
        self.disco_info.remove_feature(feature_name)

    def submit_registration_form(self, form):
        """Submit a registration form

        :Parameters:
            - `form`: the form to submit
        :Types:
            - `form`: `pyxmpp.jabber.dataforms.Form`"""
        self.stream.submit_registration_form(form)

# private methods
    def __disco_info(self,iq):
        """Handle a disco#info request.

        `self.disco_get_info` method will be used to prepare the query response.

        :Parameters:
            - `iq`: the IQ stanza received.
        :Types:
            - `iq`: `pyxmpp.iq.Iq`"""
        q=iq.get_query()
        if q.hasProp("node"):
            node=from_utf8(q.prop("node"))
        else:
            node=None
        info=self.disco_get_info(node,iq)
        if isinstance(info,DiscoInfo):
            resp=iq.make_result_response()
            self.__logger.debug("Disco-info query: %s preparing response: %s with reply: %s"
                % (iq.serialize(),resp.serialize(),info.xmlnode.serialize()))
            resp.set_content(info.xmlnode.copyNode(1))
        elif isinstance(info,Stanza):
            resp=info
        else:
            resp=iq.make_error_response("item-not-found")
        self.__logger.debug("Disco-info response: %s" % (resp.serialize(),))
        self.stream.send(resp)

    def __disco_items(self,iq):
        """Handle a disco#items request.

        `self.disco_get_items` method will be used to prepare the query response.

        :Parameters:
            - `iq`: the IQ stanza received.
        :Types:
            - `iq`: `pyxmpp.iq.Iq`"""
        q=iq.get_query()
        if q.hasProp("node"):
            node=from_utf8(q.prop("node"))
        else:
            node=None
        items=self.disco_get_items(node,iq)
        if isinstance(items,DiscoItems):
            resp=iq.make_result_response()
            self.__logger.debug("Disco-items query: %s preparing response: %s with reply: %s"
                % (iq.serialize(),resp.serialize(),items.xmlnode.serialize()))
            resp.set_content(items.xmlnode.copyNode(1))
        elif isinstance(items,Stanza):
            resp=items
        else:
            resp=iq.make_error_response("item-not-found")
        self.__logger.debug("Disco-items response: %s" % (resp.serialize(),))
        self.stream.send(resp)

    def _session_started(self):
        """Called when session is started.
        
        Activates objects from `self.interface_provides` by installing
        their disco features."""
        Client._session_started(self)
        for ob in self.interface_providers:
            if IFeaturesProvider.providedBy(ob):
                for ns in ob.get_features():
                    self.register_feature(ns)

# methods to override

    def authorized(self):
        """Handle "authorized" event. May be overriden in derived classes.
        By default: request an IM session and setup Disco handlers."""
        Client.authorized(self)
        self.stream.set_iq_get_handler("query",DISCO_ITEMS_NS,self.__disco_items)
        self.stream.set_iq_get_handler("query",DISCO_INFO_NS,self.__disco_info)
        disco.register_disco_cache_fetchers(self.cache,self.stream)

    def disco_get_info(self,node,iq):
        """Return Disco#info data for a node.

        :Parameters:
            - `node`: the node queried.
            - `iq`: the request stanza received.
        :Types:
            - `node`: `unicode`
            - `iq`: `pyxmpp.iq.Iq`

        :return: self.disco_info if `node` is empty or `None` otherwise.
        :returntype: `DiscoInfo`"""
        to=iq.get_to()
        if to and to!=self.jid:
            return iq.make_error_response("recipient-unavailable")
        if not node and self.disco_info:
            return self.disco_info
        return None

    def disco_get_items(self,node,iq):
        """Return Disco#items data for a node.

        :Parameters:
            - `node`: the node queried.
            - `iq`: the request stanza received.
        :Types:
            - `node`: `unicode`
            - `iq`: `pyxmpp.iq.Iq`

        :return: self.disco_info if `node` is empty or `None` otherwise.
        :returntype: `DiscoInfo`"""
        to=iq.get_to()
        if to and to!=self.jid:
            return iq.make_error_response("recipient-unavailable")
        if not node and self.disco_items:
            return self.disco_items
        return None

    def process_registration_form(self, stanza, form):
        """Fill-in the registration form provided by the server.

        This default implementation fills-in "username" and "passwords"
        fields only and instantly submits the form.

        :Parameters:
            - `stanza`: the stanza received.
            - `form`: the registration form.
        :Types:
            - `stanza`: `pyxmpp.iq.Iq`
            - `form`: `pyxmpp.jabber.dataforms.Form`
        """
        _unused = stanza
        self.__logger.debug(u"default registration callback started. auto-filling-in the form...")
        if not 'FORM_TYPE' in form or 'jabber:iq:register' not in form['FORM_TYPE'].values:
            raise RuntimeError, "Unknown form type: %r %r" % (form, form['FORM_TYPE'])
        for field in form:
            if field.name == u"username":
                self.__logger.debug(u"Setting username to %r" % (self.jid.node,))
                field.value = self.jid.node
            elif field.name == u"password":
                self.__logger.debug_s(u"Setting password to %r.decode('rot13')" % (self.password.encode('rot13'),))
                field.value = self.password
            elif field.required:
                self.__logger.debug(u"Unknown required field: %r" % (field.name,))
                raise RuntimeError, "Unsupported required registration form field %r" % (field.name,)
            else:
                self.__logger.debug(u"Unknown field: %r" % (field.name,))
        self.submit_registration_form(form)
Пример #9
0
Файл: disco.py Проект: dax/jcl
 def handle(self, stanza, lang_class, node, disco_obj, data):
     """Implement discovery get_info on main component JID"""
     self.__logger.debug("root_disco_get_info")
     disco_info = DiscoInfo(node)
     disco_info.add_feature("jabber:iq:version")
     disco_info.add_feature("http://jabber.org/protocol/disco#info")
     disco_info.add_feature("http://jabber.org/protocol/disco#items")
     disco_info.add_feature(vcard.VCARD_NS)
     disco_info.add_feature("jabber:iq:last")
     if not self.component.account_manager.has_multiple_account_type:
         disco_info.add_feature("jabber:iq:register")
     DiscoIdentity(disco_info, self.component.name,
                   self.component.disco_identity.category,
                   self.component.disco_identity.type)
     return [disco_info]
Пример #10
0
class Component:
    """Jabber external component ("jabber:component:accept" protocol) interface
    implementation.

    Override this class to build your components.

    :Ivariables:
        - `jid`: component JID (should contain only the domain part).
        - `secret`: the authentication secret.
        - `server`: server to which the commonent will connect.
        - `port`: port number on the server to which the commonent will
          connect.
        - `keepalive`: keepalive interval for the stream.
        - `stream`: the XMPP stream object for the active connection
          or `None` if no connection is active.
        - `disco_items`: disco items announced by the component. Created
          when a stream is connected.
        - `disco_info`: disco info announced by the component. Created
          when a stream is connected.
        - `disco_identity`: disco identity (part of disco info) announced by
          the component. Created when a stream is connected.
        - `disco_category`: disco category to be used to create
          `disco_identity`.
        - `disco_type`: disco type to be used to create `disco_identity`.

    :Types:
        - `jid`:  `pyxmpp.JID`
        - `secret`: `unicode`
        - `server`: `unicode`
        - `port`: `int`
        - `keepalive`: `int`
        - `stream`: `pyxmpp.jabberd.ComponentStream`
        - `disco_items`: `pyxmpp.jabber.DiscoItems`
        - `disco_info`: `pyxmpp.jabber.DiscoInfo`
        - `disco_identity`: `pyxmpp.jabber.DiscoIdentity`
        - `disco_category`: `str`
        - `disco_type`: `str`"""
    def __init__(self, jid=None, secret=None, server=None, port=5347,
            disco_name=u"PyXMPP based component", disco_category=u"x-service",
            disco_type=u"x-unknown", keepalive=0):
        """Initialize a `Component` object.

        :Parameters:
            - `jid`: component JID (should contain only the domain part).
            - `secret`: the authentication secret.
            - `server`: server name or address the component should connect.
            - `port`: port number on the server where the component should connect.
            - `disco_name`: disco identity name to be used in the
              disco#info responses.
            - `disco_category`: disco identity category to be used in the
              disco#info responses.  Use `the categories registered by Jabber Registrar <http://www.jabber.org/registrar/disco-categories.html>`__
            - `disco_type`: disco identity type to be used in the component's
              disco#info responses.  Use `the types registered by Jabber Registrar <http://www.jabber.org/registrar/disco-categories.html>`__
            - `keepalive`: keepalive interval for the stream.

        :Types:
            - `jid`:  `pyxmpp.JID`
            - `secret`: `unicode`
            - `server`: `str` or `unicode`
            - `port`: `int`
            - `disco_name`: `unicode`
            - `disco_category`: `unicode`
            - `disco_type`: `unicode`
            - `keepalive`: `int`"""
        self.jid=jid
        self.secret=secret
        self.server=server
        self.port=port
        self.keepalive=keepalive
        self.stream=None
        self.lock=threading.RLock()
        self.state_changed=threading.Condition(self.lock)
        self.stream_class=ComponentStream
        self.disco_items=DiscoItems()
        self.disco_info=DiscoInfo()
        self.disco_identity=DiscoIdentity(self.disco_info,
                            disco_name, disco_category, disco_type)
        self.register_feature("stringprep")
        self.__logger=logging.getLogger("pyxmpp.jabberd.Component")

# public methods

    def connect(self):
        """Establish a connection with the server.

        Set `self.stream` to the `pyxmpp.jabberd.ComponentStream` when
        initial connection succeeds.

        :raise ValueError: when some of the component properties
          (`self.jid`, `self.secret`,`self.server` or `self.port`) are wrong."""
        if not self.jid or self.jid.node or self.jid.resource:
            raise ValueError,"Cannot connect: no or bad JID given"
        if not self.secret:
            raise ValueError,"Cannot connect: no secret given"
        if not self.server:
            raise ValueError,"Cannot connect: no server given"
        if not self.port:
            raise ValueError,"Cannot connect: no port given"

        self.lock.acquire()
        try:
            stream=self.stream
            self.stream=None
            if stream:
                stream.close()

            self.__logger.debug("Creating component stream: %r" % (self.stream_class,))
            stream=self.stream_class(jid = self.jid,
                    secret = self.secret,
                    server = self.server,
                    port = self.port,
                    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 stream of the component in a safe way.

        :return: Stream object for the component or `None` if no connection is
            active.
        :returntype: `pyxmpp.jabberd.ComponentStream`"""
        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 socket(self):
        """Get the socket of the connection to the server.

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

    def loop(self,timeout=1):
        """Simple 'main loop' for a component.

        This usually will be replaced by something more sophisticated. E.g.
        handling of other input sources."""
        self.stream.loop(timeout)

    def register_feature(self, feature_name):
        """Register a feature to be announced by Service Discovery.

        :Parameters:
            - `feature_name`: feature namespace or name.
        :Types:
            - `feature_name`: `unicode`"""
        self.disco_info.add_feature(feature_name)

    def unregister_feature(self, feature_name):
        """Unregister a feature to be announced by Service Discovery.

        :Parameters:
            - `feature_name`: feature namespace or name.
        :Types:
            - `feature_name`: `unicode`"""
        self.disco_info.remove_feature(feature_name)


# private methods
    def __stream_state_change(self,state,arg):
        """Handle various stream state changes and call right
        methods of `self`.

        :Parameters:
            - `state`: state name.
            - `arg`: state parameter.
        :Types:
            - `state`: `string`
            - `arg`: any object"""
        self.stream_state_changed(state,arg)
        if state=="fully connected":
            self.connected()
        elif state=="authenticated":
            self.authenticated()
        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()

    def __disco_info(self,iq):
        """Handle a disco-info query.

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

        Types:
            - `iq`: `pyxmpp.Iq`"""
        q=iq.get_query()
        if q.hasProp("node"):
            node=from_utf8(q.prop("node"))
        else:
            node=None
        info=self.disco_get_info(node,iq)
        if isinstance(info,DiscoInfo):
            resp=iq.make_result_response()
            self.__logger.debug("Disco-info query: %s preparing response: %s with reply: %s"
                % (iq.serialize(),resp.serialize(),info.xmlnode.serialize()))
            resp.set_content(info.xmlnode.copyNode(1))
        elif isinstance(info,Stanza):
            resp=info
        else:
            resp=iq.make_error_response("item-not-found")
        self.__logger.debug("Disco-info response: %s" % (resp.serialize(),))
        self.stream.send(resp)

    def __disco_items(self,iq):
        """Handle a disco-items query.

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

        Types:
            - `iq`: `pyxmpp.Iq`"""
        q=iq.get_query()
        if q.hasProp("node"):
            node=from_utf8(q.prop("node"))
        else:
            node=None
        items=self.disco_get_items(node,iq)
        if isinstance(items,DiscoItems):
            resp=iq.make_result_response()
            self.__logger.debug("Disco-items query: %s preparing response: %s with reply: %s"
                % (iq.serialize(),resp.serialize(),items.xmlnode.serialize()))
            resp.set_content(items.xmlnode.copyNode(1))
        elif isinstance(items,Stanza):
            resp=items
        else:
            resp=iq.make_error_response("item-not-found")
        self.__logger.debug("Disco-items response: %s" % (resp.serialize(),))
        self.stream.send(resp)

# Method to override
    def idle(self):
        """Do some "housekeeping" work like <iq/> result expiration. Should be
        called on a regular basis, usually when the component is idle."""
        stream=self.get_stream()
        if stream:
            stream.idle()

    def stream_created(self,stream):
        """Handle stream creation event.

        [may be overriden in derived classes]

        By default: do nothing.

        :Parameters:
            - `stream`: the stream just created.
        :Types:
            - `stream`: `pyxmpp.jabberd.ComponentStream`"""
        pass

    def stream_closed(self,stream):
        """Handle stream closure event.

        [may be overriden in derived classes]

        By default: do nothing.

        :Parameters:
            - `stream`: the stream just created.
        :Types:
            - `stream`: `pyxmpp.jabberd.ComponentStream`"""
        pass

    def stream_error(self,err):
        """Handle a stream error received.

        [may be overriden in derived classes]

        By default: just log it. The stream will be closed anyway.

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

    def stream_state_changed(self,state,arg):
        """Handle a stream state change.

        [may be overriden in derived classes]

        By default: do nothing.

        :Parameters:
            - `state`: state name.
            - `arg`: state parameter.
        :Types:
            - `state`: `string`
            - `arg`: any object"""
        pass

    def connected(self):
        """Handle stream connection event.

        [may be overriden in derived classes]

        By default: do nothing."""
        pass

    def authenticated(self):
        """Handle successful authentication event.

        A good place to register stanza handlers and disco features.

        [should be overriden in derived classes]

        By default: set disco#info and disco#items handlers."""
        self.__logger.debug("Setting up Disco handlers...")
        self.stream.set_iq_get_handler("query","http://jabber.org/protocol/disco#items",
                                    self.__disco_items)
        self.stream.set_iq_get_handler("query","http://jabber.org/protocol/disco#info",
                                    self.__disco_info)

    def authorized(self):
        """Handle successful authorization event."""
        pass

    def disco_get_info(self,node,iq):
        """Get disco#info data for a node.

        [may be overriden in derived classes]

        By default: return `self.disco_info` if no specific node name
        is provided.

        :Parameters:
            - `node`: name of the node queried.
            - `iq`: the stanza received.
        :Types:
            - `node`: `unicode`
            - `iq`: `pyxmpp.Iq`"""
        to=iq.get_to()
        if to and to!=self.jid:
            return iq.make_error_response("recipient-unavailable")
        if not node and self.disco_info:
            return self.disco_info
        return None

    def disco_get_items(self,node,iq):
        """Get disco#items data for a node.

        [may be overriden in derived classes]

        By default: return `self.disco_items` if no specific node name
        is provided.

        :Parameters:
            - `node`: name of the node queried.
            - `iq`: the stanza received.
        :Types:
            - `node`: `unicode`
            - `iq`: `pyxmpp.Iq`"""
        to=iq.get_to()
        if to and to!=self.jid:
            return iq.make_error_response("recipient-unavailable")
        if not node and self.disco_items:
            return self.disco_items
        return None

    def disconnected(self):
        """Handle stream disconnection (connection closed by peer) event.

        [may be overriden in derived classes]

        By default: do nothing."""
        pass
Пример #11
0
 def disco_get_info(self, node, iq):
     to = iq.get_to()
     try:
         network = self.config.get_network(to)
     except KeyError:
         return iq.make_error_response("recipient-unavailable")
     if to.node is None and to.resource is None:
         di = DiscoInfo()
         if node is None:
             di.add_feature("jabber:iq:version")
             di.add_feature("jabber:iq:register")
             di.add_feature(MUC_NS)
             if network.name:
                 name = network.name
             else:
                 name = "IRC gateway"
             DiscoIdentity(di, name, "conference", "irc")
             DiscoIdentity(di, name, "conference", "text")
             DiscoIdentity(di, name, "gateway", "x-irc")
         return di
     elif len(
             to.node) > 1 and to.node[0] in u"&#+!" and to.resource is None:
         di = DiscoInfo()
         di.add_feature(MUC_NS)
         if network.name:
             name = "%s channel on %s IRC network" % (to.node, network.name)
         else:
             name = "%s IRC channel" % (to.node, )
         DiscoIdentity(di, name, "conference", "text")
         DiscoIdentity(di, name, "conference", "irc")
         return di
     return iq.make_error_response("feature-not-implemented")