| 1 | from trac.core import * |
|---|
| 2 | from trac.web.main import IRequestHandler |
|---|
| 3 | from trac.web.chrome import ITemplateProvider, add_stylesheet |
|---|
| 4 | from tracrpc.api import IXMLRPCHandler, XMLRPCSystem |
|---|
| 5 | from trac.wiki.formatter import wiki_to_oneliner |
|---|
| 6 | import xmlrpclib |
|---|
| 7 | |
|---|
| 8 | class XMLRPCWeb(Component): |
|---|
| 9 | """ Handle XML-RPC calls from HTTP clients, as well as presenting a list of |
|---|
| 10 | methods available to the currently logged in user. Browsing to |
|---|
| 11 | <trac>/xmlrpc will display this list. """ |
|---|
| 12 | |
|---|
| 13 | implements(IRequestHandler, ITemplateProvider) |
|---|
| 14 | |
|---|
| 15 | # IRequestHandler methods |
|---|
| 16 | def match_request(self, req): |
|---|
| 17 | return req.path_info in ('/login/xmlrpc', '/xmlrpc') |
|---|
| 18 | |
|---|
| 19 | def _send_response(self, req, response): |
|---|
| 20 | req.send_response(200) |
|---|
| 21 | req.send_header('Content-Type', 'text/xml') |
|---|
| 22 | req.send_header('Content-Length', len(response)) |
|---|
| 23 | req.end_headers() |
|---|
| 24 | req.write(response) |
|---|
| 25 | |
|---|
| 26 | def process_request(self, req): |
|---|
| 27 | # Need at least XML_RPC |
|---|
| 28 | req.perm.assert_permission('XML_RPC') |
|---|
| 29 | |
|---|
| 30 | # Dump RPC functions |
|---|
| 31 | content_type = req.get_header('Content-Type') |
|---|
| 32 | if content_type is None or 'text/xml' not in content_type: |
|---|
| 33 | namespaces = {} |
|---|
| 34 | for method in XMLRPCSystem(self.env).all_methods(req): |
|---|
| 35 | namespace = method.namespace.replace('.', '_') |
|---|
| 36 | if namespace not in namespaces: |
|---|
| 37 | namespaces[namespace] = { |
|---|
| 38 | 'description' : wiki_to_oneliner(method.namespace_description, self.env), |
|---|
| 39 | 'methods' : [], |
|---|
| 40 | 'namespace' : method.namespace, |
|---|
| 41 | } |
|---|
| 42 | try: |
|---|
| 43 | namespaces[namespace]['methods'].append((method.signature, wiki_to_oneliner(method.description, self.env), method.permission)) |
|---|
| 44 | except Exception, e: |
|---|
| 45 | from StringIO import StringIO |
|---|
| 46 | import traceback |
|---|
| 47 | out = StringIO() |
|---|
| 48 | traceback.print_exc(file=out) |
|---|
| 49 | raise Exception('%s: %s\n%s' % (method.name, str(e), out.getvalue())) |
|---|
| 50 | add_stylesheet(req, 'common/css/wiki.css') |
|---|
| 51 | req.hdf['xmlrpc.functions'] = namespaces |
|---|
| 52 | return 'xmlrpclist.cs', None |
|---|
| 53 | |
|---|
| 54 | # Handle XML-RPC call |
|---|
| 55 | args, method = xmlrpclib.loads(req.read(int(req.get_header('Content-Length')))) |
|---|
| 56 | try: |
|---|
| 57 | result = XMLRPCSystem(self.env).get_method(method)(req, args) |
|---|
| 58 | self._send_response(req, xmlrpclib.dumps(result, methodresponse=True)) |
|---|
| 59 | except xmlrpclib.Fault, e: |
|---|
| 60 | self.log.error(e) |
|---|
| 61 | self._send_response(req, xmlrpclib.dumps(e)) |
|---|
| 62 | except Exception, e: |
|---|
| 63 | self.log.error(e) |
|---|
| 64 | import traceback |
|---|
| 65 | from StringIO import StringIO |
|---|
| 66 | out = StringIO() |
|---|
| 67 | traceback.print_exc(file = out) |
|---|
| 68 | self.log.error(out.getvalue()) |
|---|
| 69 | self._send_response(req, xmlrpclib.dumps(xmlrpclib.Fault(2, "'%s' while executing '%s()'" % (str(e), method)))) |
|---|
| 70 | |
|---|
| 71 | # ITemplateProvider |
|---|
| 72 | def get_htdocs_dirs(self): |
|---|
| 73 | return [] |
|---|
| 74 | |
|---|
| 75 | def get_templates_dirs(self): |
|---|
| 76 | from pkg_resources import resource_filename |
|---|
| 77 | return [resource_filename(__name__, 'templates')] |
|---|