Fix the xep_0009 import (no more relatives)

Also, remove trailing spaces in all files
of this plugin
This commit is contained in:
Florent Le Coz 2011-02-09 09:45:45 +08:00 committed by Lance Stout
parent 1ed06bebcd
commit 4b71fba64c
5 changed files with 180 additions and 199 deletions

View File

@ -11,11 +11,9 @@ import base64
import logging import logging
import time import time
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
_namespace = 'jabber:iq:rpc' _namespace = 'jabber:iq:rpc'
def fault2xml(fault): def fault2xml(fault):
value = dict() value = dict()
@ -25,7 +23,7 @@ def fault2xml(fault):
fault.append(_py2xml((value))) fault.append(_py2xml((value)))
return fault return fault
def xml2fault(params): def xml2fault(params):
vals = [] vals = []
for value in params.findall('{%s}value' % _namespace): for value in params.findall('{%s}value' % _namespace):
vals.append(_xml2py(value)) vals.append(_xml2py(value))
@ -101,7 +99,7 @@ def xml2py(params):
def _xml2py(value): def _xml2py(value):
namespace = 'jabber:iq:rpc' namespace = 'jabber:iq:rpc'
if value.find('{%s}nil' % namespace) is not None: if value.find('{%s}nil' % namespace) is not None:
return None return None
if value.find('{%s}i4' % namespace) is not None: if value.find('{%s}i4' % namespace) is not None:
return int(value.find('{%s}i4' % namespace).text) return int(value.find('{%s}i4' % namespace).text)
if value.find('{%s}int' % namespace) is not None: if value.find('{%s}int' % namespace) is not None:
@ -131,7 +129,7 @@ def _xml2py(value):
class rpcbase64(object): class rpcbase64(object):
def __init__(self, data): def __init__(self, data):
#base 64 encoded string #base 64 encoded string
self.data = data self.data = data
@ -148,7 +146,7 @@ class rpcbase64(object):
class rpctime(object): class rpctime(object):
def __init__(self,data=None): def __init__(self,data=None):
#assume string data is in iso format YYYYMMDDTHH:MM:SS #assume string data is in iso format YYYYMMDDTHH:MM:SS
if type(data) is str: if type(data) is str:

View File

@ -16,19 +16,15 @@ import sys
import threading import threading
import traceback import traceback
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
def _intercept(method, name, public): def _intercept(method, name, public):
def _resolver(instance, *args, **kwargs): def _resolver(instance, *args, **kwargs):
log.debug("Locally calling %s.%s with arguments %s." % (instance.FQN(), method.__name__, args)) log.debug("Locally calling %s.%s with arguments %s." % (instance.FQN(), method.__name__, args))
try: try:
value = method(instance, *args, **kwargs) value = method(instance, *args, **kwargs)
if value == NotImplemented: if value == NotImplemented:
raise InvocationException("Local handler does not implement %s.%s!" % (instance.FQN(), method.__name__)) raise InvocationException("Local handler does not implement %s.%s!" % (instance.FQN(), method.__name__))
return value return value
except InvocationException: except InvocationException:
raise raise
@ -43,50 +39,49 @@ def remote(function_argument, public = True):
Decorator for methods which are remotely callable. This decorator Decorator for methods which are remotely callable. This decorator
works in conjunction with classes which extend ABC Endpoint. works in conjunction with classes which extend ABC Endpoint.
Example: Example:
@remote @remote
def remote_method(arg1, arg2) def remote_method(arg1, arg2)
Arguments: Arguments:
function_argument -- a stand-in for either the actual method function_argument -- a stand-in for either the actual method
OR a new name (string) for the method. In that case the OR a new name (string) for the method. In that case the
method is considered mapped: method is considered mapped:
Example: Example:
@remote("new_name") @remote("new_name")
def remote_method(arg1, arg2) def remote_method(arg1, arg2)
public -- A flag which indicates if this method should be part public -- A flag which indicates if this method should be part
of the known dictionary of remote methods. Defaults to True. of the known dictionary of remote methods. Defaults to True.
Example: Example:
@remote(False) @remote(False)
def remote_method(arg1, arg2) def remote_method(arg1, arg2)
Note: renaming and revising (public vs. private) can be combined. Note: renaming and revising (public vs. private) can be combined.
Example: Example:
@remote("new_name", False) @remote("new_name", False)
def remote_method(arg1, arg2) def remote_method(arg1, arg2)
''' '''
if hasattr(function_argument, '__call__'): if hasattr(function_argument, '__call__'):
return _intercept(function_argument, None, public) return _intercept(function_argument, None, public)
else: else:
if not isinstance(function_argument, basestring): if not isinstance(function_argument, basestring):
if not isinstance(function_argument, bool): if not isinstance(function_argument, bool):
raise Exception('Expected an RPC method name or visibility modifier!') raise Exception('Expected an RPC method name or visibility modifier!')
else: else:
def _wrap_revised(function): def _wrap_revised(function):
function = _intercept(function, None, function_argument) function = _intercept(function, None, function_argument)
return function return function
return _wrap_revised return _wrap_revised
def _wrap_remapped(function): def _wrap_remapped(function):
function = _intercept(function, function_argument, public) function = _intercept(function, function_argument, public)
return function return function
return _wrap_remapped return _wrap_remapped
class ACL: class ACL:
''' '''
An Access Control List (ACL) is a list of rules, which are evaluated An Access Control List (ACL) is a list of rules, which are evaluated
@ -102,7 +97,7 @@ class ACL:
[ (ACL.ALLOW, 'test@xmpp.org/unit', 'test.*'), [ (ACL.ALLOW, 'test@xmpp.org/unit', 'test.*'),
(ACL.DENY, '*', '*') ] deny everyone everything, except named (ACL.DENY, '*', '*') ] deny everyone everything, except named
JID, which is allowed access to endpoint 'test' only. JID, which is allowed access to endpoint 'test' only.
The use of wildcards is allowed in expressions, as follows: The use of wildcards is allowed in expressions, as follows:
'*' everyone, or everything (= all endpoints and methods) '*' everyone, or everything (= all endpoints and methods)
'test@xmpp.org/*' every JID regardless of JID resource 'test@xmpp.org/*' every JID regardless of JID resource
@ -113,7 +108,7 @@ class ACL:
''' '''
ALLOW = True ALLOW = True
DENY = False DENY = False
@classmethod @classmethod
def check(cls, rules, jid, resource): def check(cls, rules, jid, resource):
if rules is None: if rules is None:
@ -121,9 +116,9 @@ class ACL:
for rule in rules: for rule in rules:
policy = cls._check(rule, jid, resource) policy = cls._check(rule, jid, resource)
if policy is not None: if policy is not None:
return policy return policy
return cls.DENY # By default if not rule matches, deny access. return cls.DENY # By default if not rule matches, deny access.
@classmethod @classmethod
def _check(cls, rule, jid, resource): def _check(cls, rule, jid, resource):
if cls._match(jid, rule[1]) and cls._match(resource, rule[2]): if cls._match(jid, rule[1]) and cls._match(resource, rule[2]):
@ -138,13 +133,13 @@ class ACL:
return '' return ''
else: else:
if new_index == -1: if new_index == -1:
return expression[index : ] return expression[index : ]
else: else:
return expression[index : new_index] return expression[index : new_index]
@classmethod @classmethod
def _match(cls, value, expression): def _match(cls, value, expression):
#! print "_match [VALUE] %s [EXPR] %s" % (value, expression) #! print "_match [VALUE] %s [EXPR] %s" % (value, expression)
index = 0 index = 0
position = 0 position = 0
while index < len(expression): while index < len(expression):
@ -169,24 +164,23 @@ class ACL:
ANY_ALL = [ (ACL.ALLOW, '*', '*') ] ANY_ALL = [ (ACL.ALLOW, '*', '*') ]
class RemoteException(Exception): class RemoteException(Exception):
''' '''
Base exception for RPC. This exception is raised when a problem Base exception for RPC. This exception is raised when a problem
occurs in the network layer. occurs in the network layer.
''' '''
def __init__(self, message="", cause=None): def __init__(self, message="", cause=None):
''' '''
Initializes a new RemoteException. Initializes a new RemoteException.
Arguments: Arguments:
message -- The message accompanying this exception. message -- The message accompanying this exception.
cause -- The underlying cause of this exception. cause -- The underlying cause of this exception.
''' '''
self._message = message self._message = message
self._cause = cause self._cause = cause
pass pass
def __str__(self): def __str__(self):
return repr(self._message) return repr(self._message)
@ -202,7 +196,7 @@ class RemoteException(Exception):
class InvocationException(RemoteException): class InvocationException(RemoteException):
''' '''
Exception raised when a problem occurs during the remote invocation Exception raised when a problem occurs during the remote invocation
of a method. of a method.
''' '''
pass pass
@ -216,48 +210,45 @@ class AuthorizationException(RemoteException):
pass pass
class TimeoutException(Exception): class TimeoutException(Exception):
''' '''
Exception raised when the synchronous execution of a method takes Exception raised when the synchronous execution of a method takes
longer than the given threshold because an underlying asynchronous longer than the given threshold because an underlying asynchronous
reply did not arrive in time. reply did not arrive in time.
''' '''
pass pass
class Callback(object): class Callback(object):
''' '''
A base class for callback handlers. A base class for callback handlers.
''' '''
__metaclass__ = abc.ABCMeta __metaclass__ = abc.ABCMeta
@abc.abstractproperty @abc.abstractproperty
def set_value(self, value): def set_value(self, value):
return NotImplemented return NotImplemented
@abc.abstractproperty @abc.abstractproperty
def cancel_with_error(self, exception): def cancel_with_error(self, exception):
return NotImplemented return NotImplemented
class Future(Callback): class Future(Callback):
''' '''
Represents the result of an asynchronous computation. Represents the result of an asynchronous computation.
''' '''
def __init__(self): def __init__(self):
''' '''
Initializes a new Future. Initializes a new Future.
''' '''
self._value = None self._value = None
self._exception = None self._exception = None
self._event = threading.Event() self._event = threading.Event()
pass pass
def set_value(self, value): def set_value(self, value):
''' '''
Sets the value of this Future. Once the value is set, a caller Sets the value of this Future. Once the value is set, a caller
@ -265,13 +256,13 @@ class Future(Callback):
''' '''
self._value = value self._value = value
self._event.set() self._event.set()
def get_value(self, timeout=None): def get_value(self, timeout=None):
''' '''
Gets the value of this Future. This call will block until Gets the value of this Future. This call will block until
the result is available, or until an optional timeout expires. the result is available, or until an optional timeout expires.
When this Future is cancelled with an error, When this Future is cancelled with an error,
Arguments: Arguments:
timeout -- The maximum waiting time to obtain the value. timeout -- The maximum waiting time to obtain the value.
''' '''
@ -281,7 +272,7 @@ class Future(Callback):
if not self._event.is_set(): if not self._event.is_set():
raise TimeoutException raise TimeoutException
return self._value return self._value
def is_done(self): def is_done(self):
''' '''
Returns true if a value has been returned. Returns true if a value has been returned.
@ -290,23 +281,23 @@ class Future(Callback):
def cancel_with_error(self, exception): def cancel_with_error(self, exception):
''' '''
Cancels the Future because of an error. Once cancelled, a Cancels the Future because of an error. Once cancelled, a
caller blocked on get_value will be able to continue. caller blocked on get_value will be able to continue.
''' '''
self._exception = exception self._exception = exception
self._event.set() self._event.set()
class Endpoint(object): class Endpoint(object):
''' '''
The Endpoint class is an abstract base class for all objects The Endpoint class is an abstract base class for all objects
participating in an RPC-enabled XMPP network. participating in an RPC-enabled XMPP network.
A user subclassing this class is required to implement the method: A user subclassing this class is required to implement the method:
FQN(self) FQN(self)
where FQN stands for Fully Qualified Name, an unambiguous name where FQN stands for Fully Qualified Name, an unambiguous name
which specifies which object an RPC call refers to. It is the which specifies which object an RPC call refers to. It is the
first part in a RPC method name '<fqn>.<method>'. first part in a RPC method name '<fqn>.<method>'.
''' '''
__metaclass__ = abc.ABCMeta __metaclass__ = abc.ABCMeta
@ -317,38 +308,38 @@ class Endpoint(object):
Initialize a new Endpoint. This constructor should never be Initialize a new Endpoint. This constructor should never be
invoked by a user, instead it will be called by the factories invoked by a user, instead it will be called by the factories
which instantiate the RPC-enabled objects, of which only which instantiate the RPC-enabled objects, of which only
the classes are provided by the user. the classes are provided by the user.
Arguments: Arguments:
session -- An RPC session instance. session -- An RPC session instance.
target_jid -- the identity of the remote XMPP entity. target_jid -- the identity of the remote XMPP entity.
''' '''
self.session = session self.session = session
self.target_jid = target_jid self.target_jid = target_jid
@abc.abstractproperty @abc.abstractproperty
def FQN(self): def FQN(self):
return NotImplemented return NotImplemented
def get_methods(self): def get_methods(self):
''' '''
Returns a dictionary of all RPC method names provided by this Returns a dictionary of all RPC method names provided by this
class. This method returns the actual method names as found class. This method returns the actual method names as found
in the class definition which have been decorated with: in the class definition which have been decorated with:
@remote @remote
def some_rpc_method(arg1, arg2) def some_rpc_method(arg1, arg2)
Unless: Unless:
(1) the name has been remapped, in which case the new (1) the name has been remapped, in which case the new
name will be returned. name will be returned.
@remote("new_name") @remote("new_name")
def some_rpc_method(arg1, arg2) def some_rpc_method(arg1, arg2)
(2) the method is set to hidden (2) the method is set to hidden
@remote(False) @remote(False)
def some_hidden_method(arg1, arg2) def some_hidden_method(arg1, arg2)
''' '''
@ -360,7 +351,7 @@ class Endpoint(object):
result[test_attr._rpc_name] = test_attr result[test_attr._rpc_name] = test_attr
except Exception: except Exception:
pass pass
return result return result
@ -374,13 +365,13 @@ class Proxy(Endpoint):
def __init__(self, endpoint, callback = None): def __init__(self, endpoint, callback = None):
''' '''
Initializes a new Proxy. Initializes a new Proxy.
Arguments: Arguments:
endpoint -- The endpoint which is proxified. endpoint -- The endpoint which is proxified.
''' '''
self._endpoint = endpoint self._endpoint = endpoint
self._callback = callback self._callback = callback
def __getattribute__(self, name, *args): def __getattribute__(self, name, *args):
if name in ('__dict__', '_endpoint', 'async', '_callback'): if name in ('__dict__', '_endpoint', 'async', '_callback'):
return object.__getattribute__(self, name) return object.__getattribute__(self, name)
@ -389,31 +380,30 @@ class Proxy(Endpoint):
if hasattr(attribute, '__call__'): if hasattr(attribute, '__call__'):
try: try:
if attribute._rpc: if attribute._rpc:
def _remote_call(*args, **kwargs): def _remote_call(*args, **kwargs):
log.debug("Remotely calling '%s.%s' with arguments %s." % (self._endpoint.FQN(), attribute._rpc_name, args)) log.debug("Remotely calling '%s.%s' with arguments %s." % (self._endpoint.FQN(), attribute._rpc_name, args))
return self._endpoint.session._call_remote(self._endpoint.target_jid, "%s.%s" % (self._endpoint.FQN(), attribute._rpc_name), self._callback, *args, **kwargs) return self._endpoint.session._call_remote(self._endpoint.target_jid, "%s.%s" % (self._endpoint.FQN(), attribute._rpc_name), self._callback, *args, **kwargs)
return _remote_call return _remote_call
except: except:
pass # If the attribute doesn't exist, don't care! pass # If the attribute doesn't exist, don't care!
return attribute return attribute
def async(self, callback): def async(self, callback):
return Proxy(self._endpoint, callback) return Proxy(self._endpoint, callback)
def get_endpoint(self): def get_endpoint(self):
''' '''
Returns the proxified endpoint. Returns the proxified endpoint.
''' '''
return self._endpoint return self._endpoint
def FQN(self): def FQN(self):
return self._endpoint.FQN() return self._endpoint.FQN()
class JabberRPCEntry(object): class JabberRPCEntry(object):
def __init__(self, endpoint_FQN, call): def __init__(self, endpoint_FQN, call):
self._endpoint_FQN = endpoint_FQN self._endpoint_FQN = endpoint_FQN
self._call = call self._call = call
@ -424,28 +414,27 @@ class JabberRPCEntry(object):
return return_value return return_value
else: else:
return self._return(return_value) return self._return(return_value)
def get_endpoint_FQN(self): def get_endpoint_FQN(self):
return self._endpoint_FQN return self._endpoint_FQN
def _return(self, *args): def _return(self, *args):
return args return args
class RemoteSession(object): class RemoteSession(object):
''' '''
A context object for a Jabber-RPC session. A context object for a Jabber-RPC session.
''' '''
def __init__(self, client, session_close_callback): def __init__(self, client, session_close_callback):
''' '''
Initializes a new RPC session. Initializes a new RPC session.
Arguments: Arguments:
client -- The SleekXMPP client associated with this session. client -- The SleekXMPP client associated with this session.
session_close_callback -- A callback called when the session_close_callback -- A callback called when the
session is closed. session is closed.
''' '''
self._client = client self._client = client
@ -455,16 +444,16 @@ class RemoteSession(object):
self._callbacks = {} self._callbacks = {}
self._acls = {} self._acls = {}
self._lock = RLock() self._lock = RLock()
def _wait(self): def _wait(self):
self._event.wait() self._event.wait()
def _notify(self, event): def _notify(self, event):
log.debug("RPC Session as %s started." % self._client.boundjid.full) log.debug("RPC Session as %s started." % self._client.boundjid.full)
self._client.sendPresence() self._client.sendPresence()
self._event.set() self._event.set()
pass pass
def _register_call(self, endpoint, method, name=None): def _register_call(self, endpoint, method, name=None):
''' '''
Registers a method from an endpoint as remotely callable. Registers a method from an endpoint as remotely callable.
@ -487,8 +476,8 @@ class RemoteSession(object):
def _register_callback(self, pid, callback): def _register_callback(self, pid, callback):
with self._lock: with self._lock:
self._callbacks[pid] = callback self._callbacks[pid] = callback
def forget_callback(self, callback): def forget_callback(self, callback):
with self._lock: with self._lock:
pid = self._find_key(self._callbacks, callback) pid = self._find_key(self._callbacks, callback)
if pid is not None: if pid is not None:
@ -496,7 +485,7 @@ class RemoteSession(object):
else: else:
raise ValueError("Unknown callback!") raise ValueError("Unknown callback!")
pass pass
def _find_key(self, dict, value): def _find_key(self, dict, value):
"""return the key of dictionary dic given the value""" """return the key of dictionary dic given the value"""
search = [k for k, v in dict.iteritems() if v == value] search = [k for k, v in dict.iteritems() if v == value]
@ -504,7 +493,7 @@ class RemoteSession(object):
return None return None
else: else:
return search[0] return search[0]
def _unregister_call(self, key): def _unregister_call(self, key):
#removes the registered call #removes the registered call
with self._lock: with self._lock:
@ -512,18 +501,18 @@ class RemoteSession(object):
del self._entries[key] del self._entries[key]
else: else:
raise ValueError() raise ValueError()
def new_proxy(self, target_jid, endpoint_cls): def new_proxy(self, target_jid, endpoint_cls):
''' '''
Instantiates a new proxy object, which proxies to a remote Instantiates a new proxy object, which proxies to a remote
endpoint. This method uses a class reference without endpoint. This method uses a class reference without
constructor arguments to instantiate the proxy. constructor arguments to instantiate the proxy.
Arguments: Arguments:
target_jid -- the XMPP entity ID hosting the endpoint. target_jid -- the XMPP entity ID hosting the endpoint.
endpoint_cls -- The remote (duck) type. endpoint_cls -- The remote (duck) type.
''' '''
try: try:
argspec = inspect.getargspec(endpoint_cls.__init__) argspec = inspect.getargspec(endpoint_cls.__init__)
args = [None] * (len(argspec[0]) - 1) args = [None] * (len(argspec[0]) - 1)
result = endpoint_cls(*args) result = endpoint_cls(*args)
@ -531,7 +520,7 @@ class RemoteSession(object):
return Proxy(result) return Proxy(result)
except: except:
traceback.print_exc(file=sys.stdout) traceback.print_exc(file=sys.stdout)
def new_handler(self, acl, handler_cls, *args, **kwargs): def new_handler(self, acl, handler_cls, *args, **kwargs):
''' '''
Instantiates a new handler object, which is called remotely Instantiates a new handler object, which is called remotely
@ -539,7 +528,7 @@ class RemoteSession(object):
implementing the remote method in the local endpoint class. The implementing the remote method in the local endpoint class. The
returned reference can be called locally and will behave as a returned reference can be called locally and will behave as a
regular instance. regular instance.
Arguments: Arguments:
acl -- Access control list (see ACL class) acl -- Access control list (see ACL class)
handler_clss -- The local (duck) type. handler_clss -- The local (duck) type.
@ -556,11 +545,11 @@ class RemoteSession(object):
Endpoint.__init__(result, self, self._client.boundjid.full) Endpoint.__init__(result, self, self._client.boundjid.full)
method_dict = result.get_methods() method_dict = result.get_methods()
for method_name, method in method_dict.iteritems(): for method_name, method in method_dict.iteritems():
#!!! self._client.plugin['xep_0009'].register_call(result.FQN(), method, method_name) #!!! self._client.plugin['xep_0009'].register_call(result.FQN(), method, method_name)
self._register_call(result.FQN(), method, method_name) self._register_call(result.FQN(), method, method_name)
self._register_acl(result.FQN(), acl) self._register_acl(result.FQN(), acl)
return result return result
# def is_available(self, targetCls, pto): # def is_available(self, targetCls, pto):
# return self._client.is_available(pto) # return self._client.is_available(pto)
@ -571,19 +560,19 @@ class RemoteSession(object):
future = Future() future = Future()
self._register_callback(pid, future) self._register_callback(pid, future)
iq.send() iq.send()
return future.get_value(30) return future.get_value(30)
else: else:
print "[RemoteSession] _call_remote %s" % callback print "[RemoteSession] _call_remote %s" % callback
self._register_callback(pid, callback) self._register_callback(pid, callback)
iq.send() iq.send()
def close(self): def close(self):
''' '''
Closes this session. Closes this session.
''' '''
self._client.disconnect(False) self._client.disconnect(False)
self._session_close_callback() self._session_close_callback()
def _on_jabber_rpc_method_call(self, iq): def _on_jabber_rpc_method_call(self, iq):
iq.enable('rpc_query') iq.enable('rpc_query')
params = iq['rpc_query']['method_call']['params'] params = iq['rpc_query']['method_call']['params']
@ -609,15 +598,15 @@ class RemoteSession(object):
except AuthorizationException as ae: except AuthorizationException as ae:
log.error(ae.get_message()) log.error(ae.get_message())
error = self._client.plugin['xep_0009']._forbidden(iq) error = self._client.plugin['xep_0009']._forbidden(iq)
error.send() error.send()
except Exception as e: except Exception as e:
if isinstance(e, KeyError): if isinstance(e, KeyError):
log.error("No handler available for %s!" % pmethod) log.error("No handler available for %s!" % pmethod)
error = self._client.plugin['xep_0009']._item_not_found(iq) error = self._client.plugin['xep_0009']._item_not_found(iq)
else: else:
traceback.print_exc(file=sys.stderr) traceback.print_exc(file=sys.stderr)
log.error("An unexpected problem occurred invoking method %s!" % pmethod) log.error("An unexpected problem occurred invoking method %s!" % pmethod)
error = self._client.plugin['xep_0009']._undefined_condition(iq) error = self._client.plugin['xep_0009']._undefined_condition(iq)
#! print "[REMOTE.PY] _handle_remote_procedure_call AN ERROR SHOULD BE SENT NOW %s " % e #! print "[REMOTE.PY] _handle_remote_procedure_call AN ERROR SHOULD BE SENT NOW %s " % e
error.send() error.send()
@ -632,7 +621,7 @@ class RemoteSession(object):
callback.set_value(args[0]) callback.set_value(args[0])
else: else:
callback.set_value(None) callback.set_value(None)
pass pass
def _on_jabber_rpc_method_response2(self, iq): def _on_jabber_rpc_method_response2(self, iq):
iq.enable('rpc_query') iq.enable('rpc_query')
@ -648,41 +637,40 @@ class RemoteSession(object):
callback.set_value(args[0]) callback.set_value(args[0])
else: else:
callback.set_value(None) callback.set_value(None)
pass pass
def _on_jabber_rpc_method_fault(self, iq): def _on_jabber_rpc_method_fault(self, iq):
iq.enable('rpc_query') iq.enable('rpc_query')
fault = xml2fault(iq['rpc_query']['method_response']['fault']) fault = xml2fault(iq['rpc_query']['method_response']['fault'])
pid = iq['id'] pid = iq['id']
with self._lock: with self._lock:
callback = self._callbacks[pid] callback = self._callbacks[pid]
del self._callbacks[pid] del self._callbacks[pid]
e = { e = {
500: InvocationException 500: InvocationException
}[fault['code']](fault['string']) }[fault['code']](fault['string'])
callback.cancel_with_error(e) callback.cancel_with_error(e)
def _on_jabber_rpc_error(self, iq): def _on_jabber_rpc_error(self, iq):
pid = iq['id'] pid = iq['id']
pmethod = self._client.plugin['xep_0009']._extract_method(iq['rpc_query']) pmethod = self._client.plugin['xep_0009']._extract_method(iq['rpc_query'])
code = iq['error']['code'] code = iq['error']['code']
type = iq['error']['type'] type = iq['error']['type']
condition = iq['error']['condition'] condition = iq['error']['condition']
#! print("['REMOTE.PY']._BINDING_handle_remote_procedure_error -> ERROR! ERROR! ERROR! Condition is '%s'" % condition) #! print("['REMOTE.PY']._BINDING_handle_remote_procedure_error -> ERROR! ERROR! ERROR! Condition is '%s'" % condition)
with self._lock: with self._lock:
callback = self._callbacks[pid] callback = self._callbacks[pid]
del self._callbacks[pid] del self._callbacks[pid]
e = { e = {
'item-not-found': RemoteException("No remote handler available for %s at %s!" % (pmethod, iq['from'])), 'item-not-found': RemoteException("No remote handler available for %s at %s!" % (pmethod, iq['from'])),
'forbidden': AuthorizationException("Forbidden to invoke remote handler for %s at %s!" % (pmethod, iq['from'])), 'forbidden': AuthorizationException("Forbidden to invoke remote handler for %s at %s!" % (pmethod, iq['from'])),
'undefined-condition': RemoteException("An unexpected problem occured trying to invoke %s at %s!" % (pmethod, iq['from'])), 'undefined-condition': RemoteException("An unexpected problem occured trying to invoke %s at %s!" % (pmethod, iq['from'])),
}[condition] }[condition]
if e is None: if e is None:
RemoteException("An unexpected exception occurred at %s!" % iq['from']) RemoteException("An unexpected exception occurred at %s!" % iq['from'])
callback.cancel_with_error(e) callback.cancel_with_error(e)
class Remote(object): class Remote(object):
''' '''
Bootstrap class for Jabber-RPC sessions. New sessions are openend Bootstrap class for Jabber-RPC sessions. New sessions are openend
@ -691,18 +679,18 @@ class Remote(object):
_instance = None _instance = None
_sessions = dict() _sessions = dict()
_lock = threading.RLock() _lock = threading.RLock()
@classmethod @classmethod
def new_session_with_client(cls, client, callback=None): def new_session_with_client(cls, client, callback=None):
''' '''
Opens a new session with a given client. Opens a new session with a given client.
Arguments: Arguments:
client -- An XMPP client. client -- An XMPP client.
callback -- An optional callback which can be used to track callback -- An optional callback which can be used to track
the starting state of the session. the starting state of the session.
''' '''
with Remote._lock: with Remote._lock:
if(client.boundjid.bare in cls._sessions): if(client.boundjid.bare in cls._sessions):
raise RemoteException("There already is a session associated with these credentials!") raise RemoteException("There already is a session associated with these credentials!")
else: else:
@ -710,21 +698,21 @@ class Remote(object):
def _session_close_callback(): def _session_close_callback():
with Remote._lock: with Remote._lock:
del cls._sessions[client.boundjid.bare] del cls._sessions[client.boundjid.bare]
result = RemoteSession(client, _session_close_callback) result = RemoteSession(client, _session_close_callback)
client.plugin['xep_0009'].xmpp.add_event_handler('jabber_rpc_method_call', result._on_jabber_rpc_method_call) client.plugin['xep_0009'].xmpp.add_event_handler('jabber_rpc_method_call', result._on_jabber_rpc_method_call)
client.plugin['xep_0009'].xmpp.add_event_handler('jabber_rpc_method_response', result._on_jabber_rpc_method_response) client.plugin['xep_0009'].xmpp.add_event_handler('jabber_rpc_method_response', result._on_jabber_rpc_method_response)
client.plugin['xep_0009'].xmpp.add_event_handler('jabber_rpc_method_fault', result._on_jabber_rpc_method_fault) client.plugin['xep_0009'].xmpp.add_event_handler('jabber_rpc_method_fault', result._on_jabber_rpc_method_fault)
client.plugin['xep_0009'].xmpp.add_event_handler('jabber_rpc_error', result._on_jabber_rpc_error) client.plugin['xep_0009'].xmpp.add_event_handler('jabber_rpc_error', result._on_jabber_rpc_error)
if callback is None: if callback is None:
start_event_handler = result._notify start_event_handler = result._notify
else: else:
start_event_handler = callback start_event_handler = callback
client.add_event_handler("session_start", start_event_handler) client.add_event_handler("session_start", start_event_handler)
if client.connect(): if client.connect():
client.process(threaded=True) client.process(threaded=True)
else: else:
raise RemoteException("Could not connect to XMPP server!") raise RemoteException("Could not connect to XMPP server!")
pass pass
if callback is None: if callback is None:
result._wait() result._wait()
return result return result
@ -733,20 +721,19 @@ class Remote(object):
def new_session(cls, jid, password, callback=None): def new_session(cls, jid, password, callback=None):
''' '''
Opens a new session and instantiates a new XMPP client. Opens a new session and instantiates a new XMPP client.
Arguments: Arguments:
jid -- The XMPP JID for logging in. jid -- The XMPP JID for logging in.
password -- The password for logging in. password -- The password for logging in.
callback -- An optional callback which can be used to track callback -- An optional callback which can be used to track
the starting state of the session. the starting state of the session.
''' '''
client = sleekxmpp.ClientXMPP(jid, password) client = sleekxmpp.ClientXMPP(jid, password)
#? Register plug-ins. #? Register plug-ins.
client.registerPlugin('xep_0004') # Data Forms client.registerPlugin('xep_0004') # Data Forms
client.registerPlugin('xep_0009') # Jabber-RPC client.registerPlugin('xep_0009') # Jabber-RPC
client.registerPlugin('xep_0030') # Service Discovery client.registerPlugin('xep_0030') # Service Discovery
client.registerPlugin('xep_0060') # PubSub client.registerPlugin('xep_0060') # PubSub
client.registerPlugin('xep_0199') # XMPP Ping client.registerPlugin('xep_0199') # XMPP Ping
return cls.new_session_with_client(client, callback) return cls.new_session_with_client(client, callback)

View File

@ -6,8 +6,8 @@
See the file LICENSE for copying permission. See the file LICENSE for copying permission.
""" """
from .. import base from sleekxmpp.plugins import base
from stanza.RPC import RPCQuery, MethodCall, MethodResponse from sleekxmpp.plugins.xep_0009.stanza.RPC import RPCQuery, MethodCall, MethodResponse
from sleekxmpp.stanza.iq import Iq from sleekxmpp.stanza.iq import Iq
from sleekxmpp.xmlstream.handler.callback import Callback from sleekxmpp.xmlstream.handler.callback import Callback
from sleekxmpp.xmlstream.matcher.xpath import MatchXPath from sleekxmpp.xmlstream.matcher.xpath import MatchXPath
@ -27,15 +27,15 @@ class xep_0009(base.base_plugin):
self.xep = '0009' self.xep = '0009'
self.description = 'Jabber-RPC' self.description = 'Jabber-RPC'
#self.stanza = sleekxmpp.plugins.xep_0009.stanza #self.stanza = sleekxmpp.plugins.xep_0009.stanza
register_stanza_plugin(Iq, RPCQuery) register_stanza_plugin(Iq, RPCQuery)
register_stanza_plugin(RPCQuery, MethodCall) register_stanza_plugin(RPCQuery, MethodCall)
register_stanza_plugin(RPCQuery, MethodResponse) register_stanza_plugin(RPCQuery, MethodResponse)
self.xmpp.registerHandler( self.xmpp.registerHandler(
Callback('RPC Call', MatchXPath('{%s}iq/{%s}query/{%s}methodCall' % (self.xmpp.default_ns, RPCQuery.namespace, RPCQuery.namespace)), Callback('RPC Call', MatchXPath('{%s}iq/{%s}query/{%s}methodCall' % (self.xmpp.default_ns, RPCQuery.namespace, RPCQuery.namespace)),
self._handle_method_call) self._handle_method_call)
) )
self.xmpp.registerHandler( self.xmpp.registerHandler(
Callback('RPC Call', MatchXPath('{%s}iq/{%s}query/{%s}methodResponse' % (self.xmpp.default_ns, RPCQuery.namespace, RPCQuery.namespace)), Callback('RPC Call', MatchXPath('{%s}iq/{%s}query/{%s}methodResponse' % (self.xmpp.default_ns, RPCQuery.namespace, RPCQuery.namespace)),
self._handle_method_response) self._handle_method_response)
@ -46,7 +46,7 @@ class xep_0009(base.base_plugin):
) )
self.xmpp.add_event_handler('jabber_rpc_method_call', self._on_jabber_rpc_method_call) self.xmpp.add_event_handler('jabber_rpc_method_call', self._on_jabber_rpc_method_call)
self.xmpp.add_event_handler('jabber_rpc_method_response', self._on_jabber_rpc_method_response) self.xmpp.add_event_handler('jabber_rpc_method_response', self._on_jabber_rpc_method_response)
self.xmpp.add_event_handler('jabber_rpc_method_fault', self._on_jabber_rpc_method_fault) self.xmpp.add_event_handler('jabber_rpc_method_fault', self._on_jabber_rpc_method_fault)
self.xmpp.add_event_handler('jabber_rpc_error', self._on_jabber_rpc_error) self.xmpp.add_event_handler('jabber_rpc_error', self._on_jabber_rpc_error)
self.xmpp.add_event_handler('error', self._handle_error) self.xmpp.add_event_handler('error', self._handle_error)
#self.activeCalls = [] #self.activeCalls = []
@ -54,7 +54,7 @@ class xep_0009(base.base_plugin):
def post_init(self): def post_init(self):
base.base_plugin.post_init(self) base.base_plugin.post_init(self)
self.xmpp.plugin['xep_0030'].add_feature('jabber:iq:rpc') self.xmpp.plugin['xep_0030'].add_feature('jabber:iq:rpc')
self.xmpp.plugin['xep_0030'].add_identity('automation','rpc') self.xmpp.plugin['xep_0030'].add_identity('automation','rpc')
def make_iq_method_call(self, pto, pmethod, params): def make_iq_method_call(self, pto, pmethod, params):
iq = self.xmpp.makeIqSet() iq = self.xmpp.makeIqSet()
@ -64,7 +64,7 @@ class xep_0009(base.base_plugin):
iq['rpc_query']['method_call']['method_name'] = pmethod iq['rpc_query']['method_call']['method_name'] = pmethod
iq['rpc_query']['method_call']['params'] = params iq['rpc_query']['method_call']['params'] = params
return iq; return iq;
def make_iq_method_response(self, pid, pto, params): def make_iq_method_response(self, pid, pto, params):
iq = self.xmpp.makeIqResult(pid) iq = self.xmpp.makeIqResult(pid)
iq.attrib['to'] = pto iq.attrib['to'] = pto
@ -78,7 +78,7 @@ class xep_0009(base.base_plugin):
iq.attrib['to'] = pto iq.attrib['to'] = pto
iq.attrib['from'] = self.xmpp.boundjid.full iq.attrib['from'] = self.xmpp.boundjid.full
iq.enable('rpc_query') iq.enable('rpc_query')
iq['rpc_query']['method_response']['params'] = None iq['rpc_query']['method_response']['params'] = None
iq['rpc_query']['method_response']['fault'] = params iq['rpc_query']['method_response']['fault'] = params
return iq return iq
@ -100,58 +100,58 @@ class xep_0009(base.base_plugin):
iq['error']['type'] = 'cancel' iq['error']['type'] = 'cancel'
iq['error']['condition'] = 'item-not-found' iq['error']['condition'] = 'item-not-found'
return iq return iq
def _undefined_condition(self, iq): def _undefined_condition(self, iq):
payload = iq.get_payload() payload = iq.get_payload()
iq.reply().error().set_payload(payload) iq.reply().error().set_payload(payload)
iq['error']['code'] = '500' iq['error']['code'] = '500'
iq['error']['type'] = 'cancel' iq['error']['type'] = 'cancel'
iq['error']['condition'] = 'undefined-condition' iq['error']['condition'] = 'undefined-condition'
return iq return iq
def _forbidden(self, iq): def _forbidden(self, iq):
payload = iq.get_payload() payload = iq.get_payload()
iq.reply().error().set_payload(payload) iq.reply().error().set_payload(payload)
iq['error']['code'] = '403' iq['error']['code'] = '403'
iq['error']['type'] = 'auth' iq['error']['type'] = 'auth'
iq['error']['condition'] = 'forbidden' iq['error']['condition'] = 'forbidden'
return iq return iq
def _recipient_unvailable(self, iq): def _recipient_unvailable(self, iq):
payload = iq.get_payload() payload = iq.get_payload()
iq.reply().error().set_payload(payload) iq.reply().error().set_payload(payload)
iq['error']['code'] = '404' iq['error']['code'] = '404'
iq['error']['type'] = 'wait' iq['error']['type'] = 'wait'
iq['error']['condition'] = 'recipient-unavailable' iq['error']['condition'] = 'recipient-unavailable'
return iq return iq
def _handle_method_call(self, iq): def _handle_method_call(self, iq):
type = iq['type'] type = iq['type']
if type == 'set': if type == 'set':
log.debug("Incoming Jabber-RPC call from %s" % iq['from']) log.debug("Incoming Jabber-RPC call from %s" % iq['from'])
self.xmpp.event('jabber_rpc_method_call', iq) self.xmpp.event('jabber_rpc_method_call', iq)
else: else:
if type == 'error' and ['rpc_query'] is None: if type == 'error' and ['rpc_query'] is None:
self.handle_error(iq) self.handle_error(iq)
else: else:
log.debug("Incoming Jabber-RPC error from %s" % iq['from']) log.debug("Incoming Jabber-RPC error from %s" % iq['from'])
self.xmpp.event('jabber_rpc_error', iq) self.xmpp.event('jabber_rpc_error', iq)
def _handle_method_response(self, iq): def _handle_method_response(self, iq):
if iq['rpc_query']['method_response']['fault'] is not None: if iq['rpc_query']['method_response']['fault'] is not None:
log.debug("Incoming Jabber-RPC fault from %s" % iq['from']) log.debug("Incoming Jabber-RPC fault from %s" % iq['from'])
#self._on_jabber_rpc_method_fault(iq) #self._on_jabber_rpc_method_fault(iq)
self.xmpp.event('jabber_rpc_method_fault', iq) self.xmpp.event('jabber_rpc_method_fault', iq)
else: else:
log.debug("Incoming Jabber-RPC response from %s" % iq['from']) log.debug("Incoming Jabber-RPC response from %s" % iq['from'])
self.xmpp.event('jabber_rpc_method_response', iq) self.xmpp.event('jabber_rpc_method_response', iq)
def _handle_error(self, iq): def _handle_error(self, iq):
print("['XEP-0009']._handle_error -> ERROR! Iq is '%s'" % iq) print("['XEP-0009']._handle_error -> ERROR! Iq is '%s'" % iq)
print("#######################") print("#######################")
print("### NOT IMPLEMENTED ###") print("### NOT IMPLEMENTED ###")
print("#######################") print("#######################")
def _on_jabber_rpc_method_call(self, iq, forwarded=False): def _on_jabber_rpc_method_call(self, iq, forwarded=False):
""" """
A default handler for Jabber-RPC method call. If another A default handler for Jabber-RPC method call. If another
@ -161,7 +161,7 @@ class xep_0009(base.base_plugin):
forwarded set to True, then it will run as normal. forwarded set to True, then it will run as normal.
""" """
if not forwarded and self.xmpp.event_handled('jabber_rpc_method_call') > 1: if not forwarded and self.xmpp.event_handled('jabber_rpc_method_call') > 1:
return return
# Reply with error by default # Reply with error by default
error = self.client.plugin['xep_0009']._item_not_found(iq) error = self.client.plugin['xep_0009']._item_not_found(iq)
error.send() error.send()
@ -175,7 +175,7 @@ class xep_0009(base.base_plugin):
forwarded set to True, then it will run as normal. forwarded set to True, then it will run as normal.
""" """
if not forwarded and self.xmpp.event_handled('jabber_rpc_method_response') > 1: if not forwarded and self.xmpp.event_handled('jabber_rpc_method_response') > 1:
return return
error = self.client.plugin['xep_0009']._recpient_unavailable(iq) error = self.client.plugin['xep_0009']._recpient_unavailable(iq)
error.send() error.send()
@ -186,12 +186,12 @@ class xep_0009(base.base_plugin):
If this handler is called by your own custom handler with If this handler is called by your own custom handler with
forwarded set to True, then it will run as normal. forwarded set to True, then it will run as normal.
""" """
if not forwarded and self.xmpp.event_handled('jabber_rpc_method_fault') > 1: if not forwarded and self.xmpp.event_handled('jabber_rpc_method_fault') > 1:
return return
error = self.client.plugin['xep_0009']._recpient_unavailable(iq) error = self.client.plugin['xep_0009']._recpient_unavailable(iq)
error.send() error.send()
def _on_jabber_rpc_error(self, iq, forwarded=False): def _on_jabber_rpc_error(self, iq, forwarded=False):
""" """
A default handler for Jabber-RPC error response. If another A default handler for Jabber-RPC error response. If another
@ -199,23 +199,23 @@ class xep_0009(base.base_plugin):
If this handler is called by your own custom handler with If this handler is called by your own custom handler with
forwarded set to True, then it will run as normal. forwarded set to True, then it will run as normal.
""" """
if not forwarded and self.xmpp.event_handled('jabber_rpc_error') > 1: if not forwarded and self.xmpp.event_handled('jabber_rpc_error') > 1:
return return
error = self.client.plugin['xep_0009']._recpient_unavailable(iq, iq.get_payload()) error = self.client.plugin['xep_0009']._recpient_unavailable(iq, iq.get_payload())
error.send() error.send()
def _send_fault(self, iq, fault_xml): # def _send_fault(self, iq, fault_xml): #
fault = self.make_iq_method_response_fault(iq['id'], iq['from'], fault_xml) fault = self.make_iq_method_response_fault(iq['id'], iq['from'], fault_xml)
fault.send() fault.send()
def _send_error(self, iq): def _send_error(self, iq):
print("['XEP-0009']._send_error -> ERROR! Iq is '%s'" % iq) print("['XEP-0009']._send_error -> ERROR! Iq is '%s'" % iq)
print("#######################") print("#######################")
print("### NOT IMPLEMENTED ###") print("### NOT IMPLEMENTED ###")
print("#######################") print("#######################")
def _extract_method(self, stanza): def _extract_method(self, stanza):
xml = ET.fromstring("%s" % stanza) xml = ET.fromstring("%s" % stanza)
return xml.find("./methodCall/methodName").text return xml.find("./methodCall/methodName").text

View File

@ -14,55 +14,51 @@ class RPCQuery(ElementBase):
name = 'query' name = 'query'
namespace = 'jabber:iq:rpc' namespace = 'jabber:iq:rpc'
plugin_attrib = 'rpc_query' plugin_attrib = 'rpc_query'
interfaces = set(()) interfaces = set(())
subinterfaces = set(()) subinterfaces = set(())
plugin_attrib_map = {} plugin_attrib_map = {}
plugin_tag_map = {} plugin_tag_map = {}
class MethodCall(ElementBase): class MethodCall(ElementBase):
name = 'methodCall' name = 'methodCall'
namespace = 'jabber:iq:rpc' namespace = 'jabber:iq:rpc'
plugin_attrib = 'method_call' plugin_attrib = 'method_call'
interfaces = set(('method_name', 'params')) interfaces = set(('method_name', 'params'))
subinterfaces = set(()) subinterfaces = set(())
plugin_attrib_map = {} plugin_attrib_map = {}
plugin_tag_map = {} plugin_tag_map = {}
def get_method_name(self): def get_method_name(self):
return self._get_sub_text('methodName') return self._get_sub_text('methodName')
def set_method_name(self, value): def set_method_name(self, value):
return self._set_sub_text('methodName', value) return self._set_sub_text('methodName', value)
def get_params(self): def get_params(self):
return self.xml.find('{%s}params' % self.namespace) return self.xml.find('{%s}params' % self.namespace)
def set_params(self, params): def set_params(self, params):
self.append(params) self.append(params)
class MethodResponse(ElementBase): class MethodResponse(ElementBase):
name = 'methodResponse' name = 'methodResponse'
namespace = 'jabber:iq:rpc' namespace = 'jabber:iq:rpc'
plugin_attrib = 'method_response' plugin_attrib = 'method_response'
interfaces = set(('params', 'fault')) interfaces = set(('params', 'fault'))
subinterfaces = set(()) subinterfaces = set(())
plugin_attrib_map = {} plugin_attrib_map = {}
plugin_tag_map = {} plugin_tag_map = {}
def get_params(self): def get_params(self):
return self.xml.find('{%s}params' % self.namespace) return self.xml.find('{%s}params' % self.namespace)
def set_params(self, params): def set_params(self, params):
self.append(params) self.append(params)
def get_fault(self): def get_fault(self):
return self.xml.find('{%s}fault' % self.namespace) return self.xml.find('{%s}fault' % self.namespace)
def set_fault(self, fault): def set_fault(self, fault):
self.append(fault) self.append(fault)

View File

@ -6,4 +6,4 @@
See the file LICENSE for copying permission. See the file LICENSE for copying permission.
""" """
from RPC import RPCQuery, MethodCall, MethodResponse from sleekxmpp.plugins.xep_0009.stanza.RPC import RPCQuery, MethodCall, MethodResponse