Files
tryton/trytond/protocols/xmlrpc.py
T
2013-02-27 22:50:31 +01:00

254 lines
8.7 KiB
Python

#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
from trytond.protocols.sslsocket import SSLSocket
from trytond.protocols.dispatcher import dispatch
from trytond.config import CONFIG
from trytond.protocols.common import daemon, GZipRequestHandlerMixin, \
RegisterHandlerMixin
from trytond.exceptions import UserError, UserWarning, NotLogged, \
ConcurrencyException
from trytond import security
import SimpleXMLRPCServer
import SocketServer
import xmlrpclib
import traceback
import socket
import sys
import base64
import datetime
from types import DictType
# convert decimal to float before marshalling:
from decimal import Decimal
def dump_decimal(self, value, write):
value = {'__class__': 'Decimal',
'decimal': str(value),
}
self.dump_struct(value, write)
def dump_buffer(self, value, write):
self.write = write
value = xmlrpclib.Binary(value)
value.encode(self)
del self.write
def dump_date(self, value, write):
value = {'__class__': 'date',
'year': value.year,
'month': value.month,
'day': value.day,
}
self.dump_struct(value, write)
def dump_time(self, value, write):
value = {'__class__': 'time',
'hour': value.hour,
'minute': value.minute,
'second': value.second,
}
self.dump_struct(value, write)
xmlrpclib.Marshaller.dispatch[Decimal] = dump_decimal
xmlrpclib.Marshaller.dispatch[type(None)] = \
lambda self, value, write: self.dump_bool(bool(value), write)
xmlrpclib.Marshaller.dispatch[datetime.date] = dump_date
xmlrpclib.Marshaller.dispatch[datetime.time] = dump_time
xmlrpclib.Marshaller.dispatch[buffer] = dump_buffer
def dump_struct(self, value, write, escape=xmlrpclib.escape):
converted_value = {}
for k, v in value.items():
if type(k) in (int, long):
k = str(int(k))
elif type(k) == float:
k = repr(k)
converted_value[k] = v
return self.dump_struct(converted_value, write, escape=escape)
xmlrpclib.Marshaller.dispatch[DictType] = dump_struct
def end_struct(self, data):
mark = self._marks.pop()
# map structs to Python dictionaries
dct = {}
items = self._stack[mark:]
for i in range(0, len(items), 2):
dct[xmlrpclib._stringify(items[i])] = items[i + 1]
if '__class__' in dct:
if dct['__class__'] == 'date':
dct = datetime.date(dct['year'], dct['month'], dct['day'])
elif dct['__class__'] == 'time':
dct = datetime.time(dct['hour'], dct['minute'], dct['second'])
elif dct['__class__'] == 'Decimal':
dct = Decimal(dct['decimal'])
self._stack[mark:] = [dct]
self._value = 0
xmlrpclib.Unmarshaller.dispatch['struct'] = end_struct
def _end_dateTime(self, data):
value = xmlrpclib.DateTime()
value.decode(data)
value = xmlrpclib._datetime_type(data)
self.append(value)
xmlrpclib.Unmarshaller.dispatch["dateTime.iso8601"] = _end_dateTime
class GenericXMLRPCRequestHandler:
def _dispatch(self, method, params):
host, port = self.client_address[:2]
database_name = self.path[1:]
user = self.tryton['user']
session = self.tryton['session']
try:
try:
method_list = method.split('.')
object_type = method_list[0]
object_name = '.'.join(method_list[1:-1])
method = method_list[-1]
if object_type == 'system' and method == 'getCapabilities':
return {
'introspect': {
'specUrl': ('http://xmlrpc-c.sourceforge.net/'
'xmlrpc-c/introspection.html'),
'specVersion': 1,
},
}
return dispatch(host, port, 'XML-RPC', database_name, user,
session, object_type, object_name, method, *params)
except (NotLogged, ConcurrencyException), exception:
raise xmlrpclib.Fault(exception.code,
'\n'.join(exception.args))
except (UserError, UserWarning), exception:
error, description = exception.args
raise xmlrpclib.Fault(exception.code,
'\n'.join((error,) + description))
except Exception:
tb_s = ''.join(traceback.format_exception(*sys.exc_info()))
for path in sys.path:
tb_s = tb_s.replace(path, '')
if CONFIG['debug_mode']:
import pdb
traceb = sys.exc_info()[2]
pdb.post_mortem(traceb)
raise xmlrpclib.Fault(255, str(sys.exc_value) + '\n' + tb_s)
finally:
security.logout(database_name, user, session)
class SimpleXMLRPCRequestHandler(GZipRequestHandlerMixin,
RegisterHandlerMixin,
GenericXMLRPCRequestHandler,
SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
protocol_version = "HTTP/1.1"
rpc_paths = None
encode_threshold = 1400 # common MTU
def parse_request(self):
res = SimpleXMLRPCServer.SimpleXMLRPCRequestHandler.parse_request(self)
if not res:
return res
database_name = self.path[1:]
if not database_name:
self.tryton = {'user': None, 'session': None}
return res
try:
method, up64 = self.headers['Authorization'].split(None, 1)
if method.strip().lower() == 'basic':
user, password = base64.decodestring(up64).split(':', 1)
user_id, session = security.login(database_name, user,
password)
self.tryton = {'user': user_id, 'session': session}
return res
except Exception:
pass
self.send_error(401, 'Unauthorized')
self.send_header("WWW-Authenticate", 'Basic realm="Tryton"')
return False
class SecureXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
def setup(self):
self.request = SSLSocket(self.request)
SimpleXMLRPCRequestHandler.setup(self)
class SimpleThreadedXMLRPCServer(SocketServer.ThreadingMixIn,
SimpleXMLRPCServer.SimpleXMLRPCServer):
timeout = 1
daemon_threads = True
def __init__(self, server_address, HandlerClass, logRequests=1):
self.handlers = set()
SimpleXMLRPCServer.SimpleXMLRPCServer.__init__(self, server_address,
HandlerClass, logRequests)
def server_bind(self):
self.socket.setsockopt(socket.SOL_SOCKET,
socket.SO_REUSEADDR, 1)
self.socket.setsockopt(socket.SOL_SOCKET,
socket.SO_KEEPALIVE, 1)
SimpleXMLRPCServer.SimpleXMLRPCServer.server_bind(self)
def server_close(self):
SimpleXMLRPCServer.SimpleXMLRPCServer.server_close(self)
for handler in self.handlers:
self.shutdown_request(handler.request)
if sys.version_info[:2] <= (2, 6):
def shutdown_request(self, request):
"""Called to shutdown and close an individual request."""
try:
#explicitly shutdown. socket.close() merely releases
#the socket and waits for GC to perform the actual close.
request.shutdown(socket.SHUT_WR)
except socket.error:
pass # some platforms may raise ENOTCONN here
self.close_request(request)
class SimpleThreadedXMLRPCServer6(SimpleThreadedXMLRPCServer):
address_family = socket.AF_INET6
class SecureThreadedXMLRPCServer(SimpleThreadedXMLRPCServer):
def __init__(self, server_address, HandlerClass, logRequests=1):
SimpleThreadedXMLRPCServer.__init__(self, server_address, HandlerClass,
logRequests)
self.socket = socket.socket(self.address_family, self.socket_type)
self.server_bind()
self.server_activate()
class SecureThreadedXMLRPCServer6(SecureThreadedXMLRPCServer):
address_family = socket.AF_INET6
class XMLRPCDaemon(daemon):
def __init__(self, interface, port, secure=False):
daemon.__init__(self, interface, port, secure, name='XMLRPCDaemon')
if self.secure:
handler_class = SecureXMLRPCRequestHandler
server_class = SecureThreadedXMLRPCServer
if self.ipv6:
server_class = SecureThreadedXMLRPCServer6
else:
handler_class = SimpleXMLRPCRequestHandler
server_class = SimpleThreadedXMLRPCServer
if self.ipv6:
server_class = SimpleThreadedXMLRPCServer6
self.server = server_class((interface, port), handler_class, 0)