# Copyright (c) 2001 LOGILAB S.A. (Paris, FRANCE).
# http://www.logilab.fr/ -- mailto:contact@logilab.fr
#
# 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.
__revision__ = "$Id: xmlrpc.py,v 1.5 2002/09/20 14:12:34 syt Exp $"

import xmlrpclib
import BaseHTTPServer

from narval.communication.RPCFactory import ServerInterface
from narval.services.BaseService import BaseSocketService


class XMLRPCServer(ServerInterface, BaseSocketService):

    def __init__(self, port, method_handler) :
        _XMLRPCRequestHandler.call = self.call
        self.handler = method_handler
        BaseSocketService.__init__(self, port, _XMLRPCRequestHandler,
                                   'XML-RPC RpcService')

    def call(self, method, params) :
        try:
            #print 'Server call', method, self.handler.EXPORTED_METHODS[method]
            object = getattr(self.handler, self.handler.EXPORTED_METHODS[method])
            if object :
                func = getattr(object,method) 
                result = apply(func, params)
                if result != None :
                    return (result,)
            else :
                print 'RpcService error: No',object,'to call'
        except KeyError, e :
            import traceback
            traceback.print_exc()
            print '*-'*20
            print 'RPC call to undefined/forbidden method',method,params

        except Exception, e :
            print 'Error: %s in %s when calling method %s with params %s' \
                  % (str(e), self.__class__, method, str(params))
            import traceback
            traceback.print_exc()
        return ('',)

    
class XMLRPCProxy:
    
    def __init__(self, uri):
        # delegate
        self.server = xmlrpclib.Server(uri, transport=None,
                                       encoding=None, verbose=0)
    
    def __request(self, methodname, params):
        response = self.server._ServerProxy__request(methodname, tuple(map(wrap_argument,
                                                                           params)))
##        if type(response) in SUSPECT_TYPES or len(response) == 1:
        if isinstance(response, xmlrpclib.Binary) or len(response) == 1:
            return unwrap_argument(response)
        else:
            return tuple(map(unwrap_argument, response))

    def __getattr__(self, name):
        return xmlrpclib._Method(self.__request, name)

class _XMLRPCRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
    """ class for request handling on XMLRPC servers."""

    def dumps(self, method, response) :
        return xmlrpclib.dumps(response, methodresponse=1)

    def loads(self, data) :
        params, method = xmlrpclib.loads(data)
        return method, params

    def log_message(self,format,*args):
        pass

    def do_POST(self):
        try:
            # get arguments
            data = self.rfile.read(int(self.headers["content-length"]))
            method, params = self.loads(data)
            # generate response
            try:
                response = self.call(method,
                                     tuple(map(unwrap_argument,params)))
                if type(response) != type(()):
                    
                    response = (wrap_argument(response),)
            except:
                # report exception back to server
                import sys
                response = xmlrpclib.dumps(xmlrpclib.Fault(1, "%s:%s" % (sys.exc_type, sys.exc_value)))
                status = 500
                import traceback
                traceback.print_exc()
            else:
                response = self.dumps(method,
                                      tuple(map(wrap_argument,response)))
                status = 200
        except:
            import sys
            print 'internal error', sys.exc_type, sys.exc_value
            # internal error, report as HTTP server error
            self.send_response(500)
            self.end_headers()
        else:
            # got a valid response
            self.send_response(status)
            self.send_header("Content-type", "text/xml")
            self.send_header("Content-length", str(len(response)))
            self.end_headers()
            self.wfile.write(response)

            # shut down the connection (from Skip Montanaro)
            self.wfile.flush()
            self.connection.shutdown(1)



from types import StringType,UnicodeType
try:
    SUSPECT_TYPES = [StringType,UnicodeType]
    SUSPECT_TYPES.append(UnicodeType)
except:
    SUSPECT_TYPES = [StringType,]

import re
ASCII_RE = re.compile('[^\\s\x20-\x7E]')

def wrap_argument(arg):
    if type(arg) in SUSPECT_TYPES and ASCII_RE.search(arg):
        return xmlrpclib.Binary(arg)
    return arg

def unwrap_argument(arg):
    if isinstance(arg, xmlrpclib.Binary):
        return arg.data
    return arg


