| 
									
										
										
										
											2001-09-17 17:35:21 +00:00
										 |  |  | """Simple XML-RPC Server.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | This module can be used to create simple XML-RPC servers | 
					
						
							|  |  |  | by creating a server and either installing functions, a | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  | class instance, or by extending the SimpleXMLRPCServer | 
					
						
							| 
									
										
										
										
											2001-09-17 17:35:21 +00:00
										 |  |  | class. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  | It can also be used to handle XML-RPC requests in a CGI | 
					
						
							|  |  |  | environment using CGIXMLRPCRequestHandler. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-09-17 17:35:21 +00:00
										 |  |  | A list of possible usage patterns follows: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 1. Install functions: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | server = SimpleXMLRPCServer(("localhost", 8000)) | 
					
						
							|  |  |  | server.register_function(pow) | 
					
						
							|  |  |  | server.register_function(lambda x,y: x+y, 'add') | 
					
						
							|  |  |  | server.serve_forever() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 2. Install an instance: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class MyFuncs: | 
					
						
							|  |  |  |     def __init__(self): | 
					
						
							|  |  |  |         # make all of the string functions available through | 
					
						
							|  |  |  |         # string.func_name | 
					
						
							|  |  |  |         import string | 
					
						
							|  |  |  |         self.string = string | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |     def _listMethods(self): | 
					
						
							|  |  |  |         # implement this method so that system.listMethods | 
					
						
							|  |  |  |         # knows to advertise the strings methods | 
					
						
							|  |  |  |         return list_public_methods(self) + \ | 
					
						
							|  |  |  |                 ['string.' + method for method in list_public_methods(self.string)] | 
					
						
							| 
									
										
										
										
											2001-09-17 17:35:21 +00:00
										 |  |  |     def pow(self, x, y): return pow(x, y) | 
					
						
							|  |  |  |     def add(self, x, y) : return x + y | 
					
						
							| 
									
										
										
										
											2003-01-29 03:49:43 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-09-17 17:35:21 +00:00
										 |  |  | server = SimpleXMLRPCServer(("localhost", 8000)) | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  | server.register_introspection_functions() | 
					
						
							| 
									
										
										
										
											2001-09-17 17:35:21 +00:00
										 |  |  | server.register_instance(MyFuncs()) | 
					
						
							|  |  |  | server.serve_forever() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 3. Install an instance with custom dispatch method: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class Math: | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |     def _listMethods(self): | 
					
						
							|  |  |  |         # this method must be present for system.listMethods | 
					
						
							|  |  |  |         # to work | 
					
						
							|  |  |  |         return ['add', 'pow'] | 
					
						
							|  |  |  |     def _methodHelp(self, method): | 
					
						
							|  |  |  |         # this method must be present for system.methodHelp | 
					
						
							|  |  |  |         # to work | 
					
						
							|  |  |  |         if method == 'add': | 
					
						
							|  |  |  |             return "add(2,3) => 5" | 
					
						
							|  |  |  |         elif method == 'pow': | 
					
						
							|  |  |  |             return "pow(x, y[, z]) => number" | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             # By convention, return empty | 
					
						
							|  |  |  |             # string if no help is available | 
					
						
							|  |  |  |             return "" | 
					
						
							| 
									
										
										
										
											2001-09-17 17:35:21 +00:00
										 |  |  |     def _dispatch(self, method, params): | 
					
						
							|  |  |  |         if method == 'pow': | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |             return pow(*params) | 
					
						
							| 
									
										
										
										
											2001-09-17 17:35:21 +00:00
										 |  |  |         elif method == 'add': | 
					
						
							|  |  |  |             return params[0] + params[1] | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             raise 'bad method' | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-09-17 17:35:21 +00:00
										 |  |  | server = SimpleXMLRPCServer(("localhost", 8000)) | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  | server.register_introspection_functions() | 
					
						
							| 
									
										
										
										
											2001-09-17 17:35:21 +00:00
										 |  |  | server.register_instance(Math()) | 
					
						
							|  |  |  | server.serve_forever() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  | 4. Subclass SimpleXMLRPCServer: | 
					
						
							| 
									
										
										
										
											2001-09-17 17:35:21 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  | class MathServer(SimpleXMLRPCServer): | 
					
						
							| 
									
										
										
										
											2001-09-17 17:35:21 +00:00
										 |  |  |     def _dispatch(self, method, params): | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             # We are forcing the 'export_' prefix on methods that are | 
					
						
							|  |  |  |             # callable through XML-RPC to prevent potential security | 
					
						
							|  |  |  |             # problems | 
					
						
							|  |  |  |             func = getattr(self, 'export_' + method) | 
					
						
							|  |  |  |         except AttributeError: | 
					
						
							|  |  |  |             raise Exception('method "%s" is not supported' % method) | 
					
						
							|  |  |  |         else: | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |             return func(*params) | 
					
						
							| 
									
										
										
										
											2001-09-17 17:35:21 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def export_add(self, x, y): | 
					
						
							|  |  |  |         return x + y | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  | server = MathServer(("localhost", 8000)) | 
					
						
							| 
									
										
										
										
											2001-09-17 17:35:21 +00:00
										 |  |  | server.serve_forever() | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 5. CGI script: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | server = CGIXMLRPCRequestHandler() | 
					
						
							|  |  |  | server.register_function(pow) | 
					
						
							|  |  |  | server.handle_request() | 
					
						
							| 
									
										
										
										
											2001-09-17 17:35:21 +00:00
										 |  |  | """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Written by Brian Quinlan (brian@sweetapp.com). | 
					
						
							|  |  |  | # Based on code written by Fredrik Lundh. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import xmlrpclib | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  | from xmlrpclib import Fault | 
					
						
							| 
									
										
										
										
											2001-09-17 17:35:21 +00:00
										 |  |  | import SocketServer | 
					
						
							|  |  |  | import BaseHTTPServer | 
					
						
							|  |  |  | import sys | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  | import os | 
					
						
							| 
									
										
										
										
											2001-09-17 17:35:21 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-02-03 15:01:24 +00:00
										 |  |  | def resolve_dotted_attribute(obj, attr, allow_dotted_names=True): | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |     """resolve_dotted_attribute(a, 'b.c.d') => a.b.c.d
 | 
					
						
							| 
									
										
										
										
											2001-09-17 17:35:21 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |     Resolves a dotted attribute name to an object.  Raises | 
					
						
							|  |  |  |     an AttributeError if any attribute in the chain starts with a '_'. | 
					
						
							| 
									
										
										
										
											2005-02-03 15:01:24 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     If the optional allow_dotted_names argument is false, dots are not | 
					
						
							|  |  |  |     supported and this function operates similar to getattr(obj, attr). | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-02-03 15:01:24 +00:00
										 |  |  |     if allow_dotted_names: | 
					
						
							|  |  |  |         attrs = attr.split('.') | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         attrs = [attr] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for i in attrs: | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |         if i.startswith('_'): | 
					
						
							|  |  |  |             raise AttributeError( | 
					
						
							|  |  |  |                 'attempt to access private attribute "%s"' % i | 
					
						
							|  |  |  |                 ) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             obj = getattr(obj,i) | 
					
						
							|  |  |  |     return obj | 
					
						
							| 
									
										
										
										
											2001-09-17 17:35:21 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  | def list_public_methods(obj): | 
					
						
							|  |  |  |     """Returns a list of attribute strings, found in the specified
 | 
					
						
							|  |  |  |     object, which represent callable attributes"""
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return [member for member in dir(obj) | 
					
						
							|  |  |  |                 if not member.startswith('_') and | 
					
						
							|  |  |  |                     callable(getattr(obj, member))] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def remove_duplicates(lst): | 
					
						
							|  |  |  |     """remove_duplicates([2,2,2,1,3,3]) => [3,1,2]
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Returns a copy of a list without duplicates. Every list | 
					
						
							|  |  |  |     item must be hashable and the order of the items in the | 
					
						
							|  |  |  |     resulting list is not defined. | 
					
						
							| 
									
										
										
										
											2003-01-29 03:49:43 +00:00
										 |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |     u = {} | 
					
						
							|  |  |  |     for x in lst: | 
					
						
							|  |  |  |         u[x] = 1 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return u.keys() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class SimpleXMLRPCDispatcher: | 
					
						
							|  |  |  |     """Mix-in class that dispatches XML-RPC requests.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     This class is used to register XML-RPC method handlers | 
					
						
							|  |  |  |     and then to dispatch them. There should never be any | 
					
						
							|  |  |  |     reason to instantiate this class directly. | 
					
						
							| 
									
										
										
										
											2001-09-17 17:35:21 +00:00
										 |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2003-01-29 03:49:43 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |     def __init__(self): | 
					
						
							|  |  |  |         self.funcs = {} | 
					
						
							|  |  |  |         self.instance = None | 
					
						
							| 
									
										
										
										
											2001-09-17 17:35:21 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2005-02-03 15:01:24 +00:00
										 |  |  |     def register_instance(self, instance, allow_dotted_names=False): | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |         """Registers an instance to respond to XML-RPC requests.
 | 
					
						
							| 
									
										
										
										
											2001-09-17 17:35:21 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |         Only one instance can be installed at a time. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         If the registered instance has a _dispatch method then that | 
					
						
							|  |  |  |         method will be called with the name of the XML-RPC method and | 
					
						
							| 
									
										
										
										
											2005-07-22 21:49:32 +00:00
										 |  |  |         its parameters as a tuple | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |         e.g. instance._dispatch('add',(2,3)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         If the registered instance does not have a _dispatch method | 
					
						
							|  |  |  |         then the instance will be searched to find a matching method | 
					
						
							|  |  |  |         and, if found, will be called. Methods beginning with an '_' | 
					
						
							|  |  |  |         are considered private and will not be called by | 
					
						
							|  |  |  |         SimpleXMLRPCServer. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         If a registered function matches a XML-RPC request, then it | 
					
						
							|  |  |  |         will be called instead of the registered instance. | 
					
						
							| 
									
										
										
										
											2005-02-03 15:01:24 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         If the optional allow_dotted_names argument is true and the | 
					
						
							|  |  |  |         instance does not have a _dispatch method, method names | 
					
						
							|  |  |  |         containing dots are supported and resolved, as long as none of | 
					
						
							|  |  |  |         the name segments start with an '_'. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             *** SECURITY WARNING: *** | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             Enabling the allow_dotted_names options allows intruders | 
					
						
							|  |  |  |             to access your module's global variables and may allow | 
					
						
							|  |  |  |             intruders to execute arbitrary code on your machine.  Only | 
					
						
							|  |  |  |             use this option on a secure, closed network. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-09-17 17:35:21 +00:00
										 |  |  |         """
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |         self.instance = instance | 
					
						
							| 
									
										
										
										
											2005-02-03 15:01:24 +00:00
										 |  |  |         self.allow_dotted_names = allow_dotted_names | 
					
						
							| 
									
										
										
										
											2001-09-17 17:35:21 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |     def register_function(self, function, name = None): | 
					
						
							|  |  |  |         """Registers a function to respond to XML-RPC requests.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         The optional name argument can be used to set a Unicode name | 
					
						
							|  |  |  |         for the function. | 
					
						
							|  |  |  |         """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if name is None: | 
					
						
							|  |  |  |             name = function.__name__ | 
					
						
							|  |  |  |         self.funcs[name] = function | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def register_introspection_functions(self): | 
					
						
							|  |  |  |         """Registers the XML-RPC introspection methods in the system
 | 
					
						
							|  |  |  |         namespace. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         see http://xmlrpc.usefulinc.com/doc/reserved.html | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2003-01-29 03:49:43 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |         self.funcs.update({'system.listMethods' : self.system_listMethods, | 
					
						
							|  |  |  |                       'system.methodSignature' : self.system_methodSignature, | 
					
						
							|  |  |  |                       'system.methodHelp' : self.system_methodHelp}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def register_multicall_functions(self): | 
					
						
							|  |  |  |         """Registers the XML-RPC multicall method in the system
 | 
					
						
							|  |  |  |         namespace. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         see http://www.xmlrpc.com/discuss/msgReader$1208"""
 | 
					
						
							| 
									
										
										
										
											2003-01-29 03:49:43 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |         self.funcs.update({'system.multicall' : self.system_multicall}) | 
					
						
							| 
									
										
										
										
											2003-01-29 03:49:43 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |     def _marshaled_dispatch(self, data, dispatch_method = None): | 
					
						
							|  |  |  |         """Dispatches an XML-RPC method from marshalled (XML) data.
 | 
					
						
							| 
									
										
										
										
											2003-01-29 03:49:43 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |         XML-RPC methods are dispatched from the marshalled (XML) data | 
					
						
							|  |  |  |         using the _dispatch method and the result is returned as | 
					
						
							|  |  |  |         marshalled data. For backwards compatibility, a dispatch | 
					
						
							| 
									
										
										
										
											2003-01-29 03:49:43 +00:00
										 |  |  |         function can be provided as an argument (see comment in | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |         SimpleXMLRPCRequestHandler.do_POST) but overriding the | 
					
						
							|  |  |  |         existing method through subclassing is the prefered means | 
					
						
							|  |  |  |         of changing method dispatch behavior. | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2003-01-29 03:49:43 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |         params, method = xmlrpclib.loads(data) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # generate response | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             if dispatch_method is not None: | 
					
						
							|  |  |  |                 response = dispatch_method(method, params) | 
					
						
							| 
									
										
										
										
											2003-01-29 03:49:43 +00:00
										 |  |  |             else: | 
					
						
							| 
									
										
										
										
											2001-09-17 17:35:21 +00:00
										 |  |  |                 response = self._dispatch(method, params) | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |             # wrap response in a singleton tuple | 
					
						
							|  |  |  |             response = (response,) | 
					
						
							|  |  |  |             response = xmlrpclib.dumps(response, methodresponse=1) | 
					
						
							|  |  |  |         except Fault, fault: | 
					
						
							|  |  |  |             response = xmlrpclib.dumps(fault) | 
					
						
							| 
									
										
										
										
											2001-09-17 17:35:21 +00:00
										 |  |  |         except: | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |             # report exception back to server | 
					
						
							|  |  |  |             response = xmlrpclib.dumps( | 
					
						
							|  |  |  |                 xmlrpclib.Fault(1, "%s:%s" % (sys.exc_type, sys.exc_value)) | 
					
						
							|  |  |  |                 ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return response | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def system_listMethods(self): | 
					
						
							|  |  |  |         """system.listMethods() => ['add', 'subtract', 'multiple']
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Returns a list of the methods supported by the server."""
 | 
					
						
							| 
									
										
										
										
											2003-01-29 03:49:43 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |         methods = self.funcs.keys() | 
					
						
							|  |  |  |         if self.instance is not None: | 
					
						
							|  |  |  |             # Instance can implement _listMethod to return a list of | 
					
						
							|  |  |  |             # methods | 
					
						
							|  |  |  |             if hasattr(self.instance, '_listMethods'): | 
					
						
							|  |  |  |                 methods = remove_duplicates( | 
					
						
							|  |  |  |                         methods + self.instance._listMethods() | 
					
						
							|  |  |  |                     ) | 
					
						
							|  |  |  |             # if the instance has a _dispatch method then we | 
					
						
							|  |  |  |             # don't have enough information to provide a list | 
					
						
							|  |  |  |             # of methods | 
					
						
							|  |  |  |             elif not hasattr(self.instance, '_dispatch'): | 
					
						
							|  |  |  |                 methods = remove_duplicates( | 
					
						
							|  |  |  |                         methods + list_public_methods(self.instance) | 
					
						
							|  |  |  |                     ) | 
					
						
							|  |  |  |         methods.sort() | 
					
						
							|  |  |  |         return methods | 
					
						
							| 
									
										
										
										
											2003-01-29 03:49:43 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |     def system_methodSignature(self, method_name): | 
					
						
							|  |  |  |         """system.methodSignature('add') => [double, int, int]
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2004-10-03 23:21:44 +00:00
										 |  |  |         Returns a list describing the signature of the method. In the | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |         above example, the add method takes two integers as arguments | 
					
						
							|  |  |  |         and returns a double result. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         This server does NOT support system.methodSignature."""
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # See http://xmlrpc.usefulinc.com/doc/sysmethodsig.html | 
					
						
							| 
									
										
										
										
											2003-01-29 03:49:43 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |         return 'signatures not supported' | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def system_methodHelp(self, method_name): | 
					
						
							|  |  |  |         """system.methodHelp('add') => "Adds two integers together"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Returns a string containing documentation for the specified method."""
 | 
					
						
							| 
									
										
										
										
											2003-01-29 03:49:43 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |         method = None | 
					
						
							|  |  |  |         if self.funcs.has_key(method_name): | 
					
						
							|  |  |  |             method = self.funcs[method_name] | 
					
						
							|  |  |  |         elif self.instance is not None: | 
					
						
							|  |  |  |             # Instance can implement _methodHelp to return help for a method | 
					
						
							|  |  |  |             if hasattr(self.instance, '_methodHelp'): | 
					
						
							|  |  |  |                 return self.instance._methodHelp(method_name) | 
					
						
							|  |  |  |             # if the instance has a _dispatch method then we | 
					
						
							|  |  |  |             # don't have enough information to provide help | 
					
						
							|  |  |  |             elif not hasattr(self.instance, '_dispatch'): | 
					
						
							|  |  |  |                 try: | 
					
						
							|  |  |  |                     method = resolve_dotted_attribute( | 
					
						
							|  |  |  |                                 self.instance, | 
					
						
							| 
									
										
										
										
											2005-02-03 15:01:24 +00:00
										 |  |  |                                 method_name, | 
					
						
							|  |  |  |                                 self.allow_dotted_names | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |                                 ) | 
					
						
							|  |  |  |                 except AttributeError: | 
					
						
							|  |  |  |                     pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # Note that we aren't checking that the method actually | 
					
						
							|  |  |  |         # be a callable object of some kind | 
					
						
							|  |  |  |         if method is None: | 
					
						
							|  |  |  |             return "" | 
					
						
							| 
									
										
										
										
											2001-09-17 17:35:21 +00:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2003-06-29 04:16:28 +00:00
										 |  |  |             import pydoc | 
					
						
							| 
									
										
										
										
											2003-06-29 04:19:37 +00:00
										 |  |  |             return pydoc.getdoc(method) | 
					
						
							| 
									
										
										
										
											2001-09-17 17:35:21 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |     def system_multicall(self, call_list): | 
					
						
							|  |  |  |         """system.multicall([{'methodName': 'add', 'params': [2, 2]}, ...]) => \
 | 
					
						
							|  |  |  | [[4], ...] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Allows the caller to package multiple XML-RPC calls into a single | 
					
						
							|  |  |  |         request. | 
					
						
							| 
									
										
										
										
											2001-09-17 17:35:21 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-01-29 03:49:43 +00:00
										 |  |  |         See http://www.xmlrpc.com/discuss/msgReader$1208 | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2003-01-29 03:49:43 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |         results = [] | 
					
						
							|  |  |  |         for call in call_list: | 
					
						
							|  |  |  |             method_name = call['methodName'] | 
					
						
							|  |  |  |             params = call['params'] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 # XXX A marshalling error in any response will fail the entire | 
					
						
							|  |  |  |                 # multicall. If someone cares they should fix this. | 
					
						
							|  |  |  |                 results.append([self._dispatch(method_name, params)]) | 
					
						
							|  |  |  |             except Fault, fault: | 
					
						
							|  |  |  |                 results.append( | 
					
						
							|  |  |  |                     {'faultCode' : fault.faultCode, | 
					
						
							|  |  |  |                      'faultString' : fault.faultString} | 
					
						
							|  |  |  |                     ) | 
					
						
							|  |  |  |             except: | 
					
						
							|  |  |  |                 results.append( | 
					
						
							|  |  |  |                     {'faultCode' : 1, | 
					
						
							|  |  |  |                      'faultString' : "%s:%s" % (sys.exc_type, sys.exc_value)} | 
					
						
							|  |  |  |                     ) | 
					
						
							|  |  |  |         return results | 
					
						
							| 
									
										
										
										
											2003-01-29 03:49:43 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-09-17 17:35:21 +00:00
										 |  |  |     def _dispatch(self, method, params): | 
					
						
							|  |  |  |         """Dispatches the XML-RPC method.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         XML-RPC calls are forwarded to a registered function that | 
					
						
							|  |  |  |         matches the called XML-RPC method name. If no such function | 
					
						
							|  |  |  |         exists then the call is forwarded to the registered instance, | 
					
						
							|  |  |  |         if available. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         If the registered instance has a _dispatch method then that | 
					
						
							|  |  |  |         method will be called with the name of the XML-RPC method and | 
					
						
							| 
									
										
										
										
											2005-07-22 21:49:32 +00:00
										 |  |  |         its parameters as a tuple | 
					
						
							| 
									
										
										
										
											2001-09-17 17:35:21 +00:00
										 |  |  |         e.g. instance._dispatch('add',(2,3)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         If the registered instance does not have a _dispatch method | 
					
						
							|  |  |  |         then the instance will be searched to find a matching method | 
					
						
							|  |  |  |         and, if found, will be called. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Methods beginning with an '_' are considered private and will | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |         not be called. | 
					
						
							| 
									
										
										
										
											2001-09-17 17:35:21 +00:00
										 |  |  |         """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         func = None | 
					
						
							|  |  |  |         try: | 
					
						
							|  |  |  |             # check to see if a matching function has been registered | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |             func = self.funcs[method] | 
					
						
							| 
									
										
										
										
											2001-09-17 17:35:21 +00:00
										 |  |  |         except KeyError: | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |             if self.instance is not None: | 
					
						
							| 
									
										
										
										
											2001-09-17 17:35:21 +00:00
										 |  |  |                 # check for a _dispatch method | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |                 if hasattr(self.instance, '_dispatch'): | 
					
						
							|  |  |  |                     return self.instance._dispatch(method, params) | 
					
						
							| 
									
										
										
										
											2001-09-17 17:35:21 +00:00
										 |  |  |                 else: | 
					
						
							|  |  |  |                     # call instance method directly | 
					
						
							|  |  |  |                     try: | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |                         func = resolve_dotted_attribute( | 
					
						
							|  |  |  |                             self.instance, | 
					
						
							| 
									
										
										
										
											2005-02-03 15:01:24 +00:00
										 |  |  |                             method, | 
					
						
							|  |  |  |                             self.allow_dotted_names | 
					
						
							| 
									
										
										
										
											2001-09-17 17:35:21 +00:00
										 |  |  |                             ) | 
					
						
							|  |  |  |                     except AttributeError: | 
					
						
							|  |  |  |                         pass | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if func is not None: | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |             return func(*params) | 
					
						
							| 
									
										
										
										
											2001-09-17 17:35:21 +00:00
										 |  |  |         else: | 
					
						
							|  |  |  |             raise Exception('method "%s" is not supported' % method) | 
					
						
							| 
									
										
										
										
											2003-01-29 03:49:43 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  | class SimpleXMLRPCRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler): | 
					
						
							|  |  |  |     """Simple XML-RPC request handler class.
 | 
					
						
							| 
									
										
										
										
											2001-09-17 17:35:21 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |     Handles all HTTP POST requests and attempts to decode them as | 
					
						
							|  |  |  |     XML-RPC requests. | 
					
						
							|  |  |  |     """
 | 
					
						
							| 
									
										
										
										
											2001-09-17 17:35:21 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |     def do_POST(self): | 
					
						
							|  |  |  |         """Handles the HTTP POST request.
 | 
					
						
							| 
									
										
										
										
											2001-09-29 04:54:33 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |         Attempts to interpret all HTTP POST requests as XML-RPC calls, | 
					
						
							|  |  |  |         which are forwarded to the server's _dispatch method for handling. | 
					
						
							|  |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2003-01-29 03:49:43 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |         try: | 
					
						
							|  |  |  |             # get arguments | 
					
						
							|  |  |  |             data = self.rfile.read(int(self.headers["content-length"])) | 
					
						
							|  |  |  |             # In previous versions of SimpleXMLRPCServer, _dispatch | 
					
						
							|  |  |  |             # could be overridden in this class, instead of in | 
					
						
							|  |  |  |             # SimpleXMLRPCDispatcher. To maintain backwards compatibility, | 
					
						
							|  |  |  |             # check to see if a subclass implements _dispatch and dispatch | 
					
						
							|  |  |  |             # using that method if present. | 
					
						
							|  |  |  |             response = self.server._marshaled_dispatch( | 
					
						
							|  |  |  |                     data, getattr(self, '_dispatch', None) | 
					
						
							| 
									
										
										
										
											2001-09-29 04:54:33 +00:00
										 |  |  |                 ) | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |         except: # This should only happen if the module is buggy | 
					
						
							|  |  |  |             # internal error, report as HTTP server error | 
					
						
							|  |  |  |             self.send_response(500) | 
					
						
							|  |  |  |             self.end_headers() | 
					
						
							| 
									
										
										
										
											2001-09-29 04:54:33 +00:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |             # got a valid XML RPC response | 
					
						
							|  |  |  |             self.send_response(200) | 
					
						
							|  |  |  |             self.send_header("Content-type", "text/xml") | 
					
						
							|  |  |  |             self.send_header("Content-length", str(len(response))) | 
					
						
							|  |  |  |             self.end_headers() | 
					
						
							|  |  |  |             self.wfile.write(response) | 
					
						
							| 
									
										
										
										
											2001-09-29 04:54:33 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |             # shut down the connection | 
					
						
							|  |  |  |             self.wfile.flush() | 
					
						
							|  |  |  |             self.connection.shutdown(1) | 
					
						
							| 
									
										
										
										
											2003-01-29 03:49:43 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |     def log_request(self, code='-', size='-'): | 
					
						
							|  |  |  |         """Selectively log an accepted request.""" | 
					
						
							| 
									
										
										
										
											2001-09-29 04:54:33 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |         if self.server.logRequests: | 
					
						
							|  |  |  |             BaseHTTPServer.BaseHTTPRequestHandler.log_request(self, code, size) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-01-29 03:49:43 +00:00
										 |  |  | class SimpleXMLRPCServer(SocketServer.TCPServer, | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |                          SimpleXMLRPCDispatcher): | 
					
						
							| 
									
										
										
										
											2001-09-17 17:35:21 +00:00
										 |  |  |     """Simple XML-RPC server.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Simple XML-RPC server that allows functions and a single instance | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |     to be installed to handle requests. The default implementation | 
					
						
							|  |  |  |     attempts to dispatch XML-RPC calls to the functions or instance | 
					
						
							|  |  |  |     installed in the server. Override the _dispatch method inhereted | 
					
						
							|  |  |  |     from SimpleXMLRPCDispatcher to change this behavior. | 
					
						
							| 
									
										
										
										
											2001-09-17 17:35:21 +00:00
										 |  |  |     """
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, addr, requestHandler=SimpleXMLRPCRequestHandler, | 
					
						
							|  |  |  |                  logRequests=1): | 
					
						
							|  |  |  |         self.logRequests = logRequests | 
					
						
							| 
									
										
										
										
											2003-01-29 03:49:43 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |         SimpleXMLRPCDispatcher.__init__(self) | 
					
						
							| 
									
										
										
										
											2001-09-17 17:35:21 +00:00
										 |  |  |         SocketServer.TCPServer.__init__(self, addr, requestHandler) | 
					
						
							| 
									
										
										
										
											2003-01-29 03:49:43 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  | class CGIXMLRPCRequestHandler(SimpleXMLRPCDispatcher): | 
					
						
							|  |  |  |     """Simple handler for XML-RPC data passed through CGI.""" | 
					
						
							| 
									
										
										
										
											2003-01-29 03:49:43 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |     def __init__(self): | 
					
						
							|  |  |  |         SimpleXMLRPCDispatcher.__init__(self) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def handle_xmlrpc(self, request_text): | 
					
						
							|  |  |  |         """Handle a single XML-RPC request""" | 
					
						
							| 
									
										
										
										
											2003-01-29 03:49:43 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |         response = self._marshaled_dispatch(request_text) | 
					
						
							| 
									
										
										
										
											2003-01-29 03:49:43 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |         print 'Content-Type: text/xml' | 
					
						
							|  |  |  |         print 'Content-Length: %d' % len(response) | 
					
						
							|  |  |  |         print | 
					
						
							| 
									
										
										
										
											2003-05-01 05:05:09 +00:00
										 |  |  |         sys.stdout.write(response) | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def handle_get(self): | 
					
						
							|  |  |  |         """Handle a single HTTP GET request.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Default implementation indicates an error because | 
					
						
							|  |  |  |         XML-RPC uses the POST method. | 
					
						
							| 
									
										
										
										
											2001-09-17 17:35:21 +00:00
										 |  |  |         """
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |         code = 400 | 
					
						
							|  |  |  |         message, explain = \ | 
					
						
							|  |  |  |                  BaseHTTPServer.BaseHTTPRequestHandler.responses[code] | 
					
						
							| 
									
										
										
										
											2003-01-29 03:49:43 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |         response = BaseHTTPServer.DEFAULT_ERROR_MESSAGE % \ | 
					
						
							|  |  |  |             { | 
					
						
							| 
									
										
										
										
											2003-01-29 03:49:43 +00:00
										 |  |  |              'code' : code, | 
					
						
							|  |  |  |              'message' : message, | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |              'explain' : explain | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         print 'Status: %d %s' % (code, message) | 
					
						
							|  |  |  |         print 'Content-Type: text/html' | 
					
						
							|  |  |  |         print 'Content-Length: %d' % len(response) | 
					
						
							|  |  |  |         print | 
					
						
							| 
									
										
										
										
											2003-06-29 04:16:28 +00:00
										 |  |  |         sys.stdout.write(response) | 
					
						
							| 
									
										
										
										
											2003-01-29 03:49:43 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |     def handle_request(self, request_text = None): | 
					
						
							|  |  |  |         """Handle a single XML-RPC request passed through a CGI post method.
 | 
					
						
							| 
									
										
										
										
											2003-01-29 03:49:43 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |         If no XML data is given then it is read from stdin. The resulting | 
					
						
							|  |  |  |         XML-RPC response is printed to stdout along with the correct HTTP | 
					
						
							|  |  |  |         headers. | 
					
						
							| 
									
										
										
										
											2001-09-17 17:35:21 +00:00
										 |  |  |         """
 | 
					
						
							| 
									
										
										
										
											2003-01-29 03:49:43 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |         if request_text is None and \ | 
					
						
							|  |  |  |             os.environ.get('REQUEST_METHOD', None) == 'GET': | 
					
						
							|  |  |  |             self.handle_get() | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             # POST data is normally available through stdin | 
					
						
							|  |  |  |             if request_text is None: | 
					
						
							| 
									
										
										
										
											2003-01-29 03:49:43 +00:00
										 |  |  |                 request_text = sys.stdin.read() | 
					
						
							| 
									
										
										
										
											2001-09-17 17:35:21 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-01-15 11:37:23 +00:00
										 |  |  |             self.handle_xmlrpc(request_text) | 
					
						
							| 
									
										
										
										
											2003-01-29 03:49:43 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-09-17 17:35:21 +00:00
										 |  |  | if __name__ == '__main__': | 
					
						
							|  |  |  |     server = SimpleXMLRPCServer(("localhost", 8000)) | 
					
						
							|  |  |  |     server.register_function(pow) | 
					
						
							|  |  |  |     server.register_function(lambda x,y: x+y, 'add') | 
					
						
							|  |  |  |     server.serve_forever() |