Index: cred_printer/cred_printer/server.py
===================================================================
--- cred_printer/cred_printer/server.py	(revision 02dcba536bb4ec0a836dcc142d06a551653ded6c)
+++ cred_printer/cred_printer/server.py	(revision 02dcba536bb4ec0a836dcc142d06a551653ded6c)
@@ -0,0 +1,169 @@
+#!/usr/local/bin/python
+
+import sys
+
+from BaseHTTPServer import BaseHTTPRequestHandler
+
+from M2Crypto import SSL
+from M2Crypto.SSL.SSLServer import SSLServer
+from SocketServer import TCPServer
+
+from service_error import service_error
+import xmlrpclib
+
+import os.path
+
+import logging
+import traceback
+
+# The SSL server here is based on the implementation described at
+# http://www.xml.com/pub/a/ws/2004/01/20/salz.html
+
+# Turn off the matching of hostname to certificate ID
+SSL.Connection.clientPostConnectionCheck = None
+
+class error_handlers:
+    """
+    Class to encapsulate the debugging and non-debugging error handlers.
+    """
+    def __init__(self, debug):
+	"""
+	Set the error handler
+	"""
+	if debug: self.handle_error = self.handle_error_debug
+	else: self.handle_error = self.handle_error_standard
+
+    def handle_error_debug(self, request=None, client_address=None):
+	"""
+	Debugging error handler.  Prints a stack trace.
+	"""
+	print '-'*40
+	traceback.print_exc()
+	print '-'*40
+
+
+    def handle_error_standard(self, request=None, address=None):
+	"""
+	Print standard error output, suitable for human consumption.
+	"""
+	if request or address:
+	    self.log.warn("[credd] Error on incoming connection: %s %s" % \
+		    (request or "", address or ""))
+	else:
+	    self.log.warn("[credd] Error on incoming connection " + \
+		    "(Likely SSL error)")
+
+
+
+class server(SSLServer, error_handlers):
+    """
+    Overloads a TCPServer to hold an XMLRPC implementation.  The implementation
+    needs to inclide an xmlrpc_services dist that maps from method name to
+    callable mamber that takes a single parameter (usually a list or dict) and
+    a certificate.
+    """
+    def __init__(self, ME, handler, ssl_ctx, impl, debug=False):
+	"""
+	Create an SSL server that handles the transport in handler using the
+	credentials in ssl_ctx, and interfacing to the implementation of fedd
+	services in fedd.  ME is the host port pair on which to bind.
+	"""
+	SSLServer.__init__(self, ME, handler, ssl_ctx)
+	error_handlers.__init__(self, debug)
+	self.impl = impl
+	self.xmlrpc_methods = impl.xmlrpc_services
+	self.log = logging.getLogger("creds")
+
+class simpleServer(TCPServer, error_handlers):
+    """
+    Presents the same interface as server to a threaded TCP server.  Allows a
+    binding of the same implementation and handler classes to
+    unencrypted/authenticated connections.
+    """
+    def __init__(self, ME, handler, impl, debug=False):
+	TCPServer.__init__(self, ME, handler)
+	error_handlers.__init__(self, debug)
+	self.impl = impl
+	self.xmlrpc_methods = impl.xmlrpc_services
+	self.log = logging.getLogger("creds")
+
+
+class xmlrpc_handler(BaseHTTPRequestHandler):
+    """
+    Standard connection between XMLRPC and the services in impl.
+
+    Much of this is boilerplate from 
+    http://www.xml.com/pub/a/ws/2004/01/20/salz.html
+    """
+    server_version = "credd/0.1 " + BaseHTTPRequestHandler.server_version
+
+    def send_xml(self, text, code=200):
+	"""Send an XML document as reply"""
+	self.send_response(code)
+	self.send_header('Content-type', 'text/xml; charset="utf-8"')
+	self.send_header('Content-Length', str(len(text)))
+	self.end_headers()
+	self.wfile.write(text)
+	self.wfile.flush()
+	# Make sure to close the socket when we're done
+	self.request.close()
+	#self.request.socket.close()
+
+    def do_POST(self):
+	"""Treat an HTTP POST request as an XMLRPC service call"""
+	# NB: XMLRPC faults are not HTTP errors, so the code is always 200,
+	# unless an HTTP error occurs, which we don't handle.
+
+	resp = None
+	data = None
+	method = None
+	cl = int(self.headers['content-length'])
+	data = self.rfile.read(cl)
+
+	try:
+	    params, method = xmlrpclib.loads(data)
+	except xmlrpclib.ResponseError:
+	    data = xmlrpclib.dumps(xmlrpclib.Fault("Client", 
+		"Malformed request"), methodresponse=True)
+
+	# Simple servers don't have peer certificates.
+	if getattr(self.request, 'get_peer_cert', None):
+	    fid = self.request.get_peer_cert()
+	else:
+	    fid = None
+
+
+	if method != None:
+	    try:
+		resp = self.xmlrpc_dispatch(method, params, fid)
+		data = xmlrpclib.dumps((resp,), encoding='UTF-8', 
+			methodresponse=True)
+	    except xmlrpclib.Fault, f:
+		data = xmlrpclib.dumps(f, methodresponse=True)
+		resp = None
+
+	self.send_xml(data)
+
+    def log_request(self, code=0, size=0):
+	"""
+	Log request to the fedd logger
+	"""
+	self.server.log.info("Successful XMLRPC request code %d" % code)
+
+
+    def xmlrpc_dispatch(self, method, req, fid):
+	"""
+	The connection to the implementation, using the  method maps
+
+	The implementation provides a mapping from XMLRPC method name to the
+	method in the implementation that provides the service.
+	"""
+	if self.server.xmlrpc_methods.has_key(method):
+	    try:
+		return self.server.xmlrpc_methods[method](req, fid)
+	    except service_error, e:
+		raise xmlrpclib.Fault(e.code_string(), e.desc)
+
+	else:
+	    raise xmlrpclib.Fault(100, "Unknown method: %s" % method)
+
