[MAJOR IMP] Rewrite the http/RPC engine and let HTTP/1.1 features.
This patch attempts a major change in the structure of the XML-RPC framework. There is one http server, capable of multiple services (except XML-RPC). That server could handle authentication, and is also HTTP/1.1 capable, which means it supports **persistent connections** ! At this commit, the old behaviour of the XML-RPC protocol is merely working. The netsvc.Service is split, so expect wizard/report breakages. External modules (koo) also break with this API. The net-svc code is crippled and gone FTM. bzr revid: p_christ@hol.gr-20090829152346-7i1iiqs8skdddamq
This commit is contained in:
parent
8558374c2b
commit
4abaf2763e
302
bin/netsvc.py
302
bin/netsvc.py
|
@ -37,17 +37,23 @@ import time
|
||||||
import xmlrpclib
|
import xmlrpclib
|
||||||
import release
|
import release
|
||||||
|
|
||||||
SERVICES = {}
|
|
||||||
GROUPS = {}
|
|
||||||
|
|
||||||
class Service(object):
|
class Service(object):
|
||||||
|
""" Base class for *Local* services
|
||||||
|
|
||||||
|
Functionality here is trusted, no authentication.
|
||||||
|
"""
|
||||||
|
_services = {}
|
||||||
def __init__(self, name, audience=''):
|
def __init__(self, name, audience=''):
|
||||||
SERVICES[name] = self
|
Service._services[name] = self
|
||||||
self.__name = name
|
self.__name = name
|
||||||
self._methods = {}
|
self._methods = {}
|
||||||
|
|
||||||
def joinGroup(self, name):
|
def joinGroup(self, name):
|
||||||
GROUPS.setdefault(name, {})[self.__name] = self
|
raise Exception("No group for local services")
|
||||||
|
#GROUPS.setdefault(name, {})[self.__name] = self
|
||||||
|
|
||||||
|
def service_exist(self,name):
|
||||||
|
return Service._services.has_key(name)
|
||||||
|
|
||||||
def exportMethod(self, method):
|
def exportMethod(self, method):
|
||||||
if callable(method):
|
if callable(method):
|
||||||
|
@ -59,11 +65,16 @@ class Service(object):
|
||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
class LocalService(Service):
|
class LocalService(object):
|
||||||
|
""" Proxy for local services.
|
||||||
|
|
||||||
|
Any instance of this class will behave like the single instance
|
||||||
|
of Service(name)
|
||||||
|
"""
|
||||||
def __init__(self, name):
|
def __init__(self, name):
|
||||||
self.__name = name
|
self.__name = name
|
||||||
try:
|
try:
|
||||||
self._service = SERVICES[name]
|
self._service = Service._services[name]
|
||||||
for method_name, method_definition in self._service._methods.items():
|
for method_name, method_definition in self._service._methods.items():
|
||||||
setattr(self, method_name, method_definition)
|
setattr(self, method_name, method_definition)
|
||||||
except KeyError, keyError:
|
except KeyError, keyError:
|
||||||
|
@ -72,8 +83,42 @@ class LocalService(Service):
|
||||||
def __call__(self, method, *params):
|
def __call__(self, method, *params):
|
||||||
return getattr(self, method)(*params)
|
return getattr(self, method)(*params)
|
||||||
|
|
||||||
def service_exist(name):
|
class ExportService(object):
|
||||||
return SERVICES.get(name, False)
|
""" Proxy for exported services.
|
||||||
|
|
||||||
|
All methods here should take an AuthProxy as their first parameter. It
|
||||||
|
will be appended by the calling framework.
|
||||||
|
|
||||||
|
Note that this class has no direct proxy, capable of calling
|
||||||
|
eservice.method(). Rather, the proxy should call
|
||||||
|
dispatch(method,auth,params)
|
||||||
|
"""
|
||||||
|
|
||||||
|
_services = {}
|
||||||
|
_groups = {}
|
||||||
|
|
||||||
|
def __init__(self, name, audience=''):
|
||||||
|
ExportService._services[name] = self
|
||||||
|
self.__name = name
|
||||||
|
|
||||||
|
def joinGroup(self, name):
|
||||||
|
ExportService._groups.setdefault(name, {})[self.__name] = self
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def getService(cls,name):
|
||||||
|
return cls._services[name]
|
||||||
|
|
||||||
|
def dispatch(self, method, auth, params):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def new_dispatch(self,method,auth,params):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def abortResponse(self, error, description, origin, details):
|
||||||
|
if not tools.config['debug_mode']:
|
||||||
|
raise Exception("%s -- %s\n\n%s"%(origin, description, details))
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
LOG_NOTSET = 'notset'
|
LOG_NOTSET = 'notset'
|
||||||
LOG_DEBUG_RPC = 'debug_rpc'
|
LOG_DEBUG_RPC = 'debug_rpc'
|
||||||
|
@ -244,10 +289,46 @@ class Agent(object):
|
||||||
|
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
class xmlrpc(object):
|
class Server:
|
||||||
class RpcGateway(object):
|
""" Generic interface for all servers with an event loop etc.
|
||||||
def __init__(self, name):
|
Override this to impement http, net-rpc etc. servers.
|
||||||
self.name = name
|
|
||||||
|
Servers here must have threaded behaviour. start() must not block,
|
||||||
|
there is no run().
|
||||||
|
"""
|
||||||
|
__is_started = False
|
||||||
|
__servers = []
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
if Server.__is_started:
|
||||||
|
raise Exception('All instances of servers must be inited before the startAll()')
|
||||||
|
Server.__servers.append(self)
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
print "called stub Server.start"
|
||||||
|
pass
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
print "called stub Server.stop"
|
||||||
|
pass
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def startAll(cls):
|
||||||
|
if cls.__is_started:
|
||||||
|
return
|
||||||
|
print "Starting %d services" % len(cls.__servers)
|
||||||
|
for srv in cls.__servers:
|
||||||
|
srv.start()
|
||||||
|
cls.__is_started = True
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def quitAll(cls):
|
||||||
|
if not cls.__is_started:
|
||||||
|
return
|
||||||
|
for srv in cls.__servers:
|
||||||
|
srv.stop()
|
||||||
|
cls.__is_started = False
|
||||||
|
|
||||||
|
|
||||||
class OpenERPDispatcherException(Exception):
|
class OpenERPDispatcherException(Exception):
|
||||||
def __init__(self, exception, traceback):
|
def __init__(self, exception, traceback):
|
||||||
|
@ -264,7 +345,11 @@ class OpenERPDispatcher:
|
||||||
self.log('service', service_name)
|
self.log('service', service_name)
|
||||||
self.log('method', method)
|
self.log('method', method)
|
||||||
self.log('params', params)
|
self.log('params', params)
|
||||||
result = LocalService(service_name)(method, *params)
|
if hasattr(self,'auth_provider'):
|
||||||
|
auth = self.auth_provider
|
||||||
|
else:
|
||||||
|
auth = None
|
||||||
|
result = ExportService.getService(service_name).dispatch(method, auth, params)
|
||||||
self.log('result', result)
|
self.log('result', result)
|
||||||
return result
|
return result
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
|
@ -279,193 +364,4 @@ class OpenERPDispatcher:
|
||||||
pdb.post_mortem(tb[2])
|
pdb.post_mortem(tb[2])
|
||||||
raise OpenERPDispatcherException(e, tb_s)
|
raise OpenERPDispatcherException(e, tb_s)
|
||||||
|
|
||||||
class GenericXMLRPCRequestHandler(OpenERPDispatcher):
|
|
||||||
def _dispatch(self, method, params):
|
|
||||||
try:
|
|
||||||
service_name = self.path.split("/")[-1]
|
|
||||||
return self.dispatch(service_name, method, params)
|
|
||||||
except OpenERPDispatcherException, e:
|
|
||||||
raise xmlrpclib.Fault(tools.exception_to_unicode(e.exception), e.traceback)
|
|
||||||
|
|
||||||
class SSLSocket(object):
|
|
||||||
def __init__(self, socket):
|
|
||||||
if not hasattr(socket, 'sock_shutdown'):
|
|
||||||
from OpenSSL import SSL
|
|
||||||
ctx = SSL.Context(SSL.SSLv23_METHOD)
|
|
||||||
ctx.use_privatekey_file(tools.config['secure_pkey_file'])
|
|
||||||
ctx.use_certificate_file(tools.config['secure_cert_file'])
|
|
||||||
|
|
||||||
self.socket = SSL.Connection(ctx, socket)
|
|
||||||
else:
|
|
||||||
self.socket = socket
|
|
||||||
|
|
||||||
def shutdown(self, how):
|
|
||||||
return self.socket.sock_shutdown(how)
|
|
||||||
|
|
||||||
def __getattr__(self, name):
|
|
||||||
return getattr(self.socket, name)
|
|
||||||
|
|
||||||
class SimpleXMLRPCRequestHandler(GenericXMLRPCRequestHandler, SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
|
|
||||||
rpc_paths = map(lambda s: '/xmlrpc/%s' % s, SERVICES.keys())
|
|
||||||
|
|
||||||
class SecureXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
|
||||||
def setup(self):
|
|
||||||
self.connection = SSLSocket(self.request)
|
|
||||||
self.rfile = socket._fileobject(self.request, "rb", self.rbufsize)
|
|
||||||
self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)
|
|
||||||
|
|
||||||
class SimpleThreadedXMLRPCServer(SocketServer.ThreadingMixIn, SimpleXMLRPCServer.SimpleXMLRPCServer):
|
|
||||||
encoding = None
|
|
||||||
allow_none = False
|
|
||||||
|
|
||||||
def server_bind(self):
|
|
||||||
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
||||||
SimpleXMLRPCServer.SimpleXMLRPCServer.server_bind(self)
|
|
||||||
|
|
||||||
def handle_error(self, request, client_address):
|
|
||||||
""" Override the error handler
|
|
||||||
"""
|
|
||||||
import traceback
|
|
||||||
Logger().notifyChannel("init", LOG_ERROR,"Server error in request from %s:\n%s" %
|
|
||||||
(client_address,traceback.format_exc()))
|
|
||||||
|
|
||||||
class SecureThreadedXMLRPCServer(SimpleThreadedXMLRPCServer):
|
|
||||||
def __init__(self, server_address, HandlerClass, logRequests=1):
|
|
||||||
SimpleThreadedXMLRPCServer.__init__(self, server_address, HandlerClass, logRequests)
|
|
||||||
self.socket = SSLSocket(socket.socket(self.address_family, self.socket_type))
|
|
||||||
self.server_bind()
|
|
||||||
self.server_activate()
|
|
||||||
|
|
||||||
def handle_error(self, request, client_address):
|
|
||||||
""" Override the error handler
|
|
||||||
"""
|
|
||||||
import traceback
|
|
||||||
e_type, e_value, e_traceback = sys.exc_info()
|
|
||||||
Logger().notifyChannel("init", LOG_ERROR,"SSL Request handler error in request from %s: %s\n%s" %
|
|
||||||
(client_address,str(e_type),traceback.format_exc()))
|
|
||||||
|
|
||||||
class HttpDaemon(threading.Thread):
|
|
||||||
def __init__(self, interface, port, secure=False):
|
|
||||||
threading.Thread.__init__(self)
|
|
||||||
self.__port = port
|
|
||||||
self.__interface = interface
|
|
||||||
self.secure = bool(secure)
|
|
||||||
handler_class = (SimpleXMLRPCRequestHandler, SecureXMLRPCRequestHandler)[self.secure]
|
|
||||||
server_class = (SimpleThreadedXMLRPCServer, SecureThreadedXMLRPCServer)[self.secure]
|
|
||||||
|
|
||||||
if self.secure:
|
|
||||||
from OpenSSL.SSL import Error as SSLError
|
|
||||||
else:
|
|
||||||
class SSLError(Exception): pass
|
|
||||||
try:
|
|
||||||
self.server = server_class((interface, port), handler_class, 0)
|
|
||||||
except SSLError, e:
|
|
||||||
Logger().notifyChannel('xml-rpc-ssl', LOG_CRITICAL, "Can not load the certificate and/or the private key files")
|
|
||||||
sys.exit(1)
|
|
||||||
except Exception, e:
|
|
||||||
Logger().notifyChannel('xml-rpc', LOG_CRITICAL, "Error occur when starting the server daemon: %s" % (e,))
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
|
|
||||||
def attach(self, path, gw):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def stop(self):
|
|
||||||
self.running = False
|
|
||||||
if os.name != 'nt':
|
|
||||||
self.server.socket.shutdown( hasattr(socket, 'SHUT_RDWR') and socket.SHUT_RDWR or 2 )
|
|
||||||
self.server.socket.close()
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
self.server.register_introspection_functions()
|
|
||||||
|
|
||||||
self.running = True
|
|
||||||
while self.running:
|
|
||||||
self.server.handle_request()
|
|
||||||
return True
|
|
||||||
|
|
||||||
# If the server need to be run recursively
|
|
||||||
#
|
|
||||||
#signal.signal(signal.SIGALRM, self.my_handler)
|
|
||||||
#signal.alarm(6)
|
|
||||||
#while True:
|
|
||||||
# self.server.handle_request()
|
|
||||||
#signal.alarm(0) # Disable the alarm
|
|
||||||
|
|
||||||
import tiny_socket
|
|
||||||
class TinySocketClientThread(threading.Thread, OpenERPDispatcher):
|
|
||||||
def __init__(self, sock, threads):
|
|
||||||
threading.Thread.__init__(self)
|
|
||||||
self.sock = sock
|
|
||||||
self.threads = threads
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
import select
|
|
||||||
self.running = True
|
|
||||||
try:
|
|
||||||
ts = tiny_socket.mysocket(self.sock)
|
|
||||||
except:
|
|
||||||
self.sock.close()
|
|
||||||
self.threads.remove(self)
|
|
||||||
return False
|
|
||||||
while self.running:
|
|
||||||
try:
|
|
||||||
msg = ts.myreceive()
|
|
||||||
except:
|
|
||||||
self.sock.close()
|
|
||||||
self.threads.remove(self)
|
|
||||||
return False
|
|
||||||
try:
|
|
||||||
result = self.dispatch(msg[0], msg[1], msg[2:])
|
|
||||||
ts.mysend(result)
|
|
||||||
except OpenERPDispatcherException, e:
|
|
||||||
new_e = Exception(tools.exception_to_unicode(e.exception)) # avoid problems of pickeling
|
|
||||||
ts.mysend(new_e, exception=True, traceback=e.traceback)
|
|
||||||
|
|
||||||
self.sock.close()
|
|
||||||
self.threads.remove(self)
|
|
||||||
return True
|
|
||||||
|
|
||||||
def stop(self):
|
|
||||||
self.running = False
|
|
||||||
|
|
||||||
|
|
||||||
class TinySocketServerThread(threading.Thread):
|
|
||||||
def __init__(self, interface, port, secure=False):
|
|
||||||
threading.Thread.__init__(self)
|
|
||||||
self.__port = port
|
|
||||||
self.__interface = interface
|
|
||||||
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
||||||
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
||||||
self.socket.bind((self.__interface, self.__port))
|
|
||||||
self.socket.listen(5)
|
|
||||||
self.threads = []
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
import select
|
|
||||||
try:
|
|
||||||
self.running = True
|
|
||||||
while self.running:
|
|
||||||
(clientsocket, address) = self.socket.accept()
|
|
||||||
ct = TinySocketClientThread(clientsocket, self.threads)
|
|
||||||
self.threads.append(ct)
|
|
||||||
ct.start()
|
|
||||||
self.socket.close()
|
|
||||||
except Exception, e:
|
|
||||||
self.socket.close()
|
|
||||||
return False
|
|
||||||
|
|
||||||
def stop(self):
|
|
||||||
self.running = False
|
|
||||||
for t in self.threads:
|
|
||||||
t.stop()
|
|
||||||
try:
|
|
||||||
if hasattr(socket, 'SHUT_RDWR'):
|
|
||||||
self.socket.shutdown(socket.SHUT_RDWR)
|
|
||||||
else:
|
|
||||||
self.socket.shutdown(2)
|
|
||||||
self.socket.close()
|
|
||||||
except:
|
|
||||||
return False
|
|
||||||
|
|
||||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||||
|
|
|
@ -152,35 +152,22 @@ if tools.config["stop_after_init"]:
|
||||||
|
|
||||||
|
|
||||||
#----------------------------------------------------------
|
#----------------------------------------------------------
|
||||||
# Launch Server
|
# Launch Servers
|
||||||
#----------------------------------------------------------
|
#----------------------------------------------------------
|
||||||
|
|
||||||
if tools.config['xmlrpc']:
|
import service.http_server
|
||||||
port = int(tools.config['port'])
|
|
||||||
interface = tools.config["interface"]
|
|
||||||
secure = tools.config["secure"]
|
|
||||||
|
|
||||||
httpd = netsvc.HttpDaemon(interface, port, secure)
|
service.http_server.init_servers()
|
||||||
|
service.http_server.init_xmlrpc()
|
||||||
|
|
||||||
xml_gw = netsvc.xmlrpc.RpcGateway('web-services')
|
|
||||||
httpd.attach("/xmlrpc", xml_gw)
|
|
||||||
logger.notifyChannel("web-services", netsvc.LOG_INFO,
|
|
||||||
"starting XML-RPC%s services, port %s" %
|
|
||||||
((tools.config['secure'] and ' Secure' or ''), port))
|
|
||||||
|
|
||||||
#
|
# *-* TODO
|
||||||
#if tools.config["soap"]:
|
#if tools.config['netrpc']:
|
||||||
# soap_gw = netsvc.xmlrpc.RpcGateway('web-services')
|
#netport = int(tools.config['netport'])
|
||||||
# httpd.attach("/soap", soap_gw )
|
#netinterface = tools.config["netinterface"]
|
||||||
# logger.notifyChannel("web-services", netsvc.LOG_INFO, 'starting SOAP services, port '+str(port))
|
#tinySocket = netsvc.TinySocketServerThread(netinterface, netport, False)
|
||||||
#
|
#logger.notifyChannel("web-services", netsvc.LOG_INFO,
|
||||||
|
#"starting NET-RPC service, port %d" % (netport,))
|
||||||
if tools.config['netrpc']:
|
|
||||||
netport = int(tools.config['netport'])
|
|
||||||
netinterface = tools.config["netinterface"]
|
|
||||||
tinySocket = netsvc.TinySocketServerThread(netinterface, netport, False)
|
|
||||||
logger.notifyChannel("web-services", netsvc.LOG_INFO,
|
|
||||||
"starting NET-RPC service, port %d" % (netport,))
|
|
||||||
|
|
||||||
LST_SIGNALS = ['SIGINT', 'SIGTERM']
|
LST_SIGNALS = ['SIGINT', 'SIGTERM']
|
||||||
if os.name == 'posix':
|
if os.name == 'posix':
|
||||||
|
@ -196,11 +183,8 @@ def handler(signum, _):
|
||||||
:param signum: the signal number
|
:param signum: the signal number
|
||||||
:param _:
|
:param _:
|
||||||
"""
|
"""
|
||||||
if tools.config['netrpc']:
|
|
||||||
tinySocket.stop()
|
|
||||||
if tools.config['xmlrpc']:
|
|
||||||
httpd.stop()
|
|
||||||
netsvc.Agent.quit()
|
netsvc.Agent.quit()
|
||||||
|
netsvc.Server.quitAll()
|
||||||
if tools.config['pidfile']:
|
if tools.config['pidfile']:
|
||||||
os.unlink(tools.config['pidfile'])
|
os.unlink(tools.config['pidfile'])
|
||||||
logger.notifyChannel('shutdown', netsvc.LOG_INFO,
|
logger.notifyChannel('shutdown', netsvc.LOG_INFO,
|
||||||
|
@ -217,16 +201,14 @@ if tools.config['pidfile']:
|
||||||
fd.write(pidtext)
|
fd.write(pidtext)
|
||||||
fd.close()
|
fd.close()
|
||||||
|
|
||||||
|
|
||||||
|
netsvc.Server.startAll()
|
||||||
|
|
||||||
logger.notifyChannel("web-services", netsvc.LOG_INFO,
|
logger.notifyChannel("web-services", netsvc.LOG_INFO,
|
||||||
'the server is running, waiting for connections...')
|
'the server is running, waiting for connections...')
|
||||||
|
|
||||||
if tools.config['netrpc']:
|
|
||||||
tinySocket.start()
|
|
||||||
if tools.config['xmlrpc']:
|
|
||||||
httpd.start()
|
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
time.sleep(1)
|
time.sleep(60)
|
||||||
|
|
||||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||||
|
|
||||||
|
|
|
@ -86,7 +86,6 @@ class osv_pool(netsvc.Service):
|
||||||
self._init = True
|
self._init = True
|
||||||
self._init_parent = {}
|
self._init_parent = {}
|
||||||
netsvc.Service.__init__(self, 'object_proxy', audience='')
|
netsvc.Service.__init__(self, 'object_proxy', audience='')
|
||||||
self.joinGroup('web-services')
|
|
||||||
self.exportMethod(self.obj_list)
|
self.exportMethod(self.obj_list)
|
||||||
self.exportMethod(self.exec_workflow)
|
self.exportMethod(self.exec_workflow)
|
||||||
self.exportMethod(self.execute)
|
self.exportMethod(self.execute)
|
||||||
|
|
|
@ -49,7 +49,7 @@ def toxml(val):
|
||||||
|
|
||||||
class report_int(netsvc.Service):
|
class report_int(netsvc.Service):
|
||||||
def __init__(self, name, audience='*'):
|
def __init__(self, name, audience='*'):
|
||||||
assert not netsvc.service_exist(name), 'The report "%s" already exist!' % name
|
assert not self.service_exist(name), 'The report "%s" already exist!' % name
|
||||||
super(report_int, self).__init__(name, audience)
|
super(report_int, self).__init__(name, audience)
|
||||||
if name[0:7]<>'report.':
|
if name[0:7]<>'report.':
|
||||||
raise Exception, 'ConceptionError, bad report name, should start with "report."'
|
raise Exception, 'ConceptionError, bad report name, should start with "report."'
|
||||||
|
@ -57,7 +57,7 @@ class report_int(netsvc.Service):
|
||||||
self.id = 0
|
self.id = 0
|
||||||
self.name2 = '.'.join(name.split('.')[1:])
|
self.name2 = '.'.join(name.split('.')[1:])
|
||||||
self.title = None
|
self.title = None
|
||||||
self.joinGroup('report')
|
#self.joinGroup('report')
|
||||||
self.exportMethod(self.create)
|
self.exportMethod(self.create)
|
||||||
|
|
||||||
def create(self, cr, uid, ids, datas, context=None):
|
def create(self, cr, uid, ids, datas, context=None):
|
||||||
|
@ -239,8 +239,9 @@ def register_all(db):
|
||||||
cr.execute("SELECT * FROM ir_act_report_xml WHERE auto=%s ORDER BY id", (True,))
|
cr.execute("SELECT * FROM ir_act_report_xml WHERE auto=%s ORDER BY id", (True,))
|
||||||
result = cr.dictfetchall()
|
result = cr.dictfetchall()
|
||||||
cr.close()
|
cr.close()
|
||||||
|
svcs = netsvc.Service._services
|
||||||
for r in result:
|
for r in result:
|
||||||
if netsvc.service_exist('report.'+r['report_name']):
|
if svcs.has_key('report.'+r['report_name']):
|
||||||
continue
|
continue
|
||||||
if r['report_rml'] or r['report_rml_content_data']:
|
if r['report_rml'] or r['report_rml_content_data']:
|
||||||
report_sxw('report.'+r['report_name'], r['model'],
|
report_sxw('report.'+r['report_name'], r['model'],
|
||||||
|
|
|
@ -0,0 +1,211 @@
|
||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright P. Christeas <p_christ@hol.gr> 2008,2009
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# WARNING: This program as such is intended to be used by professional
|
||||||
|
# programmers who take the whole responsability of assessing all potential
|
||||||
|
# consequences resulting from its eventual inadequacies and bugs
|
||||||
|
# End users who are looking for a ready-to-use solution with commercial
|
||||||
|
# garantees and support are strongly adviced to contract a Free Software
|
||||||
|
# Service Company
|
||||||
|
#
|
||||||
|
# This program is Free Software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU General Public License
|
||||||
|
# as published by the Free Software Foundation; either version 2
|
||||||
|
# of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
""" This file contains instance of the http server.
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
from websrv_lib import *
|
||||||
|
import netsvc
|
||||||
|
import threading
|
||||||
|
import tools
|
||||||
|
import os
|
||||||
|
import socket
|
||||||
|
import xmlrpclib
|
||||||
|
|
||||||
|
from SimpleXMLRPCServer import SimpleXMLRPCDispatcher
|
||||||
|
|
||||||
|
try:
|
||||||
|
import fcntl
|
||||||
|
except ImportError:
|
||||||
|
fcntl = None
|
||||||
|
|
||||||
|
class ThreadedHTTPServer(ConnThreadingMixIn, SimpleXMLRPCDispatcher, HTTPServer):
|
||||||
|
""" A threaded httpd server, with all the necessary functionality for us.
|
||||||
|
|
||||||
|
It also inherits the xml-rpc dispatcher, so that some xml-rpc functions
|
||||||
|
will be available to the request handler
|
||||||
|
"""
|
||||||
|
encoding = None
|
||||||
|
allow_none = False
|
||||||
|
allow_reuse_address = 1
|
||||||
|
_send_traceback_header = False
|
||||||
|
i = 0
|
||||||
|
|
||||||
|
def __init__(self, addr, requestHandler,
|
||||||
|
logRequests=True, allow_none=False, encoding=None, bind_and_activate=True):
|
||||||
|
self.logRequests = logRequests
|
||||||
|
|
||||||
|
SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding)
|
||||||
|
HTTPServer.__init__(self, addr, requestHandler, bind_and_activate)
|
||||||
|
|
||||||
|
# [Bug #1222790] If possible, set close-on-exec flag; if a
|
||||||
|
# method spawns a subprocess, the subprocess shouldn't have
|
||||||
|
# the listening socket open.
|
||||||
|
if fcntl is not None and hasattr(fcntl, 'FD_CLOEXEC'):
|
||||||
|
flags = fcntl.fcntl(self.fileno(), fcntl.F_GETFD)
|
||||||
|
flags |= fcntl.FD_CLOEXEC
|
||||||
|
fcntl.fcntl(self.fileno(), fcntl.F_SETFD, flags)
|
||||||
|
|
||||||
|
def handle_error(self, request, client_address):
|
||||||
|
""" Override the error handler
|
||||||
|
"""
|
||||||
|
import traceback
|
||||||
|
netsvc.Logger().notifyChannel("init", netsvc.LOG_ERROR,"Server error in request from %s:\n%s" %
|
||||||
|
(client_address,traceback.format_exc()))
|
||||||
|
|
||||||
|
class MultiHandler2(MultiHTTPHandler):
|
||||||
|
def log_message(self, format, *args):
|
||||||
|
netsvc.Logger().notifyChannel('http',netsvc.LOG_DEBUG,format % args)
|
||||||
|
|
||||||
|
|
||||||
|
class SecureMultiHandler2(SecureMultiHTTPHandler):
|
||||||
|
def log_message(self, format, *args):
|
||||||
|
netsvc.Logger().notifyChannel('https',netsvc.LOG_DEBUG,format % args)
|
||||||
|
|
||||||
|
class HttpDaemon(threading.Thread, netsvc.Server):
|
||||||
|
def __init__(self, interface, port):
|
||||||
|
threading.Thread.__init__(self)
|
||||||
|
netsvc.Server.__init__(self)
|
||||||
|
self.__port = port
|
||||||
|
self.__interface = interface
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.server = ThreadedHTTPServer((interface, port), MultiHandler2)
|
||||||
|
self.server.vdirs = []
|
||||||
|
self.server.logRequests = True
|
||||||
|
except Exception, e:
|
||||||
|
netsvc.Logger().notifyChannel('httpd', netsvc.LOG_CRITICAL, "Error occur when starting the server daemon: %s" % (e,))
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
def attach(self, path, gw):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
self.running = False
|
||||||
|
if os.name != 'nt':
|
||||||
|
self.server.socket.shutdown( hasattr(socket, 'SHUT_RDWR') and socket.SHUT_RDWR or 2 )
|
||||||
|
self.server.socket.close()
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
#self.server.register_introspection_functions()
|
||||||
|
|
||||||
|
self.running = True
|
||||||
|
while self.running:
|
||||||
|
self.server.handle_request()
|
||||||
|
return True
|
||||||
|
|
||||||
|
class HttpSDaemon(threading.Thread, netsvc.Server):
|
||||||
|
def __init__(self, interface, port):
|
||||||
|
threading.Thread.__init__(self)
|
||||||
|
netsvc.Server.__init__(self)
|
||||||
|
self.__port = port
|
||||||
|
self.__interface = interface
|
||||||
|
|
||||||
|
from ssl import SSLError
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.server = ThreadedHTTPServer((interface, port), SecureMultiHandler2)
|
||||||
|
self.server.vdirs = []
|
||||||
|
self.server.logRequests = True
|
||||||
|
except SSLError, e:
|
||||||
|
netsvc.Logger().notifyChannel('httpd-ssl', netsvc.LOG_CRITICAL, "Can not load the certificate and/or the private key files")
|
||||||
|
raise
|
||||||
|
except Exception, e:
|
||||||
|
netsvc.Logger().notifyChannel('httpd-ssl', netsvc.LOG_CRITICAL, "Error occur when starting the server daemon: %s" % (e,))
|
||||||
|
raise
|
||||||
|
|
||||||
|
httpd = None
|
||||||
|
httpsd = None
|
||||||
|
|
||||||
|
def init_servers():
|
||||||
|
global httpd, httpsd
|
||||||
|
if tools.config.get_misc('httpd','enable', True):
|
||||||
|
print "creating a httpd"
|
||||||
|
httpd = HttpDaemon(tools.config.get_misc('httpd','interface', ''), \
|
||||||
|
tools.config.get_misc('httpd','port', 8069))
|
||||||
|
|
||||||
|
if tools.config.get_misc('httpsd','enable', False):
|
||||||
|
print "creating a httpsd"
|
||||||
|
httpsd = HttpSDaemon(tools.config.get_misc('httpsd','interface', ''), \
|
||||||
|
tools.config.get_misc('httpsd','port', 8071))
|
||||||
|
|
||||||
|
def reg_http_service(hts, secure_only = False):
|
||||||
|
""" Register some handler to httpd.
|
||||||
|
hts must be an HTTPDir
|
||||||
|
"""
|
||||||
|
global httpd, httpsd
|
||||||
|
if not isinstance(hts, HTTPDir):
|
||||||
|
raise Exception("Wrong class for http service")
|
||||||
|
|
||||||
|
if httpd and not secure_only:
|
||||||
|
httpd.server.vdirs.append(hts)
|
||||||
|
|
||||||
|
if httpsd:
|
||||||
|
httpsd.server.vdirs.append(hts)
|
||||||
|
|
||||||
|
if (not httpd) and (not httpsd):
|
||||||
|
netsvc.Logger().notifyChannel('httpd',netsvc.LOG_WARNING,"No httpd available to register service %s",hts.path)
|
||||||
|
return
|
||||||
|
|
||||||
|
import SimpleXMLRPCServer
|
||||||
|
class XMLRPCRequestHandler(netsvc.OpenERPDispatcher,SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
|
||||||
|
rpc_paths = []
|
||||||
|
protocol_version = 'HTTP/1.1'
|
||||||
|
def _dispatch(self, method, params):
|
||||||
|
try:
|
||||||
|
service_name = self.path.split("/")[-1]
|
||||||
|
return self.dispatch(service_name, method, params)
|
||||||
|
except netsvc.OpenERPDispatcherException, e:
|
||||||
|
raise xmlrpclib.Fault(tools.exception_to_unicode(e.exception), e.traceback)
|
||||||
|
|
||||||
|
def log_message(self, format, *args):
|
||||||
|
netsvc.Logger().notifyChannel('xmlrpc',netsvc.LOG_DEBUG_RPC,format % args)
|
||||||
|
|
||||||
|
def handle(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def finish(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def setup(self):
|
||||||
|
self.connection = dummyconn()
|
||||||
|
if not len(XMLRPCRequestHandler.rpc_paths):
|
||||||
|
XMLRPCRequestHandler.rpc_paths = map(lambda s: '/%s' % s, netsvc.ExportService._services.keys())
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def init_xmlrpc():
|
||||||
|
if not tools.config.get_misc('xmlrpc','enable', True):
|
||||||
|
return
|
||||||
|
reg_http_service(HTTPDir('/xmlrpc/',XMLRPCRequestHandler))
|
||||||
|
# Example of http file serving:
|
||||||
|
# reg_http_service(HTTPDir('/test/',HTTPHandler))
|
||||||
|
print "Started XML-RPC"
|
||||||
|
#eof
|
|
@ -41,28 +41,36 @@ import tools
|
||||||
import locale
|
import locale
|
||||||
logging.basicConfig()
|
logging.basicConfig()
|
||||||
|
|
||||||
class db(netsvc.Service):
|
class db(netsvc.ExportService):
|
||||||
def __init__(self, name="db"):
|
def __init__(self, name="db"):
|
||||||
netsvc.Service.__init__(self, name)
|
netsvc.ExportService.__init__(self, name)
|
||||||
self.joinGroup("web-services")
|
self.joinGroup("web-services")
|
||||||
self.exportMethod(self.create)
|
|
||||||
self.exportMethod(self.get_progress)
|
|
||||||
self.exportMethod(self.drop)
|
|
||||||
self.exportMethod(self.dump)
|
|
||||||
self.exportMethod(self.restore)
|
|
||||||
self.exportMethod(self.rename)
|
|
||||||
self.exportMethod(self.list)
|
|
||||||
self.exportMethod(self.list_lang)
|
|
||||||
self.exportMethod(self.change_admin_password)
|
|
||||||
self.exportMethod(self.server_version)
|
|
||||||
self.exportMethod(self.migrate_databases)
|
|
||||||
self.actions = {}
|
self.actions = {}
|
||||||
self.id = 0
|
self.id = 0
|
||||||
self.id_protect = threading.Semaphore()
|
self.id_protect = threading.Semaphore()
|
||||||
|
|
||||||
self._pg_psw_env_var_is_set = False # on win32, pg_dump need the PGPASSWORD env var
|
self._pg_psw_env_var_is_set = False # on win32, pg_dump need the PGPASSWORD env var
|
||||||
|
|
||||||
def create(self, password, db_name, demo, lang, user_password='admin'):
|
def dispatch(self, method, auth, params):
|
||||||
|
if method in [ 'create', 'get_progress', 'drop', 'dump',
|
||||||
|
'restore', 'rename',
|
||||||
|
'change_admin_password', 'migrate_databases' ]:
|
||||||
|
passwd = params[0]
|
||||||
|
params = params[1:]
|
||||||
|
security.check_super(password)
|
||||||
|
elif method in [ 'db_exist', 'list', 'list_lang', 'server_version' ]:
|
||||||
|
# params = params
|
||||||
|
# No security check for these methods
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
raise KeyError("Method not found: %s" % method)
|
||||||
|
fn = getattr(self, 'exp_'+method)
|
||||||
|
return fn(*params)
|
||||||
|
|
||||||
|
def new_dispatch(self,method,auth,params):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def exp_create(self, db_name, demo, lang, user_password='admin'):
|
||||||
security.check_super(password)
|
security.check_super(password)
|
||||||
self.id_protect.acquire()
|
self.id_protect.acquire()
|
||||||
self.id += 1
|
self.id += 1
|
||||||
|
@ -131,8 +139,7 @@ class db(netsvc.Service):
|
||||||
self.actions[id]['thread'] = create_thread
|
self.actions[id]['thread'] = create_thread
|
||||||
return id
|
return id
|
||||||
|
|
||||||
def get_progress(self, password, id):
|
def exp_get_progress(self, id):
|
||||||
security.check_super(password)
|
|
||||||
if self.actions[id]['thread'].isAlive():
|
if self.actions[id]['thread'].isAlive():
|
||||||
# return addons.init_progress[db_name]
|
# return addons.init_progress[db_name]
|
||||||
return (min(self.actions[id].get('progress', 0),0.95), [])
|
return (min(self.actions[id].get('progress', 0),0.95), [])
|
||||||
|
@ -147,8 +154,7 @@ class db(netsvc.Service):
|
||||||
del self.actions[id]
|
del self.actions[id]
|
||||||
raise Exception, e
|
raise Exception, e
|
||||||
|
|
||||||
def drop(self, password, db_name):
|
def exp_drop(self, db_name):
|
||||||
security.check_super(password)
|
|
||||||
sql_db.close_db(db_name)
|
sql_db.close_db(db_name)
|
||||||
logger = netsvc.Logger()
|
logger = netsvc.Logger()
|
||||||
|
|
||||||
|
@ -180,8 +186,7 @@ class db(netsvc.Service):
|
||||||
if os.name == 'nt' and self._pg_psw_env_var_is_set:
|
if os.name == 'nt' and self._pg_psw_env_var_is_set:
|
||||||
os.environ['PGPASSWORD'] = ''
|
os.environ['PGPASSWORD'] = ''
|
||||||
|
|
||||||
def dump(self, password, db_name):
|
def exp_dump(self, db_name):
|
||||||
security.check_super(password)
|
|
||||||
logger = netsvc.Logger()
|
logger = netsvc.Logger()
|
||||||
|
|
||||||
self._set_pg_psw_env_var()
|
self._set_pg_psw_env_var()
|
||||||
|
@ -210,7 +215,7 @@ class db(netsvc.Service):
|
||||||
|
|
||||||
return base64.encodestring(data)
|
return base64.encodestring(data)
|
||||||
|
|
||||||
def restore(self, password, db_name, data):
|
def exp_restore(self, db_name, data):
|
||||||
security.check_super(password)
|
security.check_super(password)
|
||||||
logger = netsvc.Logger()
|
logger = netsvc.Logger()
|
||||||
|
|
||||||
|
@ -261,8 +266,7 @@ class db(netsvc.Service):
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def rename(self, password, old_name, new_name):
|
def exp_rename(self, old_name, new_name):
|
||||||
security.check_super(password)
|
|
||||||
sql_db.close_db(old_name)
|
sql_db.close_db(old_name)
|
||||||
logger = netsvc.Logger()
|
logger = netsvc.Logger()
|
||||||
|
|
||||||
|
@ -288,14 +292,14 @@ class db(netsvc.Service):
|
||||||
sql_db.close_db('template1')
|
sql_db.close_db('template1')
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def db_exist(self, db_name):
|
def exp_db_exist(self, db_name):
|
||||||
try:
|
try:
|
||||||
db = sql_db.db_connect(db_name)
|
db = sql_db.db_connect(db_name)
|
||||||
return True
|
return True
|
||||||
except:
|
except:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def list(self):
|
def exp_list(self):
|
||||||
db = sql_db.db_connect('template1')
|
db = sql_db.db_connect('template1')
|
||||||
cr = db.cursor()
|
cr = db.cursor()
|
||||||
try:
|
try:
|
||||||
|
@ -321,27 +325,25 @@ class db(netsvc.Service):
|
||||||
res.sort()
|
res.sort()
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def change_admin_password(self, old_password, new_password):
|
def exp_change_admin_password(self, new_password):
|
||||||
security.check_super(old_password)
|
|
||||||
tools.config['admin_passwd'] = new_password
|
tools.config['admin_passwd'] = new_password
|
||||||
tools.config.save()
|
tools.config.save()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def list_lang(self):
|
def exp_list_lang(self):
|
||||||
return tools.scan_languages()
|
return tools.scan_languages()
|
||||||
|
|
||||||
def server_version(self):
|
def exp_server_version(self):
|
||||||
""" Return the version of the server
|
""" Return the version of the server
|
||||||
Used by the client to verify the compatibility with its own version
|
Used by the client to verify the compatibility with its own version
|
||||||
"""
|
"""
|
||||||
return release.version
|
return release.version
|
||||||
|
|
||||||
def migrate_databases(self, password, databases):
|
def exp_migrate_databases(self,databases):
|
||||||
|
|
||||||
from osv.orm import except_orm
|
from osv.orm import except_orm
|
||||||
from osv.osv import except_osv
|
from osv.osv import except_osv
|
||||||
|
|
||||||
security.check_super(password)
|
|
||||||
l = netsvc.Logger()
|
l = netsvc.Logger()
|
||||||
for db in databases:
|
for db in databases:
|
||||||
try:
|
try:
|
||||||
|
@ -360,64 +362,74 @@ class db(netsvc.Service):
|
||||||
return True
|
return True
|
||||||
db()
|
db()
|
||||||
|
|
||||||
class common(netsvc.Service):
|
class _ObjectService(netsvc.ExportService):
|
||||||
|
"A common base class for those who have fn(db, uid, password,...) "
|
||||||
|
|
||||||
|
def common_dispatch(self, method, auth, params):
|
||||||
|
(db, uid, passwd ) = params[0:3]
|
||||||
|
params = params[3:]
|
||||||
|
security.check(db,uid,passwd)
|
||||||
|
cr = pooler.get_db(db).cursor()
|
||||||
|
fn = getattr(self, 'exp_'+method)
|
||||||
|
res = fn(cr, uid, *params)
|
||||||
|
cr.commit()
|
||||||
|
cr.close()
|
||||||
|
return res
|
||||||
|
|
||||||
|
class common(_ObjectService):
|
||||||
def __init__(self,name="common"):
|
def __init__(self,name="common"):
|
||||||
netsvc.Service.__init__(self,name)
|
_ObjectService.__init__(self,name)
|
||||||
self.joinGroup("web-services")
|
self.joinGroup("web-services")
|
||||||
self.exportMethod(self.ir_get)
|
|
||||||
self.exportMethod(self.ir_set)
|
|
||||||
self.exportMethod(self.ir_del)
|
|
||||||
self.exportMethod(self.about)
|
|
||||||
self.exportMethod(self.login)
|
|
||||||
self.exportMethod(self.logout)
|
|
||||||
self.exportMethod(self.timezone_get)
|
|
||||||
self.exportMethod(self.get_available_updates)
|
|
||||||
self.exportMethod(self.get_migration_scripts)
|
|
||||||
self.exportMethod(self.get_server_environment)
|
|
||||||
self.exportMethod(self.login_message)
|
|
||||||
self.exportMethod(self.set_loglevel)
|
|
||||||
|
|
||||||
def ir_set(self, db, uid, password, keys, args, name, value, replace=True, isobject=False):
|
def dispatch(self, method, auth, params):
|
||||||
security.check(db, uid, password)
|
logger = netsvc.Logger()
|
||||||
cr = pooler.get_db(db).cursor()
|
if method in [ 'ir_set','ir_del', 'ir_get' ]:
|
||||||
|
return self.common_dispatch(method,auth,params)
|
||||||
|
if method == 'login':
|
||||||
|
# At this old dispatcher, we do NOT update the auth proxy
|
||||||
|
res = security.login(params[0], params[1], params[2])
|
||||||
|
msg = res and 'successful login' or 'bad login or password'
|
||||||
|
# TODO log the client ip address..
|
||||||
|
logger.notifyChannel("web-service", netsvc.LOG_INFO, "%s from '%s' using database '%s'" % (msg, params[1], params[0].lower()))
|
||||||
|
return res or False
|
||||||
|
elif method == 'logout':
|
||||||
|
if auth:
|
||||||
|
auth.logout(params[1])
|
||||||
|
logger.notifyChannel("web-service", netsvc.LOG_INFO,'Logout %s from database %s'%(login,db))
|
||||||
|
return True
|
||||||
|
elif method in ['about', 'timezone_get', 'get_server_environment', 'login_message']:
|
||||||
|
pass
|
||||||
|
elif method in ['get_available_updates', 'get_migration_scripts', 'set_loglevel']:
|
||||||
|
passwd = params[0]
|
||||||
|
params = params[1:]
|
||||||
|
security.check_super(passwd)
|
||||||
|
else:
|
||||||
|
raise Exception("Method not found: %s" % method)
|
||||||
|
|
||||||
|
fn = getattr(self, 'exp_'+method)
|
||||||
|
return fn(*params)
|
||||||
|
|
||||||
|
|
||||||
|
def new_dispatch(self,method,auth,params):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def exp_ir_set(self, cr, uid, keys, args, name, value, replace=True, isobject=False):
|
||||||
res = ir.ir_set(cr,uid, keys, args, name, value, replace, isobject)
|
res = ir.ir_set(cr,uid, keys, args, name, value, replace, isobject)
|
||||||
cr.commit()
|
|
||||||
cr.close()
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def ir_del(self, db, uid, password, id):
|
def exp_ir_del(self, cr, uid, id):
|
||||||
security.check(db, uid, password)
|
|
||||||
cr = pooler.get_db(db).cursor()
|
|
||||||
res = ir.ir_del(cr,uid, id)
|
res = ir.ir_del(cr,uid, id)
|
||||||
cr.commit()
|
|
||||||
cr.close()
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def ir_get(self, db, uid, password, keys, args=None, meta=None, context=None):
|
def exp_ir_get(self, cr, uid, keys, args=None, meta=None, context=None):
|
||||||
if not args:
|
if not args:
|
||||||
args=[]
|
args=[]
|
||||||
if not context:
|
if not context:
|
||||||
context={}
|
context={}
|
||||||
security.check(db, uid, password)
|
|
||||||
cr = pooler.get_db(db).cursor()
|
|
||||||
res = ir.ir_get(cr,uid, keys, args, meta, context)
|
res = ir.ir_get(cr,uid, keys, args, meta, context)
|
||||||
cr.commit()
|
|
||||||
cr.close()
|
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def login(self, db, login, password):
|
def exp_about(self, extended=False):
|
||||||
res = security.login(db, login, password)
|
|
||||||
logger = netsvc.Logger()
|
|
||||||
msg = res and 'successful login' or 'bad login or password'
|
|
||||||
logger.notifyChannel("web-service", netsvc.LOG_INFO, "%s from '%s' using database '%s'" % (msg, login, db.lower()))
|
|
||||||
return res or False
|
|
||||||
|
|
||||||
def logout(self, db, login, password):
|
|
||||||
logger = netsvc.Logger()
|
|
||||||
logger.notifyChannel("web-service", netsvc.LOG_INFO,'Logout %s from database %s'%(login,db))
|
|
||||||
return True
|
|
||||||
|
|
||||||
def about(self, extended=False):
|
|
||||||
"""Return information about the OpenERP Server.
|
"""Return information about the OpenERP Server.
|
||||||
|
|
||||||
@param extended: if True then return version info
|
@param extended: if True then return version info
|
||||||
|
@ -437,12 +449,11 @@ GNU Public Licence.
|
||||||
return info, release.version
|
return info, release.version
|
||||||
return info
|
return info
|
||||||
|
|
||||||
def timezone_get(self, db, login, password):
|
def exp_timezone_get(self, db, login, password):
|
||||||
return time.tzname[0]
|
return time.tzname[0]
|
||||||
|
|
||||||
|
|
||||||
def get_available_updates(self, password, contract_id, contract_password):
|
def exp_get_available_updates(self, contract_id, contract_password):
|
||||||
security.check_super(password)
|
|
||||||
import tools.maintenance as tm
|
import tools.maintenance as tm
|
||||||
try:
|
try:
|
||||||
rc = tm.remote_contract(contract_id, contract_password)
|
rc = tm.remote_contract(contract_id, contract_password)
|
||||||
|
@ -455,8 +466,7 @@ GNU Public Licence.
|
||||||
self.abortResponse(1, 'Migration Error', 'warning', str(e))
|
self.abortResponse(1, 'Migration Error', 'warning', str(e))
|
||||||
|
|
||||||
|
|
||||||
def get_migration_scripts(self, password, contract_id, contract_password):
|
def exp_get_migration_scripts(self, contract_id, contract_password):
|
||||||
security.check_super(password)
|
|
||||||
l = netsvc.Logger()
|
l = netsvc.Logger()
|
||||||
import tools.maintenance as tm
|
import tools.maintenance as tm
|
||||||
try:
|
try:
|
||||||
|
@ -528,7 +538,7 @@ GNU Public Licence.
|
||||||
l.notifyChannel('migration', netsvc.LOG_ERROR, tb_s)
|
l.notifyChannel('migration', netsvc.LOG_ERROR, tb_s)
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def get_server_environment(self):
|
def exp_get_server_environment(self):
|
||||||
os_lang = '.'.join( [x for x in locale.getdefaultlocale() if x] )
|
os_lang = '.'.join( [x for x in locale.getdefaultlocale() if x] )
|
||||||
if not os_lang:
|
if not os_lang:
|
||||||
os_lang = 'NOT SET'
|
os_lang = 'NOT SET'
|
||||||
|
@ -553,42 +563,36 @@ GNU Public Licence.
|
||||||
return environment
|
return environment
|
||||||
|
|
||||||
|
|
||||||
def login_message(self):
|
def exp_login_message(self):
|
||||||
return tools.config.get('login_message', False)
|
return tools.config.get('login_message', False)
|
||||||
|
|
||||||
def set_loglevel(self, password, loglevel):
|
def exp_set_loglevel(self,loglevel):
|
||||||
security.check_super(password)
|
|
||||||
l = netsvc.Logger()
|
l = netsvc.Logger()
|
||||||
l.set_loglevel(int(loglevel))
|
l.set_loglevel(int(loglevel))
|
||||||
return True
|
return True
|
||||||
|
|
||||||
common()
|
common()
|
||||||
|
|
||||||
class objects_proxy(netsvc.Service):
|
class objects_proxy(netsvc.ExportService):
|
||||||
def __init__(self, name="object"):
|
def __init__(self, name="object"):
|
||||||
netsvc.Service.__init__(self,name)
|
netsvc.ExportService.__init__(self,name)
|
||||||
self.joinGroup('web-services')
|
self.joinGroup('web-services')
|
||||||
self.exportMethod(self.execute)
|
|
||||||
self.exportMethod(self.exec_workflow)
|
|
||||||
self.exportMethod(self.obj_list)
|
|
||||||
|
|
||||||
def exec_workflow(self, db, uid, passwd, object, method, id):
|
def dispatch(self, method, auth, params):
|
||||||
security.check(db, uid, passwd)
|
(db, uid, passwd ) = params[0:3]
|
||||||
service = netsvc.LocalService("object_proxy")
|
params = params[3:]
|
||||||
res = service.exec_workflow(db, uid, object, method, id)
|
if method not in ['execute','exec_workflow','obj_list']:
|
||||||
return res
|
raise KeyError("Method not supported %s" % method)
|
||||||
|
security.check(db,uid,passwd)
|
||||||
|
ls = netsvc.LocalService('object_proxy')
|
||||||
|
fn = getattr(ls, method)
|
||||||
|
res = fn(db, uid, *params)
|
||||||
|
return res
|
||||||
|
|
||||||
def execute(self, db, uid, passwd, object, method, *args):
|
|
||||||
security.check(db, uid, passwd)
|
def new_dispatch(self,method,auth,params):
|
||||||
service = netsvc.LocalService("object_proxy")
|
pass
|
||||||
res = service.execute(db, uid, object, method, *args)
|
|
||||||
return res
|
|
||||||
|
|
||||||
def obj_list(self, db, uid, passwd):
|
|
||||||
security.check(db, uid, passwd)
|
|
||||||
service = netsvc.LocalService("object_proxy")
|
|
||||||
res = service.obj_list()
|
|
||||||
return res
|
|
||||||
objects_proxy()
|
objects_proxy()
|
||||||
|
|
||||||
|
|
||||||
|
@ -603,26 +607,36 @@ objects_proxy()
|
||||||
# Wizard datas: {}
|
# Wizard datas: {}
|
||||||
# TODO: change local request to OSE request/reply pattern
|
# TODO: change local request to OSE request/reply pattern
|
||||||
#
|
#
|
||||||
class wizard(netsvc.Service):
|
class wizard(netsvc.ExportService):
|
||||||
def __init__(self, name='wizard'):
|
def __init__(self, name='wizard'):
|
||||||
netsvc.Service.__init__(self,name)
|
netsvc.ExportService.__init__(self,name)
|
||||||
self.joinGroup('web-services')
|
self.joinGroup('web-services')
|
||||||
self.exportMethod(self.execute)
|
|
||||||
self.exportMethod(self.create)
|
|
||||||
self.id = 0
|
self.id = 0
|
||||||
self.wiz_datas = {}
|
self.wiz_datas = {}
|
||||||
self.wiz_name = {}
|
self.wiz_name = {}
|
||||||
self.wiz_uid = {}
|
self.wiz_uid = {}
|
||||||
|
|
||||||
|
def dispatch(self, method, auth, params):
|
||||||
|
(db, uid, passwd ) = params[0:3]
|
||||||
|
params = params[3:]
|
||||||
|
if method not in ['execute','create']:
|
||||||
|
raise KeyError("Method not supported %s" % method)
|
||||||
|
security.check(db,uid,passwd)
|
||||||
|
fn = getattr(self, 'exp_'+method)
|
||||||
|
res = fn(ls, db, uid, *params)
|
||||||
|
return res
|
||||||
|
|
||||||
|
def new_dispatch(self,method,auth,params):
|
||||||
|
pass
|
||||||
|
|
||||||
def _execute(self, db, uid, wiz_id, datas, action, context):
|
def _execute(self, db, uid, wiz_id, datas, action, context):
|
||||||
self.wiz_datas[wiz_id].update(datas)
|
self.wiz_datas[wiz_id].update(datas)
|
||||||
wiz = netsvc.LocalService('wizard.'+self.wiz_name[wiz_id])
|
wiz = netsvc.LocalService('wizard.'+self.wiz_name[wiz_id])
|
||||||
return wiz.execute(db, uid, self.wiz_datas[wiz_id], action, context)
|
return wiz.execute(db, uid, self.wiz_datas[wiz_id], action, context)
|
||||||
|
|
||||||
def create(self, db, uid, passwd, wiz_name, datas=None):
|
def exp_create(self, db, uid, wiz_name, datas=None):
|
||||||
if not datas:
|
if not datas:
|
||||||
datas={}
|
datas={}
|
||||||
security.check(db, uid, passwd)
|
|
||||||
#FIXME: this is not thread-safe
|
#FIXME: this is not thread-safe
|
||||||
self.id += 1
|
self.id += 1
|
||||||
self.wiz_datas[self.id] = {}
|
self.wiz_datas[self.id] = {}
|
||||||
|
@ -630,10 +644,9 @@ class wizard(netsvc.Service):
|
||||||
self.wiz_uid[self.id] = uid
|
self.wiz_uid[self.id] = uid
|
||||||
return self.id
|
return self.id
|
||||||
|
|
||||||
def execute(self, db, uid, passwd, wiz_id, datas, action='init', context=None):
|
def exp_execute(self, db, uid, wiz_id, datas, action='init', context=None):
|
||||||
if not context:
|
if not context:
|
||||||
context={}
|
context={}
|
||||||
security.check(db, uid, passwd)
|
|
||||||
|
|
||||||
if wiz_id in self.wiz_uid:
|
if wiz_id in self.wiz_uid:
|
||||||
if self.wiz_uid[wiz_id] == uid:
|
if self.wiz_uid[wiz_id] == uid:
|
||||||
|
@ -657,22 +670,33 @@ class ExceptionWithTraceback(Exception):
|
||||||
self.traceback = tb
|
self.traceback = tb
|
||||||
self.args = (msg, tb)
|
self.args = (msg, tb)
|
||||||
|
|
||||||
class report_spool(netsvc.Service):
|
class report_spool(netsvc.ExportService):
|
||||||
def __init__(self, name='report'):
|
def __init__(self, name='report'):
|
||||||
netsvc.Service.__init__(self, name)
|
netsvc.ExportService.__init__(self, name)
|
||||||
self.joinGroup('web-services')
|
self.joinGroup('web-services')
|
||||||
self.exportMethod(self.report)
|
|
||||||
self.exportMethod(self.report_get)
|
|
||||||
self._reports = {}
|
self._reports = {}
|
||||||
self.id = 0
|
self.id = 0
|
||||||
self.id_protect = threading.Semaphore()
|
self.id_protect = threading.Semaphore()
|
||||||
|
|
||||||
def report(self, db, uid, passwd, object, ids, datas=None, context=None):
|
def dispatch(self, method, auth, params):
|
||||||
|
(db, uid, passwd ) = params[0:3]
|
||||||
|
params = params[3:]
|
||||||
|
if method not in ['report','report_get']:
|
||||||
|
raise KeyError("Method not supported %s" % method)
|
||||||
|
security.check(db,uid,passwd)
|
||||||
|
fn = getattr(self, 'exp_' + method)
|
||||||
|
res = fn(db, uid, *params)
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
def new_dispatch(self,method,auth,params):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def exp_report(self, db, uid, object, ids, datas=None, context=None):
|
||||||
if not datas:
|
if not datas:
|
||||||
datas={}
|
datas={}
|
||||||
if not context:
|
if not context:
|
||||||
context={}
|
context={}
|
||||||
security.check(db, uid, passwd)
|
|
||||||
|
|
||||||
self.id_protect.acquire()
|
self.id_protect.acquire()
|
||||||
self.id += 1
|
self.id += 1
|
||||||
|
@ -728,8 +752,7 @@ class report_spool(netsvc.Service):
|
||||||
del self._reports[report_id]
|
del self._reports[report_id]
|
||||||
return res
|
return res
|
||||||
|
|
||||||
def report_get(self, db, uid, passwd, report_id):
|
def exp_report_get(self, db, uid, report_id):
|
||||||
security.check(db, uid, passwd)
|
|
||||||
|
|
||||||
if report_id in self._reports:
|
if report_id in self._reports:
|
||||||
if self._reports[report_id]['uid'] == uid:
|
if self._reports[report_id]['uid'] == uid:
|
||||||
|
|
|
@ -0,0 +1,357 @@
|
||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright P. Christeas <p_christ@hol.gr> 2008,2009
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# WARNING: This program as such is intended to be used by professional
|
||||||
|
# programmers who take the whole responsability of assessing all potential
|
||||||
|
# consequences resulting from its eventual inadequacies and bugs
|
||||||
|
# End users who are looking for a ready-to-use solution with commercial
|
||||||
|
# garantees and support are strongly adviced to contract a Free Software
|
||||||
|
# Service Company
|
||||||
|
#
|
||||||
|
# This program is Free Software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU General Public License
|
||||||
|
# as published by the Free Software Foundation; either version 2
|
||||||
|
# of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
""" Framework for generic http servers
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
class AuthRequiredExc(Exception):
|
||||||
|
def __init__(self,atype,realm):
|
||||||
|
Exception.__init__(self)
|
||||||
|
self.atype = atype
|
||||||
|
self.realm = realm
|
||||||
|
|
||||||
|
class AuthRejectedExc(Exception):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class AuthProvider:
|
||||||
|
def __init__(self,realm):
|
||||||
|
self.realm = realm
|
||||||
|
|
||||||
|
def setupAuth(self, multi,handler):
|
||||||
|
""" Attach an AuthProxy object to handler
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def authenticate(self, user, passwd, client_address):
|
||||||
|
#if user == 'user' and passwd == 'password':
|
||||||
|
# return (user, passwd)
|
||||||
|
#else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
class BasicAuthProvider(AuthProvider):
|
||||||
|
def setupAuth(self, multi, handler):
|
||||||
|
if not multi.sec_realms.has_key(self.realm):
|
||||||
|
multi.sec_realms[self.realm] = BasicAuthProxy(self)
|
||||||
|
|
||||||
|
|
||||||
|
class AuthProxy:
|
||||||
|
""" This class will hold authentication information for a handler,
|
||||||
|
i.e. a connection
|
||||||
|
"""
|
||||||
|
def __init__(self, provider):
|
||||||
|
self.provider = provider
|
||||||
|
|
||||||
|
def checkRequest(self,handler,path = '/'):
|
||||||
|
""" Check if we are allowed to process that request
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
import base64
|
||||||
|
class BasicAuthProxy(AuthProxy):
|
||||||
|
""" Require basic authentication..
|
||||||
|
"""
|
||||||
|
def __init__(self,provider):
|
||||||
|
AuthProxy.__init__(self,provider)
|
||||||
|
self.auth_creds = None
|
||||||
|
self.auth_tries = 0
|
||||||
|
|
||||||
|
def checkRequest(self,handler,path = '/'):
|
||||||
|
if self.auth_creds:
|
||||||
|
return True
|
||||||
|
auth_str = handler.headers.get('Authorization',False)
|
||||||
|
if auth_str and auth_str.startswith('Basic '):
|
||||||
|
auth_str=auth_str[len('Basic '):]
|
||||||
|
(user,passwd) = base64.decodestring(auth_str).split(':')
|
||||||
|
print "Found user=\"%s\", passwd=\"%s\"" %(user,passwd)
|
||||||
|
self.auth_creds = self.provider.authenticate(user,passwd,handler.client_address)
|
||||||
|
if self.auth_creds:
|
||||||
|
return True
|
||||||
|
if self.auth_tries > 5:
|
||||||
|
raise AuthRejectedExc("Authorization failed.")
|
||||||
|
self.auth_tries += 1
|
||||||
|
raise AuthRequiredExc(atype = 'Basic', realm=self.provider.realm)
|
||||||
|
|
||||||
|
|
||||||
|
from BaseHTTPServer import *
|
||||||
|
|
||||||
|
from SimpleHTTPServer import SimpleHTTPRequestHandler
|
||||||
|
class HTTPHandler(SimpleHTTPRequestHandler):
|
||||||
|
def __init__(self,request, client_address, server):
|
||||||
|
SimpleHTTPRequestHandler.__init__(self,request,client_address,server)
|
||||||
|
# print "Handler for %s inited" % str(client_address)
|
||||||
|
self.protocol_version = 'HTTP/1.1'
|
||||||
|
self.connection = dummyconn()
|
||||||
|
|
||||||
|
def handle(self):
|
||||||
|
""" Classes here should NOT handle inside their constructor
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def finish(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def setup(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class HTTPDir:
|
||||||
|
""" A dispatcher class, like a virtual folder in httpd
|
||||||
|
"""
|
||||||
|
def __init__(self,path,handler, auth_provider = None):
|
||||||
|
self.path = path
|
||||||
|
self.handler = handler
|
||||||
|
self.auth_provider = auth_provider
|
||||||
|
|
||||||
|
def matches(self, request):
|
||||||
|
""" Test if some request matches us. If so, return
|
||||||
|
the matched path. """
|
||||||
|
if request.startswith(self.path):
|
||||||
|
return self.path
|
||||||
|
return False
|
||||||
|
|
||||||
|
class noconnection:
|
||||||
|
""" a class to use instead of the real connection
|
||||||
|
"""
|
||||||
|
def makefile(self, mode, bufsize):
|
||||||
|
return None
|
||||||
|
|
||||||
|
class dummyconn:
|
||||||
|
def shutdown(self, tru):
|
||||||
|
pass
|
||||||
|
|
||||||
|
import SocketServer
|
||||||
|
class MultiHTTPHandler(BaseHTTPRequestHandler):
|
||||||
|
""" this is a multiple handler, that will dispatch each request
|
||||||
|
to a nested handler, iff it matches
|
||||||
|
|
||||||
|
The handler will also have *one* dict of authentication proxies,
|
||||||
|
groupped by their realm.
|
||||||
|
"""
|
||||||
|
|
||||||
|
protocol_version = "HTTP/1.1"
|
||||||
|
|
||||||
|
def __init__(self, request, client_address, server):
|
||||||
|
self.in_handlers = {}
|
||||||
|
self.sec_realms = {}
|
||||||
|
SocketServer.StreamRequestHandler.__init__(self,request,client_address,server)
|
||||||
|
self.log_message("MultiHttpHandler init for %s" %(str(client_address)))
|
||||||
|
|
||||||
|
def _handle_one_foreign(self,fore, path, auth_provider):
|
||||||
|
""" This method overrides the handle_one_request for *children*
|
||||||
|
handlers. It is required, since the first line should not be
|
||||||
|
read again..
|
||||||
|
|
||||||
|
"""
|
||||||
|
fore.raw_requestline = "%s %s %s\n" % (self.command, path, self.version)
|
||||||
|
if not fore.parse_request(): # An error code has been sent, just exit
|
||||||
|
return
|
||||||
|
self.request_version = fore.request_version
|
||||||
|
if auth_provider and auth_provider.realm:
|
||||||
|
try:
|
||||||
|
self.sec_realms[auth_provider.realm].checkRequest(fore,path)
|
||||||
|
except AuthRequiredExc,ae:
|
||||||
|
if self.request_version != 'HTTP/1.1':
|
||||||
|
self.log_error("Cannot require auth at %s",self.request_version)
|
||||||
|
self.send_error(401)
|
||||||
|
return
|
||||||
|
self.send_response(401,'Authorization required')
|
||||||
|
self.send_header('WWW-Authenticate','%s realm="%s"' % (ae.atype,ae.realm))
|
||||||
|
self.send_header('Content-Type','text/html')
|
||||||
|
self.send_header('Content-Length','0')
|
||||||
|
self.end_headers()
|
||||||
|
#self.wfile.write("\r\n")
|
||||||
|
return
|
||||||
|
except AuthRejectedExc,e:
|
||||||
|
self.send_error(401,e.args[0])
|
||||||
|
self.close_connection = 1
|
||||||
|
return
|
||||||
|
mname = 'do_' + fore.command
|
||||||
|
if not hasattr(fore, mname):
|
||||||
|
fore.send_error(501, "Unsupported method (%r)" % fore.command)
|
||||||
|
return
|
||||||
|
fore.close_connection = 0
|
||||||
|
method = getattr(fore, mname)
|
||||||
|
method()
|
||||||
|
if fore.close_connection:
|
||||||
|
# print "Closing connection because of handler"
|
||||||
|
self.close_connection = fore.close_connection
|
||||||
|
|
||||||
|
def parse_rawline(self):
|
||||||
|
"""Parse a request (internal).
|
||||||
|
|
||||||
|
The request should be stored in self.raw_requestline; the results
|
||||||
|
are in self.command, self.path, self.request_version and
|
||||||
|
self.headers.
|
||||||
|
|
||||||
|
Return True for success, False for failure; on failure, an
|
||||||
|
error is sent back.
|
||||||
|
|
||||||
|
"""
|
||||||
|
self.command = None # set in case of error on the first line
|
||||||
|
self.request_version = version = self.default_request_version
|
||||||
|
self.close_connection = 1
|
||||||
|
requestline = self.raw_requestline
|
||||||
|
if requestline[-2:] == '\r\n':
|
||||||
|
requestline = requestline[:-2]
|
||||||
|
elif requestline[-1:] == '\n':
|
||||||
|
requestline = requestline[:-1]
|
||||||
|
self.requestline = requestline
|
||||||
|
words = requestline.split()
|
||||||
|
if len(words) == 3:
|
||||||
|
[command, path, version] = words
|
||||||
|
if version[:5] != 'HTTP/':
|
||||||
|
self.send_error(400, "Bad request version (%r)" % version)
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
base_version_number = version.split('/', 1)[1]
|
||||||
|
version_number = base_version_number.split(".")
|
||||||
|
# RFC 2145 section 3.1 says there can be only one "." and
|
||||||
|
# - major and minor numbers MUST be treated as
|
||||||
|
# separate integers;
|
||||||
|
# - HTTP/2.4 is a lower version than HTTP/2.13, which in
|
||||||
|
# turn is lower than HTTP/12.3;
|
||||||
|
# - Leading zeros MUST be ignored by recipients.
|
||||||
|
if len(version_number) != 2:
|
||||||
|
raise ValueError
|
||||||
|
version_number = int(version_number[0]), int(version_number[1])
|
||||||
|
except (ValueError, IndexError):
|
||||||
|
self.send_error(400, "Bad request version (%r)" % version)
|
||||||
|
return False
|
||||||
|
if version_number >= (1, 1):
|
||||||
|
self.close_connection = 0
|
||||||
|
if version_number >= (2, 0):
|
||||||
|
self.send_error(505,
|
||||||
|
"Invalid HTTP Version (%s)" % base_version_number)
|
||||||
|
return False
|
||||||
|
elif len(words) == 2:
|
||||||
|
[command, path] = words
|
||||||
|
self.close_connection = 1
|
||||||
|
if command != 'GET':
|
||||||
|
self.send_error(400,
|
||||||
|
"Bad HTTP/0.9 request type (%r)" % command)
|
||||||
|
return False
|
||||||
|
elif not words:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
self.send_error(400, "Bad request syntax (%r)" % requestline)
|
||||||
|
return False
|
||||||
|
self.request_version = version
|
||||||
|
self.command, self.path, self.version = command, path, version
|
||||||
|
return True
|
||||||
|
|
||||||
|
def handle_one_request(self):
|
||||||
|
"""Handle a single HTTP request.
|
||||||
|
Dispatch to the correct handler.
|
||||||
|
"""
|
||||||
|
self.request.setblocking(True)
|
||||||
|
self.raw_requestline = self.rfile.readline()
|
||||||
|
if not self.raw_requestline:
|
||||||
|
self.close_connection = 1
|
||||||
|
# self.log_message("no requestline, connection closed?")
|
||||||
|
return
|
||||||
|
if not self.parse_rawline():
|
||||||
|
self.log_message("Could not parse rawline.")
|
||||||
|
return
|
||||||
|
# self.parse_request(): # Do NOT parse here. the first line should be the only
|
||||||
|
for vdir in self.server.vdirs:
|
||||||
|
p = vdir.matches(self.path)
|
||||||
|
if p == False:
|
||||||
|
continue
|
||||||
|
npath = self.path[len(p):]
|
||||||
|
if not npath.startswith('/'):
|
||||||
|
npath = '/' + npath
|
||||||
|
|
||||||
|
if not self.in_handlers.has_key(p):
|
||||||
|
self.in_handlers[p] = vdir.handler(noconnection(),self.client_address,self.server)
|
||||||
|
if vdir.auth_provider:
|
||||||
|
vdir.auth_provider.setupAuth(self, self.in_handlers[p])
|
||||||
|
hnd = self.in_handlers[p]
|
||||||
|
hnd.rfile = self.rfile
|
||||||
|
hnd.wfile = self.wfile
|
||||||
|
self.rlpath = self.raw_requestline
|
||||||
|
self._handle_one_foreign(hnd,npath, vdir.auth_provider)
|
||||||
|
# print "Handled, closing = ", self.close_connection
|
||||||
|
return
|
||||||
|
# if no match:
|
||||||
|
self.send_error(404, "Path not found: %s" % self.path)
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
class SecureMultiHTTPHandler(MultiHTTPHandler):
|
||||||
|
def setup(self):
|
||||||
|
import ssl
|
||||||
|
self.connection = ssl.wrap_socket(self.request,
|
||||||
|
server_side=True,
|
||||||
|
certfile="server.cert",
|
||||||
|
keyfile="server.key",
|
||||||
|
ssl_version=ssl.PROTOCOL_SSLv23)
|
||||||
|
self.rfile = self.connection.makefile('rb', self.rbufsize)
|
||||||
|
self.wfile = self.connection.makefile('wb', self.wbufsize)
|
||||||
|
self.log_message("Secure %s connection from %s",self.connection.cipher(),self.client_address)
|
||||||
|
|
||||||
|
import threading
|
||||||
|
class ConnThreadingMixIn:
|
||||||
|
"""Mix-in class to handle each _connection_ in a new thread.
|
||||||
|
|
||||||
|
This is necessary for persistent connections, where multiple
|
||||||
|
requests should be handled synchronously at each connection, but
|
||||||
|
multiple connections can run in parallel.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Decides how threads will act upon termination of the
|
||||||
|
# main process
|
||||||
|
daemon_threads = False
|
||||||
|
|
||||||
|
def _handle_request_noblock(self):
|
||||||
|
"""Start a new thread to process the request."""
|
||||||
|
t = threading.Thread(target = self._handle_request2)
|
||||||
|
print "request came, handling in new thread",t
|
||||||
|
if self.daemon_threads:
|
||||||
|
t.setDaemon (1)
|
||||||
|
t.start()
|
||||||
|
|
||||||
|
def _handle_request2(self):
|
||||||
|
"""Handle one request, without blocking.
|
||||||
|
|
||||||
|
I assume that select.select has returned that the socket is
|
||||||
|
readable before this function was called, so there should be
|
||||||
|
no risk of blocking in get_request().
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
request, client_address = self.get_request()
|
||||||
|
except socket.error:
|
||||||
|
return
|
||||||
|
if self.verify_request(request, client_address):
|
||||||
|
try:
|
||||||
|
self.process_request(request, client_address)
|
||||||
|
except:
|
||||||
|
self.handle_error(request, client_address)
|
||||||
|
self.close_request(request)
|
||||||
|
|
||||||
|
#eof
|
|
@ -473,8 +473,8 @@ def trans_generate(lang, modules, dbname=None):
|
||||||
push_translation(module, 'view', encode(obj.model), 0, t)
|
push_translation(module, 'view', encode(obj.model), 0, t)
|
||||||
elif model=='ir.actions.wizard':
|
elif model=='ir.actions.wizard':
|
||||||
service_name = 'wizard.'+encode(obj.wiz_name)
|
service_name = 'wizard.'+encode(obj.wiz_name)
|
||||||
if netsvc.SERVICES.get(service_name):
|
if netsvc.Service._services.get(service_name):
|
||||||
obj2 = netsvc.SERVICES[service_name]
|
obj2 = netsvc.Service._services[service_name]
|
||||||
for state_name, state_def in obj2.states.iteritems():
|
for state_name, state_def in obj2.states.iteritems():
|
||||||
if 'result' in state_def:
|
if 'result' in state_def:
|
||||||
result = state_def['result']
|
result = state_def['result']
|
||||||
|
|
|
@ -44,7 +44,7 @@ class interface(netsvc.Service):
|
||||||
states = {}
|
states = {}
|
||||||
|
|
||||||
def __init__(self, name):
|
def __init__(self, name):
|
||||||
assert not netsvc.service_exist('wizard.'+name), 'The wizard "%s" already exists!'%name
|
assert not self.service_exist('wizard.'+name), 'The wizard "%s" already exists!'%name
|
||||||
super(interface, self).__init__('wizard.'+name)
|
super(interface, self).__init__('wizard.'+name)
|
||||||
self.exportMethod(self.execute)
|
self.exportMethod(self.execute)
|
||||||
self.wiz_name = name
|
self.wiz_name = name
|
||||||
|
|
Loading…
Reference in New Issue