slixmpp/sleekxmpp/api.py
Lance Stout 1baae1b81e Fix issues of disco info leaking between entities with the same bare JIDs.
To ensure that disco info, or any settings which depend on the bound
JID, are correct, only set such information on or after the
session_bound event has fired.
2012-07-09 22:22:05 -07:00

198 lines
7.2 KiB
Python

from sleekxmpp.xmlstream import JID
class APIWrapper(object):
def __init__(self, api, name):
self.api = api
self.name = name
if name not in self.api.settings:
self.api.settings[name] = {}
def __getattr__(self, attr):
"""Curry API management commands with the API name."""
if attr == 'name':
return self.name
elif attr == 'settings':
return self.api.settings[self.name]
elif attr == 'register':
def partial(handler, op, jid=None, node=None, default=False):
register = getattr(self.api, attr)
return register(handler, self.name, op, jid, node, default)
return partial
elif attr == 'register_default':
def partial(handler, op, jid=None, node=None):
return getattr(self.api, attr)(handler, self.name, op)
return partial
elif attr in ('run', 'restore_default', 'unregister'):
def partial(*args, **kwargs):
return getattr(self.api, attr)(self.name, *args, **kwargs)
return partial
return None
def __getitem__(self, attr):
def partial(jid=None, node=None, ifrom=None, args=None):
return self.api.run(self.name, attr, jid, node, ifrom, args)
return partial
class APIRegistry(object):
def __init__(self, xmpp):
self._handlers = {}
self._handler_defaults = {}
self.xmpp = xmpp
self.settings = {}
def _setup(self, ctype, op):
"""Initialize the API callback dictionaries.
:param string ctype: The name of the API to initialize.
:param string op: The API operation to initialize.
"""
if ctype not in self.settings:
self.settings[ctype] = {}
if ctype not in self._handler_defaults:
self._handler_defaults[ctype] = {}
if ctype not in self._handlers:
self._handlers[ctype] = {}
if op not in self._handlers[ctype]:
self._handlers[ctype][op] = {'global': None,
'jid': {},
'node': {}}
def wrap(self, ctype):
"""Return a wrapper object that targets a specific API."""
return APIWrapper(self, ctype)
def purge(self, ctype):
"""Remove all information for a given API."""
del self.settings[ctype]
del self._handler_defaults[ctype]
del self._handlers[ctype]
def run(self, ctype, op, jid=None, node=None, ifrom=None, args=None):
"""Execute an API callback, based on specificity.
The API callback that is executed is chosen based on the combination
of the provided JID and node:
JID | node | Handler
==============================
Given | Given | Node handler
Given | None | JID handler
None | None | Global handler
A node handler is responsible for servicing a single node at a single
JID, while a JID handler may respond for any node at a given JID, and
the global handler will answer to any JID+node combination.
Handlers should check that the JID ``ifrom`` is authorized to perform
the desired action.
:param string ctype: The name of the API to use.
:param string op: The API operation to perform.
:param JID jid: Optionally provide specific JID.
:param string node: Optionally provide specific node.
:param JID ifrom: Optionally provide the requesting JID.
:param tuple args: Optional positional arguments to the handler.
"""
self._setup(ctype, op)
if not jid:
jid = self.xmpp.boundjid
if jid and not isinstance(jid, JID):
jid = JID(jid)
if node is None:
node = ''
if self.xmpp.is_component:
if self.settings[ctype].get('component_bare', False):
jid = jid.bare
else:
jid = jid.full
else:
if self.settings[ctype].get('client_bare', False):
jid = jid.bare
else:
jid = jid.full
jid = JID(jid)
handler = self._handlers[ctype][op]['node'].get((jid, node), None)
if handler is None:
handler = self._handlers[ctype][op]['jid'].get(jid, None)
if handler is None:
handler = self._handlers[ctype][op].get('global', None)
if handler:
try:
return handler(jid, node, ifrom, args)
except TypeError:
# To preserve backward compatibility, drop the ifrom
# parameter for existing handlers that don't understand it.
return handler(jid, node, args)
def register(self, handler, ctype, op, jid=None, node=None, default=False):
"""Register an API callback, with JID+node specificity.
The API callback can later be executed based on the
specificity of the provided JID+node combination.
See :meth:`~ApiRegistry.run` for more details.
:param string ctype: The name of the API to use.
:param string op: The API operation to perform.
:param JID jid: Optionally provide specific JID.
:param string node: Optionally provide specific node.
"""
self._setup(ctype, op)
if jid is None and node is None:
if handler is None:
handler = self._handler_defaults[op]
self._handlers[ctype][op]['global'] = handler
elif jid is not None and node is None:
self._handlers[ctype][op]['jid'][jid] = handler
else:
self._handlers[ctype][op]['node'][(jid, node)] = handler
if default:
self.register_default(handler, ctype, op)
def register_default(self, handler, ctype, op):
"""Register a default, global handler for an operation.
:param func handler: The default, global handler for the operation.
:param string ctype: The name of the API to modify.
:param string op: The API operation to use.
"""
self._setup(ctype, op)
self._handler_defaults[ctype][op] = handler
def unregister(self, ctype, op, jid=None, node=None):
"""Remove an API callback.
The API callback chosen for removal is based on the
specificity of the provided JID+node combination.
See :meth:`~ApiRegistry.run` for more details.
:param string ctype: The name of the API to use.
:param string op: The API operation to perform.
:param JID jid: Optionally provide specific JID.
:param string node: Optionally provide specific node.
"""
self._setup(ctype, op)
self.register(None, ctype, op, jid, node)
def restore_default(self, ctype, op, jid=None, node=None):
"""Reset an API callback to use a default handler.
:param string ctype: The name of the API to use.
:param string op: The API operation to perform.
:param JID jid: Optionally provide specific JID.
:param string node: Optionally provide specific node.
"""
self.unregister(ctype, op, jid, node)
self.register(self._handler_defaults[ctype][op], ctype, op, jid, node)