summaryrefslogtreecommitdiff
path: root/sleekxmpp/plugins/xep_0009
diff options
context:
space:
mode:
Diffstat (limited to 'sleekxmpp/plugins/xep_0009')
-rw-r--r--sleekxmpp/plugins/xep_0009/__init__.py20
-rw-r--r--sleekxmpp/plugins/xep_0009/binding.py173
-rw-r--r--sleekxmpp/plugins/xep_0009/remote.py779
-rw-r--r--sleekxmpp/plugins/xep_0009/rpc.py218
-rw-r--r--sleekxmpp/plugins/xep_0009/stanza/RPC.py64
-rw-r--r--sleekxmpp/plugins/xep_0009/stanza/__init__.py9
6 files changed, 0 insertions, 1263 deletions
diff --git a/sleekxmpp/plugins/xep_0009/__init__.py b/sleekxmpp/plugins/xep_0009/__init__.py
deleted file mode 100644
index 0ce3cf2c..00000000
--- a/sleekxmpp/plugins/xep_0009/__init__.py
+++ /dev/null
@@ -1,20 +0,0 @@
-"""
- SleekXMPP: The Sleek XMPP Library
- Copyright (C) 2011 Nathanael C. Fritz, Dann Martens (TOMOTON).
- This file is part of SleekXMPP.
-
- See the file LICENSE for copying permission.
-"""
-
-from sleekxmpp.plugins.base import register_plugin
-
-from sleekxmpp.plugins.xep_0009 import stanza
-from sleekxmpp.plugins.xep_0009.rpc import XEP_0009
-from sleekxmpp.plugins.xep_0009.stanza import RPCQuery, MethodCall, MethodResponse
-
-
-register_plugin(XEP_0009)
-
-
-# Retain some backwards compatibility
-xep_0009 = XEP_0009
diff --git a/sleekxmpp/plugins/xep_0009/binding.py b/sleekxmpp/plugins/xep_0009/binding.py
deleted file mode 100644
index a55993ad..00000000
--- a/sleekxmpp/plugins/xep_0009/binding.py
+++ /dev/null
@@ -1,173 +0,0 @@
-"""
- SleekXMPP: The Sleek XMPP Library
- Copyright (C) 2011 Nathanael C. Fritz, Dann Martens (TOMOTON).
- This file is part of SleekXMPP.
-
- See the file LICENSE for copying permission.
-"""
-
-from sleekxmpp.xmlstream import ET
-import base64
-import logging
-import time
-import sys
-
-if sys.version_info > (3, 0):
- unicode = str
-
-log = logging.getLogger(__name__)
-
-_namespace = 'jabber:iq:rpc'
-
-def fault2xml(fault):
- value = dict()
- value['faultCode'] = fault['code']
- value['faultString'] = fault['string']
- fault = ET.Element("fault", {'xmlns': _namespace})
- fault.append(_py2xml((value)))
- return fault
-
-def xml2fault(params):
- vals = []
- for value in params.findall('{%s}value' % _namespace):
- vals.append(_xml2py(value))
- fault = dict()
- fault['code'] = vals[0]['faultCode']
- fault['string'] = vals[0]['faultString']
- return fault
-
-def py2xml(*args):
- params = ET.Element("{%s}params" % _namespace)
- for x in args:
- param = ET.Element("{%s}param" % _namespace)
- param.append(_py2xml(x))
- params.append(param) #<params><param>...
- return params
-
-def _py2xml(*args):
- for x in args:
- val = ET.Element("{%s}value" % _namespace)
- if x is None:
- nil = ET.Element("{%s}nil" % _namespace)
- val.append(nil)
- elif type(x) is int:
- i4 = ET.Element("{%s}i4" % _namespace)
- i4.text = str(x)
- val.append(i4)
- elif type(x) is bool:
- boolean = ET.Element("{%s}boolean" % _namespace)
- boolean.text = str(int(x))
- val.append(boolean)
- elif type(x) in (str, unicode):
- string = ET.Element("{%s}string" % _namespace)
- string.text = x
- val.append(string)
- elif type(x) is float:
- double = ET.Element("{%s}double" % _namespace)
- double.text = str(x)
- val.append(double)
- elif type(x) is rpcbase64:
- b64 = ET.Element("{%s}base64" % _namespace)
- b64.text = x.encoded()
- val.append(b64)
- elif type(x) is rpctime:
- iso = ET.Element("{%s}dateTime.iso8601" % _namespace)
- iso.text = str(x)
- val.append(iso)
- elif type(x) in (list, tuple):
- array = ET.Element("{%s}array" % _namespace)
- data = ET.Element("{%s}data" % _namespace)
- for y in x:
- data.append(_py2xml(y))
- array.append(data)
- val.append(array)
- elif type(x) is dict:
- struct = ET.Element("{%s}struct" % _namespace)
- for y in x.keys():
- member = ET.Element("{%s}member" % _namespace)
- name = ET.Element("{%s}name" % _namespace)
- name.text = y
- member.append(name)
- member.append(_py2xml(x[y]))
- struct.append(member)
- val.append(struct)
- return val
-
-def xml2py(params):
- namespace = 'jabber:iq:rpc'
- vals = []
- for param in params.findall('{%s}param' % namespace):
- vals.append(_xml2py(param.find('{%s}value' % namespace)))
- return vals
-
-def _xml2py(value):
- namespace = 'jabber:iq:rpc'
- if value.find('{%s}nil' % namespace) is not None:
- return None
- if value.find('{%s}i4' % namespace) is not None:
- return int(value.find('{%s}i4' % namespace).text)
- if value.find('{%s}int' % namespace) is not None:
- return int(value.find('{%s}int' % namespace).text)
- if value.find('{%s}boolean' % namespace) is not None:
- return bool(int(value.find('{%s}boolean' % namespace).text))
- if value.find('{%s}string' % namespace) is not None:
- return value.find('{%s}string' % namespace).text
- if value.find('{%s}double' % namespace) is not None:
- return float(value.find('{%s}double' % namespace).text)
- if value.find('{%s}base64' % namespace) is not None:
- return rpcbase64(value.find('{%s}base64' % namespace).text.encode())
- if value.find('{%s}Base64' % namespace) is not None:
- # Older versions of XEP-0009 used Base64
- return rpcbase64(value.find('{%s}Base64' % namespace).text.encode())
- if value.find('{%s}dateTime.iso8601' % namespace) is not None:
- return rpctime(value.find('{%s}dateTime.iso8601' % namespace).text)
- if value.find('{%s}struct' % namespace) is not None:
- struct = {}
- for member in value.find('{%s}struct' % namespace).findall('{%s}member' % namespace):
- struct[member.find('{%s}name' % namespace).text] = _xml2py(member.find('{%s}value' % namespace))
- return struct
- if value.find('{%s}array' % namespace) is not None:
- array = []
- for val in value.find('{%s}array' % namespace).find('{%s}data' % namespace).findall('{%s}value' % namespace):
- array.append(_xml2py(val))
- return array
- raise ValueError()
-
-
-
-class rpcbase64(object):
-
- def __init__(self, data):
- #base 64 encoded string
- self.data = data
-
- def decode(self):
- return base64.b64decode(self.data)
-
- def __str__(self):
- return self.decode().decode()
-
- def encoded(self):
- return self.data.decode()
-
-
-
-class rpctime(object):
-
- def __init__(self,data=None):
- #assume string data is in iso format YYYYMMDDTHH:MM:SS
- if type(data) in (str, unicode):
- self.timestamp = time.strptime(data,"%Y%m%dT%H:%M:%S")
- elif type(data) is time.struct_time:
- self.timestamp = data
- elif data is None:
- self.timestamp = time.gmtime()
- else:
- raise ValueError()
-
- def iso8601(self):
- #return a iso8601 string
- return time.strftime("%Y%m%dT%H:%M:%S",self.timestamp)
-
- def __str__(self):
- return self.iso8601()
diff --git a/sleekxmpp/plugins/xep_0009/remote.py b/sleekxmpp/plugins/xep_0009/remote.py
deleted file mode 100644
index b02f587e..00000000
--- a/sleekxmpp/plugins/xep_0009/remote.py
+++ /dev/null
@@ -1,779 +0,0 @@
-"""
- SleekXMPP: The Sleek XMPP Library
- Copyright (C) 2011 Nathanael C. Fritz, Dann Martens (TOMOTON).
- This file is part of SleekXMPP.
-
- See the file LICENSE for copying permission.
-"""
-
-from sleekxmpp.plugins.xep_0009.binding import py2xml, xml2py, xml2fault, fault2xml
-from threading import RLock
-import abc
-import inspect
-import logging
-import sleekxmpp
-import sys
-import threading
-import traceback
-
-log = logging.getLogger(__name__)
-
-# Define a function _isstr() to check if an object is a string in a way
-# compatible with Python 2 and Python 3 (basestring does not exists in Python 3).
-try:
- basestring # This evaluation will throw an exception if basestring does not exists (Python 3).
- def _isstr(obj):
- return isinstance(obj, basestring)
-except NameError:
- def _isstr(obj):
- return isinstance(obj, str)
-
-
-# Class decorator to declare a metaclass to a class in a way compatible with Python 2 and 3.
-# This decorator is copied from 'six' (https://bitbucket.org/gutworth/six):
-#
-# Copyright (c) 2010-2015 Benjamin Peterson
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-def _add_metaclass(metaclass):
- def wrapper(cls):
- orig_vars = cls.__dict__.copy()
- slots = orig_vars.get('__slots__')
- if slots is not None:
- if isinstance(slots, str):
- slots = [slots]
- for slots_var in slots:
- orig_vars.pop(slots_var)
- orig_vars.pop('__dict__', None)
- orig_vars.pop('__weakref__', None)
- return metaclass(cls.__name__, cls.__bases__, orig_vars)
- return wrapper
-
-def _intercept(method, name, public):
- def _resolver(instance, *args, **kwargs):
- log.debug("Locally calling %s.%s with arguments %s.", instance.FQN(), method.__name__, args)
- try:
- value = method(instance, *args, **kwargs)
- if value == NotImplemented:
- raise InvocationException("Local handler does not implement %s.%s!" % (instance.FQN(), method.__name__))
- return value
- except InvocationException:
- raise
- except Exception as e:
- raise InvocationException("A problem occured calling %s.%s!" % (instance.FQN(), method.__name__), e)
- _resolver._rpc = public
- _resolver._rpc_name = method.__name__ if name is None else name
- return _resolver
-
-def remote(function_argument, public = True):
- '''
- Decorator for methods which are remotely callable. This decorator
- works in conjunction with classes which extend ABC Endpoint.
- Example:
-
- @remote
- def remote_method(arg1, arg2)
-
- Arguments:
- function_argument -- a stand-in for either the actual method
- OR a new name (string) for the method. In that case the
- method is considered mapped:
- Example:
-
- @remote("new_name")
- def remote_method(arg1, arg2)
-
- public -- A flag which indicates if this method should be part
- of the known dictionary of remote methods. Defaults to True.
- Example:
-
- @remote(False)
- def remote_method(arg1, arg2)
-
- Note: renaming and revising (public vs. private) can be combined.
- Example:
-
- @remote("new_name", False)
- def remote_method(arg1, arg2)
- '''
- if hasattr(function_argument, '__call__'):
- return _intercept(function_argument, None, public)
- else:
- if not _isstr(function_argument):
- if not isinstance(function_argument, bool):
- raise Exception('Expected an RPC method name or visibility modifier!')
- else:
- def _wrap_revised(function):
- function = _intercept(function, None, function_argument)
- return function
- return _wrap_revised
- def _wrap_remapped(function):
- function = _intercept(function, function_argument, public)
- return function
- return _wrap_remapped
-
-
-class ACL:
- '''
- An Access Control List (ACL) is a list of rules, which are evaluated
- in order until a match is found. The policy of the matching rule
- is then applied.
-
- Rules are 3-tuples, consisting of a policy enumerated type, a JID
- expression and a RCP resource expression.
-
- Examples:
- [ (ACL.ALLOW, '*', '*') ] allow everyone everything, no restrictions
- [ (ACL.DENY, '*', '*') ] deny everyone everything, no restrictions
- [ (ACL.ALLOW, 'test@xmpp.org/unit', 'test.*'),
- (ACL.DENY, '*', '*') ] deny everyone everything, except named
- JID, which is allowed access to endpoint 'test' only.
-
- The use of wildcards is allowed in expressions, as follows:
- '*' everyone, or everything (= all endpoints and methods)
- 'test@xmpp.org/*' every JID regardless of JID resource
- '*@xmpp.org/rpc' every JID from domain xmpp.org with JID res 'rpc'
- 'frank@*' every 'frank', regardless of domain or JID res
- 'system.*' all methods of endpoint 'system'
- '*.reboot' all methods reboot regardless of endpoint
- '''
- ALLOW = True
- DENY = False
-
- @classmethod
- def check(cls, rules, jid, resource):
- if rules is None:
- return cls.DENY # No rules means no access!
- jid = str(jid) # Check the string representation of the JID.
- if not jid:
- return cls.DENY # Can't check an empty JID.
- for rule in rules:
- policy = cls._check(rule, jid, resource)
- if policy is not None:
- return policy
- return cls.DENY # By default if not rule matches, deny access.
-
- @classmethod
- def _check(cls, rule, jid, resource):
- if cls._match(jid, rule[1]) and cls._match(resource, rule[2]):
- return rule[0]
- else:
- return None
-
- @classmethod
- def _next_token(cls, expression, index):
- new_index = expression.find('*', index)
- if new_index == 0:
- return ''
- else:
- if new_index == -1:
- return expression[index : ]
- else:
- return expression[index : new_index]
-
- @classmethod
- def _match(cls, value, expression):
- #! print "_match [VALUE] %s [EXPR] %s" % (value, expression)
- index = 0
- position = 0
- while index < len(expression):
- token = cls._next_token(expression, index)
- #! print "[TOKEN] '%s'" % token
- size = len(token)
- if size > 0:
- token_index = value.find(token, position)
- if token_index == -1:
- return False
- else:
- #! print "[INDEX-OF] %s" % token_index
- position = token_index + len(token)
- pass
- if size == 0:
- index += 1
- else:
- index += size
- #! print "index %s position %s" % (index, position)
- return True
-
-ANY_ALL = [ (ACL.ALLOW, '*', '*') ]
-
-
-class RemoteException(Exception):
- '''
- Base exception for RPC. This exception is raised when a problem
- occurs in the network layer.
- '''
-
- def __init__(self, message="", cause=None):
- '''
- Initializes a new RemoteException.
-
- Arguments:
- message -- The message accompanying this exception.
- cause -- The underlying cause of this exception.
- '''
- self._message = message
- self._cause = cause
- pass
-
- def __str__(self):
- return repr(self._message)
-
- def get_message(self):
- return self._message
-
- def get_cause(self):
- return self._cause
-
-
-
-class InvocationException(RemoteException):
- '''
- Exception raised when a problem occurs during the remote invocation
- of a method.
- '''
- pass
-
-
-
-class AuthorizationException(RemoteException):
- '''
- Exception raised when the caller is not authorized to invoke the
- remote method.
- '''
- pass
-
-
-class TimeoutException(Exception):
- '''
- Exception raised when the synchronous execution of a method takes
- longer than the given threshold because an underlying asynchronous
- reply did not arrive in time.
- '''
- pass
-
-
-@_add_metaclass(abc.ABCMeta)
-class Callback(object):
- '''
- A base class for callback handlers.
- '''
-
- @abc.abstractproperty
- def set_value(self, value):
- return NotImplemented
-
- @abc.abstractproperty
- def cancel_with_error(self, exception):
- return NotImplemented
-
-
-class Future(Callback):
- '''
- Represents the result of an asynchronous computation.
- '''
-
- def __init__(self):
- '''
- Initializes a new Future.
- '''
- self._value = None
- self._exception = None
- self._event = threading.Event()
- pass
-
- def set_value(self, value):
- '''
- Sets the value of this Future. Once the value is set, a caller
- blocked on get_value will be able to continue.
- '''
- self._value = value
- self._event.set()
-
- def get_value(self, timeout=None):
- '''
- Gets the value of this Future. This call will block until
- the result is available, or until an optional timeout expires.
- When this Future is cancelled with an error,
-
- Arguments:
- timeout -- The maximum waiting time to obtain the value.
- '''
- self._event.wait(timeout)
- if self._exception:
- raise self._exception
- if not self._event.is_set():
- raise TimeoutException
- return self._value
-
- def is_done(self):
- '''
- Returns true if a value has been returned.
- '''
- return self._event.is_set()
-
- def cancel_with_error(self, exception):
- '''
- Cancels the Future because of an error. Once cancelled, a
- caller blocked on get_value will be able to continue.
- '''
- self._exception = exception
- self._event.set()
-
-
-@_add_metaclass(abc.ABCMeta)
-class Endpoint(object):
- '''
- The Endpoint class is an abstract base class for all objects
- participating in an RPC-enabled XMPP network.
-
- A user subclassing this class is required to implement the method:
- FQN(self)
- where FQN stands for Fully Qualified Name, an unambiguous name
- which specifies which object an RPC call refers to. It is the
- first part in a RPC method name '<fqn>.<method>'.
- '''
-
- def __init__(self, session, target_jid):
- '''
- Initialize a new Endpoint. This constructor should never be
- invoked by a user, instead it will be called by the factories
- which instantiate the RPC-enabled objects, of which only
- the classes are provided by the user.
-
- Arguments:
- session -- An RPC session instance.
- target_jid -- the identity of the remote XMPP entity.
- '''
- self.session = session
- self.target_jid = target_jid
-
- @abc.abstractproperty
- def FQN(self):
- return NotImplemented
-
- def get_methods(self):
- '''
- Returns a dictionary of all RPC method names provided by this
- class. This method returns the actual method names as found
- in the class definition which have been decorated with:
-
- @remote
- def some_rpc_method(arg1, arg2)
-
-
- Unless:
- (1) the name has been remapped, in which case the new
- name will be returned.
-
- @remote("new_name")
- def some_rpc_method(arg1, arg2)
-
- (2) the method is set to hidden
-
- @remote(False)
- def some_hidden_method(arg1, arg2)
- '''
- result = dict()
- for function in dir(self):
- test_attr = getattr(self, function, None)
- try:
- if test_attr._rpc:
- result[test_attr._rpc_name] = test_attr
- except Exception:
- pass
- return result
-
-
-
-class Proxy(Endpoint):
- '''
- Implementation of the Proxy pattern which is intended to wrap
- around Endpoints in order to intercept calls, marshall them and
- forward them to the remote object.
- '''
-
- def __init__(self, endpoint, callback = None):
- '''
- Initializes a new Proxy.
-
- Arguments:
- endpoint -- The endpoint which is proxified.
- '''
- self._endpoint = endpoint
- self._callback = callback
-
- def __getattribute__(self, name, *args):
- if name in ('__dict__', '_endpoint', 'async', '_callback'):
- return object.__getattribute__(self, name)
- else:
- attribute = self._endpoint.__getattribute__(name)
- if hasattr(attribute, '__call__'):
- try:
- if attribute._rpc:
- def _remote_call(*args, **kwargs):
- 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 _remote_call
- except:
- pass # If the attribute doesn't exist, don't care!
- return attribute
-
- def async(self, callback):
- return Proxy(self._endpoint, callback)
-
- def get_endpoint(self):
- '''
- Returns the proxified endpoint.
- '''
- return self._endpoint
-
- def FQN(self):
- return self._endpoint.FQN()
-
-
-class JabberRPCEntry(object):
-
-
- def __init__(self, endpoint_FQN, call):
- self._endpoint_FQN = endpoint_FQN
- self._call = call
-
- def call_method(self, args):
- return_value = self._call(*args)
- if return_value is None:
- return return_value
- else:
- return self._return(return_value)
-
- def get_endpoint_FQN(self):
- return self._endpoint_FQN
-
- def _return(self, *args):
- return args
-
-
-class RemoteSession(object):
- '''
- A context object for a Jabber-RPC session.
- '''
-
-
- def __init__(self, client, session_close_callback):
- '''
- Initializes a new RPC session.
-
- Arguments:
- client -- The SleekXMPP client associated with this session.
- session_close_callback -- A callback called when the
- session is closed.
- '''
- self._client = client
- self._session_close_callback = session_close_callback
- self._event = threading.Event()
- self._entries = {}
- self._callbacks = {}
- self._acls = {}
- self._lock = RLock()
-
- def _wait(self):
- self._event.wait()
-
- def _notify(self, event):
- log.debug("RPC Session as %s started.", self._client.boundjid.full)
- self._client.sendPresence()
- self._event.set()
- pass
-
- def _register_call(self, endpoint, method, name=None):
- '''
- Registers a method from an endpoint as remotely callable.
- '''
- if name is None:
- name = method.__name__
- key = "%s.%s" % (endpoint, name)
- log.debug("Registering call handler for %s (%s).", key, method)
- with self._lock:
- if key in self._entries:
- raise KeyError("A handler for %s has already been regisered!" % endpoint)
- self._entries[key] = JabberRPCEntry(endpoint, method)
- return key
-
- def _register_acl(self, endpoint, acl):
- log.debug("Registering ACL %s for endpoint %s.", repr(acl), endpoint)
- with self._lock:
- self._acls[endpoint] = acl
-
- def _register_callback(self, pid, callback):
- with self._lock:
- self._callbacks[pid] = callback
-
- def forget_callback(self, callback):
- with self._lock:
- pid = self._find_key(self._callbacks, callback)
- if pid is not None:
- del self._callback[pid]
- else:
- raise ValueError("Unknown callback!")
- pass
-
- def _find_key(self, dict, value):
- """return the key of dictionary dic given the value"""
- search = [k for k, v in dict.items() if v == value]
- if len(search) == 0:
- return None
- else:
- return search[0]
-
- def _unregister_call(self, key):
- #removes the registered call
- with self._lock:
- if self._entries[key]:
- del self._entries[key]
- else:
- raise ValueError()
-
- def new_proxy(self, target_jid, endpoint_cls):
- '''
- Instantiates a new proxy object, which proxies to a remote
- endpoint. This method uses a class reference without
- constructor arguments to instantiate the proxy.
-
- Arguments:
- target_jid -- the XMPP entity ID hosting the endpoint.
- endpoint_cls -- The remote (duck) type.
- '''
- try:
- argspec = inspect.getargspec(endpoint_cls.__init__)
- args = [None] * (len(argspec[0]) - 1)
- result = endpoint_cls(*args)
- Endpoint.__init__(result, self, target_jid)
- return Proxy(result)
- except:
- traceback.print_exc(file=sys.stdout)
-
- def new_handler(self, acl, handler_cls, *args, **kwargs):
- '''
- Instantiates a new handler object, which is called remotely
- by others. The user can control the effect of the call by
- implementing the remote method in the local endpoint class. The
- returned reference can be called locally and will behave as a
- regular instance.
-
- Arguments:
- acl -- Access control list (see ACL class)
- handler_clss -- The local (duck) type.
- *args -- Constructor arguments for the local type.
- **kwargs -- Constructor keyworded arguments for the local
- type.
- '''
- argspec = inspect.getargspec(handler_cls.__init__)
- base_argspec = inspect.getargspec(Endpoint.__init__)
- if(argspec == base_argspec):
- result = handler_cls(self, self._client.boundjid.full)
- else:
- result = handler_cls(*args, **kwargs)
- Endpoint.__init__(result, self, self._client.boundjid.full)
- method_dict = result.get_methods()
- for method_name, method in method_dict.items():
- #!!! self._client.plugin['xep_0009'].register_call(result.FQN(), method, method_name)
- self._register_call(result.FQN(), method, method_name)
- self._register_acl(result.FQN(), acl)
- return result
-
-# def is_available(self, targetCls, pto):
-# return self._client.is_available(pto)
-
- def _call_remote(self, pto, pmethod, callback, *arguments):
- iq = self._client.plugin['xep_0009'].make_iq_method_call(pto, pmethod, py2xml(*arguments))
- pid = iq['id']
- if callback is None:
- future = Future()
- self._register_callback(pid, future)
- iq.send()
- return future.get_value(30)
- else:
- log.debug("[RemoteSession] _call_remote %s", callback)
- self._register_callback(pid, callback)
- iq.send()
-
- def close(self, wait=False):
- '''
- Closes this session.
- '''
- self._client.disconnect(wait=wait)
- self._session_close_callback()
-
- def _on_jabber_rpc_method_call(self, iq):
- iq.enable('rpc_query')
- params = iq['rpc_query']['method_call']['params']
- args = xml2py(params)
- pmethod = iq['rpc_query']['method_call']['method_name']
- try:
- with self._lock:
- entry = self._entries[pmethod]
- rules = self._acls[entry.get_endpoint_FQN()]
- if ACL.check(rules, iq['from'], pmethod):
- return_value = entry.call_method(args)
- else:
- raise AuthorizationException("Unauthorized access to %s from %s!" % (pmethod, iq['from']))
- if return_value is None:
- return_value = ()
- response = self._client.plugin['xep_0009'].make_iq_method_response(iq['id'], iq['from'], py2xml(*return_value))
- response.send()
- except InvocationException as ie:
- fault = dict()
- fault['code'] = 500
- fault['string'] = ie.get_message()
- self._client.plugin['xep_0009']._send_fault(iq, fault2xml(fault))
- except AuthorizationException as ae:
- log.error(ae.get_message())
- error = self._client.plugin['xep_0009']._forbidden(iq)
- error.send()
- except Exception as e:
- if isinstance(e, KeyError):
- log.error("No handler available for %s!", pmethod)
- error = self._client.plugin['xep_0009']._item_not_found(iq)
- else:
- traceback.print_exc(file=sys.stderr)
- log.error("An unexpected problem occurred invoking method %s!", pmethod)
- error = self._client.plugin['xep_0009']._undefined_condition(iq)
- #! print "[REMOTE.PY] _handle_remote_procedure_call AN ERROR SHOULD BE SENT NOW %s " % e
- error.send()
-
- def _on_jabber_rpc_method_response(self, iq):
- iq.enable('rpc_query')
- args = xml2py(iq['rpc_query']['method_response']['params'])
- pid = iq['id']
- with self._lock:
- callback = self._callbacks[pid]
- del self._callbacks[pid]
- if(len(args) > 0):
- callback.set_value(args[0])
- else:
- callback.set_value(None)
- pass
-
- def _on_jabber_rpc_method_response2(self, iq):
- iq.enable('rpc_query')
- if iq['rpc_query']['method_response']['fault'] is not None:
- self._on_jabber_rpc_method_fault(iq)
- else:
- args = xml2py(iq['rpc_query']['method_response']['params'])
- pid = iq['id']
- with self._lock:
- callback = self._callbacks[pid]
- del self._callbacks[pid]
- if(len(args) > 0):
- callback.set_value(args[0])
- else:
- callback.set_value(None)
- pass
-
- def _on_jabber_rpc_method_fault(self, iq):
- iq.enable('rpc_query')
- fault = xml2fault(iq['rpc_query']['method_response']['fault'])
- pid = iq['id']
- with self._lock:
- callback = self._callbacks[pid]
- del self._callbacks[pid]
- e = {
- 500: InvocationException
- }[fault['code']](fault['string'])
- callback.cancel_with_error(e)
-
- def _on_jabber_rpc_error(self, iq):
- pid = iq['id']
- pmethod = self._client.plugin['xep_0009']._extract_method(iq['rpc_query'])
- code = iq['error']['code']
- type = iq['error']['type']
- condition = iq['error']['condition']
- #! print("['REMOTE.PY']._BINDING_handle_remote_procedure_error -> ERROR! ERROR! ERROR! Condition is '%s'" % condition)
- with self._lock:
- callback = self._callbacks[pid]
- del self._callbacks[pid]
- e = {
- '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'])),
- 'undefined-condition': RemoteException("An unexpected problem occured trying to invoke %s at %s!" % (pmethod, iq['from'])),
- }[condition]
- if e is None:
- RemoteException("An unexpected exception occurred at %s!" % iq['from'])
- callback.cancel_with_error(e)
-
-
-class Remote(object):
- '''
- Bootstrap class for Jabber-RPC sessions. New sessions are openend
- with an existing XMPP client, or one is instantiated on demand.
- '''
- _instance = None
- _sessions = dict()
- _lock = threading.RLock()
-
- @classmethod
- def new_session_with_client(cls, client, callback=None):
- '''
- Opens a new session with a given client.
-
- Arguments:
- client -- An XMPP client.
- callback -- An optional callback which can be used to track
- the starting state of the session.
- '''
- with Remote._lock:
- if(client.boundjid.bare in cls._sessions):
- raise RemoteException("There already is a session associated with these credentials!")
- else:
- cls._sessions[client.boundjid.bare] = client
-
- def _session_close_callback():
- with Remote._lock:
- del cls._sessions[client.boundjid.bare]
- result = RemoteSession(client, _session_close_callback)
- client.plugin['xep_0009'].xmpp.add_event_handler('jabber_rpc_method_call', result._on_jabber_rpc_method_call, threaded=True)
- client.plugin['xep_0009'].xmpp.add_event_handler('jabber_rpc_method_response', result._on_jabber_rpc_method_response, threaded=True)
- client.plugin['xep_0009'].xmpp.add_event_handler('jabber_rpc_method_fault', result._on_jabber_rpc_method_fault, threaded=True)
- client.plugin['xep_0009'].xmpp.add_event_handler('jabber_rpc_error', result._on_jabber_rpc_error, threaded=True)
- if callback is None:
- start_event_handler = result._notify
- else:
- start_event_handler = callback
- client.add_event_handler("session_start", start_event_handler)
- if client.connect():
- client.process(threaded=True)
- else:
- raise RemoteException("Could not connect to XMPP server!")
- pass
- if callback is None:
- result._wait()
- return result
-
- @classmethod
- def new_session(cls, jid, password, callback=None):
- '''
- Opens a new session and instantiates a new XMPP client.
-
- Arguments:
- jid -- The XMPP JID for logging in.
- password -- The password for logging in.
- callback -- An optional callback which can be used to track
- the starting state of the session.
- '''
- client = sleekxmpp.ClientXMPP(jid, password)
- #? Register plug-ins.
- client.registerPlugin('xep_0004') # Data Forms
- client.registerPlugin('xep_0009') # Jabber-RPC
- client.registerPlugin('xep_0030') # Service Discovery
- client.registerPlugin('xep_0060') # PubSub
- client.registerPlugin('xep_0199') # XMPP Ping
- return cls.new_session_with_client(client, callback)
-
diff --git a/sleekxmpp/plugins/xep_0009/rpc.py b/sleekxmpp/plugins/xep_0009/rpc.py
deleted file mode 100644
index 6179355e..00000000
--- a/sleekxmpp/plugins/xep_0009/rpc.py
+++ /dev/null
@@ -1,218 +0,0 @@
-"""
- SleekXMPP: The Sleek XMPP Library
- Copyright (C) 2011 Nathanael C. Fritz, Dann Martens (TOMOTON).
- This file is part of SleekXMPP.
-
- See the file LICENSE for copying permission.
-"""
-
-import logging
-
-from sleekxmpp import Iq
-from sleekxmpp.xmlstream import ET, register_stanza_plugin
-from sleekxmpp.xmlstream.handler import Callback
-from sleekxmpp.xmlstream.matcher import MatchXPath
-from sleekxmpp.plugins import BasePlugin
-from sleekxmpp.plugins.xep_0009 import stanza
-from sleekxmpp.plugins.xep_0009.stanza.RPC import RPCQuery, MethodCall, MethodResponse
-
-
-log = logging.getLogger(__name__)
-
-
-class XEP_0009(BasePlugin):
-
- name = 'xep_0009'
- description = 'XEP-0009: Jabber-RPC'
- dependencies = set(['xep_0030'])
- stanza = stanza
-
- def plugin_init(self):
- register_stanza_plugin(Iq, RPCQuery)
- register_stanza_plugin(RPCQuery, MethodCall)
- register_stanza_plugin(RPCQuery, MethodResponse)
-
- self.xmpp.register_handler(
- Callback('RPC Call', MatchXPath('{%s}iq/{%s}query/{%s}methodCall' % (self.xmpp.default_ns, RPCQuery.namespace, RPCQuery.namespace)),
- self._handle_method_call)
- )
- self.xmpp.register_handler(
- Callback('RPC Call', MatchXPath('{%s}iq/{%s}query/{%s}methodResponse' % (self.xmpp.default_ns, RPCQuery.namespace, RPCQuery.namespace)),
- self._handle_method_response)
- )
- self.xmpp.register_handler(
- Callback('RPC Call', MatchXPath('{%s}iq/{%s}error' % (self.xmpp.default_ns, self.xmpp.default_ns)),
- self._handle_error)
- )
- 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_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('error', self._handle_error)
- #self.activeCalls = []
-
- self.xmpp['xep_0030'].add_feature('jabber:iq:rpc')
- self.xmpp['xep_0030'].add_identity('automation','rpc')
-
- def make_iq_method_call(self, pto, pmethod, params):
- iq = self.xmpp.makeIqSet()
- iq.attrib['to'] = pto
- iq.attrib['from'] = self.xmpp.boundjid.full
- iq.enable('rpc_query')
- iq['rpc_query']['method_call']['method_name'] = pmethod
- iq['rpc_query']['method_call']['params'] = params
- return iq
-
- def make_iq_method_response(self, pid, pto, params):
- iq = self.xmpp.makeIqResult(pid)
- iq.attrib['to'] = pto
- iq.attrib['from'] = self.xmpp.boundjid.full
- iq.enable('rpc_query')
- iq['rpc_query']['method_response']['params'] = params
- return iq
-
- def make_iq_method_response_fault(self, pid, pto, params):
- iq = self.xmpp.makeIqResult(pid)
- iq.attrib['to'] = pto
- iq.attrib['from'] = self.xmpp.boundjid.full
- iq.enable('rpc_query')
- iq['rpc_query']['method_response']['params'] = None
- iq['rpc_query']['method_response']['fault'] = params
- return iq
-
-# def make_iq_method_error(self, pto, pid, pmethod, params, code, type, condition):
-# iq = self.xmpp.makeIqError(pid)
-# iq.attrib['to'] = pto
-# iq.attrib['from'] = self.xmpp.boundjid.full
-# iq['error']['code'] = code
-# iq['error']['type'] = type
-# iq['error']['condition'] = condition
-# iq['rpc_query']['method_call']['method_name'] = pmethod
-# iq['rpc_query']['method_call']['params'] = params
-# return iq
-
- def _item_not_found(self, iq):
- payload = iq.get_payload()
- iq.reply().error().set_payload(payload)
- iq['error']['code'] = '404'
- iq['error']['type'] = 'cancel'
- iq['error']['condition'] = 'item-not-found'
- return iq
-
- def _undefined_condition(self, iq):
- payload = iq.get_payload()
- iq.reply().error().set_payload(payload)
- iq['error']['code'] = '500'
- iq['error']['type'] = 'cancel'
- iq['error']['condition'] = 'undefined-condition'
- return iq
-
- def _forbidden(self, iq):
- payload = iq.get_payload()
- iq.reply().error().set_payload(payload)
- iq['error']['code'] = '403'
- iq['error']['type'] = 'auth'
- iq['error']['condition'] = 'forbidden'
- return iq
-
- def _recipient_unvailable(self, iq):
- payload = iq.get_payload()
- iq.reply().error().set_payload(payload)
- iq['error']['code'] = '404'
- iq['error']['type'] = 'wait'
- iq['error']['condition'] = 'recipient-unavailable'
- return iq
-
- def _handle_method_call(self, iq):
- type = iq['type']
- if type == 'set':
- log.debug("Incoming Jabber-RPC call from %s", iq['from'])
- self.xmpp.event('jabber_rpc_method_call', iq)
- else:
- if type == 'error' and ['rpc_query'] is None:
- self.handle_error(iq)
- else:
- log.debug("Incoming Jabber-RPC error from %s", iq['from'])
- self.xmpp.event('jabber_rpc_error', iq)
-
- def _handle_method_response(self, iq):
- if iq['rpc_query']['method_response']['fault'] is not None:
- log.debug("Incoming Jabber-RPC fault from %s", iq['from'])
- #self._on_jabber_rpc_method_fault(iq)
- self.xmpp.event('jabber_rpc_method_fault', iq)
- else:
- log.debug("Incoming Jabber-RPC response from %s", iq['from'])
- self.xmpp.event('jabber_rpc_method_response', iq)
-
- def _handle_error(self, iq):
- print("['XEP-0009']._handle_error -> ERROR! Iq is '%s'" % iq)
- print("#######################")
- print("### NOT IMPLEMENTED ###")
- print("#######################")
-
- def _on_jabber_rpc_method_call(self, iq, forwarded=False):
- """
- A default handler for Jabber-RPC method call. If another
- handler is registered, this one will defer and not run.
-
- If this handler is called by your own custom handler with
- forwarded set to True, then it will run as normal.
- """
- if not forwarded and self.xmpp.event_handled('jabber_rpc_method_call') > 1:
- return
- # Reply with error by default
- error = self.client.plugin['xep_0009']._item_not_found(iq)
- error.send()
-
- def _on_jabber_rpc_method_response(self, iq, forwarded=False):
- """
- A default handler for Jabber-RPC method response. If another
- handler is registered, this one will defer and not run.
-
- If this handler is called by your own custom handler with
- forwarded set to True, then it will run as normal.
- """
- if not forwarded and self.xmpp.event_handled('jabber_rpc_method_response') > 1:
- return
- error = self.client.plugin['xep_0009']._recpient_unavailable(iq)
- error.send()
-
- def _on_jabber_rpc_method_fault(self, iq, forwarded=False):
- """
- A default handler for Jabber-RPC fault response. If another
- handler is registered, this one will defer and not run.
-
- If this handler is called by your own custom handler with
- forwarded set to True, then it will run as normal.
- """
- if not forwarded and self.xmpp.event_handled('jabber_rpc_method_fault') > 1:
- return
- error = self.client.plugin['xep_0009']._recpient_unavailable(iq)
- error.send()
-
- def _on_jabber_rpc_error(self, iq, forwarded=False):
- """
- A default handler for Jabber-RPC error response. If another
- handler is registered, this one will defer and not run.
-
- If this handler is called by your own custom handler with
- forwarded set to True, then it will run as normal.
- """
- if not forwarded and self.xmpp.event_handled('jabber_rpc_error') > 1:
- return
- error = self.client.plugin['xep_0009']._recpient_unavailable(iq, iq.get_payload())
- error.send()
-
- def _send_fault(self, iq, fault_xml): #
- fault = self.make_iq_method_response_fault(iq['id'], iq['from'], fault_xml)
- fault.send()
-
- def _send_error(self, iq):
- print("['XEP-0009']._send_error -> ERROR! Iq is '%s'" % iq)
- print("#######################")
- print("### NOT IMPLEMENTED ###")
- print("#######################")
-
- def _extract_method(self, stanza):
- xml = ET.fromstring("%s" % stanza)
- return xml.find("./methodCall/methodName").text
diff --git a/sleekxmpp/plugins/xep_0009/stanza/RPC.py b/sleekxmpp/plugins/xep_0009/stanza/RPC.py
deleted file mode 100644
index 3d1c77a2..00000000
--- a/sleekxmpp/plugins/xep_0009/stanza/RPC.py
+++ /dev/null
@@ -1,64 +0,0 @@
-"""
- SleekXMPP: The Sleek XMPP Library
- Copyright (C) 2011 Nathanael C. Fritz, Dann Martens (TOMOTON).
- This file is part of SleekXMPP.
-
- See the file LICENSE for copying permission.
-"""
-
-from sleekxmpp.xmlstream.stanzabase import ElementBase
-from xml.etree import cElementTree as ET
-
-
-class RPCQuery(ElementBase):
- name = 'query'
- namespace = 'jabber:iq:rpc'
- plugin_attrib = 'rpc_query'
- interfaces = set(())
- subinterfaces = set(())
- plugin_attrib_map = {}
- plugin_tag_map = {}
-
-
-class MethodCall(ElementBase):
- name = 'methodCall'
- namespace = 'jabber:iq:rpc'
- plugin_attrib = 'method_call'
- interfaces = set(('method_name', 'params'))
- subinterfaces = set(())
- plugin_attrib_map = {}
- plugin_tag_map = {}
-
- def get_method_name(self):
- return self._get_sub_text('methodName')
-
- def set_method_name(self, value):
- return self._set_sub_text('methodName', value)
-
- def get_params(self):
- return self.xml.find('{%s}params' % self.namespace)
-
- def set_params(self, params):
- self.append(params)
-
-
-class MethodResponse(ElementBase):
- name = 'methodResponse'
- namespace = 'jabber:iq:rpc'
- plugin_attrib = 'method_response'
- interfaces = set(('params', 'fault'))
- subinterfaces = set(())
- plugin_attrib_map = {}
- plugin_tag_map = {}
-
- def get_params(self):
- return self.xml.find('{%s}params' % self.namespace)
-
- def set_params(self, params):
- self.append(params)
-
- def get_fault(self):
- return self.xml.find('{%s}fault' % self.namespace)
-
- def set_fault(self, fault):
- self.append(fault)
diff --git a/sleekxmpp/plugins/xep_0009/stanza/__init__.py b/sleekxmpp/plugins/xep_0009/stanza/__init__.py
deleted file mode 100644
index 5dcbf330..00000000
--- a/sleekxmpp/plugins/xep_0009/stanza/__init__.py
+++ /dev/null
@@ -1,9 +0,0 @@
-"""
- SleekXMPP: The Sleek XMPP Library
- Copyright (C) 2011 Nathanael C. Fritz, Dann Martens (TOMOTON).
- This file is part of SleekXMPP.
-
- See the file LICENSE for copying permission.
-"""
-
-from sleekxmpp.plugins.xep_0009.stanza.RPC import RPCQuery, MethodCall, MethodResponse