bundled SOAPpy-0.12.5
authorForrest Voight <forrest@forre.st>
Wed, 3 Aug 2011 19:29:16 +0000 (15:29 -0400)
committerForrest Voight <forrest@forre.st>
Wed, 3 Aug 2011 19:29:16 +0000 (15:29 -0400)
15 files changed:
SOAPpy/Client.py [new file with mode: 0644]
SOAPpy/Config.py [new file with mode: 0644]
SOAPpy/Errors.py [new file with mode: 0644]
SOAPpy/GSIServer.py [new file with mode: 0644]
SOAPpy/NS.py [new file with mode: 0644]
SOAPpy/Parser.py [new file with mode: 0644]
SOAPpy/SOAP.py [new file with mode: 0644]
SOAPpy/SOAPBuilder.py [new file with mode: 0755]
SOAPpy/Server.py [new file with mode: 0644]
SOAPpy/Types.py [new file with mode: 0644]
SOAPpy/URLopener.py [new file with mode: 0644]
SOAPpy/Utilities.py [new file with mode: 0644]
SOAPpy/WSDL.py [new file with mode: 0644]
SOAPpy/__init__.py [new file with mode: 0644]
SOAPpy/version.py [new file with mode: 0644]

diff --git a/SOAPpy/Client.py b/SOAPpy/Client.py
new file mode 100644 (file)
index 0000000..3749879
--- /dev/null
@@ -0,0 +1,565 @@
+from __future__ import nested_scopes
+
+"""
+################################################################################
+#
+# SOAPpy - Cayce Ullman       (cayce@actzero.com)
+#          Brian Matthews     (blm@actzero.com)
+#          Gregory Warnes     (Gregory.R.Warnes@Pfizer.com)
+#          Christopher Blunck (blunck@gst.com)
+#
+################################################################################
+# Copyright (c) 2003, Pfizer
+# Copyright (c) 2001, Cayce Ullman.
+# Copyright (c) 2001, Brian Matthews.
+#
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# Redistributions of source code must retain the above copyright notice, this
+# list of conditions and the following disclaimer.
+#
+# Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# Neither the name of actzero, inc. nor the names of its contributors may
+# be used to endorse or promote products derived from this software without
+# specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
+# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+################################################################################
+"""
+ident = '$Id: Client.py 1496 2010-03-04 23:46:17Z pooryorick $'
+
+from version import __version__
+
+#import xml.sax
+import urllib
+from types import *
+import re
+import base64
+import socket, httplib
+from httplib import HTTPConnection, HTTP
+import Cookie
+
+# SOAPpy modules
+from Errors      import *
+from Config      import Config
+from Parser      import parseSOAPRPC
+from SOAPBuilder import buildSOAP
+from Utilities   import *
+from Types       import faultType, simplify
+
+################################################################################
+# Client
+################################################################################
+
+
+def SOAPUserAgent():
+    return "SOAPpy " + __version__ + " (pywebsvcs.sf.net)"
+
+
+class SOAPAddress:
+    def __init__(self, url, config = Config):
+        proto, uri = urllib.splittype(url)
+
+        # apply some defaults
+        if uri[0:2] != '//':
+            if proto != None:
+                uri = proto + ':' + uri
+
+            uri = '//' + uri
+            proto = 'http'
+
+        host, path = urllib.splithost(uri)
+
+        try:
+            int(host)
+            host = 'localhost:' + host
+        except:
+            pass
+
+        if not path:
+            path = '/'
+
+        if proto not in ('http', 'https', 'httpg'):
+            raise IOError, "unsupported SOAP protocol"
+        if proto == 'httpg' and not config.GSIclient:
+            raise AttributeError, \
+                  "GSI client not supported by this Python installation"
+        if proto == 'https' and not config.SSLclient:
+            raise AttributeError, \
+                "SSL client not supported by this Python installation"
+
+        self.user,host = urllib.splituser(host)
+        self.proto = proto
+        self.host = host
+        self.path = path
+
+    def __str__(self):
+        return "%(proto)s://%(host)s%(path)s" % self.__dict__
+
+    __repr__ = __str__
+
+class SOAPTimeoutError(socket.timeout):
+    '''This exception is raised when a timeout occurs in SOAP operations'''
+    pass
+
+class HTTPConnectionWithTimeout(HTTPConnection):
+    '''Extend HTTPConnection for timeout support'''
+
+    def __init__(self, host, port=None, strict=None, timeout=None):
+        HTTPConnection.__init__(self, host, port, strict)
+        self._timeout = timeout
+
+    def connect(self):
+        HTTPConnection.connect(self)
+        if self.sock and self._timeout:
+            self.sock.settimeout(self._timeout) 
+
+
+class HTTPWithTimeout(HTTP):
+
+    _connection_class = HTTPConnectionWithTimeout
+
+    ## this __init__ copied from httplib.HTML class
+    def __init__(self, host='', port=None, strict=None, timeout=None):
+        "Provide a default host, since the superclass requires one."
+
+        # some joker passed 0 explicitly, meaning default port
+        if port == 0:
+            port = None
+
+        # Note that we may pass an empty string as the host; this will throw
+        # an error when we attempt to connect. Presumably, the client code
+        # will call connect before then, with a proper host.
+        self._setup(self._connection_class(host, port, strict, timeout))
+
+class HTTPTransport:
+            
+
+    def __init__(self):
+        self.cookies = Cookie.SimpleCookie();
+
+    def getNS(self, original_namespace, data):
+        """Extract the (possibly extended) namespace from the returned
+        SOAP message."""
+
+        if type(original_namespace) == StringType:
+            pattern="xmlns:\w+=['\"](" + original_namespace + "[^'\"]*)['\"]"
+            match = re.search(pattern, data)
+            if match:
+                return match.group(1)
+            else:
+                return original_namespace
+        else:
+            return original_namespace
+    
+    def __addcookies(self, r):
+        '''Add cookies from self.cookies to request r
+        '''
+        for cname, morsel in self.cookies.items():
+            attrs = []
+            value = morsel.get('version', '')
+            if value != '' and value != '0':
+                attrs.append('$Version=%s' % value)
+            attrs.append('%s=%s' % (cname, morsel.coded_value))
+            value = morsel.get('path')
+            if value:
+                attrs.append('$Path=%s' % value)
+            value = morsel.get('domain')
+            if value:
+                attrs.append('$Domain=%s' % value)
+            r.putheader('Cookie', "; ".join(attrs))
+    
+    def call(self, addr, data, namespace, soapaction = None, encoding = None,
+        http_proxy = None, config = Config, timeout=None):
+
+        if not isinstance(addr, SOAPAddress):
+            addr = SOAPAddress(addr, config)
+
+        # Build a request
+        if http_proxy:
+            real_addr = http_proxy
+            real_path = addr.proto + "://" + addr.host + addr.path
+        else:
+            real_addr = addr.host
+            real_path = addr.path
+
+        if addr.proto == 'httpg':
+            from pyGlobus.io import GSIHTTP
+            r = GSIHTTP(real_addr, tcpAttr = config.tcpAttr)
+        elif addr.proto == 'https':
+            r = httplib.HTTPS(real_addr, key_file=config.SSL.key_file, cert_file=config.SSL.cert_file)
+        else:
+            r = HTTPWithTimeout(real_addr, timeout=timeout)
+
+        r.putrequest("POST", real_path)
+
+        r.putheader("Host", addr.host)
+        r.putheader("User-agent", SOAPUserAgent())
+        t = 'text/xml';
+        if encoding != None:
+            t += '; charset=%s' % encoding
+        r.putheader("Content-type", t)
+        r.putheader("Content-length", str(len(data)))
+        self.__addcookies(r);
+        
+        # if user is not a user:passwd format
+        #    we'll receive a failure from the server. . .I guess (??)
+        if addr.user != None:
+            val = base64.encodestring(addr.user) 
+            r.putheader('Authorization','Basic ' + val.replace('\012',''))
+
+        # This fixes sending either "" or "None"
+        if soapaction == None or len(soapaction) == 0:
+            r.putheader("SOAPAction", "")
+        else:
+            r.putheader("SOAPAction", '"%s"' % soapaction)
+
+        if config.dumpHeadersOut:
+            s = 'Outgoing HTTP headers'
+            debugHeader(s)
+            print "POST %s %s" % (real_path, r._http_vsn_str)
+            print "Host:", addr.host
+            print "User-agent: SOAPpy " + __version__ + " (http://pywebsvcs.sf.net)"
+            print "Content-type:", t
+            print "Content-length:", len(data)
+            print 'SOAPAction: "%s"' % soapaction
+            debugFooter(s)
+
+        r.endheaders()
+
+        if config.dumpSOAPOut:
+            s = 'Outgoing SOAP'
+            debugHeader(s)
+            print data,
+            if data[-1] != '\n':
+                print
+            debugFooter(s)
+
+        # send the payload
+        r.send(data)
+
+        # read response line
+        code, msg, headers = r.getreply()
+
+        self.cookies = Cookie.SimpleCookie();
+        if headers:
+            content_type = headers.get("content-type","text/xml")
+            content_length = headers.get("Content-length")
+
+            for cookie in headers.getallmatchingheaders("Set-Cookie"):
+                self.cookies.load(cookie);
+
+        else:
+            content_type=None
+            content_length=None
+
+        # work around OC4J bug which does '<len>, <len>' for some reaason
+        if content_length:
+            comma=content_length.find(',')
+            if comma>0:
+                content_length = content_length[:comma]
+
+        # attempt to extract integer message size
+        try:
+            message_len = int(content_length)
+        except:
+            message_len = -1
+            
+        if message_len < 0:
+            # Content-Length missing or invalid; just read the whole socket
+            # This won't work with HTTP/1.1 chunked encoding
+            data = r.getfile().read()
+            message_len = len(data)
+        else:
+            data = r.getfile().read(message_len)
+
+        if(config.debug):
+            print "code=",code
+            print "msg=", msg
+            print "headers=", headers
+            print "content-type=", content_type
+            print "data=", data
+                
+        if config.dumpHeadersIn:
+            s = 'Incoming HTTP headers'
+            debugHeader(s)
+            if headers.headers:
+                print "HTTP/1.? %d %s" % (code, msg)
+                print "\n".join(map (lambda x: x.strip(), headers.headers))
+            else:
+                print "HTTP/0.9 %d %s" % (code, msg)
+            debugFooter(s)
+
+        def startswith(string, val):
+            return string[0:len(val)] == val
+        
+        if code == 500 and not \
+               ( startswith(content_type, "text/xml") and message_len > 0 ):
+            raise HTTPError(code, msg)
+
+        if config.dumpSOAPIn:
+            s = 'Incoming SOAP'
+            debugHeader(s)
+            print data,
+            if (len(data)>0) and (data[-1] != '\n'):
+                print
+            debugFooter(s)
+
+        if code not in (200, 500):
+            raise HTTPError(code, msg)
+
+
+        # get the new namespace
+        if namespace is None:
+            new_ns = None
+        else:
+            new_ns = self.getNS(namespace, data)
+        
+        # return response payload
+        return data, new_ns
+
+################################################################################
+# SOAP Proxy
+################################################################################
+class SOAPProxy:
+    def __init__(self, proxy, namespace = None, soapaction = None,
+                 header = None, methodattrs = None, transport = HTTPTransport,
+                 encoding = 'UTF-8', throw_faults = 1, unwrap_results = None,
+                 http_proxy=None, config = Config, noroot = 0,
+                 simplify_objects=None, timeout=None):
+
+        # Test the encoding, raising an exception if it's not known
+        if encoding != None:
+            ''.encode(encoding)
+
+        # get default values for unwrap_results and simplify_objects
+        # from config
+        if unwrap_results is None:
+            self.unwrap_results=config.unwrap_results
+        else:
+            self.unwrap_results=unwrap_results
+
+        if simplify_objects is None:
+            self.simplify_objects=config.simplify_objects
+        else:
+            self.simplify_objects=simplify_objects
+
+        self.proxy          = SOAPAddress(proxy, config)
+        self.namespace      = namespace
+        self.soapaction     = soapaction
+        self.header         = header
+        self.methodattrs    = methodattrs
+        self.transport      = transport()
+        self.encoding       = encoding
+        self.throw_faults   = throw_faults
+        self.http_proxy     = http_proxy
+        self.config         = config
+        self.noroot         = noroot
+        self.timeout        = timeout
+
+        # GSI Additions
+        if hasattr(config, "channel_mode") and \
+               hasattr(config, "delegation_mode"):
+            self.channel_mode = config.channel_mode
+            self.delegation_mode = config.delegation_mode
+        #end GSI Additions
+        
+    def invoke(self, method, args):
+        return self.__call(method, args, {})
+        
+    def __call(self, name, args, kw, ns = None, sa = None, hd = None,
+        ma = None):
+
+        ns = ns or self.namespace
+        ma = ma or self.methodattrs
+
+        if sa: # Get soapaction
+            if type(sa) == TupleType:
+                sa = sa[0]
+        else:
+            if self.soapaction:
+                sa = self.soapaction
+            else:
+                sa = name
+                
+        if hd: # Get header
+            if type(hd) == TupleType:
+                hd = hd[0]
+        else:
+            hd = self.header
+
+        hd = hd or self.header
+
+        if ma: # Get methodattrs
+            if type(ma) == TupleType: ma = ma[0]
+        else:
+            ma = self.methodattrs
+        ma = ma or self.methodattrs
+
+        m = buildSOAP(args = args, kw = kw, method = name, namespace = ns,
+            header = hd, methodattrs = ma, encoding = self.encoding,
+            config = self.config, noroot = self.noroot)
+
+
+        call_retry = 0
+        try:
+            r, self.namespace = self.transport.call(self.proxy, m, ns, sa,
+                                                    encoding = self.encoding,
+                                                    http_proxy = self.http_proxy,
+                                                    config = self.config,
+                                                    timeout = self.timeout)
+
+        except socket.timeout:
+            raise SOAPTimeoutError
+
+        except Exception, ex:
+            #
+            # Call failed.
+            #
+            # See if we have a fault handling vector installed in our
+            # config. If we do, invoke it. If it returns a true value,
+            # retry the call. 
+            #
+            # In any circumstance other than the fault handler returning
+            # true, reraise the exception. This keeps the semantics of this
+            # code the same as without the faultHandler code.
+            #
+
+            if hasattr(self.config, "faultHandler"):
+                if callable(self.config.faultHandler):
+                    call_retry = self.config.faultHandler(self.proxy, ex)
+                    if not call_retry:
+                        raise
+                else:
+                    raise
+            else:
+                raise
+
+        if call_retry:
+            try:
+                r, self.namespace = self.transport.call(self.proxy, m, ns, sa,
+                                                        encoding = self.encoding,
+                                                        http_proxy = self.http_proxy,
+                                                        config = self.config,
+                                                        timeout = self.timeout)
+            except socket.timeout:
+                raise SOAPTimeoutError
+            
+
+        p, attrs = parseSOAPRPC(r, attrs = 1)
+
+        try:
+            throw_struct = self.throw_faults and \
+                isinstance (p, faultType)
+        except:
+            throw_struct = 0
+
+        if throw_struct:
+            if Config.debug:
+                print p
+            raise p
+
+        # If unwrap_results=1 and there is only element in the struct,
+        # SOAPProxy will assume that this element is the result
+        # and return it rather than the struct containing it.
+        # Otherwise SOAPproxy will return the struct with all the
+        # elements as attributes.
+        if self.unwrap_results:
+            try:
+                count = 0
+                for i in p.__dict__.keys():
+                    if i[0] != "_":  # don't count the private stuff
+                        count += 1
+                        t = getattr(p, i)
+                if count == 1: # Only one piece of data, bubble it up
+                    p = t 
+            except:
+                pass
+
+        # Automatically simplfy SOAP complex types into the
+        # corresponding python types. (structType --> dict,
+        # arrayType --> array, etc.)
+        if self.simplify_objects:
+            p = simplify(p)
+
+        if self.config.returnAllAttrs:
+            return p, attrs
+        return p
+
+    def _callWithBody(self, body):
+        return self.__call(None, body, {})
+
+    def __getattr__(self, name):  # hook to catch method calls
+        if name in ( '__del__', '__getinitargs__', '__getnewargs__',
+           '__getstate__', '__setstate__', '__reduce__', '__reduce_ex__'):
+            raise AttributeError, name
+        return self.__Method(self.__call, name, config = self.config)
+
+    # To handle attribute weirdness
+    class __Method:
+        # Some magic to bind a SOAP method to an RPC server.
+        # Supports "nested" methods (e.g. examples.getStateName) -- concept
+        # borrowed from xmlrpc/soaplib -- www.pythonware.com
+        # Altered (improved?) to let you inline namespaces on a per call
+        # basis ala SOAP::LITE -- www.soaplite.com
+
+        def __init__(self, call, name, ns = None, sa = None, hd = None,
+            ma = None, config = Config):
+
+            self.__call        = call
+            self.__name        = name
+            self.__ns          = ns
+            self.__sa          = sa
+            self.__hd          = hd
+            self.__ma           = ma
+            self.__config       = config
+            return
+
+        def __call__(self, *args, **kw):
+            if self.__name[0] == "_":
+                if self.__name in ["__repr__","__str__"]:
+                    return self.__repr__()
+                else:
+                    return self.__f_call(*args, **kw)
+            else:
+                return self.__r_call(*args, **kw)
+                        
+        def __getattr__(self, name):
+            if name == '__del__':
+                raise AttributeError, name
+            if self.__name[0] == "_":
+                # Don't nest method if it is a directive
+                return self.__class__(self.__call, name, self.__ns,
+                    self.__sa, self.__hd, self.__ma)
+
+            return self.__class__(self.__call, "%s.%s" % (self.__name, name),
+                self.__ns, self.__sa, self.__hd, self.__ma)
+
+        def __f_call(self, *args, **kw):
+            if self.__name == "_ns": self.__ns = args
+            elif self.__name == "_sa": self.__sa = args
+            elif self.__name == "_hd": self.__hd = args
+            elif self.__name == "_ma": self.__ma = args
+            return self
+
+        def __r_call(self, *args, **kw):
+            return self.__call(self.__name, args, kw, self.__ns, self.__sa,
+                self.__hd, self.__ma)
+
+        def __repr__(self):
+            return "<%s at %d>" % (self.__class__, id(self))
diff --git a/SOAPpy/Config.py b/SOAPpy/Config.py
new file mode 100644 (file)
index 0000000..deafa59
--- /dev/null
@@ -0,0 +1,211 @@
+"""
+################################################################################
+# Copyright (c) 2003, Pfizer
+# Copyright (c) 2001, Cayce Ullman.
+# Copyright (c) 2001, Brian Matthews.
+#
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# Redistributions of source code must retain the above copyright notice, this
+# list of conditions and the following disclaimer.
+#
+# Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# Neither the name of actzero, inc. nor the names of its contributors may
+# be used to endorse or promote products derived from this software without
+# specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
+# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+################################################################################
+"""
+
+ident = '$Id: Config.py 1298 2006-11-07 00:54:15Z sanxiyn $'
+from version import __version__
+
+import socket
+from types import *
+
+from NS import NS
+
+################################################################################
+# Configuration class
+################################################################################
+
+
+class SOAPConfig:
+    __readonly = ('SSLserver', 'SSLclient', 'GSIserver', 'GSIclient')
+    class SSLconfig:
+        __slots__ = ('key_file', 'cert_file')
+        key_file = None
+        cert_file = None
+
+    def __init__(self, config = None, **kw):
+        d = self.__dict__
+
+        if config:
+            if not isinstance(config, SOAPConfig):
+                raise AttributeError, \
+                    "initializer must be SOAPConfig instance"
+
+            s = config.__dict__
+
+            for k, v in s.items():
+                if k[0] != '_':
+                    d[k] = v
+        else:
+            # Setting debug also sets returnFaultInfo,
+            # dumpHeadersIn, dumpHeadersOut, dumpSOAPIn, and dumpSOAPOut
+            self.debug = 0
+            self.dumpFaultInfo = 1
+            # Setting namespaceStyle sets typesNamespace, typesNamespaceURI,
+            # schemaNamespace, and schemaNamespaceURI
+            self.namespaceStyle = '1999'
+            self.strictNamespaces = 0
+            self.typed = 1
+            self.buildWithNamespacePrefix = 1
+            self.returnAllAttrs = 0
+
+            # Strict checking of range for floats and doubles
+            self.strict_range = 0
+
+            # Default encoding for dictionary keys
+            self.dict_encoding = 'ascii'
+
+            # New argument name handling mechanism.  See
+            # README.MethodParameterNaming for details
+            self.specialArgs = 1
+
+            # If unwrap_results=1 and there is only element in the struct,
+            # SOAPProxy will assume that this element is the result
+            # and return it rather than the struct containing it.
+            # Otherwise SOAPproxy will return the struct with all the
+            # elements as attributes.
+            self.unwrap_results = 1
+
+            # Automatically convert SOAP complex types, and
+            # (recursively) public contents into the corresponding
+            # python types. (Private subobjects have names that start
+            # with '_'.)
+            #
+            # Conversions:
+            # - faultType    --> raise python exception
+            # - arrayType    --> array
+            # - compoundType --> dictionary
+            #
+            self.simplify_objects = 0
+
+            # Per-class authorization method.  If this is set, before
+            # calling a any class method, the specified authorization
+            # method will be called.  If it returns 1, the method call
+            # will proceed, otherwise the call will throw with an
+            # authorization error.
+            self.authMethod = None
+
+            # Globus Support if pyGlobus.io available
+            try:
+                from pyGlobus import io;
+                d['GSIserver'] = 1
+                d['GSIclient'] = 1
+            except:
+                d['GSIserver'] = 0
+                d['GSIclient'] = 0
+
+
+            # Server SSL support if M2Crypto.SSL available
+            try:
+                from M2Crypto import SSL
+                d['SSLserver'] = 1
+            except:
+                d['SSLserver'] = 0
+
+            # Client SSL support if socket.ssl available
+            try:
+                from socket import ssl
+                d['SSLclient'] = 1
+            except:
+                d['SSLclient'] = 0
+
+            # Cert support
+            if d['SSLclient'] or d['SSLserver']:
+                d['SSL'] = self.SSLconfig()
+
+        for k, v in kw.items():
+            if k[0] != '_':
+                setattr(self, k, v)
+
+    def __setattr__(self, name, value):
+        if name in self.__readonly:
+            raise AttributeError, "readonly configuration setting"
+
+        d = self.__dict__
+
+        if name in ('typesNamespace', 'typesNamespaceURI',
+                    'schemaNamespace', 'schemaNamespaceURI'):
+
+            if name[-3:] == 'URI':
+                base, uri = name[:-3], 1
+            else:
+                base, uri = name, 0
+
+            if type(value) == StringType:
+                if NS.NSMAP.has_key(value):
+                    n = (value, NS.NSMAP[value])
+                elif NS.NSMAP_R.has_key(value):
+                    n = (NS.NSMAP_R[value], value)
+                else:
+                    raise AttributeError, "unknown namespace"
+            elif type(value) in (ListType, TupleType):
+                if uri:
+                    n = (value[1], value[0])
+                else:
+                    n = (value[0], value[1])
+            else:
+                raise AttributeError, "unknown namespace type"
+
+            d[base], d[base + 'URI'] = n
+
+            try:
+                d['namespaceStyle'] = \
+                    NS.STMAP_R[(d['typesNamespace'], d['schemaNamespace'])]
+            except:
+                d['namespaceStyle'] = ''
+
+        elif name == 'namespaceStyle':
+            value = str(value)
+
+            if not NS.STMAP.has_key(value):
+                raise AttributeError, "unknown namespace style"
+
+            d[name] = value
+            n = d['typesNamespace'] = NS.STMAP[value][0]
+            d['typesNamespaceURI'] = NS.NSMAP[n]
+            n = d['schemaNamespace'] = NS.STMAP[value][1]
+            d['schemaNamespaceURI'] = NS.NSMAP[n]
+
+        elif name == 'debug':
+            d[name]                     = \
+                d['returnFaultInfo']    = \
+                d['dumpHeadersIn']      = \
+                d['dumpHeadersOut']     = \
+                d['dumpSOAPIn']         = \
+                d['dumpSOAPOut']        = value
+
+        else:
+            d[name] = value
+
+
+Config = SOAPConfig()
diff --git a/SOAPpy/Errors.py b/SOAPpy/Errors.py
new file mode 100644 (file)
index 0000000..1a81f91
--- /dev/null
@@ -0,0 +1,79 @@
+"""
+################################################################################
+#
+# SOAPpy - Cayce Ullman       (cayce@actzero.com)
+#          Brian Matthews     (blm@actzero.com)
+#          Gregory Warnes     (Gregory.R.Warnes@Pfizer.com)
+#          Christopher Blunck (blunck@gst.com)
+#
+################################################################################
+# Copyright (c) 2003, Pfizer
+# Copyright (c) 2001, Cayce Ullman.
+# Copyright (c) 2001, Brian Matthews.
+#
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# Redistributions of source code must retain the above copyright notice, this
+# list of conditions and the following disclaimer.
+#
+# Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# Neither the name of actzero, inc. nor the names of its contributors may
+# be used to endorse or promote products derived from this software without
+# specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
+# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+################################################################################
+"""
+
+ident = '$Id: Errors.py 921 2005-02-15 16:32:23Z warnes $'
+from version import __version__
+
+import exceptions
+
+################################################################################
+# Exceptions
+################################################################################
+class Error(exceptions.Exception):
+    def __init__(self, msg):
+        self.msg = msg
+    def __str__(self):
+        return "<Error : %s>" % self.msg
+    __repr__ = __str__
+    def __call__(self):
+        return (msg,)
+
+class RecursionError(Error):
+    pass
+
+class UnknownTypeError(Error):
+    pass
+
+class HTTPError(Error):
+    # indicates an HTTP protocol error
+    def __init__(self, code, msg):
+        self.code = code
+        self.msg  = msg
+    def __str__(self):
+        return "<HTTPError %s %s>" % (self.code, self.msg)
+    __repr__ = __str__
+    def __call___(self):
+        return (self.code, self.msg, )
+
+class UnderflowError(exceptions.ArithmeticError):
+    pass
+
diff --git a/SOAPpy/GSIServer.py b/SOAPpy/GSIServer.py
new file mode 100644 (file)
index 0000000..39f836c
--- /dev/null
@@ -0,0 +1,143 @@
+from __future__ import nested_scopes
+
+"""
+GSIServer - Contributed by Ivan R. Judson <judson@mcs.anl.gov>
+
+
+################################################################################
+#
+# SOAPpy - Cayce Ullman       (cayce@actzero.com)
+#          Brian Matthews     (blm@actzero.com)
+#          Gregory Warnes     (Gregory.R.Warnes@Pfizer.com)
+#          Christopher Blunck (blunck@gst.com)
+#
+################################################################################
+# Copyright (c) 2003, Pfizer
+# Copyright (c) 2001, Cayce Ullman.
+# Copyright (c) 2001, Brian Matthews.
+#
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# Redistributions of source code must retain the above copyright notice, this
+# list of conditions and the following disclaimer.
+#
+# Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# Neither the name of actzero, inc. nor the names of its contributors may
+# be used to endorse or promote products derived from this software without
+# specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
+# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+################################################################################
+"""
+
+ident = '$Id: GSIServer.py 1468 2008-05-24 01:55:33Z warnes $'
+from version import __version__
+
+#import xml.sax
+import re
+import socket
+import sys
+import SocketServer
+from types import *
+import BaseHTTPServer
+
+# SOAPpy modules
+from Parser      import parseSOAPRPC
+from Config      import SOAPConfig
+from Types       import faultType, voidType, simplify
+from NS          import NS
+from SOAPBuilder import buildSOAP
+from Utilities   import debugHeader, debugFooter
+
+try: from M2Crypto import SSL
+except: pass
+
+#####
+
+from Server import *
+
+from pyGlobus.io import GSITCPSocketServer, ThreadingGSITCPSocketServer
+from pyGlobus import ioc
+
+def GSIConfig():
+    config = SOAPConfig()
+    config.channel_mode = ioc.GLOBUS_IO_SECURE_CHANNEL_MODE_GSI_WRAP
+    config.delegation_mode = ioc.GLOBUS_IO_SECURE_DELEGATION_MODE_FULL_PROXY
+    config.tcpAttr = None
+    config.authMethod = "_authorize"
+    return config
+
+Config = GSIConfig()
+
+class GSISOAPServer(GSITCPSocketServer, SOAPServerBase):
+    def __init__(self, addr = ('localhost', 8000),
+                 RequestHandler = SOAPRequestHandler, log = 0,
+                 encoding = 'UTF-8', config = Config, namespace = None):
+
+        # Test the encoding, raising an exception if it's not known
+        if encoding != None:
+            ''.encode(encoding)
+
+        self.namespace          = namespace
+        self.objmap             = {}
+        self.funcmap            = {}
+        self.encoding           = encoding
+        self.config             = config
+        self.log                = log
+        
+        self.allow_reuse_address= 1
+        
+        GSITCPSocketServer.__init__(self, addr, RequestHandler,
+                                    self.config.channel_mode,
+                                    self.config.delegation_mode,
+                                    tcpAttr = self.config.tcpAttr)
+        
+    def get_request(self):
+        sock, addr = GSITCPSocketServer.get_request(self)
+
+        return sock, addr
+       
+class ThreadingGSISOAPServer(ThreadingGSITCPSocketServer, SOAPServerBase):
+
+    def __init__(self, addr = ('localhost', 8000),
+                 RequestHandler = SOAPRequestHandler, log = 0,
+                 encoding = 'UTF-8', config = Config, namespace = None):
+        
+        # Test the encoding, raising an exception if it's not known
+        if encoding != None:
+            ''.encode(encoding)
+
+        self.namespace          = namespace
+        self.objmap             = {}
+        self.funcmap            = {}
+        self.encoding           = encoding
+        self.config             = config
+        self.log                = log
+        
+        self.allow_reuse_address= 1
+        
+        ThreadingGSITCPSocketServer.__init__(self, addr, RequestHandler,
+                                             self.config.channel_mode,
+                                             self.config.delegation_mode,
+                                             tcpAttr = self.config.tcpAttr)
+
+    def get_request(self):
+        sock, addr = ThreadingGSITCPSocketServer.get_request(self)
+
+        return sock, addr
+
diff --git a/SOAPpy/NS.py b/SOAPpy/NS.py
new file mode 100644 (file)
index 0000000..124dad0
--- /dev/null
@@ -0,0 +1,104 @@
+from __future__ import nested_scopes
+
+"""
+################################################################################
+#
+# SOAPpy - Cayce Ullman       (cayce@actzero.com)
+#          Brian Matthews     (blm@actzero.com)
+#          Gregory Warnes     (Gregory.R.Warnes@Pfizer.com)
+#          Christopher Blunck (blunck@gst.com)
+#
+################################################################################
+# Copyright (c) 2003, Pfizer
+# Copyright (c) 2001, Cayce Ullman.
+# Copyright (c) 2001, Brian Matthews.
+#
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# Redistributions of source code must retain the above copyright notice, this
+# list of conditions and the following disclaimer.
+#
+# Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# Neither the name of actzero, inc. nor the names of its contributors may
+# be used to endorse or promote products derived from this software without
+# specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
+# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+################################################################################
+"""
+
+ident = '$Id: NS.py 1468 2008-05-24 01:55:33Z warnes $'
+from version import __version__
+
+##############################################################################
+# Namespace Class
+################################################################################
+def invertDict(dict):
+    d = {}
+
+    for k, v in dict.items():
+        d[v] = k
+
+    return d
+
+class NS:
+    XML  = "http://www.w3.org/XML/1998/namespace"
+
+    ENV  = "http://schemas.xmlsoap.org/soap/envelope/"
+    ENC  = "http://schemas.xmlsoap.org/soap/encoding/"
+
+    XSD  = "http://www.w3.org/1999/XMLSchema"
+    XSD2 = "http://www.w3.org/2000/10/XMLSchema"
+    XSD3 = "http://www.w3.org/2001/XMLSchema"
+
+    XSD_L = [XSD, XSD2, XSD3]
+    EXSD_L= [ENC, XSD, XSD2, XSD3]
+
+    XSI   = "http://www.w3.org/1999/XMLSchema-instance"
+    XSI2  = "http://www.w3.org/2000/10/XMLSchema-instance"
+    XSI3  = "http://www.w3.org/2001/XMLSchema-instance"
+    XSI_L = [XSI, XSI2, XSI3]
+
+    URN   = "http://soapinterop.org/xsd"
+
+    # For generated messages
+    XML_T = "xml"
+    ENV_T = "SOAP-ENV"
+    ENC_T = "SOAP-ENC"
+    XSD_T = "xsd"
+    XSD2_T= "xsd2"
+    XSD3_T= "xsd3"
+    XSI_T = "xsi"
+    XSI2_T= "xsi2"
+    XSI3_T= "xsi3"
+    URN_T = "urn"
+
+    NSMAP       = {ENV_T: ENV, ENC_T: ENC, XSD_T: XSD, XSD2_T: XSD2,
+                    XSD3_T: XSD3, XSI_T: XSI, XSI2_T: XSI2, XSI3_T: XSI3,
+                    URN_T: URN}
+    NSMAP_R     = invertDict(NSMAP)
+
+    STMAP       = {'1999': (XSD_T, XSI_T), '2000': (XSD2_T, XSI2_T),
+                    '2001': (XSD3_T, XSI3_T)}
+    STMAP_R     = invertDict(STMAP)
+
+    def __init__(self):
+        raise Error, "Don't instantiate this"
+
+
+
diff --git a/SOAPpy/Parser.py b/SOAPpy/Parser.py
new file mode 100644 (file)
index 0000000..93da26e
--- /dev/null
@@ -0,0 +1,1092 @@
+# SOAPpy modules
+from Config    import Config
+from Types     import *
+from NS        import NS
+from Utilities import *
+
+import string
+import fpconst
+import xml.sax
+from wstools.XMLname import fromXMLname
+
+try: from M2Crypto import SSL
+except: pass
+
+ident = '$Id: Parser.py 1497 2010-03-08 06:06:52Z pooryorick $'
+from version import __version__
+
+
+################################################################################
+# SOAP Parser
+################################################################################
+class RefHolder:
+    def __init__(self, name, frame):
+        self.name = name
+        self.parent = frame
+        self.pos = len(frame)
+        self.subpos = frame.namecounts.get(name, 0)
+
+    def __repr__(self):
+        return "<%s %s at %d>" % (self.__class__, self.name, id(self))
+
+    def __str__(self):
+        return "<%s %s at %d>" % (self.__class__, self.name, id(self))
+
+class SOAPParser(xml.sax.handler.ContentHandler):
+    class Frame:
+        def __init__(self, name, kind = None, attrs = {}, rules = {}):
+            self.name = name
+            self.kind = kind
+            self.attrs = attrs
+            self.rules = rules
+
+            self.contents = []
+            self.names = []
+            self.namecounts = {}
+            self.subattrs = []
+
+        def append(self, name, data, attrs):
+            self.names.append(name)
+            self.contents.append(data)
+            self.subattrs.append(attrs)
+
+            if self.namecounts.has_key(name):
+                self.namecounts[name] += 1
+            else:
+                self.namecounts[name] = 1
+
+        def _placeItem(self, name, value, pos, subpos = 0, attrs = None):
+            self.contents[pos] = value
+
+            if attrs:
+                self.attrs.update(attrs)
+
+        def __len__(self):
+            return len(self.contents)
+
+        def __repr__(self):
+            return "<%s %s at %d>" % (self.__class__, self.name, id(self))
+
+    def __init__(self, rules = None):
+        xml.sax.handler.ContentHandler.__init__(self)
+        self.body       = None
+        self.header     = None
+        self.attrs      = {}
+        self._data      = None
+        self._next      = "E" # Keeping state for message validity
+        self._stack     = [self.Frame('SOAP')]
+
+        # Make two dictionaries to store the prefix <-> URI mappings, and
+        # initialize them with the default
+        self._prem      = {NS.XML_T: NS.XML}
+        self._prem_r    = {NS.XML: NS.XML_T}
+        self._ids       = {}
+        self._refs      = {}
+        self._rules    = rules
+
+    def startElementNS(self, name, qname, attrs):
+
+        def toStr( name ):
+            prefix = name[0]
+            tag    = name[1]
+            if self._prem_r.has_key(prefix):
+               tag = self._prem_r[name[0]] + ':' + name[1]
+            elif prefix:
+               tag = prefix + ":" + tag
+            return tag
+        
+        # Workaround two sax bugs
+        if name[0] == None and name[1][0] == ' ':
+            name = (None, name[1][1:])
+        else:
+            name = tuple(name)
+
+        # First some checking of the layout of the message
+
+        if self._next == "E":
+            if name[1] != 'Envelope':
+                raise Error, "expected `SOAP-ENV:Envelope', " \
+                    "got `%s'" % toStr( name )
+            if name[0] != NS.ENV:
+                raise faultType, ("%s:VersionMismatch" % NS.ENV_T,
+                    "Don't understand version `%s' Envelope" % name[0])
+            else:
+                self._next = "HorB"
+        elif self._next == "HorB":
+            if name[0] == NS.ENV and name[1] in ("Header", "Body"):
+                self._next = None
+            else:
+                raise Error, \
+                    "expected `SOAP-ENV:Header' or `SOAP-ENV:Body', " \
+                    "got `%s'" % toStr( name )
+        elif self._next == "B":
+            if name == (NS.ENV, "Body"):
+                self._next = None
+            else:
+                raise Error, "expected `SOAP-ENV:Body', " \
+                      "got `%s'" % toStr( name )
+        elif self._next == "":
+            raise Error, "expected nothing, " \
+                  "got `%s'" % toStr( name )
+                  
+
+        if len(self._stack) == 2:
+            rules = self._rules
+        else:
+            try:
+                rules = self._stack[-1].rules[name[1]]
+            except:
+                rules = None
+
+        if type(rules) not in (NoneType, DictType):
+            kind = rules
+        else:
+            kind = attrs.get((NS.ENC, 'arrayType'))
+
+            if kind != None:
+                del attrs._attrs[(NS.ENC, 'arrayType')]
+
+                i = kind.find(':')
+                if i >= 0:
+                    try:
+                        kind = (self._prem[kind[:i]], kind[i + 1:])
+                    except:
+                        kind = None
+                else:
+                    kind = None
+
+        self.pushFrame(self.Frame(name[1], kind, attrs._attrs, rules))
+
+        self._data = [] # Start accumulating
+
+    def pushFrame(self, frame):
+        self._stack.append(frame)
+
+    def popFrame(self):
+        return self._stack.pop()
+
+    def endElementNS(self, name, qname):
+        # Workaround two sax bugs
+        if name[0] == None and name[1][0] == ' ':
+            ns, name = None, name[1][1:]
+        else:
+            ns, name = tuple(name)
+
+        name = fromXMLname(name) # convert to SOAP 1.2 XML name encoding
+
+        if self._next == "E":
+            raise Error, "didn't get SOAP-ENV:Envelope"
+        if self._next in ("HorB", "B"):
+            raise Error, "didn't get SOAP-ENV:Body"
+
+        cur = self.popFrame()
+        attrs = cur.attrs
+
+        idval = None
+
+        if attrs.has_key((None, 'id')):
+            idval = attrs[(None, 'id')]
+
+            if self._ids.has_key(idval):
+                raise Error, "duplicate id `%s'" % idval
+
+            del attrs[(None, 'id')]
+
+        root = 1
+
+        if len(self._stack) == 3:
+            if attrs.has_key((NS.ENC, 'root')):
+                root = int(attrs[(NS.ENC, 'root')])
+
+                # Do some preliminary checks. First, if root="0" is present,
+                # the element must have an id. Next, if root="n" is present,
+                # n something other than 0 or 1, raise an exception.
+
+                if root == 0:
+                    if idval == None:
+                        raise Error, "non-root element must have an id"
+                elif root != 1:
+                    raise Error, "SOAP-ENC:root must be `0' or `1'"
+
+                del attrs[(NS.ENC, 'root')]
+
+        while 1:
+            href = attrs.get((None, 'href'))
+            if href:
+                if href[0] != '#':
+                    raise Error, "Non-local hrefs are not yet suppported."
+                if self._data != None and \
+                   string.join(self._data, "").strip() != '':
+                    raise Error, "hrefs can't have data"
+
+                href = href[1:]
+
+                if self._ids.has_key(href):
+                    data = self._ids[href]
+                else:
+                    data = RefHolder(name, self._stack[-1])
+
+                    if self._refs.has_key(href):
+                        self._refs[href].append(data)
+                    else:
+                        self._refs[href] = [data]
+
+                del attrs[(None, 'href')]
+
+                break
+
+            kind = None
+
+            if attrs:
+                for i in NS.XSI_L:
+                    if attrs.has_key((i, 'type')):
+                        kind = attrs[(i, 'type')]
+                        del attrs[(i, 'type')]
+
+                if kind != None:
+                    i = kind.find(':')
+                    if i >= 0:
+                        try:
+                            kind = (self._prem[kind[:i]], kind[i + 1:])
+                        except:
+                            kind = (None, kind)
+                    else:
+# XXX What to do here? (None, kind) is just going to fail in convertType
+                        #print "Kind with no NS:", kind
+                        kind = (None, kind)
+
+            null = 0
+
+            if attrs:
+                for i in (NS.XSI, NS.XSI2):
+                    if attrs.has_key((i, 'null')):
+                        null = attrs[(i, 'null')]
+                        del attrs[(i, 'null')]
+
+                if attrs.has_key((NS.XSI3, 'nil')):
+                    null = attrs[(NS.XSI3, 'nil')]
+                    del attrs[(NS.XSI3, 'nil')]
+
+
+                ## Check for nil
+
+                # check for nil='true'
+                if type(null) in (StringType, UnicodeType):
+                    if null.lower() == 'true':
+                        null = 1
+
+                # check for nil=1, but watch out for string values
+                try:                
+                    null = int(null)
+                except ValueError, e:
+                    if not e[0].startswith("invalid literal for int()"):
+                        raise e
+                    null = 0
+
+                if null:
+                    if len(cur) or \
+                        (self._data != None and string.join(self._data, "").strip() != ''):
+                        raise Error, "nils can't have data"
+
+                    data = None
+
+                    break
+
+            if len(self._stack) == 2:
+                if (ns, name) == (NS.ENV, "Header"):
+                    self.header = data = headerType(attrs = attrs)
+                    self._next = "B"
+                    break
+                elif (ns, name) == (NS.ENV, "Body"):
+                    self.body = data = bodyType(attrs = attrs)
+                    self._next = ""
+                    break
+            elif len(self._stack) == 3 and self._next == None:
+                if (ns, name) == (NS.ENV, "Fault"):
+                    data = faultType()
+                    self._next = None # allow followons
+                    break
+
+            #print "\n"
+            #print "data=", self._data
+            #print "kind=", kind
+            #print "cur.kind=", cur.kind
+            #print "cur.rules=", cur.rules
+            #print "\n"
+                        
+
+            if cur.rules != None:
+                rule = cur.rules
+
+                if type(rule) in (StringType, UnicodeType):
+                    rule = (None, rule) # none flags special handling
+                elif type(rule) == ListType:
+                    rule = tuple(rule)
+
+                #print "kind=",kind
+                #print "rule=",rule
+
+
+# XXX What if rule != kind?
+                if callable(rule):
+                    data = rule(string.join(self._data, ""))
+                elif type(rule) == DictType:
+                    data = structType(name = (ns, name), attrs = attrs)
+                elif rule[1][:9] == 'arrayType':
+                    data = self.convertType(cur.contents,
+                                            rule, attrs)
+                else:
+                    data = self.convertType(string.join(self._data, ""),
+                                            rule, attrs)
+
+                break
+
+            #print "No rules, using kind or cur.kind..."
+
+            if (kind == None and cur.kind != None) or \
+                (kind == (NS.ENC, 'Array')):
+                kind = cur.kind
+
+                if kind == None:
+                    kind = 'ur-type[%d]' % len(cur)
+                else:
+                    kind = kind[1]
+
+                if len(cur.namecounts) == 1:
+                    elemsname = cur.names[0]
+                else:
+                    elemsname = None
+
+                data = self.startArray((ns, name), kind, attrs, elemsname)
+
+                break
+
+            if len(self._stack) == 3 and kind == None and \
+                len(cur) == 0 and \
+                (self._data == None or string.join(self._data, "").strip() == ''):
+                data = structType(name = (ns, name), attrs = attrs)
+                break
+
+            if len(cur) == 0 and ns != NS.URN:
+                # Nothing's been added to the current frame so it must be a
+                # simple type.
+
+#                 print "cur:", cur
+#                 print "ns:", ns
+#                 print "attrs:", attrs
+#                 print "kind:", kind
+                
+
+                if kind == None:
+                    # If the current item's container is an array, it will
+                    # have a kind. If so, get the bit before the first [,
+                    # which is the type of the array, therefore the type of
+                    # the current item.
+
+                    kind = self._stack[-1].kind
+
+                    if kind != None:
+                        i = kind[1].find('[')
+                        if i >= 0:
+                            kind = (kind[0], kind[1][:i])
+                    elif ns != None:
+                        kind = (ns, name)
+
+                if kind != None:
+                    try:
+                        data = self.convertType(string.join(self._data, ""),
+                                                kind, attrs)
+                    except UnknownTypeError:
+                        data = None
+                else:
+                    data = None
+
+                if data == None:
+                    if self._data == None:
+                        data = ''
+                    else:
+                        data = string.join(self._data, "")
+
+                    if len(attrs) == 0:
+                        try: data = str(data)
+                        except: pass
+
+                break
+
+            data = structType(name = (ns, name), attrs = attrs)
+
+            break
+
+        if isinstance(data, compoundType):
+            for i in range(len(cur)):
+                v = cur.contents[i]
+                data._addItem(cur.names[i], v, cur.subattrs[i])
+
+                if isinstance(v, RefHolder):
+                    v.parent = data
+
+        if root:
+            self._stack[-1].append(name, data, attrs)
+
+        if idval != None:
+            self._ids[idval] = data
+
+            if self._refs.has_key(idval):
+                for i in self._refs[idval]:
+                    i.parent._placeItem(i.name, data, i.pos, i.subpos, attrs)
+
+                del self._refs[idval]
+
+        self.attrs[id(data)] = attrs
+
+        if isinstance(data, anyType):
+            data._setAttrs(attrs)
+
+        self._data = None       # Stop accumulating
+
+    def endDocument(self):
+        if len(self._refs) == 1:
+            raise Error, \
+                "unresolved reference " + self._refs.keys()[0]
+        elif len(self._refs) > 1:
+            raise Error, \
+                "unresolved references " + ', '.join(self._refs.keys())
+
+    def startPrefixMapping(self, prefix, uri):
+        self._prem[prefix] = uri
+        self._prem_r[uri] = prefix
+
+    def endPrefixMapping(self, prefix):
+        try:
+            del self._prem_r[self._prem[prefix]]
+            del self._prem[prefix]
+        except:
+            pass
+
+    def characters(self, c):
+        if self._data != None:
+            self._data.append(c)
+
+    arrayre = '^(?:(?P<ns>[^:]*):)?' \
+        '(?P<type>[^[]+)' \
+        '(?:\[(?P<rank>,*)\])?' \
+        '(?:\[(?P<asize>\d+(?:,\d+)*)?\])$'
+
+    def startArray(self, name, kind, attrs, elemsname):
+        if type(self.arrayre) == StringType:
+            self.arrayre = re.compile (self.arrayre)
+
+        offset = attrs.get((NS.ENC, "offset"))
+
+        if offset != None:
+            del attrs[(NS.ENC, "offset")]
+
+            try:
+                if offset[0] == '[' and offset[-1] == ']':
+                    offset = int(offset[1:-1])
+                    if offset < 0:
+                        raise Exception
+                else:
+                    raise Exception
+            except:
+                raise AttributeError, "invalid Array offset"
+        else:
+            offset = 0
+
+        try:
+            m = self.arrayre.search(kind)
+
+            if m == None:
+                raise Exception
+
+            t = m.group('type')
+
+            if t == 'ur-type':
+                return arrayType(None, name, attrs, offset, m.group('rank'),
+                    m.group('asize'), elemsname)
+            elif m.group('ns') != None:
+                return typedArrayType(None, name,
+                    (self._prem[m.group('ns')], t), attrs, offset,
+                    m.group('rank'), m.group('asize'), elemsname)
+            else:
+                return typedArrayType(None, name, (None, t), attrs, offset,
+                    m.group('rank'), m.group('asize'), elemsname)
+        except:
+            raise AttributeError, "invalid Array type `%s'" % kind
+
+    # Conversion
+
+    class DATETIMECONSTS:
+        SIGNre = '(?P<sign>-?)'
+        CENTURYre = '(?P<century>\d{2,})'
+        YEARre = '(?P<year>\d{2})'
+        MONTHre = '(?P<month>\d{2})'
+        DAYre = '(?P<day>\d{2})'
+        HOURre = '(?P<hour>\d{2})'
+        MINUTEre = '(?P<minute>\d{2})'
+        SECONDre = '(?P<second>\d{2}(?:\.\d*)?)'
+        TIMEZONEre = '(?P<zulu>Z)|(?P<tzsign>[-+])(?P<tzhour>\d{2}):' \
+            '(?P<tzminute>\d{2})'
+        BOSre = '^\s*'
+        EOSre = '\s*$'
+
+        __allres = {'sign': SIGNre, 'century': CENTURYre, 'year': YEARre,
+            'month': MONTHre, 'day': DAYre, 'hour': HOURre,
+            'minute': MINUTEre, 'second': SECONDre, 'timezone': TIMEZONEre,
+            'b': BOSre, 'e': EOSre}
+
+        dateTime = '%(b)s%(sign)s%(century)s%(year)s-%(month)s-%(day)sT' \
+            '%(hour)s:%(minute)s:%(second)s(%(timezone)s)?%(e)s' % __allres
+        timeInstant = dateTime
+        timePeriod = dateTime
+        time = '%(b)s%(hour)s:%(minute)s:%(second)s(%(timezone)s)?%(e)s' % \
+            __allres
+        date = '%(b)s%(sign)s%(century)s%(year)s-%(month)s-%(day)s' \
+            '(%(timezone)s)?%(e)s' % __allres
+        century = '%(b)s%(sign)s%(century)s(%(timezone)s)?%(e)s' % __allres
+        gYearMonth = '%(b)s%(sign)s%(century)s%(year)s-%(month)s' \
+            '(%(timezone)s)?%(e)s' % __allres
+        gYear = '%(b)s%(sign)s%(century)s%(year)s(%(timezone)s)?%(e)s' % \
+            __allres
+        year = gYear
+        gMonthDay = '%(b)s--%(month)s-%(day)s(%(timezone)s)?%(e)s' % __allres
+        recurringDate = gMonthDay
+        gDay = '%(b)s---%(day)s(%(timezone)s)?%(e)s' % __allres
+        recurringDay = gDay
+        gMonth = '%(b)s--%(month)s--(%(timezone)s)?%(e)s' % __allres
+        month = gMonth
+
+        recurringInstant = '%(b)s%(sign)s(%(century)s|-)(%(year)s|-)-' \
+            '(%(month)s|-)-(%(day)s|-)T' \
+            '(%(hour)s|-):(%(minute)s|-):(%(second)s|-)' \
+            '(%(timezone)s)?%(e)s' % __allres
+
+        duration = '%(b)s%(sign)sP' \
+            '((?P<year>\d+)Y)?' \
+            '((?P<month>\d+)M)?' \
+            '((?P<day>\d+)D)?' \
+            '((?P<sep>T)' \
+            '((?P<hour>\d+)H)?' \
+            '((?P<minute>\d+)M)?' \
+            '((?P<second>\d*(?:\.\d*)?)S)?)?%(e)s' % \
+            __allres
+
+        timeDuration = duration
+
+        # The extra 31 on the front is:
+        # - so the tuple is 1-based
+        # - so months[month-1] is December's days if month is 1
+
+        months = (31, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
+
+    def convertDateTime(self, value, kind):
+        def getZoneOffset(d):
+            zoffs = 0
+
+            try:
+                if d['zulu'] == None:
+                    zoffs = 60 * int(d['tzhour']) + int(d['tzminute'])
+                    if d['tzsign'] != '-':
+                        zoffs = -zoffs
+            except TypeError:
+                pass
+
+            return zoffs
+
+        def applyZoneOffset(months, zoffs, date, minfield, posday = 1):
+            if zoffs == 0 and (minfield > 4 or 0 <= date[5] < 60):
+                return date
+
+            if minfield > 5: date[5] = 0
+            if minfield > 4: date[4] = 0
+
+            if date[5] < 0:
+                date[4] += int(date[5]) / 60
+                date[5] %= 60
+
+            date[4] += zoffs
+
+            if minfield > 3 or 0 <= date[4] < 60: return date
+
+            date[3] += date[4] / 60
+            date[4] %= 60
+
+            if minfield > 2 or 0 <= date[3] < 24: return date
+
+            date[2] += date[3] / 24
+            date[3] %= 24
+
+            if minfield > 1:
+                if posday and date[2] <= 0:
+                    date[2] += 31       # zoffs is at most 99:59, so the
+                                        # day will never be less than -3
+                return date
+
+            while 1:
+                # The date[1] == 3 (instead of == 2) is because we're
+                # going back a month, so we need to know if the previous
+                # month is February, so we test if this month is March.
+
+                leap = minfield == 0 and date[1] == 3 and \
+                    date[0] % 4 == 0 and \
+                    (date[0] % 100 != 0 or date[0] % 400 == 0)
+
+                if 0 < date[2] <= months[date[1]] + leap: break
+
+                date[2] += months[date[1] - 1] + leap
+
+                date[1] -= 1
+
+                if date[1] > 0: break
+
+                date[1] = 12
+
+                if minfield > 0: break
+
+                date[0] -= 1
+
+            return date
+
+        try:
+            exp = getattr(self.DATETIMECONSTS, kind)
+        except AttributeError:
+            return None
+
+        if type(exp) == StringType:
+            exp = re.compile(exp)
+            setattr (self.DATETIMECONSTS, kind, exp)
+
+        m = exp.search(value)
+
+        try:
+            if m == None:
+                raise Exception
+
+            d = m.groupdict()
+            f = ('century', 'year', 'month', 'day',
+                'hour', 'minute', 'second')
+            fn = len(f)         # Index of first non-None value
+            r = []
+
+            if kind in ('duration', 'timeDuration'):
+                if d['sep'] != None and d['hour'] == None and \
+                    d['minute'] == None and d['second'] == None:
+                    raise Exception
+
+                f = f[1:]
+
+                for i in range(len(f)):
+                    s = d[f[i]]
+
+                    if s != None:
+                        if f[i] == 'second':
+                            s = float(s)
+                        else:
+                            try: s = int(s)
+                            except ValueError: s = long(s)
+
+                        if i < fn: fn = i
+
+                    r.append(s)
+
+                if fn > len(r):         # Any non-Nones?
+                    raise Exception
+
+                if d['sign'] == '-':
+                    r[fn] = -r[fn]
+
+                return tuple(r)
+
+            if kind == 'recurringInstant':
+                for i in range(len(f)):
+                    s = d[f[i]]
+
+                    if s == None or s == '-':
+                        if i > fn:
+                            raise Exception
+                        s = None
+                    else:
+                        if i < fn:
+                            fn = i
+
+                        if f[i] == 'second':
+                            s = float(s)
+                        else:
+                            try:
+                                s = int(s)
+                            except ValueError:
+                                s = long(s)
+
+                    r.append(s)
+
+                s = r.pop(0)
+
+                if fn == 0:
+                    r[0] += s * 100
+                else:
+                    fn -= 1
+
+                if fn < len(r) and d['sign'] == '-':
+                    r[fn] = -r[fn]
+
+                cleanDate(r, fn)
+
+                return tuple(applyZoneOffset(self.DATETIMECONSTS.months,
+                    getZoneOffset(d), r, fn, 0))
+
+            r = [0, 0, 1, 1, 0, 0, 0]
+
+            for i in range(len(f)):
+                field = f[i]
+
+                s = d.get(field)
+
+                if s != None:
+                    if field == 'second':
+                        s = float(s)
+                    else:
+                        try:
+                            s = int(s)
+                        except ValueError:
+                            s = long(s)
+
+                    if i < fn:
+                        fn = i
+
+                    r[i] = s
+
+            if fn > len(r):     # Any non-Nones?
+                raise Exception
+
+            s = r.pop(0)
+
+            if fn == 0:
+                r[0] += s * 100
+            else:
+                fn -= 1
+
+            if d.get('sign') == '-':
+                r[fn] = -r[fn]
+
+            cleanDate(r, fn)
+
+            zoffs = getZoneOffset(d)
+
+            if zoffs:
+                r = applyZoneOffset(self.DATETIMECONSTS.months, zoffs, r, fn)
+
+            if kind == 'century':
+                return r[0] / 100
+
+            s = []
+
+            for i in range(1, len(f)):
+                if d.has_key(f[i]):
+                    s.append(r[i - 1])
+
+            if len(s) == 1:
+                return s[0]
+            return tuple(s)
+        except Exception, e:
+            raise Error, "invalid %s value `%s' - %s" % (kind, value, e)
+
+    intlimits = \
+    {
+        'nonPositiveInteger':   (0, None, 0),
+        'non-positive-integer': (0, None, 0),
+        'negativeInteger':      (0, None, -1),
+        'negative-integer':     (0, None, -1),
+        'long':                 (1, -9223372036854775808L,
+                                    9223372036854775807L),
+        'int':                  (0, -2147483648L, 2147483647L),
+        'short':                (0, -32768, 32767),
+        'byte':                 (0, -128, 127),
+        'nonNegativeInteger':   (0, 0, None),
+        'non-negative-integer': (0, 0, None),
+        'positiveInteger':      (0, 1, None),
+        'positive-integer':     (0, 1, None),
+        'unsignedLong':         (1, 0, 18446744073709551615L),
+        'unsignedInt':          (0, 0, 4294967295L),
+        'unsignedShort':        (0, 0, 65535),
+        'unsignedByte':         (0, 0, 255),
+    }
+    floatlimits = \
+    {
+        'float':        (7.0064923216240861E-46, -3.4028234663852886E+38,
+                         3.4028234663852886E+38),
+        'double':       (2.4703282292062327E-324, -1.7976931348623158E+308,
+                         1.7976931348623157E+308),
+    }
+    zerofloatre = '[1-9]'
+
+
+    def convertType(self, d, t, attrs, config=Config):
+        if t[0] is None and t[1] is not None:
+            type = t[1].strip()
+            if type[:9] == 'arrayType':
+                index_eq = type.find('=')
+                index_obr = type.find('[')
+                index_cbr = type.find(']')
+                elemtype = type[index_eq+1:index_obr]
+                elemnum  = type[index_obr+1:index_cbr]
+                if elemtype=="ur-type":
+                    return(d)
+                else:
+                    newarr = map( lambda(di):
+                                  self.convertToBasicTypes(d=di,
+                                                       t = ( NS.XSD, elemtype),
+                                                       attrs=attrs,
+                                                       config=config),
+                                  d)
+                    return newarr
+            else:
+                t = (NS.XSD, t[1])
+
+        return self.convertToBasicTypes(d, t, attrs, config)
+
+
+    def convertToSOAPpyTypes(self, d, t, attrs, config=Config):
+        pass
+
+
+    def convertToBasicTypes(self, d, t, attrs, config=Config):
+        dnn = d or ''
+
+        #if Config.debug:
+            #print "convertToBasicTypes:"
+            #print "   requested_type=", t
+            #print "   data=", d
+
+
+#         print "convertToBasicTypes:"
+#         print "   requested_type=", t
+#         print "   data=", d
+#         print "   attrs=", attrs
+#         print "   t[0]=", t[0]
+#         print "   t[1]=", t[1]
+            
+#         print "   in?", t[0] in NS.EXSD_L
+
+        if t[0] in NS.EXSD_L:
+            if t[1]=="integer": # unbounded integer type
+                try:
+                    d = int(d)
+                    if len(attrs):
+                        d = long(d)
+                except:
+                    d = long(d)
+                return d
+            if self.intlimits.has_key (t[1]): # range-bounded integer types
+                l = self.intlimits[t[1]]
+                try: d = int(d)
+                except: d = long(d)
+
+                if l[1] != None and d < l[1]:
+                    raise UnderflowError, "%s too small" % d
+                if l[2] != None and d > l[2]:
+                    raise OverflowError, "%s too large" % d
+
+                if l[0] or len(attrs):
+                    return long(d)
+                return d
+            if t[1] == "string":
+                if len(attrs):
+                    return unicode(dnn)
+                try:
+                    return str(dnn)
+                except:
+                    return dnn
+            if t[1] in ("bool", "boolean"):
+                d = d.strip().lower()
+                if d in ('0', 'false'):
+                    return False
+                if d in ('1', 'true'):
+                    return True
+                raise AttributeError, "invalid boolean value"
+            if t[1] in ('double','float'):
+                l = self.floatlimits[t[1]]
+                s = d.strip().lower()
+
+                # Explicitly check for NaN and Infinities
+                if s == "nan":
+                    d = fpconst.NaN
+                elif s[0:2]=="inf" or s[0:3]=="+inf":
+                    d = fpconst.PosInf
+                elif s[0:3] == "-inf":
+                    d = fpconst.NegInf
+                else :
+                    d = float(s)
+
+                if config.strict_range:
+                    if fpconst.isNaN(d):
+                        if s[0:2] != 'nan':
+                            raise ValueError, "invalid %s: %s" % (t[1], s)
+                    elif fpconst.isNegInf(d):
+                        if s[0:3] != '-inf':
+                            raise UnderflowError, "%s too small: %s" % (t[1], s)
+                    elif fpconst.isPosInf(d):
+                        if s[0:2] != 'inf' and s[0:3] != '+inf':
+                            raise OverflowError, "%s too large: %s" % (t[1], s)
+                    elif d < 0 and d < l[1]:
+                            raise UnderflowError, "%s too small: %s" % (t[1], s)
+                    elif d > 0 and ( d < l[0] or d > l[2] ):
+                            raise OverflowError, "%s too large: %s" % (t[1], s)
+                    elif d == 0:
+                        if type(self.zerofloatre) == StringType:
+                            self.zerofloatre = re.compile(self.zerofloatre)
+    
+                        if self.zerofloatre.search(s):
+                            raise UnderflowError, "invalid %s: %s" % (t[1], s)
+                return d
+            
+            if t[1] in ("dateTime", "date", "timeInstant", "time"):
+                return self.convertDateTime(d, t[1])
+            if t[1] == "decimal":
+                return float(d)
+            if t[1] in ("language", "QName", "NOTATION", "NMTOKEN", "Name",
+                "NCName", "ID", "IDREF", "ENTITY"):
+                return collapseWhiteSpace(d)
+            if t[1] in ("IDREFS", "ENTITIES", "NMTOKENS"):
+                d = collapseWhiteSpace(d)
+                return d.split()
+        if t[0] in NS.XSD_L:
+            if t[1] in ("base64", "base64Binary"):
+                if d:
+                    return base64.decodestring(d)
+                else:
+                    return ''
+            if t[1] == "hexBinary":
+                if d:
+                    return decodeHexString(d)
+                else:
+                    return
+            if t[1] == "anyURI":
+                return urllib.unquote(collapseWhiteSpace(d))
+            if t[1] in ("normalizedString", "token"):
+                return collapseWhiteSpace(d)
+        if t[0] == NS.ENC:
+            if t[1] == "base64":
+                if d:
+                    return base64.decodestring(d)
+                else:
+                    return ''
+        if t[0] == NS.XSD:
+            if t[1] == "binary":
+                try:
+                    e = attrs[(None, 'encoding')]
+
+                    if d:
+                        if e == 'hex':
+                            return decodeHexString(d)
+                        elif e == 'base64':
+                            return base64.decodestring(d)
+                    else:
+                        return ''
+                except:
+                    pass
+
+                raise Error, "unknown or missing binary encoding"
+            if t[1] == "uri":
+                return urllib.unquote(collapseWhiteSpace(d))
+            if t[1] == "recurringInstant":
+                return self.convertDateTime(d, t[1])
+        if t[0] in (NS.XSD2, NS.ENC):
+            if t[1] == "uriReference":
+                return urllib.unquote(collapseWhiteSpace(d))
+            if t[1] == "timePeriod":
+                return self.convertDateTime(d, t[1])
+            if t[1] in ("century", "year"):
+                return self.convertDateTime(d, t[1])
+        if t[0] in (NS.XSD, NS.XSD2, NS.ENC):
+            if t[1] == "timeDuration":
+                return self.convertDateTime(d, t[1])
+        if t[0] == NS.XSD3:
+            if t[1] == "anyURI":
+                return urllib.unquote(collapseWhiteSpace(d))
+            if t[1] in ("gYearMonth", "gMonthDay"):
+                return self.convertDateTime(d, t[1])
+            if t[1] == "gYear":
+                return self.convertDateTime(d, t[1])
+            if t[1] == "gMonth":
+                return self.convertDateTime(d, t[1])
+            if t[1] == "gDay":
+                return self.convertDateTime(d, t[1])
+            if t[1] == "duration":
+                return self.convertDateTime(d, t[1])
+        if t[0] in (NS.XSD2, NS.XSD3):
+            if t[1] == "token":
+                return collapseWhiteSpace(d)
+            if t[1] == "recurringDate":
+                return self.convertDateTime(d, t[1])
+            if t[1] == "month":
+                return self.convertDateTime(d, t[1])
+            if t[1] == "recurringDay":
+                return self.convertDateTime(d, t[1])
+        if t[0] == NS.XSD2:
+            if t[1] == "CDATA":
+                return collapseWhiteSpace(d)
+
+        raise UnknownTypeError, "unknown type `%s'" % (str(t[0]) + ':' + t[1])
+
+
+################################################################################
+# call to SOAPParser that keeps all of the info
+################################################################################
+def _parseSOAP(xml_str, rules = None):
+    try:
+        from cStringIO import StringIO
+    except ImportError:
+        from StringIO import StringIO
+
+    parser = xml.sax.make_parser()
+    t = SOAPParser(rules = rules)
+    parser.setContentHandler(t)
+    e = xml.sax.handler.ErrorHandler()
+    parser.setErrorHandler(e)
+
+    inpsrc = xml.sax.xmlreader.InputSource()
+    inpsrc.setByteStream(StringIO(xml_str))
+
+    # turn on namespace mangeling
+    parser.setFeature(xml.sax.handler.feature_namespaces,1)
+
+    try:
+        parser.parse(inpsrc)
+    except xml.sax.SAXParseException, e:
+        parser._parser = None
+        raise e
+    
+    return t
+
+################################################################################
+# SOAPParser's more public interface
+################################################################################
+def parseSOAP(xml_str, attrs = 0):
+    t = _parseSOAP(xml_str)
+
+    if attrs:
+        return t.body, t.attrs
+    return t.body
+
+
+def parseSOAPRPC(xml_str, header = 0, body = 0, attrs = 0, rules = None):
+
+    t = _parseSOAP(xml_str, rules = rules)
+    p = t.body[0]
+
+    # Empty string, for RPC this translates into a void
+    if type(p) in (type(''), type(u'')) and p in ('', u''):
+        name = "Response"
+        for k in t.body.__dict__.keys():
+            if k[0] != "_":
+                name = k
+        p = structType(name)
+        
+    if header or body or attrs:
+        ret = (p,)
+        if header : ret += (t.header,)
+        if body: ret += (t.body,)
+        if attrs: ret += (t.attrs,)
+        return ret
+    else:
+        return p
diff --git a/SOAPpy/SOAP.py b/SOAPpy/SOAP.py
new file mode 100644 (file)
index 0000000..b197507
--- /dev/null
@@ -0,0 +1,40 @@
+"""This file is here for backward compatibility with versions <= 0.9.9 
+
+Delete when 1.0.0 is released!
+"""
+
+ident = '$Id: SOAP.py 541 2004-01-31 04:20:06Z warnes $'
+from version import __version__
+
+from Client      import *
+from Config      import *
+from Errors      import *
+from NS          import *
+from Parser      import *
+from SOAPBuilder import *
+from Server      import *
+from Types       import *
+from Utilities     import *
+import wstools
+import WSDL
+
+from warnings import warn
+
+warn("""
+
+The sub-module SOAPpy.SOAP is deprecated and is only
+provided for short-term backward compatibility.  Objects are now
+available directly within the SOAPpy module.  Thus, instead of
+
+   from SOAPpy import SOAP
+   ...
+   SOAP.SOAPProxy(...)
+
+use
+
+   from SOAPpy import SOAPProxy
+   ...
+   SOAPProxy(...)
+
+instead.
+""", DeprecationWarning)
diff --git a/SOAPpy/SOAPBuilder.py b/SOAPpy/SOAPBuilder.py
new file mode 100755 (executable)
index 0000000..f2eaee2
--- /dev/null
@@ -0,0 +1,648 @@
+"""
+################################################################################
+# Copyright (c) 2003, Pfizer
+# Copyright (c) 2001, Cayce Ullman.
+# Copyright (c) 2001, Brian Matthews.
+#
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# Redistributions of source code must retain the above copyright notice, this
+# list of conditions and the following disclaimer.
+#
+# Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# Neither the name of actzero, inc. nor the names of its contributors may
+# be used to endorse or promote products derived from this software without
+# specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
+# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+################################################################################
+"""
+
+ident = '$Id: SOAPBuilder.py 1498 2010-03-12 02:13:19Z pooryorick $'
+from version import __version__
+
+import cgi
+from wstools.XMLname import toXMLname, fromXMLname
+import fpconst
+
+# SOAPpy modules
+from Config import Config
+from NS     import NS
+from Types  import *
+
+# Test whether this Python version has Types.BooleanType
+# If it doesn't have it, then False and True are serialized as integers
+try:
+    BooleanType
+    pythonHasBooleanType = 1
+except NameError:
+    pythonHasBooleanType = 0
+
+################################################################################
+# SOAP Builder
+################################################################################
+class SOAPBuilder:
+    _xml_top = '<?xml version="1.0"?>\n'
+    _xml_enc_top = '<?xml version="1.0" encoding="%s"?>\n'
+    _env_top = ( '%(ENV_T)s:Envelope\n' + \
+                 '  %(ENV_T)s:encodingStyle="%(ENC)s"\n' ) % \
+                 NS.__dict__
+    _env_bot = '</%(ENV_T)s:Envelope>\n' % NS.__dict__
+
+    # Namespaces potentially defined in the Envelope tag.
+
+    _env_ns = {NS.ENC: NS.ENC_T, NS.ENV: NS.ENV_T,
+        NS.XSD: NS.XSD_T, NS.XSD2: NS.XSD2_T, NS.XSD3: NS.XSD3_T,
+        NS.XSI: NS.XSI_T, NS.XSI2: NS.XSI2_T, NS.XSI3: NS.XSI3_T}
+
+    def __init__(self, args = (), kw = {}, method = None, namespace = None,
+        header = None, methodattrs = None, envelope = 1, encoding = 'UTF-8',
+        use_refs = 0, config = Config, noroot = 0):
+
+        # Test the encoding, raising an exception if it's not known
+        if encoding != None:
+            ''.encode(encoding)
+
+        self.args       = args
+        self.kw         = kw
+        self.envelope   = envelope
+        self.encoding   = encoding
+        self.method     = method
+        self.namespace  = namespace
+        self.header     = header
+        self.methodattrs= methodattrs
+        self.use_refs   = use_refs
+        self.config     = config
+        self.out        = []
+        self.tcounter   = 0
+        self.ncounter   = 1
+        self.icounter   = 1
+        self.envns      = {}
+        self.ids        = {}
+        self.depth      = 0
+        self.multirefs  = []
+        self.multis     = 0
+        self.body       = not isinstance(args, bodyType)
+        self.noroot     = noroot
+
+    def build(self):
+        if Config.debug: print "In build."
+        ns_map = {}
+
+        # Cache whether typing is on or not
+        typed = self.config.typed
+
+        if self.header:
+            # Create a header.
+            self.dump(self.header, "Header", typed = typed)
+            #self.header = None # Wipe it out so no one is using it.
+
+        if self.body:
+            # Call genns to record that we've used SOAP-ENV.
+            self.depth += 1
+            body_ns = self.genns(ns_map, NS.ENV)[0]
+            self.out.append("<%sBody>\n" % body_ns)
+
+        if self.method:
+            # Save the NS map so that it can be restored when we
+            # fall out of the scope of the method definition
+            save_ns_map = ns_map.copy()
+            self.depth += 1
+            a = ''
+            if self.methodattrs:
+                for (k, v) in self.methodattrs.items():
+                    a += ' %s="%s"' % (k, v)
+
+            if self.namespace:  # Use the namespace info handed to us
+                methodns, n = self.genns(ns_map, self.namespace)
+            else:
+                methodns, n = '', ''
+
+            self.out.append('<%s%s%s%s%s>\n' % (
+                methodns, self.method, n, a, self.genroot(ns_map)))
+
+        try:
+            if type(self.args) != TupleType:
+                args = (self.args,)
+            else:
+                args = self.args
+
+            for i in args:
+                self.dump(i, typed = typed, ns_map = ns_map)
+
+            if hasattr(self.config, "argsOrdering") and self.config.argsOrdering.has_key(self.method):
+                for k in self.config.argsOrdering.get(self.method):
+                    self.dump(self.kw.get(k), k, typed = typed, ns_map = ns_map)                
+            else:
+                for (k, v) in self.kw.items():
+                    self.dump(v, k, typed = typed, ns_map = ns_map)
+                
+        except RecursionError:
+            if self.use_refs == 0:
+                # restart
+                b = SOAPBuilder(args = self.args, kw = self.kw,
+                    method = self.method, namespace = self.namespace,
+                    header = self.header, methodattrs = self.methodattrs,
+                    envelope = self.envelope, encoding = self.encoding,
+                    use_refs = 1, config = self.config)
+                return b.build()
+            raise
+
+        if self.method:
+            self.out.append("</%s%s>\n" % (methodns, self.method))
+            # End of the method definition; drop any local namespaces
+            ns_map = save_ns_map
+            self.depth -= 1
+
+        if self.body:
+            # dump may add to self.multirefs, but the for loop will keep
+            # going until it has used all of self.multirefs, even those
+            # entries added while in the loop.
+
+            self.multis = 1
+
+            for obj, tag in self.multirefs:
+                self.dump(obj, tag, typed = typed, ns_map = ns_map)
+
+            self.out.append("</%sBody>\n" % body_ns)
+            self.depth -= 1
+
+        if self.envelope:
+            e = map (lambda ns: '  xmlns:%s="%s"\n' % (ns[1], ns[0]),
+                self.envns.items())
+
+            self.out = ['<', self._env_top] + e + ['>\n'] + \
+                       self.out + \
+                       [self._env_bot]
+
+        if self.encoding != None:
+            self.out.insert(0, self._xml_enc_top % self.encoding)
+            return ''.join(self.out).encode(self.encoding)
+
+        self.out.insert(0, self._xml_top)
+        return ''.join(self.out)
+
+    def gentag(self):
+        if Config.debug: print "In gentag."
+        self.tcounter += 1
+        return "v%d" % self.tcounter
+
+    def genns(self, ns_map, nsURI):
+        if nsURI == None:
+            return ('', '')
+
+        if type(nsURI) == TupleType: # already a tuple
+            if len(nsURI) == 2:
+                ns, nsURI = nsURI
+            else:
+                ns, nsURI = None, nsURI[0]
+        else:
+            ns = None
+
+        if ns_map.has_key(nsURI):
+            return (ns_map[nsURI] + ':', '')
+
+        if self._env_ns.has_key(nsURI):
+            ns = self.envns[nsURI] = ns_map[nsURI] = self._env_ns[nsURI]
+            return (ns + ':', '')
+
+        if not ns:
+            ns = "ns%d" % self.ncounter
+            self.ncounter += 1
+        ns_map[nsURI] = ns
+        if self.config.buildWithNamespacePrefix:
+            return (ns + ':', ' xmlns:%s="%s"' % (ns, nsURI))
+        else:
+            return ('', ' xmlns="%s"' % (nsURI))
+
+    def genroot(self, ns_map):
+        if self.noroot:
+            return ''
+
+        if self.depth != 2:
+            return ''
+
+        ns, n = self.genns(ns_map, NS.ENC)
+        return ' %sroot="%d"%s' % (ns, not self.multis, n)
+
+    # checkref checks an element to see if it needs to be encoded as a
+    # multi-reference element or not. If it returns None, the element has
+    # been handled and the caller can continue with subsequent elements.
+    # If it returns a string, the string should be included in the opening
+    # tag of the marshaled element.
+
+    def checkref(self, obj, tag, ns_map):
+        if self.depth < 2:
+            return ''
+
+        if not self.ids.has_key(id(obj)):
+            n = self.ids[id(obj)] = self.icounter
+            self.icounter = n + 1
+
+            if self.use_refs == 0:
+                return ''
+
+            if self.depth == 2:
+                return ' id="i%d"' % n
+
+            self.multirefs.append((obj, tag))
+        else:
+            if self.use_refs == 0:
+                raise RecursionError, "Cannot serialize recursive object"
+
+            n = self.ids[id(obj)]
+
+            if self.multis and self.depth == 2:
+                return ' id="i%d"' % n
+
+        self.out.append('<%s href="#i%d"%s/>\n' %
+                        (tag, n, self.genroot(ns_map)))
+        return None
+
+    # dumpers
+
+    def dump(self, obj, tag = None, typed = 1, ns_map = {}):
+        if Config.debug: print "In dump.", "obj=", obj
+        ns_map = ns_map.copy()
+        self.depth += 1
+
+        if type(tag) not in (NoneType, StringType, UnicodeType):
+            raise KeyError, "tag must be a string or None"
+
+        self.dump_dispatch(obj, tag, typed, ns_map)
+        self.depth -= 1
+
+    # generic dumper
+    def dumper(self, nsURI, obj_type, obj, tag, typed = 1, ns_map = {},
+               rootattr = '', id = '',
+               xml = '<%(tag)s%(type)s%(id)s%(attrs)s%(root)s>%(data)s</%(tag)s>\n'):
+        if Config.debug: print "In dumper."
+
+        if nsURI == None:
+            nsURI = self.config.typesNamespaceURI
+
+        tag = tag or self.gentag()
+
+        tag = toXMLname(tag) # convert from SOAP 1.2 XML name encoding
+
+        a = n = t = ''
+        if typed and obj_type:
+            ns, n = self.genns(ns_map, nsURI)
+            ins = self.genns(ns_map, self.config.schemaNamespaceURI)[0]
+            t = ' %stype="%s%s"%s' % (ins, ns, obj_type, n)
+
+        try: a = obj._marshalAttrs(ns_map, self)
+        except: pass
+
+        try: data = obj._marshalData()
+        except:
+            if (obj_type != "string"): # strings are already encoded
+                data = cgi.escape(str(obj))
+            else:
+                data = obj
+
+
+
+        return xml % {"tag": tag, "type": t, "data": data, "root": rootattr,
+            "id": id, "attrs": a}
+
+    def dump_float(self, obj, tag, typed = 1, ns_map = {}):
+        if Config.debug: print "In dump_float."
+        tag = tag or self.gentag()
+
+        tag = toXMLname(tag) # convert from SOAP 1.2 XML name encoding
+
+        if Config.strict_range:
+            doubleType(obj)
+
+        if fpconst.isPosInf(obj):
+            obj = "INF"
+        elif fpconst.isNegInf(obj):
+            obj = "-INF"
+        elif fpconst.isNaN(obj):
+            obj = "NaN"
+        else:
+            obj = repr(obj)
+
+        # Note: python 'float' is actually a SOAP 'double'.
+        self.out.append(self.dumper(
+            None, "double", obj, tag, typed, ns_map, self.genroot(ns_map)))
+
+    def dump_int(self, obj, tag, typed = 1, ns_map = {}):
+        if Config.debug: print "In dump_int."
+        self.out.append(self.dumper(None, 'integer', obj, tag, typed,
+                                     ns_map, self.genroot(ns_map)))
+
+    def dump_bool(self, obj, tag, typed = 1, ns_map = {}):
+        if Config.debug: print "In dump_bool."
+        self.out.append(self.dumper(None, 'boolean', obj, tag, typed,
+                                     ns_map, self.genroot(ns_map)))
+        
+    def dump_string(self, obj, tag, typed = 0, ns_map = {}):
+        if Config.debug: print "In dump_string."
+        tag = tag or self.gentag()
+        tag = toXMLname(tag) # convert from SOAP 1.2 XML name encoding
+
+        id = self.checkref(obj, tag, ns_map)
+        if id == None:
+            return
+
+        try: data = obj._marshalData()
+        except: data = obj
+
+        self.out.append(self.dumper(None, "string", cgi.escape(data), tag,
+                                    typed, ns_map, self.genroot(ns_map), id))
+
+    dump_str = dump_string # For Python 2.2+
+    dump_unicode = dump_string
+
+    def dump_None(self, obj, tag, typed = 0, ns_map = {}):
+        if Config.debug: print "In dump_None."
+        tag = tag or self.gentag()
+        tag = toXMLname(tag) # convert from SOAP 1.2 XML name encoding
+        ns = self.genns(ns_map, self.config.schemaNamespaceURI)[0]
+
+        self.out.append('<%s %snull="1"%s/>\n' %
+                        (tag, ns, self.genroot(ns_map)))
+
+    dump_NoneType = dump_None # For Python 2.2+
+
+    def dump_list(self, obj, tag, typed = 1, ns_map = {}):
+        if Config.debug: print "In dump_list.",  "obj=", obj
+        tag = tag or self.gentag()
+        tag = toXMLname(tag) # convert from SOAP 1.2 XML name encoding
+
+        if type(obj) == InstanceType:
+            data = obj.data
+        else:
+            data = obj
+
+        if typed:
+            id = self.checkref(obj, tag, ns_map)
+            if id == None:
+                return
+
+        try:
+            sample = data[0]
+            empty = 0
+        except:
+            # preserve type if present
+            if getattr(obj,"_typed",None) and getattr(obj,"_type",None):
+                if getattr(obj, "_complexType", None):
+                    sample = typedArrayType(typed=obj._type,
+                                            complexType = obj._complexType)
+                    sample._typename = obj._type
+                    if not getattr(obj,"_ns",None): obj._ns = NS.URN
+                else:
+                    sample = typedArrayType(typed=obj._type)
+            else:
+                sample = structType()
+            empty = 1
+
+        # First scan list to see if all are the same type
+        same_type = 1
+
+        if not empty:
+            for i in data[1:]:
+                if type(sample) != type(i) or \
+                    (type(sample) == InstanceType and \
+                        sample.__class__ != i.__class__):
+                    same_type = 0
+                    break
+
+        ndecl = ''
+        if same_type:
+            if (isinstance(sample, structType)) or \
+                   type(sample) == DictType or \
+                   (isinstance(sample, anyType) and \
+                    (getattr(sample, "_complexType", None) and \
+                     sample._complexType)): # force to urn struct
+                try:
+                    tns = obj._ns or NS.URN
+                except:
+                    tns = NS.URN
+
+                ns, ndecl = self.genns(ns_map, tns)
+
+                try:
+                    typename = sample._typename
+                except:
+                    typename = "SOAPStruct"
+
+                t = ns + typename
+                                
+            elif isinstance(sample, anyType):
+                ns = sample._validNamespaceURI(self.config.typesNamespaceURI,
+                                               self.config.strictNamespaces)
+                if ns:
+                    ns, ndecl = self.genns(ns_map, ns)
+                    t = ns + str(sample._type)
+                else:
+                    t = 'ur-type'
+            else:
+                typename = type(sample).__name__
+
+                # For Python 2.2+
+                if type(sample) == StringType: typename = 'string'
+
+                # HACK: unicode is a SOAP string
+                if type(sample) == UnicodeType: typename = 'string'
+                
+                # HACK: python 'float' is actually a SOAP 'double'.
+                if typename=="float": typename="double"  
+                t = self.genns(
+                ns_map, self.config.typesNamespaceURI)[0] + typename
+
+        else:
+            t = self.genns(ns_map, self.config.typesNamespaceURI)[0] + \
+                "ur-type"
+
+        try: a = obj._marshalAttrs(ns_map, self)
+        except: a = ''
+
+        ens, edecl = self.genns(ns_map, NS.ENC)
+        ins, idecl = self.genns(ns_map, self.config.schemaNamespaceURI)
+
+        if typed:
+            self.out.append(
+                '<%s %sarrayType="%s[%d]" %stype="%sArray"%s%s%s%s%s%s>\n' %
+                (tag, ens, t, len(data), ins, ens, ndecl, edecl, idecl,
+                 self.genroot(ns_map), id, a))
+
+        if typed:
+            try: elemsname = obj._elemsname
+            except: elemsname = "item"
+        else:
+            elemsname = tag
+            
+        if isinstance(data, (list, tuple, arrayType)):
+            should_drill = True
+        else:
+            should_drill = not same_type
+        
+        for i in data:
+            self.dump(i, elemsname, should_drill, ns_map)
+
+        if typed: self.out.append('</%s>\n' % tag)
+
+    dump_tuple = dump_list
+
+    def dump_exception(self, obj, tag, typed = 0, ns_map = {}):
+        if isinstance(obj, faultType):    # Fault
+            cns, cdecl = self.genns(ns_map, NS.ENC)
+            vns, vdecl = self.genns(ns_map, NS.ENV)
+            self.out.append('<%sFault %sroot="1"%s%s>' % (vns, cns, vdecl, cdecl))
+            self.dump(obj.faultcode, "faultcode", typed, ns_map)
+            self.dump(obj.faultstring, "faultstring", typed, ns_map)
+            if hasattr(obj, "detail"):
+                self.dump(obj.detail, "detail", typed, ns_map)
+            self.out.append("</%sFault>\n" % vns)
+
+    def dump_dictionary(self, obj, tag, typed = 1, ns_map = {}):
+        if Config.debug: print "In dump_dictionary."
+        tag = tag or self.gentag()
+        tag = toXMLname(tag) # convert from SOAP 1.2 XML name encoding
+
+        id = self.checkref(obj, tag, ns_map)
+        if id == None:
+            return
+
+        try: a = obj._marshalAttrs(ns_map, self)
+        except: a = ''
+
+        self.out.append('<%s%s%s%s>\n' % 
+                        (tag, id, a, self.genroot(ns_map)))
+
+        for (k, v) in obj.items():
+            if k[0] != "_":
+                self.dump(v, k, 1, ns_map)
+
+        self.out.append('</%s>\n' % tag)
+
+    dump_dict = dump_dictionary # For Python 2.2+
+
+    def dump_dispatch(self, obj, tag, typed = 1, ns_map = {}):
+        if not tag:
+            # If it has a name use it.
+            if isinstance(obj, anyType) and obj._name:
+                tag = obj._name
+            else:
+                tag = self.gentag()
+
+        # watch out for order! 
+        dumpmap = (
+            (Exception, self.dump_exception),
+            (arrayType, self.dump_list),
+            (basestring, self.dump_string),
+            (NoneType, self.dump_None),
+            (bool, self.dump_bool),
+            (int, self.dump_int),
+            (long, self.dump_int),
+            (list, self.dump_list),
+            (tuple, self.dump_list),
+            (dict, self.dump_dictionary),
+            (float, self.dump_float),
+        )
+        for dtype, func in dumpmap:
+            if isinstance(obj, dtype):
+                func(obj, tag, typed, ns_map)
+                return
+
+        r = self.genroot(ns_map)
+
+        try: a = obj._marshalAttrs(ns_map, self)
+        except: a = ''
+
+        if isinstance(obj, voidType):     # void
+            self.out.append("<%s%s%s></%s>\n" % (tag, a, r, tag))
+        else:
+            id = self.checkref(obj, tag, ns_map)
+            if id == None:
+                return
+
+        if isinstance(obj, structType):
+            # Check for namespace
+            ndecl = ''
+            ns = obj._validNamespaceURI(self.config.typesNamespaceURI,
+                self.config.strictNamespaces)
+            if ns:
+                ns, ndecl = self.genns(ns_map, ns)
+                tag = ns + tag
+            self.out.append("<%s%s%s%s%s>\n" % (tag, ndecl, id, a, r))
+
+            keylist = obj.__dict__.keys()
+
+            # first write out items with order information
+            if hasattr(obj, '_keyord'):
+                for i in range(len(obj._keyord)):
+                    self.dump(obj._aslist(i), obj._keyord[i], 1, ns_map)
+                    keylist.remove(obj._keyord[i])
+
+            # now write out the rest
+            for k in keylist:
+                if (k[0] != "_"):
+                    self.dump(getattr(obj,k), k, 1, ns_map)
+
+            if isinstance(obj, bodyType):
+                self.multis = 1
+
+                for v, k in self.multirefs:
+                    self.dump(v, k, typed = typed, ns_map = ns_map)
+
+            self.out.append('</%s>\n' % tag)
+
+        elif isinstance(obj, anyType):
+            t = ''
+
+            if typed:
+                ns = obj._validNamespaceURI(self.config.typesNamespaceURI,
+                    self.config.strictNamespaces)
+                if ns:
+                    ons, ondecl = self.genns(ns_map, ns)
+                    ins, indecl = self.genns(ns_map,
+                        self.config.schemaNamespaceURI)
+                    t = ' %stype="%s%s"%s%s' % \
+                        (ins, ons, obj._type, ondecl, indecl)
+
+            self.out.append('<%s%s%s%s%s>%s</%s>\n' %
+                            (tag, t, id, a, r, obj._marshalData(), tag))
+
+        else:                           # Some Class
+            self.out.append('<%s%s%s>\n' % (tag, id, r))
+
+            d1 = getattr(obj, '__dict__', None)
+            if d1 is not None:
+                for (k, v) in d1:
+                    if k[0] != "_":
+                        self.dump(v, k, 1, ns_map)
+
+            self.out.append('</%s>\n' % tag)
+
+
+
+################################################################################
+# SOAPBuilder's more public interface
+################################################################################
+
+def buildSOAP(args=(), kw={}, method=None, namespace=None,
+              header=None, methodattrs=None, envelope=1, encoding='UTF-8',
+              config=Config, noroot = 0):
+    t = SOAPBuilder(args=args, kw=kw, method=method, namespace=namespace,
+                    header=header, methodattrs=methodattrs,envelope=envelope,
+                    encoding=encoding, config=config,noroot=noroot)
+    return t.build()
diff --git a/SOAPpy/Server.py b/SOAPpy/Server.py
new file mode 100644 (file)
index 0000000..a01a105
--- /dev/null
@@ -0,0 +1,706 @@
+from __future__ import nested_scopes
+
+"""
+################################################################################
+#
+# SOAPpy - Cayce Ullman       (cayce@actzero.com)
+#          Brian Matthews     (blm@actzero.com)
+#          Gregory Warnes     (Gregory.R.Warnes@Pfizer.com)
+#          Christopher Blunck (blunck@gst.com)
+#
+################################################################################
+# Copyright (c) 2003, Pfizer
+# Copyright (c) 2001, Cayce Ullman.
+# Copyright (c) 2001, Brian Matthews.
+#
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# Redistributions of source code must retain the above copyright notice, this
+# list of conditions and the following disclaimer.
+#
+# Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# Neither the name of actzero, inc. nor the names of its contributors may
+# be used to endorse or promote products derived from this software without
+# specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
+# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+################################################################################
+"""
+
+ident = '$Id: Server.py 1468 2008-05-24 01:55:33Z warnes $'
+from version import __version__
+
+#import xml.sax
+import socket
+import sys
+import SocketServer
+from types import *
+import BaseHTTPServer
+import thread
+
+# SOAPpy modules
+from Parser      import parseSOAPRPC
+from Config      import Config
+from Types       import faultType, voidType, simplify
+from NS          import NS
+from SOAPBuilder import buildSOAP
+from Utilities   import debugHeader, debugFooter
+
+try: from M2Crypto import SSL
+except: pass
+
+ident = '$Id: Server.py 1468 2008-05-24 01:55:33Z warnes $'
+
+from version import __version__
+
+################################################################################
+# Call context dictionary
+################################################################################
+
+_contexts = dict()
+
+def GetSOAPContext():
+    global _contexts
+    return _contexts[thread.get_ident()]
+
+################################################################################
+# Server
+################################################################################
+
+# Method Signature class for adding extra info to registered funcs, right now
+# used just to indicate it should be called with keywords, instead of ordered
+# params.
+class MethodSig:
+    def __init__(self, func, keywords=0, context=0):
+        self.func     = func
+        self.keywords = keywords
+        self.context  = context
+        self.__name__ = func.__name__
+
+    def __call__(self, *args, **kw):
+        return apply(self.func,args,kw)
+
+class SOAPContext:
+    def __init__(self, header, body, attrs, xmldata, connection, httpheaders,
+        soapaction):
+
+        self.header     = header
+        self.body       = body
+        self.attrs      = attrs
+        self.xmldata    = xmldata
+        self.connection = connection
+        self.httpheaders= httpheaders
+        self.soapaction = soapaction
+
+# A class to describe how header messages are handled
+class HeaderHandler:
+    # Initially fail out if there are any problems.
+    def __init__(self, header, attrs):
+        for i in header.__dict__.keys():
+            if i[0] == "_":
+                continue
+
+            d = getattr(header, i)
+
+            try:
+                fault = int(attrs[id(d)][(NS.ENV, 'mustUnderstand')])
+            except:
+                fault = 0
+
+            if fault:
+                raise faultType, ("%s:MustUnderstand" % NS.ENV_T,
+                                  "Required Header Misunderstood",
+                                  "%s" % i)
+
+################################################################################
+# SOAP Server
+################################################################################
+class SOAPServerBase:
+
+    def get_request(self):
+        sock, addr = SocketServer.TCPServer.get_request(self)
+
+        if self.ssl_context:
+            sock = SSL.Connection(self.ssl_context, sock)
+            sock._setup_ssl(addr)
+            if sock.accept_ssl() != 1:
+                raise socket.error, "Couldn't accept SSL connection"
+
+        return sock, addr
+
+    def registerObject(self, object, namespace = '', path = ''):
+        if namespace == '' and path == '': namespace = self.namespace
+        if namespace == '' and path != '':
+            namespace = path.replace("/", ":")
+            if namespace[0] == ":": namespace = namespace[1:]
+        self.objmap[namespace] = object
+
+    def registerFunction(self, function, namespace = '', funcName = None,
+                         path = ''):
+        if not funcName : funcName = function.__name__
+        if namespace == '' and path == '': namespace = self.namespace
+        if namespace == '' and path != '':
+            namespace = path.replace("/", ":")
+            if namespace[0] == ":": namespace = namespace[1:]
+        if self.funcmap.has_key(namespace):
+            self.funcmap[namespace][funcName] = function
+        else:
+            self.funcmap[namespace] = {funcName : function}
+
+    def registerKWObject(self, object, namespace = '', path = ''):
+        if namespace == '' and path == '': namespace = self.namespace
+        if namespace == '' and path != '':
+            namespace = path.replace("/", ":")
+            if namespace[0] == ":": namespace = namespace[1:]
+        for i in dir(object.__class__):
+            if i[0] != "_" and callable(getattr(object, i)):
+                self.registerKWFunction(getattr(object,i), namespace)
+
+    # convenience  - wraps your func for you.
+    def registerKWFunction(self, function, namespace = '', funcName = None,
+                           path = ''):
+        if namespace == '' and path == '': namespace = self.namespace
+        if namespace == '' and path != '':
+            namespace = path.replace("/", ":")
+            if namespace[0] == ":": namespace = namespace[1:]
+        self.registerFunction(MethodSig(function,keywords=1), namespace,
+        funcName)
+
+    def unregisterObject(self, object, namespace = '', path = ''):
+        if namespace == '' and path == '': namespace = self.namespace
+        if namespace == '' and path != '':
+            namespace = path.replace("/", ":")
+            if namespace[0] == ":": namespace = namespace[1:]
+
+        del self.objmap[namespace]
+        
+class SOAPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
+    def version_string(self):
+        return '<a href="http://pywebsvcs.sf.net">' + \
+            'SOAPpy ' + __version__ + '</a> (Python ' + \
+            sys.version.split()[0] + ')'
+
+    def date_time_string(self):
+        self.__last_date_time_string = \
+            BaseHTTPServer.BaseHTTPRequestHandler.\
+            date_time_string(self)
+
+        return self.__last_date_time_string
+
+    def do_POST(self):
+        global _contexts
+        
+        status = 500
+        try:
+            if self.server.config.dumpHeadersIn:
+                s = 'Incoming HTTP headers'
+                debugHeader(s)
+                print self.raw_requestline.strip()
+                print "\n".join(map (lambda x: x.strip(),
+                    self.headers.headers))
+                debugFooter(s)
+
+            data = self.rfile.read(int(self.headers["Content-length"]))
+
+            if self.server.config.dumpSOAPIn:
+                s = 'Incoming SOAP'
+                debugHeader(s)
+                print data,
+                if data[-1] != '\n':
+                    print
+                debugFooter(s)
+
+            (r, header, body, attrs) = \
+                parseSOAPRPC(data, header = 1, body = 1, attrs = 1)
+
+            method = r._name
+            args   = r._aslist()
+            kw     = r._asdict()
+
+            if Config.simplify_objects:
+                args = simplify(args)
+                kw = simplify(kw)
+
+            # Handle mixed named and unnamed arguments by assuming
+            # that all arguments with names of the form "v[0-9]+"
+            # are unnamed and should be passed in numeric order,
+            # other arguments are named and should be passed using
+            # this name.
+
+            # This is a non-standard exension to the SOAP protocol,
+            # but is supported by Apache AXIS.
+
+            # It is enabled by default.  To disable, set
+            # Config.specialArgs to False.
+
+
+            ordered_args = {}
+            named_args   = {}
+
+            if Config.specialArgs: 
+                
+                for (k,v) in  kw.items():
+
+                    if k[0]=="v":
+                        try:
+                            i = int(k[1:])
+                            ordered_args[i] = v
+                        except ValueError:
+                            named_args[str(k)] = v
+
+                    else:
+                        named_args[str(k)] = v
+
+            # We have to decide namespace precedence
+            # I'm happy with the following scenario
+            # if r._ns is specified use it, if not check for
+            # a path, if it's specified convert it and use it as the
+            # namespace. If both are specified, use r._ns.
+            
+            ns = r._ns
+
+            if len(self.path) > 1 and not ns:
+                ns = self.path.replace("/", ":")
+                if ns[0] == ":": ns = ns[1:]
+            
+            # authorization method
+            a = None
+
+            keylist = ordered_args.keys()
+            keylist.sort()
+
+            # create list in proper order w/o names
+            tmp = map( lambda x: ordered_args[x], keylist)
+            ordered_args = tmp
+
+            #print '<-> Argument Matching Yielded:'
+            #print '<-> Ordered Arguments:' + str(ordered_args)
+            #print '<-> Named Arguments  :' + str(named_args)
+             
+            resp = ""
+            
+            # For fault messages
+            if ns:
+                nsmethod = "%s:%s" % (ns, method)
+            else:
+                nsmethod = method
+
+            try:
+                # First look for registered functions
+                if self.server.funcmap.has_key(ns) and \
+                    self.server.funcmap[ns].has_key(method):
+                    f = self.server.funcmap[ns][method]
+
+                    # look for the authorization method
+                    if self.server.config.authMethod != None:
+                        authmethod = self.server.config.authMethod
+                        if self.server.funcmap.has_key(ns) and \
+                               self.server.funcmap[ns].has_key(authmethod):
+                            a = self.server.funcmap[ns][authmethod]
+                else:
+                    # Now look at registered objects
+                    # Check for nested attributes. This works even if
+                    # there are none, because the split will return
+                    # [method]
+                    f = self.server.objmap[ns]
+                    
+                    # Look for the authorization method
+                    if self.server.config.authMethod != None:
+                        authmethod = self.server.config.authMethod
+                        if hasattr(f, authmethod):
+                            a = getattr(f, authmethod)
+
+                    # then continue looking for the method
+                    l = method.split(".")
+                    for i in l:
+                        f = getattr(f, i)
+            except:
+                info = sys.exc_info()
+                try:
+                    resp = buildSOAP(faultType("%s:Client" % NS.ENV_T,
+                                               "Method Not Found",
+                                               "%s : %s %s %s" % (nsmethod,
+                                                                  info[0],
+                                                                  info[1],
+                                                                  info[2])),
+                                     encoding = self.server.encoding,
+                                     config = self.server.config)
+                finally:
+                    del info
+                status = 500
+            else:
+                try:
+                    if header:
+                        x = HeaderHandler(header, attrs)
+
+                    fr = 1
+
+                    # call context book keeping
+                    # We're stuffing the method into the soapaction if there
+                    # isn't one, someday, we'll set that on the client
+                    # and it won't be necessary here
+                    # for now we're doing both
+
+                    if "SOAPAction".lower() not in self.headers.keys() or \
+                       self.headers["SOAPAction"] == "\"\"":
+                        self.headers["SOAPAction"] = method
+                        
+                    thread_id = thread.get_ident()
+                    _contexts[thread_id] = SOAPContext(header, body,
+                                                       attrs, data,
+                                                       self.connection,
+                                                       self.headers,
+                                                       self.headers["SOAPAction"])
+
+                    # Do an authorization check
+                    if a != None:
+                        if not apply(a, (), {"_SOAPContext" :
+                                             _contexts[thread_id] }):
+                            raise faultType("%s:Server" % NS.ENV_T,
+                                            "Authorization failed.",
+                                            "%s" % nsmethod)
+                    
+                    # If it's wrapped, some special action may be needed
+                    if isinstance(f, MethodSig):
+                        c = None
+                    
+                        if f.context:  # retrieve context object
+                            c = _contexts[thread_id]
+
+                        if Config.specialArgs:
+                            if c:
+                                named_args["_SOAPContext"] = c
+                            fr = apply(f, ordered_args, named_args)
+                        elif f.keywords:
+                            # This is lame, but have to de-unicode
+                            # keywords
+                            
+                            strkw = {}
+                            
+                            for (k, v) in kw.items():
+                                strkw[str(k)] = v
+                            if c:
+                                strkw["_SOAPContext"] = c
+                            fr = apply(f, (), strkw)
+                        elif c:
+                            fr = apply(f, args, {'_SOAPContext':c})
+                        else:
+                            fr = apply(f, args, {})
+
+                    else:
+                        if Config.specialArgs:
+                            fr = apply(f, ordered_args, named_args)
+                        else:
+                            fr = apply(f, args, {})
+
+                    
+                    if type(fr) == type(self) and \
+                        isinstance(fr, voidType):
+                        resp = buildSOAP(kw = {'%sResponse' % method: fr},
+                            encoding = self.server.encoding,
+                            config = self.server.config)
+                    else:
+                        resp = buildSOAP(kw =
+                            {'%sResponse' % method: {'Result': fr}},
+                            encoding = self.server.encoding,
+                            config = self.server.config)
+
+                    # Clean up _contexts
+                    if _contexts.has_key(thread_id):
+                        del _contexts[thread_id]
+                        
+                except Exception, e:
+                    import traceback
+                    info = sys.exc_info()
+
+                    try:
+                        if self.server.config.dumpFaultInfo:
+                            s = 'Method %s exception' % nsmethod
+                            debugHeader(s)
+                            traceback.print_exception(info[0], info[1],
+                                                      info[2])
+                            debugFooter(s)
+
+                        if isinstance(e, faultType):
+                            f = e
+                        else:
+                            f = faultType("%s:Server" % NS.ENV_T,
+                                          "Method Failed",
+                                          "%s" % nsmethod)
+
+                        if self.server.config.returnFaultInfo:
+                            f._setDetail("".join(traceback.format_exception(
+                                info[0], info[1], info[2])))
+                        elif not hasattr(f, 'detail'):
+                            f._setDetail("%s %s" % (info[0], info[1]))
+                    finally:
+                        del info
+
+                    resp = buildSOAP(f, encoding = self.server.encoding,
+                       config = self.server.config)
+                    status = 500
+                else:
+                    status = 200
+        except faultType, e:
+            import traceback
+            info = sys.exc_info()
+            try:
+                if self.server.config.dumpFaultInfo:
+                    s = 'Received fault exception'
+                    debugHeader(s)
+                    traceback.print_exception(info[0], info[1],
+                        info[2])
+                    debugFooter(s)
+
+                if self.server.config.returnFaultInfo:
+                    e._setDetail("".join(traceback.format_exception(
+                            info[0], info[1], info[2])))
+                elif not hasattr(e, 'detail'):
+                    e._setDetail("%s %s" % (info[0], info[1]))
+            finally:
+                del info
+
+            resp = buildSOAP(e, encoding = self.server.encoding,
+                config = self.server.config)
+            status = 500
+        except Exception, e:
+            # internal error, report as HTTP server error
+
+            if self.server.config.dumpFaultInfo:
+                s = 'Internal exception %s' % e
+                import traceback
+                debugHeader(s)
+                info = sys.exc_info()
+                try:
+                    traceback.print_exception(info[0], info[1], info[2])
+                finally:
+                    del info
+
+                debugFooter(s)
+
+            self.send_response(500)
+            self.end_headers()
+
+            if self.server.config.dumpHeadersOut and \
+                self.request_version != 'HTTP/0.9':
+                s = 'Outgoing HTTP headers'
+                debugHeader(s)
+                if self.responses.has_key(status):
+                    s = ' ' + self.responses[status][0]
+                else:
+                    s = ''
+                print "%s %d%s" % (self.protocol_version, 500, s)
+                print "Server:", self.version_string()
+                print "Date:", self.__last_date_time_string
+                debugFooter(s)
+        else:
+            # got a valid SOAP response
+            self.send_response(status)
+
+            t = 'text/xml';
+            if self.server.encoding != None:
+                t += '; charset=%s' % self.server.encoding
+            self.send_header("Content-type", t)
+            self.send_header("Content-length", str(len(resp)))
+            self.end_headers()
+
+            if self.server.config.dumpHeadersOut and \
+                self.request_version != 'HTTP/0.9':
+                s = 'Outgoing HTTP headers'
+                debugHeader(s)
+                if self.responses.has_key(status):
+                    s = ' ' + self.responses[status][0]
+                else:
+                    s = ''
+                print "%s %d%s" % (self.protocol_version, status, s)
+                print "Server:", self.version_string()
+                print "Date:", self.__last_date_time_string
+                print "Content-type:", t
+                print "Content-length:", len(resp)
+                debugFooter(s)
+
+            if self.server.config.dumpSOAPOut:
+                s = 'Outgoing SOAP'
+                debugHeader(s)
+                print resp,
+                if resp[-1] != '\n':
+                    print
+                debugFooter(s)
+
+            self.wfile.write(resp)
+            self.wfile.flush()
+
+            # We should be able to shut down both a regular and an SSL
+            # connection, but under Python 2.1, calling shutdown on an
+            # SSL connections drops the output, so this work-around.
+            # This should be investigated more someday.
+
+            if self.server.config.SSLserver and \
+                isinstance(self.connection, SSL.Connection):
+                self.connection.set_shutdown(SSL.SSL_SENT_SHUTDOWN |
+                    SSL.SSL_RECEIVED_SHUTDOWN)
+            else:
+                self.connection.shutdown(1)
+
+        def do_GET(self):
+            
+            #print 'command        ', self.command
+            #print 'path           ', self.path
+            #print 'request_version', self.request_version
+            #print 'headers'
+            #print '   type    ', self.headers.type
+            #print '   maintype', self.headers.maintype
+            #print '   subtype ', self.headers.subtype
+            #print '   params  ', self.headers.plist
+            
+            path = self.path.lower()
+            if path.endswith('wsdl'):
+                method = 'wsdl'
+                function = namespace = None
+                if self.server.funcmap.has_key(namespace) \
+                        and self.server.funcmap[namespace].has_key(method):
+                    function = self.server.funcmap[namespace][method]
+                else: 
+                    if namespace in self.server.objmap.keys():
+                        function = self.server.objmap[namespace]
+                        l = method.split(".")
+                        for i in l:
+                            function = getattr(function, i)
+            
+                if function:
+                    self.send_response(200)
+                    self.send_header("Content-type", 'text/plain')
+                    self.end_headers()
+                    response = apply(function, ())
+                    self.wfile.write(str(response))
+                    return
+            
+            # return error
+            self.send_response(200)
+            self.send_header("Content-type", 'text/html')
+            self.end_headers()
+            self.wfile.write('''\
+<title>
+<head>Error!</head>
+</title>
+
+<body>
+<h1>Oops!</h1>
+
+<p>
+  This server supports HTTP GET requests only for the the purpose of
+  obtaining Web Services Description Language (WSDL) for a specific
+  service.
+
+  Either you requested an URL that does not end in "wsdl" or this
+  server does not implement a wsdl method.
+</p>
+
+
+</body>''')
+
+            
+    def log_message(self, format, *args):
+        if self.server.log:
+            BaseHTTPServer.BaseHTTPRequestHandler.\
+                log_message (self, format, *args)
+
+
+
+class SOAPServer(SOAPServerBase, SocketServer.TCPServer):
+
+    def __init__(self, addr = ('localhost', 8000),
+        RequestHandler = SOAPRequestHandler, log = 0, encoding = 'UTF-8',
+        config = Config, namespace = None, ssl_context = None):
+
+        # Test the encoding, raising an exception if it's not known
+        if encoding != None:
+            ''.encode(encoding)
+
+        if ssl_context != None and not config.SSLserver:
+            raise AttributeError, \
+                "SSL server not supported by this Python installation"
+
+        self.namespace          = namespace
+        self.objmap             = {}
+        self.funcmap            = {}
+        self.ssl_context        = ssl_context
+        self.encoding           = encoding
+        self.config             = config
+        self.log                = log
+
+        self.allow_reuse_address= 1
+
+        SocketServer.TCPServer.__init__(self, addr, RequestHandler)
+
+
+class ThreadingSOAPServer(SOAPServerBase, SocketServer.ThreadingTCPServer):
+
+    def __init__(self, addr = ('localhost', 8000),
+        RequestHandler = SOAPRequestHandler, log = 0, encoding = 'UTF-8',
+        config = Config, namespace = None, ssl_context = None):
+
+        # Test the encoding, raising an exception if it's not known
+        if encoding != None:
+            ''.encode(encoding)
+
+        if ssl_context != None and not config.SSLserver:
+            raise AttributeError, \
+                "SSL server not supported by this Python installation"
+
+        self.namespace          = namespace
+        self.objmap             = {}
+        self.funcmap            = {}
+        self.ssl_context        = ssl_context
+        self.encoding           = encoding
+        self.config             = config
+        self.log                = log
+
+        self.allow_reuse_address= 1
+
+        SocketServer.ThreadingTCPServer.__init__(self, addr, RequestHandler)
+
+# only define class if Unix domain sockets are available
+if hasattr(socket, "AF_UNIX"):
+
+    class SOAPUnixSocketServer(SOAPServerBase, SocketServer.UnixStreamServer):
+    
+        def __init__(self, addr = 8000,
+            RequestHandler = SOAPRequestHandler, log = 0, encoding = 'UTF-8',
+            config = Config, namespace = None, ssl_context = None):
+    
+            # Test the encoding, raising an exception if it's not known
+            if encoding != None:
+                ''.encode(encoding)
+    
+            if ssl_context != None and not config.SSLserver:
+                raise AttributeError, \
+                    "SSL server not supported by this Python installation"
+    
+            self.namespace          = namespace
+            self.objmap             = {}
+            self.funcmap            = {}
+            self.ssl_context        = ssl_context
+            self.encoding           = encoding
+            self.config             = config
+            self.log                = log
+    
+            self.allow_reuse_address= 1
+    
+            SocketServer.UnixStreamServer.__init__(self, str(addr), RequestHandler)
+    
diff --git a/SOAPpy/Types.py b/SOAPpy/Types.py
new file mode 100644 (file)
index 0000000..8cfee53
--- /dev/null
@@ -0,0 +1,1747 @@
+from __future__ import nested_scopes
+
+"""
+################################################################################
+# Copyright (c) 2003, Pfizer
+# Copyright (c) 2001, Cayce Ullman.
+# Copyright (c) 2001, Brian Matthews.
+#
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# Redistributions of source code must retain the above copyright notice, this
+# list of conditions and the following disclaimer.
+#
+# Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# Neither the name of actzero, inc. nor the names of its contributors may
+# be used to endorse or promote products derived from this software without
+# specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
+# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+################################################################################
+"""
+
+ident = '$Id: Types.py 1496 2010-03-04 23:46:17Z pooryorick $'
+from version import __version__
+
+import UserList
+import base64
+import cgi
+import urllib
+import copy
+import re
+import time
+from types import *
+
+# SOAPpy modules
+from Errors    import *
+from NS        import NS
+from Utilities import encodeHexString, cleanDate
+from Config    import Config
+
+###############################################################################
+# Utility functions
+###############################################################################
+
+def isPrivate(name): return name[0]=='_'
+def isPublic(name):  return name[0]!='_'
+
+###############################################################################
+# Types and Wrappers
+###############################################################################
+
+class anyType:
+    _validURIs = (NS.XSD, NS.XSD2, NS.XSD3, NS.ENC)
+
+    def __init__(self, data = None, name = None, typed = 1, attrs = None):
+        if self.__class__ == anyType:
+            raise Error, "anyType can't be instantiated directly"
+
+        if type(name) in (ListType, TupleType):
+            self._ns, self._name = name
+        else:
+            self._ns = self._validURIs[0]
+            self._name = name
+            
+        self._typed = typed
+        self._attrs = {}
+
+        self._cache = None
+        self._type = self._typeName()
+
+        self._data = self._checkValueSpace(data)
+
+        if attrs != None:
+            self._setAttrs(attrs)
+
+    def __str__(self):
+        if hasattr(self,'_name') and self._name:
+            return "<%s %s at %d>" % (self.__class__, self._name, id(self))
+        return "<%s at %d>" % (self.__class__, id(self))
+
+    __repr__ = __str__
+
+    def _checkValueSpace(self, data):
+        return data
+
+    def _marshalData(self):
+        return str(self._data)
+
+    def _marshalAttrs(self, ns_map, builder):
+        a = ''
+
+        for attr, value in self._attrs.items():
+            ns, n = builder.genns(ns_map, attr[0])
+            a += n + ' %s%s="%s"' % \
+                (ns, attr[1], cgi.escape(str(value), 1))
+
+        return a
+
+    def _fixAttr(self, attr):
+        if type(attr) in (StringType, UnicodeType):
+            attr = (None, attr)
+        elif type(attr) == ListType:
+            attr = tuple(attr)
+        elif type(attr) != TupleType:
+            raise AttributeError, "invalid attribute type"
+
+        if len(attr) != 2:
+            raise AttributeError, "invalid attribute length"
+
+        if type(attr[0]) not in (NoneType, StringType, UnicodeType):
+            raise AttributeError, "invalid attribute namespace URI type"
+
+        return attr
+
+    def _getAttr(self, attr):
+        attr = self._fixAttr(attr)
+
+        try:
+            return self._attrs[attr]
+        except:
+            return None
+
+    def _setAttr(self, attr, value):
+        attr = self._fixAttr(attr)
+
+        if type(value) is StringType:
+            value = unicode(value)
+
+        self._attrs[attr] = value
+            
+
+    def _setAttrs(self, attrs):
+        if type(attrs) in (ListType, TupleType):
+            for i in range(0, len(attrs), 2):
+                self._setAttr(attrs[i], attrs[i + 1])
+
+            return
+
+        if type(attrs) == DictType:
+            d = attrs
+        elif isinstance(attrs, anyType):
+            d = attrs._attrs
+        else:
+            raise AttributeError, "invalid attribute type"
+
+        for attr, value in d.items():
+            self._setAttr(attr, value)
+
+    def _setMustUnderstand(self, val):
+        self._setAttr((NS.ENV, "mustUnderstand"), val)
+
+    def _getMustUnderstand(self):
+        return self._getAttr((NS.ENV, "mustUnderstand"))
+
+    def _setActor(self, val):
+        self._setAttr((NS.ENV, "actor"), val)
+
+    def _getActor(self):
+        return self._getAttr((NS.ENV, "actor"))
+
+    def _typeName(self):
+        return self.__class__.__name__[:-4]
+
+    def _validNamespaceURI(self, URI, strict):
+        if not hasattr(self, '_typed') or not self._typed:
+            return None
+        if URI in self._validURIs:
+            return URI
+        if not strict:
+            return self._ns
+        raise AttributeError, \
+            "not a valid namespace for type %s" % self._type
+
+class voidType(anyType):
+    pass
+
+class stringType(anyType):
+    def _checkValueSpace(self, data):
+        if data == None:
+            raise ValueError, "must supply initial %s value" % self._type
+
+        if type(data) not in (StringType, UnicodeType):
+            raise AttributeError, "invalid %s type:" % self._type
+
+        return data
+
+    def _marshalData(self):
+        return self._data
+
+
+class untypedType(stringType):
+    def __init__(self, data = None, name = None, attrs = None):
+        stringType.__init__(self, data, name, 0, attrs)
+
+class IDType(stringType): pass
+class NCNameType(stringType): pass
+class NameType(stringType): pass
+class ENTITYType(stringType): pass
+class IDREFType(stringType): pass
+class languageType(stringType): pass
+class NMTOKENType(stringType): pass
+class QNameType(stringType): pass
+
+class tokenType(anyType):
+    _validURIs = (NS.XSD2, NS.XSD3)
+    __invalidre = '[\n\t]|^ | $|  '
+
+    def _checkValueSpace(self, data):
+        if data == None:
+            raise ValueError, "must supply initial %s value" % self._type
+
+        if type(data) not in (StringType, UnicodeType):
+            raise AttributeError, "invalid %s type" % self._type
+
+        if type(self.__invalidre) == StringType:
+            self.__invalidre = re.compile(self.__invalidre)
+
+            if self.__invalidre.search(data):
+                raise ValueError, "invalid %s value" % self._type
+
+        return data
+
+class normalizedStringType(anyType):
+    _validURIs = (NS.XSD3,)
+    __invalidre = '[\n\r\t]'
+
+    def _checkValueSpace(self, data):
+        if data == None:
+            raise ValueError, "must supply initial %s value" % self._type
+
+        if type(data) not in (StringType, UnicodeType):
+            raise AttributeError, "invalid %s type" % self._type
+
+        if type(self.__invalidre) == StringType:
+            self.__invalidre = re.compile(self.__invalidre)
+
+            if self.__invalidre.search(data):
+                raise ValueError, "invalid %s value" % self._type
+
+        return data
+
+class CDATAType(normalizedStringType):
+    _validURIs = (NS.XSD2,)
+
+class booleanType(anyType):
+    def __int__(self):
+        return self._data
+
+    __nonzero__ = __int__
+
+    def _marshalData(self):
+        return ['false', 'true'][self._data]
+
+    def _checkValueSpace(self, data):
+        if data == None:
+            raise ValueError, "must supply initial %s value" % self._type
+
+        if data in (0, '0', 'false', ''):
+            return 0
+        if data in (1, '1', 'true'):
+            return 1
+        raise ValueError, "invalid %s value" % self._type
+
+class decimalType(anyType):
+    def _checkValueSpace(self, data):
+        if data == None:
+            raise ValueError, "must supply initial %s value" % self._type
+
+        if type(data) not in (IntType, LongType, FloatType):
+            raise Error, "invalid %s value" % self._type
+
+        return data
+
+class floatType(anyType):
+    def _checkValueSpace(self, data):
+        if data == None:
+            raise ValueError, "must supply initial %s value" % self._type
+
+        if type(data) not in (IntType, LongType, FloatType) or \
+            data < -3.4028234663852886E+38 or \
+            data >  3.4028234663852886E+38:
+            raise ValueError, "invalid %s value: %s" % (self._type, repr(data))
+
+        return data
+
+    def _marshalData(self):
+        return "%.18g" % self._data # More precision
+
+class doubleType(anyType):
+    def _checkValueSpace(self, data):
+        if data == None:
+            raise ValueError, "must supply initial %s value" % self._type
+
+        if type(data) not in (IntType, LongType, FloatType) or \
+            data < -1.7976931348623158E+308 or \
+            data  > 1.7976931348623157E+308:
+            raise ValueError, "invalid %s value: %s" % (self._type, repr(data))
+
+        return data
+
+    def _marshalData(self):
+        return "%.18g" % self._data # More precision
+
+class durationType(anyType):
+    _validURIs = (NS.XSD3,)
+
+    def _checkValueSpace(self, data):
+        if data == None:
+            raise ValueError, "must supply initial %s value" % self._type
+
+        try:
+            # A tuple or a scalar is OK, but make them into a list
+
+            if type(data) == TupleType:
+                data = list(data)
+            elif type(data) != ListType:
+                data = [data]
+
+            if len(data) > 6:
+                raise Exception, "too many values"
+
+            # Now check the types of all the components, and find
+            # the first nonzero element along the way.
+
+            f = -1
+
+            for i in range(len(data)):
+                if data[i] == None:
+                    data[i] = 0
+                    continue
+
+                if type(data[i]) not in \
+                    (IntType, LongType, FloatType):
+                    raise Exception, "element %d a bad type" % i
+
+                if data[i] and f == -1:
+                    f = i
+
+            # If they're all 0, just use zero seconds.
+
+            if f == -1:
+                self._cache = 'PT0S'
+
+                return (0,) * 6
+
+            # Make sure only the last nonzero element has a decimal fraction
+            # and only the first element is negative.
+
+            d = -1
+
+            for i in range(f, len(data)):
+                if data[i]:
+                    if d != -1:
+                        raise Exception, \
+                            "all except the last nonzero element must be " \
+                            "integers"
+                    if data[i] < 0 and i > f:
+                        raise Exception, \
+                            "only the first nonzero element can be negative"
+                    elif data[i] != long(data[i]):
+                        d = i
+
+            # Pad the list on the left if necessary.
+
+            if len(data) < 6:
+                n = 6 - len(data)
+                f += n
+                d += n
+                data = [0] * n + data
+
+            # Save index of the first nonzero element and the decimal
+            # element for _marshalData.
+
+            self.__firstnonzero = f
+            self.__decimal = d
+
+        except Exception, e:
+            raise ValueError, "invalid %s value - %s" % (self._type, e)
+
+        return tuple(data)
+
+    def _marshalData(self):
+        if self._cache == None:
+            d = self._data
+            t = 0
+
+            if d[self.__firstnonzero] < 0:
+                s = '-P'
+            else:
+                s = 'P'
+
+            t = 0
+
+            for i in range(self.__firstnonzero, len(d)):
+                if d[i]:
+                    if i > 2 and not t:
+                        s += 'T'
+                        t = 1
+                    if self.__decimal == i:
+                        s += "%g" % abs(d[i])
+                    else:
+                        s += "%d" % long(abs(d[i]))
+                    s += ['Y', 'M', 'D', 'H', 'M', 'S'][i]
+
+            self._cache = s
+
+        return self._cache
+
+class timeDurationType(durationType):
+    _validURIs = (NS.XSD, NS.XSD2, NS.ENC)
+
+class dateTimeType(anyType):
+    _validURIs = (NS.XSD3,)
+
+    def _checkValueSpace(self, data):
+        try:
+            if data == None:
+                data = time.time()
+
+            if (type(data) in (IntType, LongType)):
+                data = list(time.gmtime(data)[:6])
+            elif (type(data) == FloatType):
+                f = data - int(data)
+                data = list(time.gmtime(int(data))[:6])
+                data[5] += f
+            elif type(data) in (ListType, TupleType):
+                if len(data) < 6:
+                    raise Exception, "not enough values"
+                if len(data) > 9:
+                    raise Exception, "too many values"
+
+                data = list(data[:6])
+
+                cleanDate(data)
+            else:
+                raise Exception, "invalid type"
+        except Exception, e:
+            raise ValueError, "invalid %s value - %s" % (self._type, e)
+
+        return tuple(data)
+
+    def _marshalData(self):
+        if self._cache == None:
+            d = self._data
+            s = "%04d-%02d-%02dT%02d:%02d:%02d" % ((abs(d[0]),) + d[1:])
+            if d[0] < 0:
+                s = '-' + s
+            f = d[5] - int(d[5])
+            if f != 0:
+                s += ("%g" % f)[1:]
+            s += 'Z'
+
+            self._cache = s
+
+        return self._cache
+
+class recurringInstantType(anyType):
+    _validURIs = (NS.XSD,)
+
+    def _checkValueSpace(self, data):
+        try:
+            if data == None:
+                data = list(time.gmtime(time.time())[:6])
+            if (type(data) in (IntType, LongType)):
+                data = list(time.gmtime(data)[:6])
+            elif (type(data) == FloatType):
+                f = data - int(data)
+                data = list(time.gmtime(int(data))[:6])
+                data[5] += f
+            elif type(data) in (ListType, TupleType):
+                if len(data) < 1:
+                    raise Exception, "not enough values"
+                if len(data) > 9:
+                    raise Exception, "too many values"
+
+                data = list(data[:6])
+
+                if len(data) < 6:
+                    data += [0] * (6 - len(data))
+
+                f = len(data)
+
+                for i in range(f):
+                    if data[i] == None:
+                        if f < i:
+                            raise Exception, \
+                                "only leftmost elements can be none"
+                    else:
+                        f = i
+                        break
+
+                cleanDate(data, f)
+            else:
+                raise Exception, "invalid type"
+        except Exception, e:
+            raise ValueError, "invalid %s value - %s" % (self._type, e)
+
+        return tuple(data)
+
+    def _marshalData(self):
+        if self._cache == None:
+            d = self._data
+            e = list(d)
+            neg = ''
+
+            if not e[0]:
+                e[0] = '--'
+            else:
+                if e[0] < 0:
+                    neg = '-'
+                    e[0] = abs(e[0])
+                if e[0] < 100:
+                    e[0] = '-' + "%02d" % e[0]
+                else:
+                    e[0] = "%04d" % e[0]
+
+            for i in range(1, len(e)):
+                if e[i] == None or (i < 3 and e[i] == 0):
+                    e[i] = '-'
+                else:
+                    if e[i] < 0:
+                        neg = '-'
+                        e[i] = abs(e[i])
+
+                    e[i] = "%02d" % e[i]
+
+            if d[5]:
+                f = abs(d[5] - int(d[5]))
+
+                if f:
+                    e[5] += ("%g" % f)[1:]
+
+            s = "%s%s-%s-%sT%s:%s:%sZ" % ((neg,) + tuple(e))
+
+            self._cache = s
+
+        return self._cache
+
+class timeInstantType(dateTimeType):
+    _validURIs = (NS.XSD, NS.XSD2, NS.ENC)
+
+class timePeriodType(dateTimeType):
+    _validURIs = (NS.XSD2, NS.ENC)
+
+class timeType(anyType):
+    def _checkValueSpace(self, data):
+        try:
+            if data == None:
+                data = time.gmtime(time.time())[3:6]
+            elif (type(data) == FloatType):
+                f = data - int(data)
+                data = list(time.gmtime(int(data))[3:6])
+                data[2] += f
+            elif type(data) in (IntType, LongType):
+                data = time.gmtime(data)[3:6]
+            elif type(data) in (ListType, TupleType):
+                if len(data) == 9:
+                    data = data[3:6]
+                elif len(data) > 3:
+                    raise Exception, "too many values"
+
+                data = [None, None, None] + list(data)
+
+                if len(data) < 6:
+                    data += [0] * (6 - len(data))
+
+                cleanDate(data, 3)
+
+                data = data[3:]
+            else:
+                raise Exception, "invalid type"
+        except Exception, e:
+            raise ValueError, "invalid %s value - %s" % (self._type, e)
+
+        return tuple(data)
+
+    def _marshalData(self):
+        if self._cache == None:
+            d = self._data
+            #s = ''
+            #
+            #s = time.strftime("%H:%M:%S", (0, 0, 0) + d + (0, 0, -1))
+            s = "%02d:%02d:%02d" % d
+            f = d[2] - int(d[2])
+            if f != 0:
+                s += ("%g" % f)[1:]
+            s += 'Z'
+
+            self._cache = s
+
+        return self._cache
+
+class dateType(anyType):
+    def _checkValueSpace(self, data):
+        try:
+            if data == None:
+                data = time.gmtime(time.time())[0:3]
+            elif type(data) in (IntType, LongType, FloatType):
+                data = time.gmtime(data)[0:3]
+            elif type(data) in (ListType, TupleType):
+                if len(data) == 9:
+                    data = data[0:3]
+                elif len(data) > 3:
+                    raise Exception, "too many values"
+
+                data = list(data)
+
+                if len(data) < 3:
+                    data += [1, 1, 1][len(data):]
+
+                data += [0, 0, 0]
+
+                cleanDate(data)
+
+                data = data[:3]
+            else:
+                raise Exception, "invalid type"
+        except Exception, e:
+            raise ValueError, "invalid %s value - %s" % (self._type, e)
+
+        return tuple(data)
+
+    def _marshalData(self):
+        if self._cache == None:
+            d = self._data
+            s = "%04d-%02d-%02dZ" % ((abs(d[0]),) + d[1:])
+            if d[0] < 0:
+                s = '-' + s
+
+            self._cache = s
+
+        return self._cache
+
+class gYearMonthType(anyType):
+    _validURIs = (NS.XSD3,)
+
+    def _checkValueSpace(self, data):
+        try:
+            if data == None:
+                data = time.gmtime(time.time())[0:2]
+            elif type(data) in (IntType, LongType, FloatType):
+                data = time.gmtime(data)[0:2]
+            elif type(data) in (ListType, TupleType):
+                if len(data) == 9:
+                    data = data[0:2]
+                elif len(data) > 2:
+                    raise Exception, "too many values"
+
+                data = list(data)
+
+                if len(data) < 2:
+                    data += [1, 1][len(data):]
+
+                data += [1, 0, 0, 0]
+
+                cleanDate(data)
+
+                data = data[:2]
+            else:
+                raise Exception, "invalid type"
+        except Exception, e:
+            raise ValueError, "invalid %s value - %s" % (self._type, e)
+
+        return tuple(data)
+
+    def _marshalData(self):
+        if self._cache == None:
+            d = self._data
+            s = "%04d-%02dZ" % ((abs(d[0]),) + d[1:])
+            if d[0] < 0:
+                s = '-' + s
+
+            self._cache = s
+
+        return self._cache
+
+class gYearType(anyType):
+    _validURIs = (NS.XSD3,)
+
+    def _checkValueSpace(self, data):
+        try:
+            if data == None:
+                data = time.gmtime(time.time())[0:1]
+            elif type(data) in (IntType, LongType, FloatType):
+                data = [data]
+
+            if type(data) in (ListType, TupleType):
+                if len(data) == 9:
+                    data = data[0:1]
+                elif len(data) < 1:
+                    raise Exception, "too few values"
+                elif len(data) > 1:
+                    raise Exception, "too many values"
+
+                if type(data[0]) == FloatType:
+                    try: s = int(data[0])
+                    except: s = long(data[0])
+
+                    if s != data[0]:
+                        raise Exception, "not integral"
+
+                    data = [s]
+                elif type(data[0]) not in (IntType, LongType):
+                    raise Exception, "bad type"
+            else:
+                raise Exception, "invalid type"
+        except Exception, e:
+            raise ValueError, "invalid %s value - %s" % (self._type, e)
+
+        return data[0]
+
+    def _marshalData(self):
+        if self._cache == None:
+            d = self._data
+            s = "%04dZ" % abs(d)
+            if d < 0:
+                s = '-' + s
+
+            self._cache = s
+
+        return self._cache
+
+class centuryType(anyType):
+    _validURIs = (NS.XSD2, NS.ENC)
+
+    def _checkValueSpace(self, data):
+        try:
+            if data == None:
+                data = time.gmtime(time.time())[0:1] / 100
+            elif type(data) in (IntType, LongType, FloatType):
+                data = [data]
+
+            if type(data) in (ListType, TupleType):
+                if len(data) == 9:
+                    data = data[0:1] / 100
+                elif len(data) < 1:
+                    raise Exception, "too few values"
+                elif len(data) > 1:
+                    raise Exception, "too many values"
+
+                if type(data[0]) == FloatType:
+                    try: s = int(data[0])
+                    except: s = long(data[0])
+
+                    if s != data[0]:
+                        raise Exception, "not integral"
+
+                    data = [s]
+                elif type(data[0]) not in (IntType, LongType):
+                    raise Exception, "bad type"
+            else:
+                raise Exception, "invalid type"
+        except Exception, e:
+            raise ValueError, "invalid %s value - %s" % (self._type, e)
+
+        return data[0]
+
+    def _marshalData(self):
+        if self._cache == None:
+            d = self._data
+            s = "%02dZ" % abs(d)
+            if d < 0:
+                s = '-' + s
+
+            self._cache = s
+
+        return self._cache
+
+class yearType(gYearType):
+    _validURIs = (NS.XSD2, NS.ENC)
+
+class gMonthDayType(anyType):
+    _validURIs = (NS.XSD3,)
+
+    def _checkValueSpace(self, data):
+        try:
+            if data == None:
+                data = time.gmtime(time.time())[1:3]
+            elif type(data) in (IntType, LongType, FloatType):
+                data = time.gmtime(data)[1:3]
+            elif type(data) in (ListType, TupleType):
+                if len(data) == 9:
+                    data = data[0:2]
+                elif len(data) > 2:
+                    raise Exception, "too many values"
+
+                data = list(data)
+
+                if len(data) < 2:
+                    data += [1, 1][len(data):]
+
+                data = [0] + data + [0, 0, 0]
+
+                cleanDate(data, 1)
+
+                data = data[1:3]
+            else:
+                raise Exception, "invalid type"
+        except Exception, e:
+            raise ValueError, "invalid %s value - %s" % (self._type, e)
+
+        return tuple(data)
+
+    def _marshalData(self):
+        if self._cache == None:
+            self._cache = "--%02d-%02dZ" % self._data
+
+        return self._cache
+
+class recurringDateType(gMonthDayType):
+    _validURIs = (NS.XSD2, NS.ENC)
+
+class gMonthType(anyType):
+    _validURIs = (NS.XSD3,)
+
+    def _checkValueSpace(self, data):
+        try:
+            if data == None:
+                data = time.gmtime(time.time())[1:2]
+            elif type(data) in (IntType, LongType, FloatType):
+                data = [data]
+
+            if type(data) in (ListType, TupleType):
+                if len(data) == 9:
+                    data = data[1:2]
+                elif len(data) < 1:
+                    raise Exception, "too few values"
+                elif len(data) > 1:
+                    raise Exception, "too many values"
+
+                if type(data[0]) == FloatType:
+                    try: s = int(data[0])
+                    except: s = long(data[0])
+
+                    if s != data[0]:
+                        raise Exception, "not integral"
+
+                    data = [s]
+                elif type(data[0]) not in (IntType, LongType):
+                    raise Exception, "bad type"
+
+                if data[0] < 1 or data[0] > 12:
+                    raise Exception, "bad value"
+            else:
+                raise Exception, "invalid type"
+        except Exception, e:
+            raise ValueError, "invalid %s value - %s" % (self._type, e)
+
+        return data[0]
+
+    def _marshalData(self):
+        if self._cache == None:
+            self._cache = "--%02d--Z" % self._data
+
+        return self._cache
+
+class monthType(gMonthType):
+    _validURIs = (NS.XSD2, NS.ENC)
+
+class gDayType(anyType):
+    _validURIs = (NS.XSD3,)
+
+    def _checkValueSpace(self, data):
+        try:
+            if data == None:
+                data = time.gmtime(time.time())[2:3]
+            elif type(data) in (IntType, LongType, FloatType):
+                data = [data]
+
+            if type(data) in (ListType, TupleType):
+                if len(data) == 9:
+                    data = data[2:3]
+                elif len(data) < 1:
+                    raise Exception, "too few values"
+                elif len(data) > 1:
+                    raise Exception, "too many values"
+
+                if type(data[0]) == FloatType:
+                    try: s = int(data[0])
+                    except: s = long(data[0])
+
+                    if s != data[0]:
+                        raise Exception, "not integral"
+
+                    data = [s]
+                elif type(data[0]) not in (IntType, LongType):
+                    raise Exception, "bad type"
+
+                if data[0] < 1 or data[0] > 31:
+                    raise Exception, "bad value"
+            else:
+                raise Exception, "invalid type"
+        except Exception, e:
+            raise ValueError, "invalid %s value - %s" % (self._type, e)
+
+        return data[0]
+
+    def _marshalData(self):
+        if self._cache == None:
+            self._cache = "---%02dZ" % self._data
+
+        return self._cache
+
+class recurringDayType(gDayType):
+    _validURIs = (NS.XSD2, NS.ENC)
+
+class hexBinaryType(anyType):
+    _validURIs = (NS.XSD3,)
+
+    def _checkValueSpace(self, data):
+        if data == None:
+            raise ValueError, "must supply initial %s value" % self._type
+
+        if type(data) not in (StringType, UnicodeType):
+            raise AttributeError, "invalid %s type" % self._type
+
+        return data
+
+    def _marshalData(self):
+        if self._cache == None:
+            self._cache = encodeHexString(self._data)
+
+        return self._cache
+
+class base64BinaryType(anyType):
+    _validURIs = (NS.XSD3,)
+
+    def _checkValueSpace(self, data):
+        if data == None:
+            raise ValueError, "must supply initial %s value" % self._type
+
+        if type(data) not in (StringType, UnicodeType):
+            raise AttributeError, "invalid %s type" % self._type
+
+        return data
+
+    def _marshalData(self):
+        if self._cache == None:
+            self._cache = base64.encodestring(self._data)
+
+        return self._cache
+
+class base64Type(base64BinaryType):
+    _validURIs = (NS.ENC,)
+
+class binaryType(anyType):
+    _validURIs = (NS.XSD, NS.ENC)
+
+    def __init__(self, data, name = None, typed = 1, encoding = 'base64',
+        attrs = None):
+
+        anyType.__init__(self, data, name, typed, attrs)
+
+        self._setAttr('encoding', encoding)
+
+    def _marshalData(self):
+        if self._cache == None:
+            if self._getAttr((None, 'encoding')) == 'base64':
+                self._cache = base64.encodestring(self._data)
+            else:
+                self._cache = encodeHexString(self._data)
+
+        return self._cache
+
+    def _checkValueSpace(self, data):
+        if data == None:
+            raise ValueError, "must supply initial %s value" % self._type
+
+        if type(data) not in (StringType, UnicodeType):
+            raise AttributeError, "invalid %s type" % self._type
+
+        return data
+
+    def _setAttr(self, attr, value):
+        attr = self._fixAttr(attr)
+
+        if attr[1] == 'encoding':
+            if attr[0] != None or value not in ('base64', 'hex'):
+                raise AttributeError, "invalid encoding"
+
+            self._cache = None
+
+        anyType._setAttr(self, attr, value)
+
+
+class anyURIType(anyType):
+    _validURIs = (NS.XSD3,)
+
+    def _checkValueSpace(self, data):
+        if data == None:
+            raise ValueError, "must supply initial %s value" % self._type
+
+        if type(data) not in (StringType, UnicodeType):
+            raise AttributeError, "invalid %s type" % self._type
+
+        return data
+
+    def _marshalData(self):
+        if self._cache == None:
+            self._cache = urllib.quote(self._data)
+
+        return self._cache
+
+class uriType(anyURIType):
+    _validURIs = (NS.XSD,)
+
+class uriReferenceType(anyURIType):
+    _validURIs = (NS.XSD2,)
+
+class NOTATIONType(anyType):
+    def __init__(self, data, name = None, typed = 1, attrs = None):
+
+        if self.__class__ == NOTATIONType:
+            raise Error, "a NOTATION can't be instantiated directly"
+
+        anyType.__init__(self, data, name, typed, attrs)
+
+class ENTITIESType(anyType):
+    def _checkValueSpace(self, data):
+        if data == None:
+            raise ValueError, "must supply initial %s value" % self._type
+
+        if type(data) in (StringType, UnicodeType):
+            return (data,)
+
+        if type(data) not in (ListType, TupleType) or \
+            filter (lambda x: type(x) not in (StringType, UnicodeType), data):
+            raise AttributeError, "invalid %s type" % self._type
+
+        return data
+
+    def _marshalData(self):
+        return ' '.join(self._data)
+
+class IDREFSType(ENTITIESType): pass
+class NMTOKENSType(ENTITIESType): pass
+
+class integerType(anyType):
+    def _checkValueSpace(self, data):
+        if data == None:
+            raise ValueError, "must supply initial %s value" % self._type
+
+        if type(data) not in (IntType, LongType):
+            raise ValueError, "invalid %s value" % self._type
+
+        return data
+
+class nonPositiveIntegerType(anyType):
+    _validURIs = (NS.XSD2, NS.XSD3, NS.ENC)
+
+    def _checkValueSpace(self, data):
+        if data == None:
+            raise ValueError, "must supply initial %s value" % self._type
+
+        if type(data) not in (IntType, LongType) or data > 0:
+            raise ValueError, "invalid %s value" % self._type
+
+        return data
+
+class non_Positive_IntegerType(nonPositiveIntegerType):
+    _validURIs = (NS.XSD,)
+
+    def _typeName(self):
+        return 'non-positive-integer'
+
+class negativeIntegerType(anyType):
+    _validURIs = (NS.XSD2, NS.XSD3, NS.ENC)
+
+    def _checkValueSpace(self, data):
+        if data == None:
+            raise ValueError, "must supply initial %s value" % self._type
+
+        if type(data) not in (IntType, LongType) or data >= 0:
+            raise ValueError, "invalid %s value" % self._type
+
+        return data
+
+class negative_IntegerType(negativeIntegerType):
+    _validURIs = (NS.XSD,)
+
+    def _typeName(self):
+        return 'negative-integer'
+
+class longType(anyType):
+    _validURIs = (NS.XSD2, NS.XSD3, NS.ENC)
+
+    def _checkValueSpace(self, data):
+        if data == None:
+            raise ValueError, "must supply initial %s value" % self._type
+
+        if type(data) not in (IntType, LongType) or \
+            data < -9223372036854775808L or \
+            data >  9223372036854775807L:
+            raise ValueError, "invalid %s value" % self._type
+
+        return data
+
+class intType(anyType):
+    _validURIs = (NS.XSD2, NS.XSD3, NS.ENC)
+
+    def _checkValueSpace(self, data):
+        if data == None:
+            raise ValueError, "must supply initial %s value" % self._type
+
+        if type(data) not in (IntType, LongType) or \
+            data < -2147483648L or \
+            data >  2147483647L:
+            raise ValueError, "invalid %s value" % self._type
+
+        return data
+
+class shortType(anyType):
+    _validURIs = (NS.XSD2, NS.XSD3, NS.ENC)
+
+    def _checkValueSpace(self, data):
+        if data == None:
+            raise ValueError, "must supply initial %s value" % self._type
+
+        if type(data) not in (IntType, LongType) or \
+            data < -32768 or \
+            data >  32767:
+            raise ValueError, "invalid %s value" % self._type
+
+        return data
+
+class byteType(anyType):
+    _validURIs = (NS.XSD2, NS.XSD3, NS.ENC)
+
+    def _checkValueSpace(self, data):
+        if data == None:
+            raise ValueError, "must supply initial %s value" % self._type
+
+        if type(data) not in (IntType, LongType) or \
+            data < -128 or \
+            data >  127:
+            raise ValueError, "invalid %s value" % self._type
+
+        return data
+
+class nonNegativeIntegerType(anyType):
+    _validURIs = (NS.XSD2, NS.XSD3, NS.ENC)
+
+    def _checkValueSpace(self, data):
+        if data == None:
+            raise ValueError, "must supply initial %s value" % self._type
+
+        if type(data) not in (IntType, LongType) or data < 0:
+            raise ValueError, "invalid %s value" % self._type
+
+        return data
+
+class non_Negative_IntegerType(nonNegativeIntegerType):
+    _validURIs = (NS.XSD,)
+
+    def _typeName(self):
+        return 'non-negative-integer'
+
+class unsignedLongType(anyType):
+    _validURIs = (NS.XSD2, NS.XSD3, NS.ENC)
+
+    def _checkValueSpace(self, data):
+        if data == None:
+            raise ValueError, "must supply initial %s value" % self._type
+
+        if type(data) not in (IntType, LongType) or \
+            data < 0 or \
+            data > 18446744073709551615L:
+            raise ValueError, "invalid %s value" % self._type
+
+        return data
+
+class unsignedIntType(anyType):
+    _validURIs = (NS.XSD2, NS.XSD3, NS.ENC)
+
+    def _checkValueSpace(self, data):
+        if data == None:
+            raise ValueError, "must supply initial %s value" % self._type
+
+        if type(data) not in (IntType, LongType) or \
+            data < 0 or \
+            data > 4294967295L:
+            raise ValueError, "invalid %s value" % self._type
+
+        return data
+
+class unsignedShortType(anyType):
+    _validURIs = (NS.XSD2, NS.XSD3, NS.ENC)
+
+    def _checkValueSpace(self, data):
+        if data == None:
+            raise ValueError, "must supply initial %s value" % self._type
+
+        if type(data) not in (IntType, LongType) or \
+            data < 0 or \
+            data > 65535:
+            raise ValueError, "invalid %s value" % self._type
+
+        return data
+
+class unsignedByteType(anyType):
+    _validURIs = (NS.XSD2, NS.XSD3, NS.ENC)
+
+    def _checkValueSpace(self, data):
+        if data == None:
+            raise ValueError, "must supply initial %s value" % self._type
+
+        if type(data) not in (IntType, LongType) or \
+            data < 0 or \
+            data > 255:
+            raise ValueError, "invalid %s value" % self._type
+
+        return data
+
+class positiveIntegerType(anyType):
+    _validURIs = (NS.XSD2, NS.XSD3, NS.ENC)
+
+    def _checkValueSpace(self, data):
+        if data == None:
+            raise ValueError, "must supply initial %s value" % self._type
+
+        if type(data) not in (IntType, LongType) or data <= 0:
+            raise ValueError, "invalid %s value" % self._type
+
+        return data
+
+class positive_IntegerType(positiveIntegerType):
+    _validURIs = (NS.XSD,)
+
+    def _typeName(self):
+        return 'positive-integer'
+
+# Now compound types
+
+class compoundType(anyType):
+    def __init__(self, data = None, name = None, typed = 1, attrs = None):
+        if self.__class__ == compoundType:
+            raise Error, "a compound can't be instantiated directly"
+
+        anyType.__init__(self, data, name, typed, attrs)
+        self._keyord    = []
+
+        if type(data) == DictType:
+            self.__dict__.update(data)
+
+    def _aslist(self, item=None):
+        if item is not None:
+            return self.__dict__[self._keyord[item]]
+        else:
+            return map( lambda x: self.__dict__[x], self._keyord)
+
+    def _asdict(self, item=None, encoding=Config.dict_encoding):
+        if item is not None:
+            if type(item) in (UnicodeType,StringType):
+                item = item.encode(encoding)
+            return self.__dict__[item]
+        else:
+            retval = {}
+            def fun(x): retval[x.encode(encoding)] = self.__dict__[x]
+
+            if hasattr(self, '_keyord'):
+                map( fun, self._keyord)
+            else:
+                for name in dir(self):
+                    if isPublic(name):
+                        retval[name] = getattr(self,name)
+            return retval
+
+    def __getitem__(self, item):
+        if type(item) == IntType:
+            return self.__dict__[self._keyord[item]]
+        else:
+            return getattr(self, item)
+
+    def __len__(self):
+        return len(self._keyord)
+
+    def __nonzero__(self):
+        return 1
+
+    def _keys(self):
+        return filter(lambda x: x[0] != '_', self.__dict__.keys())
+
+    def _addItem(self, name, value, attrs = None):
+
+        if name in self._keyord:
+            if type(self.__dict__[name]) != ListType:
+                self.__dict__[name] = [self.__dict__[name]]
+            self.__dict__[name].append(value)
+        else:
+            self.__dict__[name] = value
+            self._keyord.append(name)
+            
+    def _placeItem(self, name, value, pos, subpos = 0, attrs = None):
+
+        if subpos == 0 and type(self.__dict__[name]) != ListType:
+            self.__dict__[name] = value
+        else:
+            self.__dict__[name][subpos] = value
+
+        # only add to key order list if it does not already 
+        # exist in list
+        if not (name in self._keyord):
+            if pos < len(x):
+                self._keyord[pos] = name
+            else:
+                self._keyord.append(name)
+              
+
+    def _getItemAsList(self, name, default = []):
+        try:
+            d = self.__dict__[name]
+        except:
+            return default
+
+        if type(d) == ListType:
+            return d
+        return [d]
+
+    def __str__(self):
+        return anyType.__str__(self) + ": " + str(self._asdict())
+
+    def __repr__(self):
+        return self.__str__()
+
+class structType(compoundType):
+    pass
+
+class headerType(structType):
+    _validURIs = (NS.ENV,)
+
+    def __init__(self, data = None, typed = 1, attrs = None):
+        structType.__init__(self, data, "Header", typed, attrs)
+
+class bodyType(structType):
+    _validURIs = (NS.ENV,)
+
+    def __init__(self, data = None, typed = 1, attrs = None):
+        structType.__init__(self, data, "Body", typed, attrs)
+
+class arrayType(UserList.UserList, compoundType):
+    def __init__(self, data = None, name = None, attrs = None,
+        offset = 0, rank = None, asize = 0, elemsname = None):
+
+        if data:
+            if type(data) not in (ListType, TupleType):
+                raise Error, "Data must be a sequence"
+
+        UserList.UserList.__init__(self, data)
+        compoundType.__init__(self, data, name, 0, attrs)
+
+        self._elemsname = elemsname or "item"
+
+        if data == None:
+            self._rank = rank
+
+            # According to 5.4.2.2 in the SOAP spec, each element in a
+            # sparse array must have a position. _posstate keeps track of
+            # whether we've seen a position or not. It's possible values
+            # are:
+            # -1 No elements have been added, so the state is indeterminate
+            #  0 An element without a position has been added, so no
+            #    elements can have positions
+            #  1 An element with a position has been added, so all elements
+            #    must have positions
+
+            self._posstate = -1
+
+            self._full = 0
+
+            if asize in ('', None):
+                asize = '0'
+
+            self._dims = map (lambda x: int(x), str(asize).split(','))
+            self._dims.reverse()   # It's easier to work with this way
+            self._poss = [0] * len(self._dims)      # This will end up
+                                                    # reversed too
+
+            for i in range(len(self._dims)):
+                if self._dims[i] < 0 or \
+                    self._dims[i] == 0 and len(self._dims) > 1:
+                    raise TypeError, "invalid Array dimensions"
+
+                if offset > 0:
+                    self._poss[i] = offset % self._dims[i]
+                    offset = int(offset / self._dims[i])
+
+                # Don't break out of the loop if offset is 0 so we test all the
+                # dimensions for > 0.
+            if offset:
+                raise AttributeError, "invalid Array offset"
+
+            a = [None] * self._dims[0]
+
+            for i in range(1, len(self._dims)):
+                b = []
+
+                for j in range(self._dims[i]):
+                    b.append(copy.deepcopy(a))
+
+                a = b
+
+            self.data = a
+
+
+    def _aslist(self, item=None):
+        if item is not None:
+            return self.data[int(item)]
+        else:
+            return self.data
+
+    def _asdict(self, item=None, encoding=Config.dict_encoding):
+        if item is not None:
+            if type(item) in (UnicodeType,StringType):
+                item = item.encode(encoding)
+            return self.data[int(item)]
+        else:
+            retval = {}
+            def fun(x): retval[str(x).encode(encoding)] = self.data[x]
+            
+            map( fun, range(len(self.data)) )
+            return retval
+    def __getitem__(self, item):
+        try:
+            return self.data[int(item)]
+        except ValueError:
+            return getattr(self, item)
+
+    def __len__(self):
+        return len(self.data)
+
+    def __nonzero__(self):
+        return 1
+
+    def __str__(self):
+        return anyType.__str__(self) + ": " + str(self._aslist())
+
+    def _keys(self):
+        return filter(lambda x: x[0] != '_', self.__dict__.keys())
+
+    def _addItem(self, name, value, attrs):
+        if self._full:
+            raise ValueError, "Array is full"
+
+        pos = attrs.get((NS.ENC, 'position'))
+
+        if pos != None:
+            if self._posstate == 0:
+                raise AttributeError, \
+                    "all elements in a sparse Array must have a " \
+                    "position attribute"
+
+            self._posstate = 1
+
+            try:
+                if pos[0] == '[' and pos[-1] == ']':
+                    pos = map (lambda x: int(x), pos[1:-1].split(','))
+                    pos.reverse()
+
+                    if len(pos) == 1:
+                        pos = pos[0]
+
+                        curpos = [0] * len(self._dims)
+
+                        for i in range(len(self._dims)):
+                            curpos[i] = pos % self._dims[i]
+                            pos = int(pos / self._dims[i])
+
+                            if pos == 0:
+                                break
+
+                        if pos:
+                            raise Exception
+                    elif len(pos) != len(self._dims):
+                        raise Exception
+                    else:
+                        for i in range(len(self._dims)):
+                            if pos[i] >= self._dims[i]:
+                                raise Exception
+
+                        curpos = pos
+                else:
+                    raise Exception
+            except:
+                raise AttributeError, \
+                    "invalid Array element position %s" % str(pos)
+        else:
+            if self._posstate == 1:
+                raise AttributeError, \
+                    "only elements in a sparse Array may have a " \
+                    "position attribute"
+
+            self._posstate = 0
+
+            curpos = self._poss
+
+        a = self.data
+
+        for i in range(len(self._dims) - 1, 0, -1):
+            a = a[curpos[i]]
+
+        if curpos[0] >= len(a):
+            a += [None] * (len(a) - curpos[0] + 1)
+
+        a[curpos[0]] = value
+
+        if pos == None:
+            self._poss[0] += 1
+
+            for i in range(len(self._dims) - 1):
+                if self._poss[i] < self._dims[i]:
+                    break
+
+                self._poss[i] = 0
+                self._poss[i + 1] += 1
+
+            if self._dims[-1] and self._poss[-1] >= self._dims[-1]:
+                #self._full = 1
+                #FIXME: why is this occuring?
+                pass
+
+    def _placeItem(self, name, value, pos, subpos, attrs = None):
+        curpos = [0] * len(self._dims)
+
+        for i in range(len(self._dims)):
+            if self._dims[i] == 0:
+                curpos[0] = pos
+                break
+
+            curpos[i] = pos % self._dims[i]
+            pos = int(pos / self._dims[i])
+
+            if pos == 0:
+                break
+
+        if self._dims[i] != 0 and pos:
+            raise Error, "array index out of range"
+
+        a = self.data
+
+        for i in range(len(self._dims) - 1, 0, -1):
+            a = a[curpos[i]]
+
+        if curpos[0] >= len(a):
+            a += [None] * (len(a) - curpos[0] + 1)
+
+        a[curpos[0]] = value
+
+class typedArrayType(arrayType):
+    def __init__(self, data = None, name = None, typed = None, attrs = None,
+        offset = 0, rank = None, asize = 0, elemsname = None, complexType = 0):
+
+        arrayType.__init__(self, data, name, attrs, offset, rank, asize,
+            elemsname)
+
+        self._typed = 1
+        self._type = typed
+        self._complexType = complexType
+
+class faultType(structType, Error):
+    def __init__(self, faultcode = "", faultstring = "", detail = None):
+        self.faultcode = faultcode
+        self.faultstring = faultstring
+        if detail != None:
+            self.detail = detail
+
+        structType.__init__(self, None, 0)
+
+    def _setDetail(self, detail = None):
+        if detail != None:
+            self.detail = detail
+        else:
+            try: del self.detail
+            except AttributeError: pass
+
+    def __repr__(self):
+        if getattr(self, 'detail', None) != None:
+            return "<Fault %s: %s: %s>" % (self.faultcode,
+                                           self.faultstring,
+                                           self.detail)
+        else:
+            return "<Fault %s: %s>" % (self.faultcode, self.faultstring)
+
+    __str__ = __repr__
+
+    def __call__(self):
+        return (self.faultcode, self.faultstring, self.detail)        
+
+class SOAPException(Exception):
+    def __init__(self, code="", string="", detail=None):
+        self.value = ("SOAPpy SOAP Exception", code, string, detail)
+        self.code = code
+        self.string = string
+        self.detail = detail
+
+    def __str__(self):
+        return repr(self.value)
+
+class RequiredHeaderMismatch(Exception):
+    def __init__(self, value):
+        self.value = value
+
+    def __str__(self):
+        return repr(self.value)
+
+class MethodNotFound(Exception):
+    def __init__(self, value):
+        (val, detail) = value.split(":")
+        self.value = val
+        self.detail = detail
+
+    def __str__(self):
+        return repr(self.value, self.detail)
+
+class AuthorizationFailed(Exception):
+    def __init__(self, value):
+        self.value = value
+
+    def __str__(self):
+        return repr(self.value)
+
+class MethodFailed(Exception):
+    def __init__(self, value):
+        self.value = value
+
+    def __str__(self):
+        return repr(self.value)
+        
+#######
+# Convert complex SOAPpy objects to native python equivalents
+#######
+
+def simplify(object, level=0):
+    """
+    Convert the SOAPpy objects and their contents to simple python types.
+
+    This function recursively converts the passed 'container' object,
+    and all public subobjects. (Private subobjects have names that
+    start with '_'.)
+    
+    Conversions:
+    - faultType    --> raise python exception
+    - arrayType    --> array
+    - compoundType --> dictionary
+    """
+    
+    if level > 10:
+        return object
+    
+    if isinstance( object, faultType ):
+        if object.faultstring == "Required Header Misunderstood":
+            raise RequiredHeaderMismatch(object.detail)
+        elif object.faultstring == "Method Not Found":
+            raise MethodNotFound(object.detail)
+        elif object.faultstring == "Authorization Failed":
+            raise AuthorizationFailed(object.detail)
+        elif object.faultstring == "Method Failed":
+            raise MethodFailed(object.detail)
+        else:
+            se = SOAPException(object.faultcode, object.faultstring,
+                               object.detail)
+            raise se
+    elif isinstance( object, arrayType ):
+        data = object._aslist()
+        for k in range(len(data)):
+            data[k] = simplify(data[k], level=level+1)
+        return data
+    elif isinstance( object, compoundType ) or isinstance(object, structType):
+        data = object._asdict()
+        for k in data.keys():
+            if isPublic(k):
+                data[k] = simplify(data[k], level=level+1)
+        return data
+    elif type(object)==DictType:
+        for k in object.keys():
+            if isPublic(k):
+                object[k] = simplify(object[k])
+        return object
+    elif type(object)==list:
+        for k in range(len(object)):
+            object[k] = simplify(object[k])
+        return object
+    else:
+        return object
+
+
+def simplify_contents(object, level=0):
+    """
+    Convert the contents of SOAPpy objects to simple python types.
+
+    This function recursively converts the sub-objects contained in a
+    'container' object to simple python types.
+    
+    Conversions:
+    - faultType    --> raise python exception
+    - arrayType    --> array
+    - compoundType --> dictionary
+    """
+    
+    if level>10: return object
+
+    if isinstance( object, faultType ):
+        for k in object._keys():
+            if isPublic(k):
+                setattr(object, k, simplify(object[k], level=level+1))
+        raise object
+    elif isinstance( object, arrayType ): 
+        data = object._aslist()
+        for k in range(len(data)):
+            object[k] = simplify(data[k], level=level+1)
+    elif isinstance(object, structType):
+        data = object._asdict()
+        for k in data.keys():
+            if isPublic(k):
+                setattr(object, k, simplify(data[k], level=level+1))
+    elif isinstance( object, compoundType ) :
+        data = object._asdict()
+        for k in data.keys():
+            if isPublic(k):
+                object[k] = simplify(data[k], level=level+1)
+    elif type(object)==DictType:
+        for k in object.keys():
+            if isPublic(k):
+                object[k] = simplify(object[k])
+    elif type(object)==list:
+        for k in range(len(object)):
+            object[k] = simplify(object[k])
+    
+    return object
+
+
diff --git a/SOAPpy/URLopener.py b/SOAPpy/URLopener.py
new file mode 100644 (file)
index 0000000..2c04c86
--- /dev/null
@@ -0,0 +1,23 @@
+"""Provide a class for loading data from URL's that handles basic
+authentication"""
+
+ident = '$Id: URLopener.py 541 2004-01-31 04:20:06Z warnes $'
+from version import __version__
+
+from Config import Config
+from urllib import FancyURLopener
+
+class URLopener(FancyURLopener):
+
+    username = None
+    passwd = None
+
+
+    def __init__(self, username=None, passwd=None, *args, **kw):
+        FancyURLopener.__init__( self, *args, **kw)
+        self.username = username
+        self.passwd = passwd
+
+
+    def prompt_user_passwd(self, host, realm):
+       return self.username, self.passwd
diff --git a/SOAPpy/Utilities.py b/SOAPpy/Utilities.py
new file mode 100644 (file)
index 0000000..1b163de
--- /dev/null
@@ -0,0 +1,176 @@
+"""
+################################################################################
+# Copyright (c) 2003, Pfizer
+# Copyright (c) 2001, Cayce Ullman.
+# Copyright (c) 2001, Brian Matthews.
+#
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+# Redistributions of source code must retain the above copyright notice, this
+# list of conditions and the following disclaimer.
+#
+# Redistributions in binary form must reproduce the above copyright notice,
+# this list of conditions and the following disclaimer in the documentation
+# and/or other materials provided with the distribution.
+#
+# Neither the name of actzero, inc. nor the names of its contributors may
+# be used to endorse or promote products derived from this software without
+# specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
+# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+################################################################################
+"""
+
+ident = '$Id: Utilities.py 1298 2006-11-07 00:54:15Z sanxiyn $'
+from version import __version__
+
+import re
+import string
+import sys
+from types import *
+
+# SOAPpy modules
+from Errors import *
+
+################################################################################
+# Utility infielders
+################################################################################
+def collapseWhiteSpace(s):
+    return re.sub('\s+', ' ', s).strip()
+
+def decodeHexString(data):
+    conv = {
+            '0': 0x0, '1': 0x1, '2': 0x2, '3': 0x3, '4': 0x4,
+            '5': 0x5, '6': 0x6, '7': 0x7, '8': 0x8, '9': 0x9,
+            
+            'a': 0xa, 'b': 0xb, 'c': 0xc, 'd': 0xd, 'e': 0xe,
+            'f': 0xf,
+            
+            'A': 0xa, 'B': 0xb, 'C': 0xc, 'D': 0xd, 'E': 0xe,
+            'F': 0xf,
+            }
+    
+    ws = string.whitespace
+
+    bin = ''
+
+    i = 0
+
+    while i < len(data):
+        if data[i] not in ws:
+            break
+        i += 1
+
+    low = 0
+
+    while i < len(data):
+        c = data[i]
+
+        if c in string.whitespace:
+            break
+
+        try:
+            c = conv[c]
+        except KeyError:
+            raise ValueError, \
+                "invalid hex string character `%s'" % c
+
+        if low:
+            bin += chr(high * 16 + c)
+            low = 0
+        else:
+            high = c
+            low = 1
+
+        i += 1
+
+    if low:
+        raise ValueError, "invalid hex string length"
+
+    while i < len(data):
+        if data[i] not in string.whitespace:
+            raise ValueError, \
+                "invalid hex string character `%s'" % c
+
+        i += 1
+
+    return bin
+
+def encodeHexString(data):
+    h = ''
+
+    for i in data:
+        h += "%02X" % ord(i)
+
+    return h
+
+def leapMonth(year, month):
+    return month == 2 and \
+        year % 4 == 0 and \
+        (year % 100 != 0 or year % 400 == 0)
+
+def cleanDate(d, first = 0):
+    ranges = (None, (1, 12), (1, 31), (0, 23), (0, 59), (0, 61))
+    months = (0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)
+    names = ('year', 'month', 'day', 'hours', 'minutes', 'seconds')
+
+    if len(d) != 6:
+        raise ValueError, "date must have 6 elements"
+
+    for i in range(first, 6):
+        s = d[i]
+
+        if type(s) == FloatType:
+            if i < 5:
+                try:
+                    s = int(s)
+                except OverflowError:
+                    if i > 0:
+                        raise
+                    s = long(s)
+
+                if s != d[i]:
+                    raise ValueError, "%s must be integral" % names[i]
+
+                d[i] = s
+        elif type(s) == LongType:
+            try: s = int(s)
+            except: pass
+        elif type(s) != IntType:
+            raise TypeError, "%s isn't a valid type" % names[i]
+
+        if i == first and s < 0:
+            continue
+
+        if ranges[i] != None and \
+            (s < ranges[i][0] or ranges[i][1] < s):
+            raise ValueError, "%s out of range" % names[i]
+
+    if first < 6 and d[5] >= 61:
+        raise ValueError, "seconds out of range"
+
+    if first < 2:
+        leap = first < 1 and leapMonth(d[0], d[1])
+
+        if d[2] > months[d[1]] + leap:
+            raise ValueError, "day out of range"
+
+def debugHeader(title):
+    s = '*** ' + title + ' '
+    print s + ('*' * (72 - len(s)))
+
+def debugFooter(title):
+    print '*' * 72
+    sys.stdout.flush()
diff --git a/SOAPpy/WSDL.py b/SOAPpy/WSDL.py
new file mode 100644 (file)
index 0000000..84f7d3f
--- /dev/null
@@ -0,0 +1,137 @@
+"""Parse web services description language to get SOAP methods.
+
+Rudimentary support."""
+
+ident = '$Id: WSDL.py 1467 2008-05-16 23:32:51Z warnes $'
+from version import __version__
+
+import wstools
+import xml
+from Errors import Error
+from Client import SOAPProxy, SOAPAddress
+from Config import Config
+import urllib
+
+class Proxy:
+    """WSDL Proxy.
+    
+    SOAPProxy wrapper that parses method names, namespaces, soap actions from
+    the web service description language (WSDL) file passed into the
+    constructor.  The WSDL reference can be passed in as a stream, an url, a
+    file name, or a string.
+
+    Loads info into self.methods, a dictionary with methodname keys and values
+    of WSDLTools.SOAPCallinfo.
+
+    For example,
+    
+        url = 'http://www.xmethods.org/sd/2001/TemperatureService.wsdl'
+        wsdl = WSDL.Proxy(url)
+        print len(wsdl.methods)          # 1
+        print wsdl.methods.keys()        # getTemp
+
+
+    See WSDLTools.SOAPCallinfo for more info on each method's attributes.
+    """
+
+    def __init__(self, wsdlsource, config=Config, **kw ):
+
+        reader = wstools.WSDLTools.WSDLReader()
+        self.wsdl = None
+
+        # From Mark Pilgrim's "Dive Into Python" toolkit.py--open anything.
+        if self.wsdl is None and hasattr(wsdlsource, "read"):
+            print 'stream:', wsdlsource
+            try:
+                self.wsdl = reader.loadFromStream(wsdlsource)
+            except xml.parsers.expat.ExpatError, e:
+                newstream = urllib.URLopener(key_file=config.SSL.key_file, cert_file=config.SSL.cert_file).open(wsdlsource)
+                buf = newstream.readlines()
+                raise Error, "Unable to parse WSDL file at %s: \n\t%s" % \
+                      (wsdlsource, "\t".join(buf))
+                
+
+        # NOT TESTED (as of April 17, 2003)
+        #if self.wsdl is None and wsdlsource == '-':
+        #    import sys
+        #    self.wsdl = reader.loadFromStream(sys.stdin)
+        #    print 'stdin'
+
+        if self.wsdl is None:
+            try: 
+                file(wsdlsource)
+                self.wsdl = reader.loadFromFile(wsdlsource)
+                #print 'file'
+            except (IOError, OSError): pass
+            except xml.parsers.expat.ExpatError, e:
+                newstream = urllib.urlopen(wsdlsource)
+                buf = newstream.readlines()
+                raise Error, "Unable to parse WSDL file at %s: \n\t%s" % \
+                      (wsdlsource, "\t".join(buf))
+            
+        if self.wsdl is None:
+            try:
+                stream = urllib.URLopener(key_file=config.SSL.key_file, cert_file=config.SSL.cert_file).open(wsdlsource)
+                self.wsdl = reader.loadFromStream(stream, wsdlsource)
+            except (IOError, OSError): pass
+            except xml.parsers.expat.ExpatError, e:
+                newstream = urllib.urlopen(wsdlsource)
+                buf = newstream.readlines()
+                raise Error, "Unable to parse WSDL file at %s: \n\t%s" % \
+                      (wsdlsource, "\t".join(buf))
+            
+        if self.wsdl is None:
+            import StringIO
+            self.wsdl = reader.loadFromString(str(wsdlsource))
+            #print 'string'
+
+        # Package wsdl info as a dictionary of remote methods, with method name
+        # as key (based on ServiceProxy.__init__ in ZSI library).
+        self.methods = {}
+        service = self.wsdl.services[0]
+        port = service.ports[0]
+        name = service.name
+        binding = port.getBinding()
+        portType = binding.getPortType()
+        for operation in portType.operations:
+            callinfo = wstools.WSDLTools.callInfoFromWSDL(port, operation.name)
+            self.methods[callinfo.methodName] = callinfo
+
+        self.soapproxy = SOAPProxy('http://localhost/dummy.webservice',
+                                   config=config, **kw)
+
+    def __str__(self): 
+        s = ''
+        for method in self.methods.values():
+            s += str(method)
+        return s
+
+    def __getattr__(self, name):
+        """Set up environment then let parent class handle call.
+
+        Raises AttributeError is method name is not found."""
+
+        if not self.methods.has_key(name): raise AttributeError, name
+
+        callinfo = self.methods[name]
+        self.soapproxy.proxy = SOAPAddress(callinfo.location)
+        self.soapproxy.namespace = callinfo.namespace
+        self.soapproxy.soapaction = callinfo.soapAction
+        return self.soapproxy.__getattr__(name)
+
+    def show_methods(self):
+        for key in self.methods.keys():
+            method = self.methods[key]
+            print "Method Name:", key.ljust(15)
+            print
+            inps = method.inparams
+            for parm in range(len(inps)):
+                details = inps[parm]
+                print "   In #%d: %s  (%s)" % (parm, details.name, details.type)
+            print
+            outps = method.outparams
+            for parm in range(len(outps)):
+                details = outps[parm]
+                print "   Out #%d: %s  (%s)" % (parm, details.name, details.type)
+            print
+
diff --git a/SOAPpy/__init__.py b/SOAPpy/__init__.py
new file mode 100644 (file)
index 0000000..f5f5419
--- /dev/null
@@ -0,0 +1,15 @@
+
+ident = '$Id: __init__.py 541 2004-01-31 04:20:06Z warnes $'
+from version import __version__
+
+from Client      import *
+from Config      import *
+from Errors      import *
+from NS          import *
+from Parser      import *
+from SOAPBuilder import *
+from Server      import *
+from Types       import *
+from Utilities     import *
+import wstools
+import WSDL
diff --git a/SOAPpy/version.py b/SOAPpy/version.py
new file mode 100644 (file)
index 0000000..dd00d45
--- /dev/null
@@ -0,0 +1,2 @@
+__version__="0.12.5"
+