Index: fedd/fedd_client.py
===================================================================
--- fedd/fedd_client.py	(revision cfabc4029587360a09aa12f17fb3b457c2e3f18c)
+++ fedd/fedd_client.py	(revision f8b118eb2c0ca5d221a4f88d13d1c780602d1584)
@@ -20,6 +20,5 @@
 import xmlrpclib
 
-from fedd_util import fedid, fedd_ssl_context, pack_soap, unpack_soap, \
-	pack_id, unpack_id, encapsulate_binaries, decapsulate_binaries, \
+from fedd_util import fedid, fedd_ssl_context, pack_id, unpack_id, \
 	service_caller
 from service_error import *
Index: fedd/fedd_util.py
===================================================================
--- fedd/fedd_util.py	(revision cfabc4029587360a09aa12f17fb3b457c2e3f18c)
+++ fedd/fedd_util.py	(revision f8b118eb2c0ca5d221a4f88d13d1c780602d1584)
@@ -174,5 +174,5 @@
     creating dictionaries to be converted to messages.
     """
-    if isinstance(id, type(fedid())): return { 'fedid': id }
+    if isinstance(id, fedid): return { 'fedid': id }
     elif id.startswith("http:") or id.startswith("https:"): return { 'uri': id }
     else: return { 'localname': id}
@@ -180,145 +180,10 @@
 def unpack_id(id):
     """return id as a type determined by the key"""
-    if id.has_key("fedid"): return fedid(id["fedid"])
-    else:
-	for k in ("localname", "uri", "kerberosUsername"):
-	    if id.has_key(k): return id[k]
+    for k in ("localname", "fedid", "uri", "kerberosUsername"):
+	if id.has_key(k): return id[k]
     return None
 
-def pack_soap(container, name, contents):
-    """
-    Convert the dictionary in contents into a tree of ZSI classes.
-
-    The holder classes are constructed from factories in container and assigned
-    to either the element or attribute name.  This is used to recursively
-    create the SOAP message.
-    """
-    if getattr(contents, "__iter__", None) != None:
-	attr =getattr(container, "new_%s" % name, None)
-	if attr: obj = attr()
-	else:
-	    raise TypeError("%s does not have a new_%s attribute" % \
-		    (container, name))
-	for e, v in contents.iteritems():
-	    assign = getattr(obj, "set_element_%s" % e, None) or \
-		    getattr(obj, "set_attribute_%s" % e, None)
-	    if isinstance(v, type(dict())):
-		assign(pack_soap(obj, e, v))
-	    elif getattr(v, "__iter__", None) != None:
-		assign([ pack_soap(obj, e, val ) for val in v])
-	    elif getattr(v, "pack_soap", None) != None:
-		assign(v.pack_soap())
-	    else:
-		assign(v)
-	return obj
-    else: return contents
-
-def unpack_soap(element):
-    """
-    Convert a tree of ZSI SOAP classes intro a hash.  The inverse of pack_soap
-
-    Elements or elements that are empty are ignored.
-    """
-    methods = [ m for m in dir(element) \
-	    if m.startswith("get_element") or m.startswith("get_attribute")]
-    if len(methods) > 0:
-	rv = { }
-	for m in methods:
-	    if m.startswith("get_element_"): n = m.replace("get_element_","",1)
-	    else: n = m.replace("get_attribute_", "", 1)
-	    sub = getattr(element, m)()
-	    if sub != None:
-		if isinstance(sub, basestring):
-		    rv[n] = sub
-		elif getattr(sub, "__iter__", None) != None:
-		    if len(sub) > 0: rv[n] = [unpack_soap(e) for e in sub]
-		else:
-		    rv[n] = unpack_soap(sub)
-	return rv
-    else: 
-	return element
-def apply_to_tags(e, map):
-    """
-    Map is an iterable of ordered pairs (tuples) that map a key to a function.
-    This function walks the given message and replaces any object with a key in
-    the map with the result of applying that function to the object.
-    """
-    dict_type = type(dict())
-    list_type = type(list())
-    str_type = type(str())
-
-    if isinstance(e, dict_type):
-	for k in e.keys():
-	    for tag, fcn in map:
-		if k == tag:
-		    if isinstance(e[k], list_type):
-			e[k] = [ fcn(b) for b in e[k]]
-		    else:
-			e[k] = fcn(e[k])
-		elif isinstance(e[k], dict_type):
-		    apply_to_tags(e[k], map)
-		elif isinstance(e[k], list_type):
-		    for ee in e[k]:
-			apply_to_tags(ee, map)
-    # Other types end the recursion - they should be leaves
-    return e
-
-# These are all just specializations of apply_to_tags
-def fedids_to_obj(e, tags=('fedid',)):
-    """
-    Turn the fedids in a message that are encoded as bitstrings into fedid
-    objects.
-    """
-    map = [ (t, lambda x: fedid(bits=x)) for t in tags]
-    return apply_to_tags(e, map)
-
-def encapsulate_binaries(e, tags):
-    """Walk through a message and encapsulate any dictionary entries in 
-    tags into a binary object."""
-
-    def to_binary(o):
-	pack = getattr(o, 'pack_xmlrpc', None)
-	if callable(pack): return Binary(pack())
-	else: return Binary(o)
-
-    map = [ (t, to_binary) for t in tags]
-    return apply_to_tags(e, map)
-
-def decapsulate_binaries(e, tags):
-    """Walk through a message and encapsulate any dictionary entries in 
-    tags into a binary object."""
-
-    map = [ (t, lambda x: x.data) for t in tags]
-    return apply_to_tags(e, map)
-#end of tag specializations
-
-def strip_unicode(obj):
-    """Walk through a message and convert all strings to non-unicode strings"""
-    if isinstance(obj, dict):
-	for k in obj.keys():
-	    obj[k] = strip_unicode(obj[k])
-	return obj
-    elif isinstance(obj, basestring):
-	return str(obj)
-    elif getattr(obj, "__iter__", None):
-	return [ strip_unicode(x) for x in obj]
-    else:
-	return obj
-
-def make_unicode(obj):
-    """Walk through a message and convert all strings to unicode"""
-    if isinstance(obj, dict):
-	for k in obj.keys():
-	    obj[k] = make_unicode(obj[k])
-	return obj
-    elif isinstance(obj, basestring):
-	return unicode(obj)
-    elif getattr(obj, "__iter__", None):
-	return [ make_unicode(x) for x in obj]
-    else:
-	return obj
-
-
-def generate_fedid(subj, bits=2048, log=None, dir=None, trace=sys.stderr):
+def generate_fedid(subj, bits=2048, log=None, dir=None, trace=sys.stderr,
+	ssl_prog="/usr/bin/openssl"):
     """
     Create a new certificate and derive a fedid from it.
@@ -336,5 +201,5 @@
 		    suffix=".pem")
 
-	    cmd = ["/usr/bin/openssl", "req", "-text", "-newkey", 
+	    cmd = [ssl_prog, "req", "-text", "-newkey", 
 		    "rsa:%d" % bits, "-keyout", keypath,  "-nodes", 
 		    "-subj", "/CN=%s" % subj, "-x509", "-days", "30", 
@@ -367,6 +232,150 @@
 	if certpath: os.remove(certpath)
 
-
-class soap_handler:
+# Used by the remote_service_base class.
+def to_binary(o):
+    """
+    A function that converts an object into an xmlrpclib.Binary using
+    either its internal packing method, or the standard Binary constructor.
+    """
+    pack = getattr(o, 'pack_xmlrpc', None)
+    if callable(pack): return Binary(pack())
+    else: return Binary(o)
+
+# Classes that encapsulate the process of making and dealing with requests to
+# WSDL-generated and XMLRPC remote accesses.  
+
+class remote_service_base:
+    """
+    This invisible base class encapsulates the functions used to massage the
+    dictionaries used to pass parameters into and out of the RPC formats.  It's
+    mostly a container for the static methods to do that work, but defines some
+    maps sued by sub classes on apply_to_tags
+    """
+    # A map used to convert fedid fields to fedid objects (when the field is
+    # already a string)
+    fedid_to_object = ( ('fedid', lambda x: fedid(bits=x)),)
+    # A map used by apply_to_tags to convert fedids from xmlrpclib.Binary
+    # objects to fedid objects in one sweep.
+    decap_fedids = (('fedid', lambda x: fedid(bits=x.data)),)
+    # A map used to encapsulate fedids into xmlrpclib.Binary objects
+    encap_fedids = (('fedid', to_binary),)
+
+    @staticmethod
+    def pack_soap(container, name, contents):
+	"""
+	Convert the dictionary in contents into a tree of ZSI classes.
+
+	The holder classes are constructed from factories in container and
+	assigned to either the element or attribute name.  This is used to
+	recursively create the SOAP message.
+	"""
+	if getattr(contents, "__iter__", None) != None:
+	    attr =getattr(container, "new_%s" % name, None)
+	    if attr: obj = attr()
+	    else:
+		raise TypeError("%s does not have a new_%s attribute" % \
+			(container, name))
+	    for e, v in contents.iteritems():
+		assign = getattr(obj, "set_element_%s" % e, None) or \
+			getattr(obj, "set_attribute_%s" % e, None)
+		if isinstance(v, type(dict())):
+		    assign(remote_service_base.pack_soap(obj, e, v))
+		elif getattr(v, "__iter__", None) != None:
+		    assign([ remote_service_base.pack_soap(obj, e, val ) \
+			    for val in v])
+		elif getattr(v, "pack_soap", None) != None:
+		    assign(v.pack_soap())
+		else:
+		    assign(v)
+	    return obj
+	else: return contents
+
+    @staticmethod
+    def unpack_soap(element):
+	"""
+	Convert a tree of ZSI SOAP classes intro a hash.  The inverse of
+	pack_soap
+
+	Elements or elements that are empty are ignored.
+	"""
+	methods = [ m for m in dir(element) \
+		if m.startswith("get_element") or m.startswith("get_attribute")]
+	if len(methods) > 0:
+	    rv = { }
+	    for m in methods:
+		if m.startswith("get_element_"):
+		    n = m.replace("get_element_","",1)
+		else:
+		    n = m.replace("get_attribute_", "", 1)
+		sub = getattr(element, m)()
+		if sub != None:
+		    if isinstance(sub, basestring):
+			rv[n] = sub
+		    elif getattr(sub, "__iter__", None) != None:
+			if len(sub) > 0: rv[n] = \
+				[remote_service_base.unpack_soap(e) \
+				    for e in sub]
+		    else:
+			rv[n] = remote_service_base.unpack_soap(sub)
+	    return rv
+	else: 
+	    return element
+
+    @staticmethod
+    def apply_to_tags(e, map):
+	"""
+	Map is an iterable of ordered pairs (tuples) that map a key to a
+	function.
+	This function walks the given message and replaces any object with a
+	key in the map with the result of applying that function to the object.
+	"""
+	if isinstance(e, dict):
+	    for k in e.keys():
+		for tag, fcn in map:
+		    if k == tag:
+			if isinstance(e[k], list):
+			    e[k] = [ fcn(b) for b in e[k]]
+			else:
+			    e[k] = fcn(e[k])
+		    elif isinstance(e[k], dict):
+			remote_service_base.apply_to_tags(e[k], map)
+		    elif isinstance(e[k], list):
+			for ee in e[k]:
+			    remote_service_base.apply_to_tags(ee, map)
+	# Other types end the recursion - they should be leaves
+	return e
+
+    @staticmethod
+    def strip_unicode(obj):
+	"""Walk through a message and convert all strings to non-unicode
+	strings"""
+	if isinstance(obj, dict):
+	    for k in obj.keys():
+		obj[k] = remote_service_base.strip_unicode(obj[k])
+	    return obj
+	elif isinstance(obj, basestring) and not isinstance(obj, str):
+	    return str(obj)
+	elif getattr(obj, "__iter__", None):
+	    return [ remote_service_base.strip_unicode(x) for x in obj]
+	else:
+	    return obj
+
+    @staticmethod
+    def make_unicode(obj):
+	"""Walk through a message and convert all strings to unicode"""
+	if isinstance(obj, dict):
+	    for k in obj.keys():
+		obj[k] = remote_service_base.make_unicode(obj[k])
+	    return obj
+	elif isinstance(obj, basestring) and not isinstance(obj, unicode):
+	    return unicode(obj)
+	elif getattr(obj, "__iter__", None):
+	    return [ remote_service_base.make_unicode(x) for x in obj]
+	else:
+	    return obj
+
+
+
+class soap_handler(remote_service_base):
     """
     Encapsulate the handler code to unpack and pack SOAP requests and responses
@@ -391,6 +400,9 @@
     def __call__(self, ps, fid):
 	req = ps.Parse(self.typecode)
-
-	msg = self.method(fedids_to_obj(unpack_soap(req)), fid)
+	# Convert the message to a dict with the fedid strings converted to
+	# fedid objects
+	req = self.apply_to_tags(self.unpack_soap(req), self.fedid_to_object)
+
+	msg = self.method(req, fid)
 
 	resp = self.constructor()
@@ -398,5 +410,5 @@
 	if set_element and callable(set_element):
 	    try:
-		set_element(pack_soap(resp, self.body_name, msg))
+		set_element(self.pack_soap(resp, self.body_name, msg))
 		return resp
 	    except (NameError, TypeError):
@@ -405,5 +417,5 @@
 	    return None
 
-class xmlrpc_handler:
+class xmlrpc_handler(remote_service_base):
     """
     Generate the handler code to unpack and pack XMLRPC requests and responses
@@ -422,12 +434,9 @@
 	self.method = method
 	self.body_name = body_name
-	# A map used by apply_to_tags to convert fedids from xmlrpclib.Binary
-	# objects to fedid objects in one sweep.
-	self.decap_fedids = (('fedid', lambda x: fedid(bits=x.data)),)
 
     def __call__(self, params, fid):
 	msg = None
 
-	p = apply_to_tags(params[0], self.decap_fedids)
+	p = self.apply_to_tags(params[0], self.decap_fedids)
 	try:
 	    msg = self.method(p, fid)
@@ -435,10 +444,10 @@
 	    raise Fault(e.code_string(), e.desc)
 	if msg != None:
-	    return make_unicode(encapsulate_binaries(\
-		    { self.body_name: msg }, ('fedid',)))
+	    return self.make_unicode(self.apply_to_tags(\
+		    { self.body_name: msg }, self.encap_fedids))
 	else:
 	    return None
 
-class service_caller:
+class service_caller(remote_service_base):
     def __init__(self, service_name, port_name, locator, request_message, 
 	    request_body_name, tracefile=None):
@@ -454,5 +463,4 @@
 	    trusted_certs=None, context=None, tracefile=None):
 	"""Send an XMLRPC request.  """
-	decap_fedids = (('fedid', lambda x: fedid(bits=x.data)),)
 
 
@@ -473,5 +481,5 @@
 	# the unicode objects converted.  We also convert the url to a
 	# basic string if it isn't one already.
-	r = strip_unicode(copy.deepcopy(req))
+	r = self.strip_unicode(copy.deepcopy(req))
 	url = str(url)
 	
@@ -481,6 +489,6 @@
 	try:
 	    remote_method = getattr(port, self.service_name, None)
-	    resp = remote_method(encapsulate_binaries(\
-		    { self.request_body_name: r}, ('fedid',)))
+	    resp = remote_method(self.apply_to_tags(\
+		    { self.request_body_name: r}, self.encap_fedids))
 	except Fault, f:
 	    raise service_error(None, f.faultString, f.faultCode)
@@ -489,5 +497,5 @@
 		    "Remote XMLRPC Fault: %s" % e)
 
-	return apply_to_tags(resp, decap_fedids) 
+	return self.apply_to_tags(resp, self.decap_fedids) 
 
     def call_soap_service(self, url, req, cert_file=None, cert_pwd=None,
@@ -535,5 +543,5 @@
 		    "Cannot get element setting method for %s" % \
 			    self.request_body_name)
-	set_element(pack_soap(msg, self.request_body_name, req))
+	set_element(self.pack_soap(msg, self.request_body_name, req))
 	try:
 	    resp = remote_method(msg)
@@ -542,5 +550,5 @@
 		    "Bad format message (XMLRPC??): %s" % e)
 	except ZSI.FaultException, e:
-	    ee = unpack_soap(e.fault.detail[0]).get('FeddFaultBody', { })
+	    ee = self.unpack_soap(e.fault.detail[0]).get('FeddFaultBody', { })
 	    if ee:
 		raise service_error(ee['code'], ee['desc'])
@@ -548,5 +556,8 @@
 		raise service_error(service_error.internal,
 			"Unexpected fault body")
-	r = make_unicode(fedids_to_obj(unpack_soap(resp)))
+	# Unpack and convert fedids to objects
+	r = self.apply_to_tags(self.unpack_soap(resp), self.fedid_to_object)
+	#  Make sure all strings are unicode
+	r = self.make_unicode(r)
 	return r
 
