source: n4d/trunk/fuentes/install-files/usr/share/n4d/xmlrpc-server/server.py @ 103

Last change on this file since 103 was 103, checked in by hectorgh, 4 years ago

adding files

  • Property svn:executable set to *
File size: 12.2 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4import locale
5locale.setlocale(locale.LC_ALL, 'C.UTF-8')
6
7"""
8 *******************************************************************************
9 *
10 * $Id: SecureDocXMLRPCServer.py 4 2008-06-04 18:44:13Z yingera $
11 * $URL: https://xxxxxx/repos/utils/trunk/tools/SVNRPCServer.py $
12 *
13 * $Date: 2008-06-04 13:44:13 -0500 (Wed, 04 Jun 2008) $
14 * $Author: yingera $
15 *
16 * Authors: Laszlo Nagy, Andrew Yinger
17 *
18 * Description: Threaded, Documenting SecureDocXMLRPCServer.py - over HTTPS.
19 *
20 *  requires pyOpenSSL: http://sourceforge.net/project/showfiles.php?group_id=31249
21 *   ...and open SSL certs installed.
22 *
23 * Based on this article: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81549
24 *
25 *******************************************************************************
26"""
27
28import SocketServer
29import BaseHTTPServer
30import SimpleHTTPServer
31import SimpleXMLRPCServer
32import imp
33import glob
34
35
36import fcntl
37import time
38import random
39import base64
40import socket, os
41from OpenSSL import SSL
42from threading import Event, currentThread, Thread, Condition
43from thread import start_new_thread as start
44from DocXMLRPCServer import DocXMLRPCServer, DocXMLRPCRequestHandler
45
46import xmlrpclib
47import threading
48
49threading._DummyThread._Thread__stop = lambda x: 42
50
51#locale.resetlocale()
52
53# static stuff
54
55'''
56DEFAULTKEYFILE='/etc/lliurex-secrets/pki/n4d/n4d.key'   # Replace with your PEM formatted key file
57DEFAULTCERTFILE='/etc/lliurex-secrets/certs/n4d/n4d'  # Replace with your PEM formatted certificate file
58'''
59
60DEFAULTKEYFILE='/etc/n4d/cert/n4dkey.pem'       # Replace with your PEM formatted key file
61DEFAULTCERTFILE='/etc/n4d/cert/n4dcert.pem'  # Replace with your PEM formatted certificate file
62
63SITESENABLEDPATH="/usr/share/n4d/sites-enabled/"
64
65TRIGGER_BLOCK=False
66
67
68class SecureDocXMLRpcRequestHandler(DocXMLRPCRequestHandler):
69        """Secure Doc XML-RPC request handler class.
70        It it very similar to DocXMLRPCRequestHandler but it uses HTTPS for transporting XML data.
71        """
72       
73       
74        def setup(self):
75                self.connection = self.request
76                self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)
77                self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)
78
79        def address_string(self):
80                "getting 'FQDN' from host seems to stall on some ip addresses, so... just (quickly!) return raw host address"
81                host, port = self.client_address
82                #print dir(self)
83                try:
84                        self.client_url=self.headers["host"]
85                except Exception as e:
86                        self.client_url=None
87                #return socket.getfqdn(host)
88                return host
89
90
91        def do_POST(self):
92                """Handles the HTTPS POST request.
93                It was copied out from SimpleXMLRPCServer.py and modified to shutdown the socket cleanly.
94                """
95                try:
96                        # get arguments
97                        data = self.rfile.read(int(self.headers["content-length"]))
98                        # In previous versions of SimpleXMLRPCServer, _dispatch
99                        # could be overridden in this class, instead of in
100                        # SimpleXMLRPCDispatcher. To maintain backwards compatibility,
101                        # check to see if a subclass implements _dispatch and dispatch
102                        # using that method if present.
103                        addr,num=self.client_address
104                        response = self.server._marshaled_dispatch(data, getattr(self, '_dispatch', None),client_address=addr)
105                       
106                       
107                except: # This should only happen if the module is buggy
108                        # internal error, report as HTTP server error
109                        self.send_response(500)
110                        self.end_headers()
111                else:
112                        # got a valid XML RPC response
113                        self.send_response(200)
114                        self.send_header("Content-type", "text/xml")
115                        self.send_header("Content-length", str(len(response)))
116                        self.end_headers()
117                        self.wfile.write(response)
118
119                        # shut down the connection
120                        self.wfile.flush()
121                        '''
122                        global TRIGGER_BLOCK
123                        while(TRIGGER_BLOCK):
124                                print "waiting for trigger_block to be unlocked..."
125                                time.sleep(int(5*random.random()))
126                        TRIGGER_BLOCK=True
127                        print "done with response. Sleeping..."
128                        time.sleep(5)
129                        print "done sleeping."
130                        TRIGGER_BLOCK=False
131                        '''
132                        self.connection.shutdown() # Modified here!
133
134        def do_GET(self):
135                """Handles the HTTP GET request.
136
137                Interpret all HTTP GET requests as requests for server
138                documentation.
139                """
140               
141                self.address_string()
142               
143                web_modules_list=os.listdir(SITESENABLEDPATH)
144                path=self.path.lstrip("/")
145               
146               
147                if path+".py" in web_modules_list:
148                       
149                        try:
150                                obj=imp.load_source(path,SITESENABLEDPATH+path+".py")
151                                inst=getattr(obj,path)()
152                                info={}
153                                info["client_ip"]=self.client_address[0]
154                                info["client_url"]=self.client_url
155                                response=inst.get_response(info)
156                                self.send_response(200)
157                                self.send_header("Content-type", "text/html")
158                                self.send_header("Content-length", str(len(response)))
159                                self.end_headers()
160                                self.wfile.write(response)                     
161                                return
162                               
163                        except Exception as e:
164                                print e
165                               
166               
167                '''
168                if "/n4d/" in self.path:
169                       
170                        if os.path.exists(self.path) and os.path.isfile(self.path):
171                       
172                                file_length=os.stat(self.path).st_size
173                                f=open(self.path,"rB")
174                                response=f.read(file_length)
175                                f.close()
176                       
177                                self.send_response(200)
178                                self.send_header("Content-type","text/html")
179                                self.send_header("Content-length",file_length)
180                                self.end_headers()
181                               
182                                self.wfile.write(response)     
183                       
184                                return
185                '''
186               
187                # Check that the path is legal
188               
189                if not self.is_rpc_path_valid():
190                        self.report_404()
191                        return
192               
193               
194                #print self.path
195                #response = self.server.generate_html_documentation()
196               
197                header="<html><body>"
198                foot="</body></html>"
199               
200                response=header
201               
202                for plugin in l.cm.plugins:
203                        response+="<b>["+plugin.type+":"+plugin.class_name+"]</b><br>"
204                        for method in plugin.function:
205                                try:
206                                        #server.register_function(getattr(l.objects[plugin.class_name],method))
207                                        args=getattr(l.objects[plugin.class_name],method).func_code.co_varnames[:getattr(l.objects[plugin.class_name],method).func_code.co_argcount]
208                                        response+="<pre>      " + method+str(args).replace("'self',","")+" , " + str(getattr(l.objects[plugin.class_name],method).__doc__) + "</pre>"
209                                except:
210                                        pass
211               
212                response+=foot
213               
214
215               
216                self.send_response(200)
217                self.send_header("Content-type", "text/html")
218                self.send_header("Content-length", str(len(response)))
219                self.end_headers()
220                self.wfile.write(response)
221
222                # shut down the connection
223                self.wfile.flush()
224                self.connection.shutdown() # Modified here!
225               
226               
227
228        def report_404 (self):
229                # Report a 404 error
230                self.send_response(404)
231                response = 'No such page'
232                self.send_header("Content-type", "text/plain")
233                self.send_header("Content-length", str(len(response)))
234                self.end_headers()
235                self.wfile.write(response)
236                # shut down the connection
237                self.wfile.flush()
238                self.connection.shutdown() # Modified here!
239               
240        def do_OPTIONS(self):
241                self.send_response(200)
242                self.end_headers()
243                self.wfile.flush()
244                self.connection.shutdown() # Modified here!
245
246        def end_headers(self):
247                self.send_header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept")
248                self.send_header("Access-Control-Allow-Origin", "*")
249                DocXMLRPCRequestHandler.end_headers(self)
250
251class CustomThreadingMixIn:
252        """Mix-in class to handle each request in a new thread."""
253        # Decides how threads will act upon termination of the main process
254        daemon_threads = True
255
256        def process_request_thread(self, request, client_address):
257                """Same as in BaseServer but as a thread.
258                In addition, exception handling is done here.
259                """
260                try:
261                        self.finish_request(request, client_address)
262                        self.close_request(request)
263                except (socket.error, SSL.SysCallError), why:
264                        print 'socket.error finishing request from "%s"; Error: %s' % (client_address, str(why))
265                        self.close_request(request)
266                except:
267                        self.handle_error(request, client_address)
268                        self.close_request(request)
269
270        def process_request(self, request, client_address):
271                """Start a new thread to process the request."""
272                t = Thread(target = self.process_request_thread, args = (request, client_address))
273                if self.daemon_threads:
274                        t.setDaemon(1)
275                t.start()
276
277
278
279class SecureDocXMLRPCServer(CustomThreadingMixIn, DocXMLRPCServer):
280        def __init__(self, registerInstance, server_address, keyFile=DEFAULTKEYFILE, certFile=DEFAULTCERTFILE, logRequests=True):
281                """Secure Documenting XML-RPC server.
282                It it very similar to DocXMLRPCServer but it uses HTTPS for transporting XML data.
283                """
284                DocXMLRPCServer.__init__(self, server_address, SecureDocXMLRpcRequestHandler, logRequests,True)
285                self.logRequests = logRequests
286
287                # stuff for doc server
288                try: self.set_server_title(registerInstance.title)
289                except AttributeError: self.set_server_title('N4D Documentation page')
290                try: self.set_server_name(registerInstance.name)
291                except AttributeError: self.set_server_name('N4D')
292                #for plugin in registerInstace
293                if registerInstance.__doc__: self.set_server_documentation(registerInstance.__doc__)
294                else: self.set_server_documentation('default documentation')
295               
296               
297                #self.register_introspection_functions()
298               
299                # init stuff, handle different versions:
300                try:
301                        SimpleXMLRPCServer.SimpleXMLRPCDispatcher.__init__(self,allow_none=True)
302                except TypeError:
303                        # An exception is raised in Python 2.5 as the prototype of the __init__
304                        # method has changed and now has 3 arguments (self, allow_none, encoding)
305                        SimpleXMLRPCServer.SimpleXMLRPCDispatcher.__init__(self, allow_none=True, encoding=None)
306                SocketServer.BaseServer.__init__(self, server_address, SecureDocXMLRpcRequestHandler)
307                self.register_instance(registerInstance) # for some reason, have to register instance down here!
308
309                # SSL socket stuff
310                ctx = SSL.Context(SSL.SSLv23_METHOD)
311                ctx.use_privatekey_file(keyFile)
312                ctx.use_certificate_file(certFile)
313                self.socket = SSL.Connection(ctx, socket.socket(self.address_family, self.socket_type))
314                #self.socket = socket.socket(self.address_family, self.socket_type)
315               
316                old = fcntl.fcntl(self.socket.fileno(), fcntl.F_GETFD) 
317                fcntl.fcntl(self.socket.fileno(), fcntl.F_SETFD, old | fcntl.FD_CLOEXEC)       
318               
319                self.server_bind()
320                self.server_activate()
321
322                # requests count and condition, to allow for keyboard quit via CTL-C
323                self.requests = 0
324                self.rCondition = Condition()
325
326
327        def startup(self):
328                'run until quit signaled from keyboard...'
329                print '[SERVER] Starting; Press CTRL-C to quit ...'
330                while True:
331                        try:
332                                self.rCondition.acquire()
333                                start(self.handle_request, ()) # we do this async, because handle_request blocks!
334                                while not self.requests:
335                                        self.rCondition.wait(timeout=3.0)
336                                if self.requests: self.requests -= 1
337                                self.rCondition.release()
338                        except KeyboardInterrupt:
339                                print "[SERVER] Exiting..."
340                                return
341
342        def get_request(self):
343                request, client_address = self.socket.accept()
344                self.rCondition.acquire()
345                self.requests += 1
346                self.rCondition.notifyAll()
347                self.rCondition.release()
348                return (request, client_address)
349
350        def listMethods(self):
351                'return list of method names (strings)'
352                methodNames = self.funcs.keys()
353                methodNames.sort()
354                return methodNames
355
356        def methodHelp(self, methodName):
357                'method help'
358                if methodName in self.funcs:
359                        return self.funcs[methodName].__doc__
360                else:
361                        raise Exception('method "%s" is not supported' % methodName)
362
363
364        def _marshaled_dispatch(self, data, dispatch_method = None, path = None, client_address=None):
365                self.allow_none=True
366                try:
367                        params, method = xmlrpclib.loads(data)
368                        params=(client_address,)+params
369
370                        if dispatch_method is not None:
371                                response = dispatch_method(method, params)
372                        else:
373
374                                response = self._dispatch(method, params)
375
376                        response = (response,)
377                        response = xmlrpclib.dumps(response, methodresponse=1,allow_none=self.allow_none, encoding=self.encoding)
378
379                except Fault, fault:
380                        response = xmlrpclib.dumps(fault, allow_none=self.allow_none,encoding=self.encoding)
381                except:
382
383                        exc_type, exc_value, exc_tb = sys.exc_info()
384                        response = xmlrpclib.dumps(xmlrpclib.Fault(1, "%s:%s" % (exc_type, exc_value)),encoding=self.encoding, allow_none=self.allow_none,)
385
386                return response
387
388
389
390if __name__ == '__main__':
391       
392        obj=imp.load_source("core","/usr/share/n4d/xmlrpc-server/core.py")
393        l=obj.Core()
394        server_address = ('', 9779) # (address, port)
395        server = SecureDocXMLRPCServer(l, server_address, DEFAULTKEYFILE, DEFAULTCERTFILE)     
396        #server.register_introspection_functions()
397        '''
398        for plugin in l.cm.plugins:
399                for method in plugin.function:
400                        server.register_function(getattr(l.objects[plugin.class_name],method))
401        '''
402       
403                       
404        sa = server.socket.getsockname()
405        print "[SERVER] HTTPS on", sa[0], "port", sa[1]
406        server.startup()
407
408
Note: See TracBrowser for help on using the repository browser.