#!/usr/bin/env python

import sys
import SimpleXMLRPCServer
import logging
import xmlrpclib
import re
import threading
import commands
from ipalib import api
from ipa_server import conn
from ipa_server.servercore import context
import ipalib.load_plugins
from ipalib.util import xmlrpc_unmarshal
import traceback

PORT=8888

class StoppableXMLRPCServer(SimpleXMLRPCServer.SimpleXMLRPCServer):
    """Override of TIME_WAIT"""
    allow_reuse_address = True

    def serve_forever(self):
        self.stop = False
        while not self.stop:
          self.handle_request()

class LoggingSimpleXMLRPCRequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
    """Overides the default SimpleXMLRPCRequestHander to support logging.
       Logs client IP and the XML request and response.
    """

    def parse(self, given):
        """Convert the incoming arguments into the format IPA expects"""
        args = []
        kw = {}
        for g in given:
            kw[g] = unicode(given[g])
        return (args, kw)

    def _dispatch(self, method, params):
        """Dispatches the XML-RPC method.

        Methods beginning with an '_' are considered private and will
        not be called.
        """

        # this is fine for our test server
        uid = commands.getoutput('/usr/bin/id -u')
        krbccache = "FILE:/tmp/krb5cc_" + uid

        func = None
        try:
            # FIXME: don't hardcode host and port
            context.conn = conn.IPAConn("localhost", 389, krbccache)
            try:
                # check to see if a matching function has been registered
                func = funcs[method]
            except KeyError:
                raise Exception('method "%s" is not supported' % method)
            (args, kw) = xmlrpc_unmarshal(*params)
            return func(*args, **kw)
        finally:
            # Clean up any per-request data and connections
#            for k in context.__dict__.keys():
#                del context.__dict__[k]
            pass

    def _marshaled_dispatch(self, data, dispatch_method = None):
        try:
            params, method = xmlrpclib.loads(data)

            # generate response
            if dispatch_method is not None:
                response = dispatch_method(method, params)
            else:
                response = self._dispatch(method, params)
            # wrap response in a singleton tuple
            response = (response,)
            response = xmlrpclib.dumps(response, methodresponse=1)
        except:
            # report exception back to client. This is needed to report
            # tracebacks found in server code.
            e_class, e = sys.exc_info()[:2]
            # FIXME, need to get this number from somewhere...
            faultCode = getattr(e_class,'faultCode',1)
            tb_str = ''.join(traceback.format_exception(*sys.exc_info()))
            faultString = tb_str
            response = xmlrpclib.dumps(xmlrpclib.Fault(faultCode, faultString))

        return response

    def do_POST(self):
        clientIP, port = self.client_address
	# Log client IP and Port
        logger.info('Client IP: %s - Port: %s' % (clientIP, port))
        try:
            # get arguments
            data = self.rfile.read(int(self.headers["content-length"]))

            # unmarshal the XML data
            params, method = xmlrpclib.loads(data)

            # Log client request
	    logger.info('Client request: \n%s\n' % data)

            response = self._marshaled_dispatch(
                    data, getattr(self, '_dispatch', None))

	    # Log server response
            logger.info('Server response: \n%s\n' % response)
	except Exception, e:
            # This should only happen if the module is buggy
            # internal error, report as HTTP server error
            print e
            self.send_response(500)
            self.end_headers()
        else:
            # got a valid XML-RPC response
            self.send_response(200)
            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
            self.wfile.flush()
            self.connection.shutdown(1)

# Set up our logger
logger = logging.getLogger('xmlrpcserver')
hdlr = logging.FileHandler('xmlrpcserver.log')
formatter = logging.Formatter("%(asctime)s  %(levelname)s  %(message)s")
hdlr.setFormatter(formatter)
logger.addHandler(hdlr)
logger.setLevel(logging.INFO)

# Set up the server
XMLRPCServer = StoppableXMLRPCServer(("",PORT), LoggingSimpleXMLRPCRequestHandler)

XMLRPCServer.register_introspection_functions()

# Get and register all the methods
api.env.server_context = True
api.finalize()
for cmd in api.Method:
    logger.info("registering %s" % cmd)
    XMLRPCServer.register_function(api.Method[cmd], cmd)

funcs = XMLRPCServer.funcs

print "Listening on port %d" % PORT
try:
    XMLRPCServer.serve_forever()
except KeyboardInterrupt:
    XMLRPCServer.server_close()
    print "Server shutdown."
