try: from cStringIO import StringIO except ImportError: from StringIO import StringIO import xmlrpclib import sys from trac.core import * from trac.perm import IPermissionRequestor from trac.web.main import IRequestHandler from trac.wiki.api import WikiSystem from trac.wiki.model import WikiPage RPC_TYPES = {int: 'int', bool: 'boolean', str: 'string', float: 'double', xmlrpclib.DateTime: 'dateTime.iso8601', xmlrpclib.base64: 'base64', list: 'array', dict: 'struct'} class IXmlRpcHandler(Interface): def xmlrpc_module(): pass def xmlrpc_methods(): pass class XmlRpcModule(Component): implements(IPermissionRequestor, IRequestHandler, IXmlRpcHandler) rpc_handlers = ExtensionPoint(IXmlRpcHandler) def get_permission_actions(self): return ['XML_RPC'] def match_request(self, req): return req.path_info == '/xmlrpc' def process_request(self, req): try: req.perm.assert_permission('XML_RPC') xml = req.read(int(req.get_header('Content-Length'))) params, method = xmlrpclib.loads(xml) result = self._call(method, params, req) if result is None: raise xmlrpclib.Fault(4, 'Method call did not return a value') result = xmlrpclib.dumps((result,), methodresponse=True) except: self.log.error(sys.exc_value) result = xmlrpclib.dumps(self._wrap_except()) req.send_response(200) req.send_header('Content-Type', 'text/xml') req.end_headers() req.write(result) def _get_info(self, method): parts = method.split('.', 1) if len(parts) < 2: return None moduleName, methodName = parts module = None for rpc in self.rpc_handlers: if rpc.xmlrpc_module() == moduleName: module = rpc break if not module or methodName not in module.xmlrpc_methods(): return None return module, methodName def _get_method(self, method): info = self._get_info(method) if not info: return None module, methodName = info return getattr(module, methodName) def _get_sig(self, method): info = self._get_info(method) if not info: return None module, methodName = info return module.xmlrpc_methods()[methodName] def _call(self, methodName, params, req): func = self._get_method(methodName) if func: return func(req, *params) else: raise xmlrpclib.Fault(2, 'Unknown method') def _wrap_except(self): e = sys.exc_value if isinstance(e, xmlrpclib.Fault): return e return xmlrpclib.Fault(1, '%s:%s' % (sys.exc_type, e)) def xmlrpc_module(self): return 'system' def xmlrpc_methods(self): return {'listMethods': [(list,)], 'methodHelp': [(str, str)], 'methodSignature': [(list, str)], 'multicall': [(list, dict)]} def listMethods(self, req): methods = [] for rpc in self.rpc_handlers: module = rpc.xmlrpc_module() for method in rpc.xmlrpc_methods(): methods.append('.'.join((module, method))) return methods def methodHelp(self, req, methodName): func = self._get_method(methodName) if func: import pydoc return pydoc.getdoc(func) else: raise xmlrpclib.Fault(3, "Can't get help for unknown method: %s" % methodName) def methodSignature(self, req, methodName): sig = self._get_sig(methodName) return [', '.join([RPC_TYPES[t] for t in s]) for s in sig] def multicall(self, req, callParams): results = [] for meth_sig in callParams: try: r = [self._call(**meth_sig)] except: r = self._wrap_except() results.append(r) return results class WikiRpc(Component): implements(IXmlRpcHandler) def __init__(self): self.wiki = WikiSystem(self.env) def xmlrpc_module(self): return 'wiki' def xmlrpc_methods(self): return {'getRecentChanges': [(dict, xmlrpclib.DateTime)], 'getRPCVersionSupported': [(int,)], 'getPage': [(str, str), (str, str, int)], 'getPageVersion': [(str, str, int)], 'getPageInfo': [(dict, str), (dict, str, int)], 'getPageInfoVersion': [(dict, str, int)], 'getAllPages': [(list,)]} def _to_timestamp(self, datetime): import time return time.mktime(time.strptime(datetime.value, '%Y%m%dT%H:%M:%S')) def _page_info(self, name, time, author, version): return dict(name=name, lastModified=xmlrpclib.DateTime(int(time)), author=author, version=int(version)) def getRecentChanges(self, req, since): req.perm.assert_permission('WIKI_VIEW') since = self._to_timestamp(since) db = self.env.get_db_cnx() cursor = db.cursor() cursor.execute('SELECT name, max(time), author, version FROM wiki' ' WHERE time >= %s GROUP BY name ORDER BY max(time) DESC', since) result = [] for name, time, author, version in cursor: result.append(self._page_info(name, time, author, version)) return result def getRPCVersionSupported(self, req): req.perm.assert_permission('WIKI_VIEW') return 2 def getPage(self, req, pagename, version=None): req.perm.assert_permission('WIKI_VIEW') page = WikiPage(self.env, pagename, version) if page.exists: return page.text else: msg = 'Wiki page "%s" does not exist' % pagename if version is not None: msg += ' at version %s' % version raise xmlrpclib.Fault(0, msg) getPageVersion = getPage def getPageHTML(self, req, pagename, version=None): req.perm.assert_permission('WIKI_VIEW') pass getPageHTMLVersion = getPageHTML def getAllPages(self, req): ''' Returns a list of all pages. The result is an array of utf8 pagenames. ''' req.perm.assert_permission('WIKI_VIEW') return list(self.wiki.get_pages()) def getPageInfo(self, req, pagename, version=None): req.perm.assert_permission('WIKI_VIEW') page = WikiPage(self.env, pagename, version) if page.exists: return self._page_info(page.name, page.time, page.author, page.version) getPageInfoVersion = getPageInfo def listLinks(self, req, pagename): req.perm.assert_permission('WIKI_VIEW') pass