diff -Nru ladon.old/interfaces/__init__.py ladon/interfaces/__init__.py --- ladon.old/interfaces/__init__.py 2013-07-02 12:13:33.300028612 +0200 +++ ladon/interfaces/__init__.py 2013-07-02 12:14:42.540029684 +0200 @@ -72,4 +72,5 @@ import ladon.interfaces.soap11 import ladon.interfaces.jsonwsp import ladon.interfaces.jsonrpc10 -import ladon.interfaces.xmlrpc \ No newline at end of file +import ladon.interfaces.xmlrpc +import ladon.interfaces.soapdocumentliteral diff -Nru ladon.old/interfaces/soapdocumentliteral.py ladon/interfaces/soapdocumentliteral.py --- ladon.old/interfaces/soapdocumentliteral.py 1970-01-01 01:00:00.000000000 +0100 +++ ladon/interfaces/soapdocumentliteral.py 2013-07-02 12:14:12.850029224 +0200 @@ -0,0 +1,806 @@ +# -*- coding: utf-8 -*- +""" +Uses Document/literal rather than RPC/encoded. +https://bugs.launchpad.net/ladon/+bug/1096004 +""" +from ladon.interfaces.base import BaseInterface,ServiceDescriptor,BaseRequestHandler,BaseResponseHandler,BaseFaultHandler +from ladon.interfaces import expose +from ladon.compat import PORTABLE_STRING,type_to_xsd,pytype_support,BytesIO +from xml.sax.handler import ContentHandler,feature_namespaces +from xml.sax import make_parser +from xml.sax.xmlreader import InputSource +import sys,re,traceback + +rx_nil_attr = re.compile(PORTABLE_STRING('^\w*[:]{0,1}nil$'),re.I) + +class SOAPServiceDescriptor(ServiceDescriptor): + + xsd_type_map = type_to_xsd + _content_type = 'text/xml' + + def generate(self,servicename,servicenumber,typemanager,methodlist,service_url,encoding): + """ + Generate WSDL file for DocumentLiteralSOAPInterface + """ + type_dict = typemanager.type_dict + type_order = typemanager.type_order + + def map_type(typ): + if typ in SOAPServiceDescriptor.xsd_type_map: + return SOAPServiceDescriptor.xsd_type_map[typ] + else: + return typ.__name__ + + import xml.dom.minidom as md + doc = md.Document() + + # SERVICE DEFINITION + # Create the definitions element for the service + definitions = doc.createElement('definitions') + definitions.setAttribute('xmlns','http://schemas.xmlsoap.org/wsdl/') + definitions.setAttribute('xmlns:SOAP','http://schemas.xmlsoap.org/wsdl/soap/') + definitions.setAttribute('xmlns:SOAP-ENC','http://schemas.xmlsoap.org/soap/encoding/') + definitions.setAttribute('xmlns:SOAP-ENV','http://schemas.xmlsoap.org/soap/envelope/') + definitions.setAttribute('xmlns:WSDL','http://schemas.xmlsoap.org/wsdl/') + definitions.setAttribute('xmlns:mime','http://schemas.xmlsoap.org/wsdl/mime/') + definitions.setAttribute('xmlns:xsd','http://www.w3.org/2001/XMLSchema') + definitions.setAttribute('xmlns:xsi','http://www.w3.org/2001/XMLSchema-instance') + definitions.setAttribute('name', servicename) + definitions.setAttribute('xmlns:ns%d' % servicenumber,'urn:%s' % servicename) + definitions.setAttribute('xmlns:tns', 'http://tempuri.org/') + definitions.setAttribute('targetNamespace', 'http://tempuri.org/') + doc.appendChild(definitions) + + # TYPES + # The types element + types = doc.createElement('types') + definitions.appendChild(types) + + # Service schema for types required by the target namespace we defined in the definition element + schema = doc.createElement('schema') + schema.setAttribute('xmlns','http://www.w3.org/2001/XMLSchema') + schema.setAttribute('xmlns:SOAP-ENC','http://schemas.xmlsoap.org/soap/encoding/') + schema.setAttribute('xmlns:SOAP-ENV','http://schemas.xmlsoap.org/soap/envelope/') + schema.setAttribute('xmlns:tns', 'http://schemas.microsoft.com/2003/10/Serialization/Arrays') + schema.setAttribute('elementFormDefault','qualified') + schema.setAttribute('xmlns:xsd','http://www.w3.org/2001/XMLSchema') + schema.setAttribute('xmlns:xsi','http://www.w3.org/2001/XMLSchema-instance') + schema.setAttribute('targetNamespace', 'http://schemas.microsoft.com/2003/10/Serialization/Arrays') + types.appendChild(schema) + + # Import namespace schema + import_tag = doc.createElement('import') + import_tag.setAttribute('namespace','http://schemas.xmlsoap.org/soap/encoding/') + schema.appendChild(import_tag) + + # Define types, the type_order variable holds all that need to be defined and in the + # correct order. + # * If a list is encountered as a type it will be handled as a complex type with a single element reflecting the inner type. + # * LadonTypes (identified by being contained in type_dict) are also handled as complex types with an element-tag per attribute + # * Primitive types (either as LadonType attributes or list inner-types) are added as xsd - SOAP types. + for typ in type_order: + if isinstance(typ, list): + inner = typ[0] + # TODO: add missing attributes + schema.setAttribute('xmlns:tns','http://schemas.microsoft.com/2003/10/Serialization/Arrays') + schema.setAttribute('targetNamespace','http://schemas.microsoft.com/2003/10/Serialization/Arrays') + complextype = doc.createElement('complexType') + complextype.setAttribute('name','ArrayOf%s' % inner.__name__) + schema.appendChild(complextype) + sequence = doc.createElement('sequence') + complextype.appendChild(sequence) + element = doc.createElement('element') + element.setAttribute('name','item') + if inner in type_dict: + element.setAttribute('type','tns:%s' % (inner.__name__)) + else: + element.setAttribute('type','xsd:%s' % map_type(inner)) + element.setAttribute('minOccurs','0') + element.setAttribute('maxOccurs','unbounded') + sequence.appendChild(element) + attribute = doc.createElement('attribute') + attribute.setAttribute('ref','SOAP-ENC:arrayType') + if inner in type_dict: + attribute.setAttribute('WSDL:arrayType','tns:%s[]' % (inner.__name__)) + else: + attribute.setAttribute('WSDL:arrayType','xsd:%s[]' % map_type(inner)) + element = doc.createElement('element') + schema.appendChild(element) + element.setAttribute('name','ArrayOf%s' % inner.__name__) + element.setAttribute('nillable','true') + element.setAttribute('type','tns:ArrayOf%s' % inner.__name__) + + else: + complextype = doc.createElement('complexType') + complextype.setAttribute('name',typ['name']) + schema.appendChild(complextype) + sequence = doc.createElement('sequence') + complextype.appendChild(sequence) + for k,v,props in typ['attributes']: + element = doc.createElement('element') + element.setAttribute('name',k.replace('_','-')) + element.setAttribute('maxOccurs','1') + element.setAttribute('minOccurs','1') + if props.get('nullable')==True: + element.setAttribute('minOccurs','0') + element.setAttribute('nillable','true') + if isinstance(v, list): + inner = v[0] + # TODO: namespace + # element.setAttribute('type','ns%d:ArrayOf%s' % (servicenumber,inner.__name__)) + element.setAttribute('type','tns:ArrayOf%s' % (inner.__name__)) + element.setAttribute('minOccurs','0') + element.setAttribute('nillable','true') + else: + if v in type_dict: + element.setAttribute('type','tns:%s' % (v.__name__)) + else: + element.setAttribute('type','xsd:%s' % map_type(v)) + sequence.appendChild(element) + element = doc.createElement('element') + schema.appendChild(element) + element.setAttribute('name','%s' % typ['name']) + element.setAttribute('nillable','true') # TODO: verify attributes are correct + element.setAttribute('type','tns:%s' % typ['name']) + + """ + + + + + + + + + + + + + + + + + + """ + schema = doc.createElement('schema') + schema.setAttribute('xmlns','http://www.w3.org/2001/XMLSchema') + schema.setAttribute('elementFormDefault','qualified') + schema.setAttribute('targetNamespace', 'http://tempuri.org/') + import_element = doc.createElement('import') + import_element.setAttribute('namespace', 'http://schemas.microsoft.com/2003/10/Serialization/Arrays') + schema.appendChild(import_element) + for m in methodlist: + # input message + element = doc.createElement('element') + element.setAttribute('name', m.name()) + schema.appendChild(element) + complextype = doc.createElement('complexType') + element.appendChild(complextype) + schema.appendChild(element) + sequence = doc.createElement('sequence') + complextype.appendChild(sequence) + for arg in m.args(): + # TODO: add the elements e.g. + element = doc.createElement('element') + sequence.appendChild(element) + element.setAttribute('name', arg['name'].replace('_','-')) + if isinstance(arg['type'], (list, tuple)): + element.setAttribute('type','tns:ArrayOf%s' % (arg['type'][0].__name__)) + else: + if arg['type'] in type_dict: + element.setAttribute('type','tns:%s' % (arg['type'].__name__)) + else: + element.setAttribute('type','xsd:%s' % map_type(arg['type'])) + + # output message + # element response begins + element = doc.createElement('element') + element.setAttribute('name', "%sResponse" % m.name()) + schema.appendChild(element) + complextype = doc.createElement('complexType') + element.appendChild(complextype) + sequence = doc.createElement('sequence') + complextype.appendChild(sequence) + if isinstance(m._rtype, (list, tuple)): + element = doc.createElement('element') + element.setAttribute('name','result') + element.setAttribute('type','q%d:ArrayOf%s' % (servicenumber,m._rtype[0].__name__)) + element.setAttribute('xmlns:q%d' % servicenumber, 'http://schemas.microsoft.com/2003/10/Serialization/Arrays') + element.setAttribute('minOccurs','0') + element.setAttribute('nillable','true') + sequence.appendChild(element) + elif m._rtype in type_dict: + for k,v,props in type_dict[m._rtype]['attributes']: + element = doc.createElement('element') + element.setAttribute('name', k.replace('_','-')) + #part.setAttribute('maxOccurs','1') + if isinstance(v, list): + inner = v[0] + element.setAttribute('type','tns:ArrayOf%s' % (inner.__name__)) + element.setAttribute('xmlns:q%d' % servicenumber, 'http://schemas.microsoft.com/2003/10/Serialization/Arrays') + element.setAttribute('minOccurs','0') + element.setAttribute('nillable','true') + else: + if v in type_dict: + element.setAttribute('type','tns:%s' % (v.__name__)) + else: + element.setAttribute('type','xsd:%s' % map_type(v)) + #part.setAttribute('minOccurs','1') + sequence.appendChild(element) + else: + element = doc.createElement('element') + element.setAttribute('name','result') + element.setAttribute('type','xsd:%s' % map_type(m._rtype)) + sequence.appendChild(element) + # element response ends + types.appendChild(schema) + + + # + # + # + # + # + # + # + # + # + # + + # + # + # + # + # + + # + # + # + # + + """ + + + + + + + + + """ + + for m in methodlist: + # input message + message = doc.createElement('message') + message.setAttribute('name', m.name()) + definitions.appendChild(message) + + part = doc.createElement('part') + message.appendChild(part) + # part.setAttribute('name',arg['name'].replace('_','-')) + part.setAttribute('name', 'parameters') + part.setAttribute('element', "tns:%s" % m.name()) + + # output message + message = doc.createElement('message') + message.setAttribute('name',"%sResponse" % m.name()) + definitions.appendChild(message) + part = doc.createElement('part') + message.appendChild(part) + part.setAttribute('name', 'parameters') + part.setAttribute('element',"tns:%sResponse" % m.name()) + """ + if isinstance(m._rtype, (list, tuple)): + part = doc.createElement('part') + part.setAttribute('name','result') + part.setAttribute('type','ns%d:ArrayOf%s' % (servicenumber,m._rtype[0].__name__)) + message.appendChild(part) + elif m._rtype in type_dict: + for k,v,props in type_dict[m._rtype]['attributes']: + part = doc.createElement('part') + part.setAttribute('name',k.replace('_','-')) + #part.setAttribute('maxOccurs','1') + if isinstance(v, list): + inner = v[0] + part.setAttribute('type','ns%d:ArrayOf%s' % (servicenumber,inner.__name__)) + #part.setAttribute('minOccurs','0') + #part.setAttribute('nillable','true') + else: + if v in type_dict: + part.setAttribute('type','ns%d:%s' % (servicenumber,v.__name__)) + else: + part.setAttribute('type','xsd:%s' % map_type(v)) + #part.setAttribute('minOccurs','1') + message.appendChild(part) + else: + part = doc.createElement('part') + part.setAttribute('name','result') + part.setAttribute('type','xsd:%s' % map_type(m._rtype)) + message.appendChild(part) + """ + + # + # + #Service definition of function ns2__createUser + # + # + # + porttype = doc.createElement('portType') + porttype.setAttribute('name','%sPortType' % servicename) + definitions.appendChild(porttype) + + for m in methodlist: + operation = doc.createElement('operation') + operation.setAttribute('name',m.name()) + porttype.appendChild(operation) + if m.__doc__: + documentation = doc.createElement('documentation') + documentation.appendChild(doc.createTextNode(m.__doc__)) + operation.appendChild(documentation) + input_tag = doc.createElement('input') + input_tag.setAttribute('message','tns:%s' % m.name()) + operation.appendChild(input_tag) + output_tag = doc.createElement('output') + output_tag.setAttribute('message','tns:%sResponse' % m.name()) + operation.appendChild(output_tag) + + + + """ + + + + + + + + + + + + """ + + binding = doc.createElement('binding') + binding.setAttribute('name',servicename) + binding.setAttribute('type',"tns:%sPortType" % servicename) + transport = doc.createElement('SOAP:binding') + transport.setAttribute('transport','http://schemas.xmlsoap.org/soap/http') + binding.appendChild(transport) + definitions.appendChild(binding) + + for m in methodlist: + operation = doc.createElement('operation') + operation.setAttribute('name',m.name()) + binding.appendChild(operation) + soapaction = doc.createElement('SOAP:operation') + soapaction.setAttribute('style','document') + soapaction.setAttribute('soapAction',"%s/%s" % (service_url,m.name())) + operation.appendChild(soapaction) + + m._multipart_response_required + input_tag = doc.createElement('input') + body_parent = input_tag + if m._multipart_request_required: + multipart_related = doc.createElement('mime:multipartRelated') + mime_body_part = doc.createElement('mime:part') + body_parent = mime_body_part + mime_content_part = doc.createElement('mime:part') + mime_content = doc.createElement('mime:content') + mime_content.setAttribute('type','*/*') + input_tag.appendChild(multipart_related) + multipart_related.appendChild(mime_body_part) + multipart_related.appendChild(mime_content_part) + mime_content_part.appendChild(mime_content) + + input_soapbody = doc.createElement('SOAP:body') + input_soapbody.setAttribute('use','literal') + body_parent.appendChild(input_soapbody) + operation.appendChild(input_tag) + output_tag = doc.createElement('output') + body_parent = output_tag + if m._multipart_request_required: + multipart_related = doc.createElement('mime:multipartRelated') + mime_body_part = doc.createElement('mime:part') + body_parent = mime_body_part + mime_content_part = doc.createElement('mime:part') + mime_content = doc.createElement('content:part') + mime_content.setAttribute('type','*/*') + output_tag.appendChild(multipart_related) + multipart_related.appendChild(mime_body_part) + multipart_related.appendChild(mime_content_part) + mime_content_part.appendChild(mime_content) + + output_soapbody = doc.createElement('SOAP:body') + output_soapbody.setAttribute('use','literal') + body_parent.appendChild(output_soapbody) + operation.appendChild(output_tag) + + + # + #gSOAP 2.7.10 generated service definition + # + # + # + # + + service = doc.createElement('service') + service.setAttribute('name',servicename) + documentation = doc.createElement('documentation') + documentation.appendChild(doc.createTextNode('Ladon generated service definition')) + service.appendChild(documentation) + port = doc.createElement('port') + port.setAttribute('name',servicename) + port.setAttribute('binding','tns:%s' % servicename) + service.appendChild(port) + address = doc.createElement('SOAP:address') + address.setAttribute('location',service_url) + port.appendChild(address) + definitions.appendChild(service) + if sys.version_info[0]>=3: + return doc.toxml() + return doc.toxml(encoding) + + +def u(instring): + if sys.version_info[0]==2: + return PORTABLE_STRING(instring,'utf-8') + else: + return PORTABLE_STRING(instring) + +class ContainerSetRef(object): + def __init__(self,c,refval): + self.c = c + self.refval = refval + + def set(self,val): + self.c[self.refval] = val + +class SOAPContentHandler(ContentHandler): + # Define relevant XML parser states + ENVELOPE_IN = 1 + HEADER_IN = 2 + BODY_IN = 3 + METHOD_IN = 4 + ARGS_IN = 5 + ARGS_OUT = 6 + + def __init__(self,parser): + """ + Initialize + """ + self.parser = parser + self.depth = 0 + self.method_depth = -1 + self.state = None + self.req_dict = {'args':{}} + self.current_arg_object = self.req_dict['args'] + self.arg_object_stack = [] + self.arg_tagname_stack = [] + self.prev_depth = None + self.in_cdata = False + self.multi_ref_hrefs = {} + self.multi_ref_ids = {} + self.cur_id_depth = None + self.cur_id_val = None + + def startCdataSection(self): + """ + Mark that parser is in CDATA Section. + """ + self.in_cdata = True + + def endCdataSection(self): + """ + Mark that parser stepped out of a CDATA Section. + """ + self.in_cdata = False + + def startDocument(self): + """ + Setup XMLParser callbacks, callbacks are setup here as the XMLParser + is not initialized by XMLReader before it starts parsing. + """ + self.parser._parser.StartCdataSectionHandler = self.startCdataSection + self.parser._parser.EndCdataSectionHandler = self.endCdataSection + + def endDocument(self): + for id_val,v in self.multi_ref_hrefs.items(): + if id_val in self.multi_ref_ids: + v.set(self.multi_ref_ids[id_val]) + + def startElement(self,name,attrs): + """ + Handle element entrance. + """ + # FIXME: qucik fix for name conversion + is_null = False + href_val,id_val = None,None + for ak,av in attrs.items(): + if rx_nil_attr.match(ak) and av.lower()=='true': + is_null = True + elif ak.lower()=='href': + href_val = av[1:] + elif ak.lower()=='id': + self.cur_id_val = av + self.cur_id_depth = self.depth + self.multi_ref_ids[av] = {} + self.current_arg_object = self.multi_ref_ids[av] + + name = name.replace('-', '_') + # Split element tag into tagname and prefix if nessecary. + m = name.split(':') + if len(m)>1: + prefix,tagname = m + else: + prefix,tagname = [''] + m + # Detect key positions in the SOAP envolope updating the parse pregress state. + if not self.depth and tagname.lower()=='envelope': + self.state = self.ENVELOPE_IN + elif self.state == self.ENVELOPE_IN and tagname.lower()=='header': + self.state = self.HEADER_IN + elif self.state in [ self.HEADER_IN,self.ENVELOPE_IN ] and tagname.lower()=='body': + self.state = self.BODY_IN + elif self.state == self.BODY_IN: + # Stepping into the method-name tag. + self.state = self.METHOD_IN + self.req_dict['methodname'] = tagname + self.method_depth = self.depth + elif self.state == self.METHOD_IN: + # Stepping into the arguments section. + self.state = self.ARGS_IN + + if self.state == self.ARGS_IN or self.cur_id_val: + # Starting the argument parsing. + self.pickup_content = u('') + + if is_null: + newobject = None + else: + newobject = {} + if tagname.lower() == 'item': + # If the tagname "item" is intercepted it means that we are looking at an array. + # Therefore the parent dict must be re-assigned to a list object and all objects + # that follow (until the parser steps out of the item tag) be appended to this + # list. Lists are ignored by the stack of objects and tagnames + parent_tagname = self.arg_tagname_stack[-1:][0] + parent_object = self.arg_object_stack[-1:][0] + if isinstance(parent_object[parent_tagname], dict): + # first child object + parent_object[parent_tagname] = [newobject] + else: + # 2nd, 3rd ... + parent_object[parent_tagname] += [newobject] + if href_val: + self.multi_ref_hrefs[href_val] = ContainerSetRef(parent_object,len(parent_object)-1) + else: + # Append to the stacks + self.current_arg_object[tagname] = newobject + self.arg_object_stack.append(self.current_arg_object) + self.arg_tagname_stack.append(tagname) + if href_val: + self.multi_ref_hrefs[href_val] = ContainerSetRef(self.current_arg_object,tagname) + self.current_arg_object = newobject + self.prev_depth = self.depth + self.depth += 1 + + + def endElement(self,name): + # FIXME: qucik fix for name conversion + name = name.replace('-', '_') + self.depth -= 1 + # Split element tag into tagname and prefix if nessecary. + m = name.split(':') + if len(m)>1: + prefix,tagname = m + else: + prefix,tagname = [''] + m + # Stop collecting arguments when the parser steps out of the method-name tag. + if self.depth==self.method_depth: + # State ARGS_OUT means done parsing arguments. + self.state = self.ARGS_OUT + # In all other occations than tagname=='item' pop the stack of argument objects and tagnames. + if self.depth==self.cur_id_depth: + if len(self.multi_ref_ids[self.cur_id_val].items()[0][1]): + self.multi_ref_ids[self.cur_id_val] = self.multi_ref_ids[self.cur_id_val].items()[0][1] + else: + self.multi_ref_ids[self.cur_id_val] = self.pickup_content + self.cur_id_val = None + self.cur_id_depth = None + if self.state == self.ARGS_IN or self.cur_id_val: + if not tagname.lower()=='item': + if self.depth == self.prev_depth: + # If parser is stepping out of the element it previously entered it means that + # there were no child elements, therefore content in between becomes actual + # argument data. + parent_tagname = self.arg_tagname_stack[-1:][0] + parent_object = self.arg_object_stack[-1:][0] + if not parent_object[parent_tagname]==None: + parent_object[parent_tagname] = self.pickup_content + # pop stack of argument objects and tagnames. + self.current_arg_object = self.arg_object_stack.pop() + self.arg_tagname_stack.pop() + else: + # If list object is empty it must be a primitive, so replace + # dict with pickup_content + parent_tagname = self.arg_tagname_stack[-1:][0] + parent_object = self.arg_object_stack[-1:][0] + if len(parent_object[parent_tagname][-1:][0])==0: + parent_object[parent_tagname][-1:] = [self.pickup_content] + + + def characters(self,content): + # Collect data continuosly, reset every time a new element is entered. + # If it has been detected that the parser is in a CDATA Section all content + # will be collected as is (raw) otherwise content is stripped. + if self.state == self.ARGS_IN or self.cur_id_val: + if self.in_cdata: + # in CDATA Section + self.pickup_content += content + else: + self.pickup_content += content.strip() + + +class SOAPRequestHandler(BaseRequestHandler): + + def parse_request(self,soap_body,sinfo,encoding): + parser = make_parser() + ch = SOAPContentHandler(parser) + parser.setContentHandler(ch) + inpsrc = InputSource() + inpsrc.setByteStream(BytesIO(soap_body)) + parser.parse(inpsrc) + return ch.req_dict + + +class SOAPResponseHandler(BaseResponseHandler): + + _content_type = 'text/xml' + _stringify_res_dict = True + + @staticmethod + def value_to_soapxml(value,parent,doc,is_toplevel=False): + if isinstance(value, dict): + for attr_name,attr_val in value.items(): + xml_attr_name = attr_name.replace('_','-') + attr_elem = doc.createElement(xml_attr_name) + parent.appendChild(attr_elem) + SOAPResponseHandler.value_to_soapxml(attr_val,attr_elem,doc) + else: + if is_toplevel: + value_parent = doc.createElement('result') + parent.appendChild(value_parent) + else: + value_parent = parent + + if isinstance(value, (list, tuple)): + if not len(value): + # Translate empty response arrays to SOAP Null (xsi:nil) value + value_parent.setAttribute('xsi:nil','true') + else: + for item in value: + item_element = doc.createElement('item') + item_element.setAttribute('xmlns', 'http://schemas.microsoft.com/2003/10/Serialization/Arrays') + SOAPResponseHandler.value_to_soapxml(item,item_element,doc) + value_parent.appendChild(item_element) + else: + if value==None: + # Set xsi:nil to true if value is None + value_parent.setAttribute('xsi:nil','true') + else: + value_parent.appendChild(doc.createTextNode(value)) + + @staticmethod + def value_to_soapxml_patch(value,parent,doc,is_toplevel=False): + if isinstance(value, dict): + for attr_name,attr_val in value.items(): + xml_attr_name = attr_name.replace('_','-') + attr_elem = doc.createElement(xml_attr_name) + parent.appendChild(attr_elem) + SOAPResponseHandler.value_to_soapxml(attr_val,attr_elem,doc) + else: + if is_toplevel: + value_parent = doc.createElement('result') + parent.appendChild(value_parent) + else: + value_parent = parent + + if isinstance(value, (list, tuple)): + array_element = doc.createElement('SOAP-ENC:Array') + array_element.setAttribute('SOAP-ENC:arrayType', 'xsd:string[%s]' % len(value)) + for item in value: + item_element = doc.createElement('item') + SOAPResponseHandler.value_to_soapxml(item,item_element,doc) + array_element.appendChild(item_element) + value_parent.appendChild(array_element) + else: + value_parent.setAttribute('xsi:type', 'xsd:string') + value_parent.appendChild(doc.createTextNode(value)) + + + def build_response(self,res_dict,sinfo,encoding): + import xml.dom.minidom as md + doc = md.Document() + envelope = doc.createElement('SOAP-ENV:Envelope') + envelope.setAttribute('xmlns:SOAP-ENV','http://schemas.xmlsoap.org/soap/envelope/') + envelope.setAttribute('xmlns:SOAP-ENC','http://schemas.xmlsoap.org/soap/encoding/') + envelope.setAttribute('xmlns:xsi','http://www.w3.org/2001/XMLSchema-instance') + envelope.setAttribute('xmlns:xsd','http://www.w3.org/2001/XMLSchema') + envelope.setAttribute('xmlns:ns','urn:%s' % res_dict['servicename']) + doc.appendChild(envelope) + body_elem = doc.createElement('SOAP-ENV:Body') + body_elem.setAttribute('SOAP-ENV:encodingStyle','http://schemas.xmlsoap.org/soap/encoding/') + envelope.appendChild(body_elem) + method_elem = doc.createElement("%sResponse" % res_dict['method']) + method_elem.setAttribute('xmlns', 'http://tempuri.org/') + SOAPResponseHandler.value_to_soapxml(res_dict['result'],method_elem,doc,is_toplevel=True) + body_elem.appendChild(method_elem) + return doc.toxml(encoding=encoding) + +class SOAPFaultHandler(BaseFaultHandler): + + _content_type = 'text/xml' + _stringify_res_dict = True + soapfault_template = """ + + + + + + + + +""" + + def build_fault_response(self,service_exc,sinfo,methodname,encoding,reflection): + import xml.dom.minidom as md + if service_exc.detail: + detail = service_exc.detail + else: + detail = traceback.format_exc() + d = md.parseString(self.soapfault_template) + # Extract fault DOM elements + faultcode_elem = d.getElementsByTagName('faultcode')[0] + faultstring_elem = d.getElementsByTagName('faultstring')[0] + detail_elem = d.getElementsByTagName('detail')[0] + # Set the fault values + faultcode_elem.appendChild(d.createTextNode(service_exc.faultcode)) + faultstring_elem.appendChild(d.createTextNode(service_exc.faultstring)) + detail_elem.appendChild(d.createTextNode(detail)) + # Return the SoapFault XML object + return d.toxml(encoding=encoding) + +@expose +class DocumentLiteralSOAPInterface(BaseInterface): + """ + Uses Document/literal rather than RPC/encoded. + https://bugs.launchpad.net/ladon/+bug/1096004 + """ + + def __init__(self,sinfo,**kw): + def_kw = { + 'service_descriptor': SOAPServiceDescriptor, + 'request_handler': SOAPRequestHandler, + 'response_handler': SOAPResponseHandler, + 'fault_handler': SOAPFaultHandler} + def_kw.update(kw) + BaseInterface.__init__(self,sinfo,**def_kw) + + @staticmethod + def _interface_name(): + return 'soapdocumentliteral' + + @staticmethod + def _accept_basetype(typ): + return pytype_support.count(typ)>0 + + @staticmethod + def _accept_list(): + return True + + @staticmethod + def _accept_dict(): + return False +