diff options
author | Lance Stout <lancestout@gmail.com> | 2010-12-16 22:03:56 -0500 |
---|---|---|
committer | Lance Stout <lancestout@gmail.com> | 2010-12-16 22:03:56 -0500 |
commit | adade2e5eccf5a0c48b0b6541fc3d990d732710c (patch) | |
tree | 9d5bd167058070bfe2b90a7a093e53978a4e29ed /sleekxmpp/plugins/xep_0030 | |
parent | c16913c99929a6a5a57611ec8a1757e3e82d4a45 (diff) | |
parent | cbc42c29fb02a6fd22a0c303e8d02364545c86cf (diff) | |
download | slixmpp-adade2e5eccf5a0c48b0b6541fc3d990d732710c.tar.gz slixmpp-adade2e5eccf5a0c48b0b6541fc3d990d732710c.tar.bz2 slixmpp-adade2e5eccf5a0c48b0b6541fc3d990d732710c.tar.xz slixmpp-adade2e5eccf5a0c48b0b6541fc3d990d732710c.zip |
Merge branch 'develop' into roster
Diffstat (limited to 'sleekxmpp/plugins/xep_0030')
-rw-r--r-- | sleekxmpp/plugins/xep_0030/disco.py | 366 | ||||
-rw-r--r-- | sleekxmpp/plugins/xep_0030/stanza/disco.py | 0 | ||||
-rw-r--r-- | sleekxmpp/plugins/xep_0030/stanza/items.py | 4 | ||||
-rw-r--r-- | sleekxmpp/plugins/xep_0030/static.py | 49 |
4 files changed, 335 insertions, 84 deletions
diff --git a/sleekxmpp/plugins/xep_0030/disco.py b/sleekxmpp/plugins/xep_0030/disco.py index c323ba7c..ad3d0ae2 100644 --- a/sleekxmpp/plugins/xep_0030/disco.py +++ b/sleekxmpp/plugins/xep_0030/disco.py @@ -26,30 +26,66 @@ class xep_0030(base_plugin): """ XEP-0030: Service Discovery + Service discovery in XMPP allows entities to discover information about + other agents in the network, such as the feature sets supported by a + client, or signposts to other, related entities. + + Also see <http://www.xmpp.org/extensions/xep-0030.html>. + + The XEP-0030 plugin works using a hierarchy of dynamic + node handlers, ranging from global handlers to specific + JID+node handlers. The default set of handlers operate + in a static manner, storing disco information in memory. + However, custom handlers may use any available backend + storage mechanism desired, such as SQLite or Redis. + + Node handler hierarchy: + JID | Node | Level + --------------------- + None | None | Global + Given | None | All nodes for the JID + None | Given | Node on self.xmpp.boundjid + Given | Given | A single node + Stream Handlers: - Disco Info -- - Disco Items -- + Disco Info -- Any Iq stanze that includes a query with the + namespace http://jabber.org/protocol/disco#info. + Disco Items -- Any Iq stanze that includes a query with the + namespace http://jabber.org/protocol/disco#items. Events: - disco_info -- - disco_items -- - disco_info_query -- - disco_items_query -- + disco_info -- Received a disco#info Iq query result. + disco_items -- Received a disco#items Iq query result. + disco_info_query -- Received a disco#info Iq query request. + disco_items_query -- Received a disco#items Iq query request. + + Attributes: + stanza -- A reference to the module containing the stanza classes + provided by this plugin. + static -- Object containing the default set of static node handlers. + xmpp -- The main SleekXMPP object. Methods: - set_node_handler -- - del_node_handler -- - add_identity -- + set_node_handler -- Assign a handler to a JID/node combination. + del_node_handler -- Remove a handler from a JID/node combination. + get_info -- Retrieve disco#info data, locally or remote. + get_items -- Retrieve disco#items data, locally or remote. + set_identities -- + set_features -- + set_items -- + del_items -- del_identity -- - add_feature -- del_feature -- - add_item -- del_item -- - get_info -- - get_items -- + add_identity -- + add_feature -- + add_item -- """ def plugin_init(self): + """ + Start the XEP-0030 plugin. + """ self.xep = '0030' self.description = 'Service Discovery' self.stanza = sleekxmpp.plugins.xep_0030.stanza @@ -70,42 +106,89 @@ class xep_0030(base_plugin): self.static = StaticDisco(self.xmpp) self._disco_ops = ['get_info', 'set_identities', 'set_features', - 'del_info', 'get_items', 'set_items', 'del_items', + 'get_items', 'set_items', 'del_items', 'add_identity', 'del_identity', 'add_feature', 'del_feature', 'add_item', 'del_item'] - self.handlers = {} + self._handlers = {} for op in self._disco_ops: - self.handlers[op] = {'global': getattr(self.static, op), - 'jid': {}, - 'node': {}} - + self._handlers[op] = {'global': getattr(self.static, op), + 'jid': {}, + 'node': {}} def set_node_handler(self, htype, jid=None, node=None, handler=None): """ + Add a node handler for the given hierarchy level and + handler type. + + Node handlers are ordered in a hierarchy where the + most specific handler is executed. Thus, a fallback, + global handler can be used for the majority of cases + with a few node specific handler that override the + global behavior. + + Node handler hierarchy: + JID | Node | Level + --------------------- + None | None | Global + Given | None | All nodes for the JID + None | Given | Node on self.xmpp.boundjid + Given | Given | A single node + + Handler types: + get_info + get_items + set_identities + set_features + set_items + del_items + del_identity + del_feature + del_item + add_identity + add_feature + add_item + Arguments: - htype - jid - node - handler + htype -- The operation provided by the handler. + jid -- The JID the handler applies to. May be narrowed + further if a node is given. + node -- The particular node the handler is for. If no JID + is given, then the self.xmpp.boundjid.full is + assumed. + handler -- The handler function to use. """ if htype not in self._disco_ops: return if jid is None and node is None: - self.handlers[htype]['global'] = handler + self._handlers[htype]['global'] = handler elif node is None: - self.handlers[htype]['jid'][jid] = handler + self._handlers[htype]['jid'][jid] = handler elif jid is None: jid = self.xmpp.boundjid.full - self.handlers[htype]['node'][(jid, node)] = handler + self._handlers[htype]['node'][(jid, node)] = handler else: - self.handlers[htype]['node'][(jid, node)] = handler + self._handlers[htype]['node'][(jid, node)] = handler def del_node_handler(self, htype, jid, node): """ + Remove a handler type for a JID and node combination. + + The next handler in the hierarchy will be used if one + exists. If removing the global handler, make sure that + other handlers exist to process existing nodes. + + Node handler hierarchy: + JID | Node | Level + --------------------- + None | None | Global + Given | None | All nodes for the JID + None | Given | Node on self.xmpp.boundjid + Given | Given | A single node + Arguments: - htype - jid - node + htype -- The type of handler to remove. + jid -- The JID from which to remove the handler. + node -- The node from which to remove the handler. """ self.set_node_handler(htype, jid, node, None) @@ -132,14 +215,28 @@ class xep_0030(base_plugin): def get_info(self, jid=None, node=None, local=False, **kwargs): """ + Retrieve the disco#info results from a given JID/node combination. + + Info may be retrieved from both local resources and remote agents; + the local parameter indicates if the information should be gathered + by executing the local node handlers, or if a disco#info stanza + must be generated and sent. + Arguments: - jid -- - node -- - local -- - dfrom -- - block -- - timeout -- - callback -- + jid -- Request info from this JID. + node -- The particular node to query. + local -- If true, then the query is for a JID/node + combination handled by this Sleek instance and + no stanzas need to be sent. + Otherwise, a disco stanza must be sent to the + remove JID to retrieve the info. + dfrom -- Specifiy the sender's JID. + block -- If true, block and wait for the stanzas' reply. + timeout -- The time in seconds to block while waiting for + a reply. If None, then wait indefinitely. + callback -- Optional callback to execute when a reply is + received instead of blocking and waiting for + the reply. """ if local or jid is None: log.debug("Looking up local disco#info data " + \ @@ -158,14 +255,28 @@ class xep_0030(base_plugin): def get_items(self, jid=None, node=None, local=False, **kwargs): """ + Retrieve the disco#items results from a given JID/node combination. + + Items may be retrieved from both local resources and remote agents; + the local parameter indicates if the items should be gathered by + executing the local node handlers, or if a disco#items stanza must + be generated and sent. + Arguments: - jid -- - node -- - local -- - dfrom -- - block -- - timeout -- - callback -- + jid -- Request info from this JID. + node -- The particular node to query. + local -- If true, then the query is for a JID/node + combination handled by this Sleek instance and + no stanzas need to be sent. + Otherwise, a disco stanza must be sent to the + remove JID to retrieve the items. + dfrom -- Specifiy the sender's JID. + block -- If true, block and wait for the stanzas' reply. + timeout -- The time in seconds to block while waiting for + a reply. If None, then wait indefinitely. + callback -- Optional callback to execute when a reply is + received instead of blocking and waiting for + the reply. """ if local or jid is None: return self._run_node_handler('get_items', jid, node, kwargs) @@ -179,37 +290,169 @@ class xep_0030(base_plugin): block=kwargs.get('block', None), callback=kwargs.get('callback', None)) - def set_info(self, jid=None, node=None, **kwargs): - self._run_node_handler('set_info', jid, node, kwargs) + def set_items(self, jid=None, node=None, **kwargs): + """ + Set or replace all items for the specified JID/node combination. - def del_info(self, jid=None, node=None, **kwargs): - self._run_node_handler('del_info', jid, node, kwargs) + The given items must be in a list or set where each item is a + tuple of the form: (jid, node, name). - def set_items(self, jid=None, node=None, **kwargs): + Arguments: + jid -- The JID to modify. + node -- Optional node to modify. + items -- A series of items in tuple format. + """ self._run_node_handler('set_items', jid, node, kwargs) def del_items(self, jid=None, node=None, **kwargs): + """ + Remove all items from the given JID/node combination. + + Arguments: + jid -- The JID to modify. + node -- Optional node to modify. + """ self._run_node_handler('del_items', jid, node, kwargs) + def add_item(self, jid=None, node=None, **kwargs): + """ + Add a new item element to the given JID/node combination. + + Each item is required to have a JID, but may also specify + a node value to reference non-addressable entities. + + Arguments: + jid -- The JID to modify. + node -- The node to modify. + ijid -- The JID for the item. + inode -- Optional node for the item. + name -- Optional name for the item. + """ + self._run_node_handler('add_item', jid, node, kwargs) + + def del_item(self, jid=None, node=None, **kwargs): + """ + Remove a single item from the given JID/node combination. + + Arguments: + jid -- The JID to modify. + node -- The node to modify. + ijid -- The item's JID. + inode -- The item's node. + """ + self._run_node_handler('del_item', jid, node, kwargs) + def add_identity(self, jid=None, node=None, **kwargs): + """ + Add a new identity to the given JID/node combination. + + Each identity must be unique in terms of all four identity + components: category, type, name, and language. + + Multiple, identical category/type pairs are allowed only + if the xml:lang values are different. Likewise, multiple + category/type/xml:lang pairs are allowed so long as the + names are different. A category and type is always required. + + Arguments: + jid -- The JID to modify. + node -- The node to modify. + category -- The identity's category. + itype -- The identity's type. + name -- Optional name for the identity. + lang -- Optional two-letter language code. + """ self._run_node_handler('add_identity', jid, node, kwargs) def add_feature(self, jid=None, node=None, **kwargs): + """ + Add a feature to a JID/node combination. + + Arguments: + jid -- The JID to modify. + node -- The node to modify. + feature -- The namespace of the supported feature. + """ self._run_node_handler('add_feature', jid, node, kwargs) def del_identity(self, jid=None, node=None, **kwargs): + """ + Remove an identity from the given JID/node combination. + + Arguments: + jid -- The JID to modify. + node -- The node to modify. + category -- The identity's category. + itype -- The identity's type value. + name -- Optional, human readable name for the identity. + lang -- Optional, the identity's xml:lang value. + """ self._run_node_handler('del_identity', jid, node, kwargs) def del_feature(self, jid=None, node=None, **kwargs): + """ + Remove a feature from a given JID/node combination. + + Arguments: + jid -- The JID to modify. + node -- The node to modify. + feature -- The feature's namespace. + """ self._run_node_handler('del_feature', jid, node, kwargs) - def add_item(self, jid=None, node=None, **kwargs): - self._run_node_handler('add_item', jid, node, kwargs) + def set_identities(self, jid=None, node=None, **kwargs): + """ + Add or replace all identities for the given JID/node combination. - def del_item(self, jid=None, node=None, **kwargs): - self._run_node_handler('del_item', jid, node, kwargs) + The identities must be in a set where each identity is a tuple + of the form: (category, type, lang, name) + + Arguments: + jid -- The JID to modify. + node -- The node to modify. + identities -- A set of identities in tuple form. + lang -- Optional, xml:lang value. + """ + self._run_node_handler('set_identities', jid, node, kwargs) - def _run_node_handler(self, htype, jid, node, data=None): + def del_identities(self, jid=None, node=None, **kwargs): + """ + Remove all identities for a JID/node combination. + + If a language is specified, only identities using that + language will be removed. + + Arguments: + jid -- The JID to modify. + node -- The node to modify. + lang -- Optional. If given, only remove identities + using this xml:lang value. + """ + self._run_node_handler('del_identities', jid, node, kwargs) + + def set_features(self, jid=None, node=None, **kwargs): + """ + Add or replace the set of supported features + for a JID/node combination. + + Arguments: + jid -- The JID to modify. + node -- The node to modify. + features -- The new set of supported features. + """ + self._run_node_handler('set_features', jid, node, kwargs) + + def del_features(self, jid=None, node=None, **kwargs): + """ + Remove all features from a JID/node combination. + + Arguments: + jid -- The JID to modify. + node -- The node to modify. + """ + self._run_node_handler('del_features', jid, node, kwargs) + + def _run_node_handler(self, htype, jid, node, data={}): """ Execute the most specific node handler for the given JID/node combination. @@ -218,19 +461,19 @@ class xep_0030(base_plugin): htype -- The handler type to execute. jid -- The JID requested. node -- The node requested. - dat -- Optional, custom data to pass to the handler. + data -- Optional, custom data to pass to the handler. """ if jid is None: jid = self.xmpp.boundjid.full if node is None: node = '' - if self.handlers[htype]['node'].get((jid, node), False): - return self.handlers[htype]['node'][(jid, node)](jid, node, data) - elif self.handlers[htype]['jid'].get(jid, False): - return self.handlers[htype]['jid'][jid](jid, node, data) - elif self.handlers[htype]['global']: - return self.handlers[htype]['global'](jid, node, data) + if self._handlers[htype]['node'].get((jid, node), False): + return self._handlers[htype]['node'][(jid, node)](jid, node, data) + elif self._handlers[htype]['jid'].get(jid, False): + return self._handlers[htype]['jid'][jid](jid, node, data) + elif self._handlers[htype]['global']: + return self._handlers[htype]['global'](jid, node, data) else: return None @@ -311,4 +554,3 @@ class xep_0030(base_plugin): "Using default disco#info feature.") info.add_feature(info.namespace) return info - diff --git a/sleekxmpp/plugins/xep_0030/stanza/disco.py b/sleekxmpp/plugins/xep_0030/stanza/disco.py deleted file mode 100644 index e69de29b..00000000 --- a/sleekxmpp/plugins/xep_0030/stanza/disco.py +++ /dev/null diff --git a/sleekxmpp/plugins/xep_0030/stanza/items.py b/sleekxmpp/plugins/xep_0030/stanza/items.py index 319e666f..a1fb819c 100644 --- a/sleekxmpp/plugins/xep_0030/stanza/items.py +++ b/sleekxmpp/plugins/xep_0030/stanza/items.py @@ -12,8 +12,6 @@ from sleekxmpp.xmlstream import ElementBase, ET class DiscoItems(ElementBase): """ - - Example disco#items stanzas: <iq type="get"> <query xmlns="http://jabber.org/protocol/disco#items" /> @@ -74,7 +72,7 @@ class DiscoItems(ElementBase): Arguments: jid -- The JID for the item. - node -- Optional additional information to reference + node -- Optional additional information to reference non-addressable items. name -- Optional human readable name for the item. """ diff --git a/sleekxmpp/plugins/xep_0030/static.py b/sleekxmpp/plugins/xep_0030/static.py index f3693228..b0e931b4 100644 --- a/sleekxmpp/plugins/xep_0030/static.py +++ b/sleekxmpp/plugins/xep_0030/static.py @@ -35,6 +35,11 @@ class StaticDisco(object): def __init__(self, xmpp): """ + Create a static disco interface. Sets of disco#info and + disco#items are maintained for every given JID and node + combination. These stanzas are used to store disco + information in memory without any additional processing. + Arguments: xmpp -- The main SleekXMPP object. """ @@ -52,7 +57,7 @@ class StaticDisco(object): self.nodes[(jid, node)]['info']['node'] = node self.nodes[(jid, node)]['items']['node'] = node - def get_info(self, jid, node, data=None): + def get_info(self, jid, node, data): if (jid, node) not in self.nodes: if not node: return DiscoInfo() @@ -61,11 +66,11 @@ class StaticDisco(object): else: return self.nodes[(jid, node)]['info'] - def del_info(self, jid, node, data=None): + def del_info(self, jid, node, data): if (jid, node) in self.nodes: self.nodes[(jid, node)]['info'] = DiscoInfo() - def get_items(self, jid, node, data=None): + def get_items(self, jid, node, data): if (jid, node) not in self.nodes: if not node: return DiscoInfo() @@ -74,14 +79,16 @@ class StaticDisco(object): else: return self.nodes[(jid, node)]['items'] - def set_items(self, jid, node, data=None): - pass + def set_items(self, jid, node, data): + items = data.get('items', set()) + self.add_node(jid, node) + self.nodes[(jid, node)]['items']['items'] = items - def del_items(self, jid, node, data=None): + def del_items(self, jid, node, data): if (jid, node) in self.nodes: self.nodes[(jid, node)]['items'] = DiscoItems() - def add_identity(self, jid, node, data={}): + def add_identity(self, jid, node, data): self.add_node(jid, node) self.nodes[(jid, node)]['info'].add_identity( data.get('category', ''), @@ -89,10 +96,12 @@ class StaticDisco(object): data.get('name', None), data.get('lang', None)) - def set_identities(self, jid, node, data=None): - pass + def set_identities(self, jid, node, data): + identities = data.get('identities', set()) + self.add_node(jid, node) + self.nodes[(jid, node)]['info']['identities'] = identities - def del_identity(self, jid, node, data=None): + def del_identity(self, jid, node, data): if (jid, node) not in self.nodes: return self.nodes[(jid, node)]['info'].del_identity( @@ -101,27 +110,29 @@ class StaticDisco(object): data.get('name', None), data.get('lang', None)) - - def add_feature(self, jid, node, data=None): + def add_feature(self, jid, node, data): self.add_node(jid, node) self.nodes[(jid, node)]['info'].add_feature(data.get('feature', '')) - def set_features(self, jid, node, data=None): - pass + def set_features(self, jid, node, data): + features = data.get('features', set()) + self.add_node(jid, node) + self.nodes[(jid, node)]['info']['features'] = features - def del_feature(self, jid, node, data=None): + def del_feature(self, jid, node, data): if (jid, node) not in self.nodes: return self.nodes[(jid, node)]['info'].del_feature(data.get('feature', '')) - def add_item(self, jid, node, data=None): + def add_item(self, jid, node, data): self.add_node(jid, node) self.nodes[(jid, node)]['items'].add_item( data.get('ijid', ''), node=data.get('inode', None), name=data.get('name', None)) - def del_item(self, jid, node, data=None): + def del_item(self, jid, node, data): if (jid, node) in self.nodes: - self.nodes[(jid, node)]['items'].del_item(**data) - + self.nodes[(jid, node)]['items'].del_item( + data.get('ijid', ''), + node=data.get('inode', None)) |