#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.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): 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 = SSLSocket(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)