You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

1170 lines
40 KiB

  1. # Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
  2. #
  3. # This software is subject to the provisions of the Zope Public License,
  4. # Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
  5. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
  6. # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  7. # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
  8. # FOR A PARTICULAR PURPOSE.
  9. from Utility import DOM, Collection
  10. from XMLSchema import XMLSchema
  11. from StringIO import StringIO
  12. import urllib
  13. class WSDLReader:
  14. """A WSDLReader creates WSDL instances from urls and xml data."""
  15. # Custom subclasses of WSDLReader may wish to implement a caching
  16. # strategy or other optimizations. Because application needs vary
  17. # so widely, we don't try to provide any caching by default.
  18. def loadFromStream(self, file):
  19. """Return a WSDL instance loaded from a file object."""
  20. document = DOM.loadDocument(file)
  21. wsdl = WSDL()
  22. wsdl.load(document)
  23. return wsdl
  24. def loadFromURL(self, url):
  25. """Return a WSDL instance loaded from the given url."""
  26. document = DOM.loadFromURL(url)
  27. wsdl = WSDL()
  28. wsdl.location = url
  29. wsdl.load(document)
  30. return wsdl
  31. def loadFromString(self, data):
  32. """Return a WSDL instance loaded from an xml string."""
  33. return self.loadFromStream(StringIO(data))
  34. def loadFromFile(self, filename):
  35. """Return a WSDL instance loaded from the given file."""
  36. file = open(filename, 'rb')
  37. try:
  38. wsdl = self.loadFromStream(file)
  39. finally:
  40. file.close()
  41. return wsdl
  42. class WSDL:
  43. """A WSDL object models a WSDL service description. WSDL objects
  44. may be created manually or loaded from an xml representation
  45. using a WSDLReader instance."""
  46. def __init__(self, targetNamespace=None):
  47. self.targetNamespace = targetNamespace or 'urn:this-document.wsdl'
  48. self.documentation = ''
  49. self.location = None
  50. self.document = None
  51. self.name = None
  52. self.services = Collection(self)
  53. self.messages = Collection(self)
  54. self.portTypes = Collection(self)
  55. self.bindings = Collection(self)
  56. self.imports = Collection(self)
  57. self.types = Types(self)
  58. self.extensions = []
  59. def __del__(self):
  60. if self.document is not None:
  61. self.document.unlink()
  62. self.document = None
  63. version = '1.1'
  64. def addService(self, name, documentation=''):
  65. if self.services.has_key(name):
  66. raise WSDLError(
  67. 'Duplicate service element: %s' % name
  68. )
  69. item = Service(name, documentation)
  70. self.services[name] = item
  71. return item
  72. def addMessage(self, name, documentation=''):
  73. if self.messages.has_key(name):
  74. raise WSDLError(
  75. 'Duplicate message element: %s.' % name
  76. )
  77. item = Message(name, documentation)
  78. self.messages[name] = item
  79. return item
  80. def addPortType(self, name, documentation=''):
  81. if self.portTypes.has_key(name):
  82. raise WSDLError(
  83. 'Duplicate portType element: name'
  84. )
  85. item = PortType(name, documentation)
  86. self.portTypes[name] = item
  87. return item
  88. def addBinding(self, name, type, documentation=''):
  89. if self.bindings.has_key(name):
  90. raise WSDLError(
  91. 'Duplicate binding element: %s' % name
  92. )
  93. item = Binding(name, type, documentation)
  94. self.bindings[name] = item
  95. return item
  96. def addImport(self, namespace, location):
  97. item = ImportElement(namespace, location)
  98. self.imports[namespace] = item
  99. return item
  100. def load(self, document):
  101. # We save a reference to the DOM document to ensure that elements
  102. # saved as "extensions" will continue to have a meaningful context
  103. # for things like namespace references. The lifetime of the DOM
  104. # document is bound to the lifetime of the WSDL instance.
  105. self.document = document
  106. definitions = DOM.getElement(document, 'definitions', None, None)
  107. if definitions is None:
  108. raise WSDLError(
  109. 'Missing <definitions> element.'
  110. )
  111. self.version = DOM.WSDLUriToVersion(definitions.namespaceURI)
  112. NS_WSDL = DOM.GetWSDLUri(self.version)
  113. self.targetNamespace = DOM.getAttr(definitions, 'targetNamespace',
  114. None, None)
  115. self.name = DOM.getAttr(definitions, 'name', None, None)
  116. self.documentation = GetDocumentation(definitions)
  117. # Resolve (recursively) any import elements in the document.
  118. imported = {}
  119. while 1:
  120. imports = []
  121. for element in DOM.getElements(definitions, 'import', NS_WSDL):
  122. location = DOM.getAttr(element, 'location')
  123. if not imported.has_key(location):
  124. imports.append(element)
  125. if not imports:
  126. break
  127. for element in imports:
  128. self._import(document, element)
  129. location = DOM.getAttr(element, 'location')
  130. imported[location] = 1
  131. for element in DOM.getElements(definitions, None, None):
  132. localName = element.localName
  133. if not DOM.nsUriMatch(element.namespaceURI, NS_WSDL):
  134. if localName == 'schema':
  135. self.types.addSchema(XMLSchema(element))
  136. else:
  137. self.extensions.append(element)
  138. continue
  139. elif localName == 'message':
  140. name = DOM.getAttr(element, 'name')
  141. docs = GetDocumentation(element)
  142. message = self.addMessage(name, docs)
  143. parts = DOM.getElements(element, 'part', NS_WSDL)
  144. message.load(parts)
  145. continue
  146. elif localName == 'portType':
  147. name = DOM.getAttr(element, 'name')
  148. docs = GetDocumentation(element)
  149. ptype = self.addPortType(name, docs)
  150. operations = DOM.getElements(element, 'operation', NS_WSDL)
  151. ptype.load(operations)
  152. continue
  153. elif localName == 'binding':
  154. name = DOM.getAttr(element, 'name')
  155. type = DOM.getAttr(element, 'type', default=None)
  156. if type is None:
  157. raise WSDLError(
  158. 'Missing type attribute for binding %s.' % name
  159. )
  160. type = type.split(':', 1)[-1]
  161. docs = GetDocumentation(element)
  162. binding = self.addBinding(name, type, docs)
  163. operations = DOM.getElements(element, 'operation', NS_WSDL)
  164. binding.load(operations)
  165. binding.load_ex(GetExtensions(element))
  166. continue
  167. elif localName == 'service':
  168. name = DOM.getAttr(element, 'name')
  169. docs = GetDocumentation(element)
  170. service = self.addService(name, docs)
  171. ports = DOM.getElements(element, 'port', NS_WSDL)
  172. service.load(ports)
  173. service.load_ex(GetExtensions(element))
  174. continue
  175. elif localName == 'types':
  176. self.types.documentation = GetDocumentation(element)
  177. for item in DOM.getElements(element, None, None):
  178. if item.localName == 'schema':
  179. self.types.addSchema(XMLSchema(item))
  180. else:
  181. self.types.addExtension(item)
  182. continue
  183. def _import(self, document, element):
  184. namespace = DOM.getAttr(element, 'namespace', default=None)
  185. location = DOM.getAttr(element, 'location', default=None)
  186. if namespace is None or location is None:
  187. raise WSDLError(
  188. 'Invalid import element (missing namespace or location).'
  189. )
  190. # Sort-of support relative locations to simplify unit testing. The
  191. # WSDL specification actually doesn't allow relative URLs, so its
  192. # ok that this only works with urls relative to the initial document.
  193. location = urllib.basejoin(self.location, location)
  194. obimport = self.addImport(namespace, location)
  195. obimport._loaded = 1
  196. importdoc = DOM.loadFromURL(location)
  197. try:
  198. if location.find('#') > -1:
  199. idref = location.split('#')[-1]
  200. imported = DOM.getElementById(importdoc, idref)
  201. else:
  202. imported = importdoc.documentElement
  203. if imported is None:
  204. raise WSDLError(
  205. 'Import target element not found for: %s' % location
  206. )
  207. imported_tns = DOM.findTargetNS(imported)
  208. if imported_tns != namespace:
  209. return
  210. if imported.localName == 'definitions':
  211. imported_nodes = imported.childNodes
  212. else:
  213. imported_nodes = [imported]
  214. parent = element.parentNode
  215. for node in imported_nodes:
  216. if node.nodeType != node.ELEMENT_NODE:
  217. continue
  218. child = DOM.importNode(document, node, 1)
  219. parent.appendChild(child)
  220. child.setAttribute('targetNamespace', namespace)
  221. attrsNS = imported._attrsNS
  222. for attrkey in attrsNS.keys():
  223. if attrkey[0] == DOM.NS_XMLNS:
  224. attr = attrsNS[attrkey].cloneNode(1)
  225. child.setAttributeNode(attr)
  226. finally:
  227. importdoc.unlink()
  228. class Element:
  229. """A class that provides common functions for WSDL element classes."""
  230. def __init__(self, name=None, documentation=''):
  231. self.name = name
  232. self.documentation = documentation
  233. self.extensions = []
  234. def addExtension(self, item):
  235. self.extensions.append(item)
  236. class ImportElement(Element):
  237. def __init__(self, namespace, location):
  238. self.namespace = namespace
  239. self.location = location
  240. _loaded = None
  241. class Types(Collection):
  242. def __init__(self, parent):
  243. Collection.__init__(self, parent)
  244. self.documentation = ''
  245. self.extensions = []
  246. def addSchema(self, schema):
  247. name = schema.targetNamespace
  248. self[name] = schema
  249. return schema
  250. def keys(self):
  251. return map(lambda i: i.targetNamespace, self.list)
  252. def items(self):
  253. return map(lambda i: i.targetNamespace, self.list)
  254. def addExtension(self, item):
  255. self.extensions.append(item)
  256. class Message(Element):
  257. def __init__(self, name, documentation=''):
  258. Element.__init__(self, name, documentation)
  259. self.parts = Collection(self)
  260. def addPart(self, name, type=None, element=None):
  261. if self.parts.has_key(name):
  262. raise WSDLError(
  263. 'Duplicate message part element: %s' % name
  264. )
  265. if type is None and element is None:
  266. raise WSDLError(
  267. 'Missing type or element attribute for part: %s' % name
  268. )
  269. item = MessagePart(name)
  270. item.element = element
  271. item.type = type
  272. self.parts[name] = item
  273. return item
  274. def load(self, elements):
  275. for element in elements:
  276. name = DOM.getAttr(element, 'name')
  277. part = MessagePart(name)
  278. self.parts[name] = part
  279. elemref = DOM.getAttr(element, 'element', default=None)
  280. typeref = DOM.getAttr(element, 'type', default=None)
  281. if typeref is None and elemref is None:
  282. raise WSDLError(
  283. 'No type or element attribute for part: %s' % name
  284. )
  285. if typeref is not None:
  286. part.type = ParseTypeRef(typeref, element)
  287. if elemref is not None:
  288. part.element = ParseTypeRef(elemref, element)
  289. class MessagePart(Element):
  290. def __init__(self, name):
  291. Element.__init__(self, name, '')
  292. self.element = None
  293. self.type = None
  294. class PortType(Element):
  295. def __init__(self, name, documentation=''):
  296. Element.__init__(self, name, documentation)
  297. self.operations = Collection(self)
  298. def getWSDL(self):
  299. return self.parent().parent()
  300. def addOperation(self, name, documentation='', parameterOrder=None):
  301. item = Operation(name, documentation, parameterOrder)
  302. self.operations[name] = item
  303. return item
  304. def load(self, elements):
  305. for element in elements:
  306. name = DOM.getAttr(element, 'name')
  307. docs = GetDocumentation(element)
  308. param_order = DOM.getAttr(element, 'parameterOrder', default=None)
  309. if param_order is not None:
  310. param_order = param_order.split(' ')
  311. operation = self.addOperation(name, docs, param_order)
  312. item = DOM.getElement(element, 'input', None, None)
  313. if item is not None:
  314. name = DOM.getAttr(item, 'name')
  315. docs = GetDocumentation(item)
  316. msgref = DOM.getAttr(item, 'message')
  317. message = msgref.split(':', 1)[-1]
  318. operation.setInput(message, name, docs)
  319. item = DOM.getElement(element, 'output', None, None)
  320. if item is not None:
  321. name = DOM.getAttr(item, 'name')
  322. docs = GetDocumentation(item)
  323. msgref = DOM.getAttr(item, 'message')
  324. message = msgref.split(':', 1)[-1]
  325. operation.setOutput(message, name, docs)
  326. for item in DOM.getElements(element, 'fault', None):
  327. name = DOM.getAttr(item, 'name')
  328. docs = GetDocumentation(item)
  329. msgref = DOM.getAttr(item, 'message')
  330. message = msgref.split(':', 1)[-1]
  331. operation.addFault(message, name, docs)
  332. class Operation(Element):
  333. def __init__(self, name, documentation='', parameterOrder=None):
  334. Element.__init__(self, name, documentation)
  335. self.parameterOrder = parameterOrder
  336. self.faults = Collection(self)
  337. self.input = None
  338. self.output = None
  339. def getPortType(self):
  340. return self.parent().parent()
  341. def getInputMessage(self):
  342. if self.input is None:
  343. return None
  344. wsdl = self.getPortType().getWSDL()
  345. return wsdl.messages[self.input.message]
  346. def getOutputMessage(self):
  347. if self.output is None:
  348. return None
  349. wsdl = self.getPortType().getWSDL()
  350. return wsdl.messages[self.output.message]
  351. def getFaultMessage(self, name):
  352. wsdl = self.getPortType().getWSDL()
  353. return wsdl.messages[self.faults[name].message]
  354. def addFault(self, name, message, documentation=''):
  355. if self.faults.has_key(name):
  356. raise WSDLError(
  357. 'Duplicate fault element: %s' % name
  358. )
  359. item = MessageRole('fault', message, name, documentation)
  360. self.faults[name] = item
  361. return item
  362. def setInput(self, message, name='', documentation=''):
  363. self.input = MessageRole('input', message, name, documentation)
  364. return self.input
  365. def setOutput(self, message, name='', documentation=''):
  366. self.output = MessageRole('output', message, name, documentation)
  367. return self.output
  368. class MessageRole(Element):
  369. def __init__(self, type, message, name='', documentation=''):
  370. Element.__init__(self, name, documentation)
  371. self.message = message
  372. self.type = type
  373. class Binding(Element):
  374. def __init__(self, name, type, documentation=''):
  375. Element.__init__(self, name, documentation)
  376. self.operations = Collection(self)
  377. self.type = type
  378. def getWSDL(self):
  379. """Return the WSDL object that contains this binding."""
  380. return self.parent().parent()
  381. def getPortType(self):
  382. """Return the PortType object associated with this binding."""
  383. return self.getWSDL().portTypes[self.type]
  384. def findBinding(self, kind):
  385. for item in self.extensions:
  386. if isinstance(item, kind):
  387. return item
  388. return None
  389. def findBindings(self, kind):
  390. return [ item for item in self.extensions if isinstance(item, kind) ]
  391. def addOperationBinding(self, name, documentation=''):
  392. item = OperationBinding(name, documentation)
  393. self.operations[name] = item
  394. return item
  395. def load(self, elements):
  396. for element in elements:
  397. name = DOM.getAttr(element, 'name')
  398. docs = GetDocumentation(element)
  399. opbinding = self.addOperationBinding(name, docs)
  400. opbinding.load_ex(GetExtensions(element))
  401. item = DOM.getElement(element, 'input', None, None)
  402. if item is not None:
  403. mbinding = MessageRoleBinding('input')
  404. mbinding.documentation = GetDocumentation(item)
  405. opbinding.input = mbinding
  406. mbinding.load_ex(GetExtensions(item))
  407. item = DOM.getElement(element, 'output', None, None)
  408. if item is not None:
  409. mbinding = MessageRoleBinding('output')
  410. mbinding.documentation = GetDocumentation(item)
  411. opbinding.output = mbinding
  412. mbinding.load_ex(GetExtensions(item))
  413. for item in DOM.getElements(element, 'fault', None):
  414. name = DOM.getAttr(item, 'name')
  415. mbinding = MessageRoleBinding('fault', name)
  416. mbinding.documentation = GetDocumentation(item)
  417. opbinding.faults[name] = mbinding
  418. mbinding.load_ex(GetExtensions(item))
  419. def load_ex(self, elements):
  420. for e in elements:
  421. ns, name = e.namespaceURI, e.localName
  422. if ns in DOM.NS_SOAP_BINDING_ALL and name == 'binding':
  423. transport = DOM.getAttr(e, 'transport', default=None)
  424. style = DOM.getAttr(e, 'style', default='document')
  425. ob = SoapBinding(transport, style)
  426. self.addExtension(ob)
  427. continue
  428. elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'binding':
  429. verb = DOM.getAttr(e, 'verb')
  430. ob = HttpBinding(verb)
  431. self.addExtension(ob)
  432. continue
  433. else:
  434. self.addExtension(e)
  435. class OperationBinding(Element):
  436. def __init__(self, name, documentation=''):
  437. Element.__init__(self, name, documentation)
  438. self.input = None
  439. self.output = None
  440. self.faults = Collection(self)
  441. def getBinding(self):
  442. """Return the parent Binding object of the operation binding."""
  443. return self.parent().parent()
  444. def getOperation(self):
  445. """Return the abstract Operation associated with this binding."""
  446. return self.getBinding().getPortType().operations[self.name]
  447. def findBinding(self, kind):
  448. for item in self.extensions:
  449. if isinstance(item, kind):
  450. return item
  451. return None
  452. def findBindings(self, kind):
  453. return [ item for item in self.extensions if isinstance(item, kind) ]
  454. def addInputBinding(self, binding):
  455. if self.input is None:
  456. self.input = MessageRoleBinding('input')
  457. self.input.addExtension(binding)
  458. return binding
  459. def addOutputBinding(self, binding):
  460. if self.output is None:
  461. self.output = MessageRoleBinding('output')
  462. self.output.addExtension(binding)
  463. return binding
  464. def addFaultBinding(self, name, binding):
  465. fault = self.get(name, None)
  466. if fault is None:
  467. fault = MessageRoleBinding('fault', name)
  468. fault.addExtension(binding)
  469. return binding
  470. def load_ex(self, elements):
  471. for e in elements:
  472. ns, name = e.namespaceURI, e.localName
  473. if ns in DOM.NS_SOAP_BINDING_ALL and name == 'operation':
  474. soapaction = DOM.getAttr(e, 'soapAction', default=None)
  475. style = DOM.getAttr(e, 'style', default=None)
  476. ob = SoapOperationBinding(soapaction, style)
  477. self.addExtension(ob)
  478. continue
  479. elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'operation':
  480. location = DOM.getAttr(e, 'location')
  481. ob = HttpOperationBinding(location)
  482. self.addExtension(ob)
  483. continue
  484. else:
  485. self.addExtension(e)
  486. class MessageRoleBinding(Element):
  487. def __init__(self, type, name='', documentation=''):
  488. Element.__init__(self, name, documentation)
  489. self.type = type
  490. def findBinding(self, kind):
  491. for item in self.extensions:
  492. if isinstance(item, kind):
  493. return item
  494. return None
  495. def findBindings(self, kind):
  496. return [ item for item in self.extensions if isinstance(item, kind) ]
  497. def load_ex(self, elements):
  498. for e in elements:
  499. ns, name = e.namespaceURI, e.localName
  500. if ns in DOM.NS_SOAP_BINDING_ALL and name == 'body':
  501. encstyle = DOM.getAttr(e, 'encodingStyle', default=None)
  502. namespace = DOM.getAttr(e, 'namespace', default=None)
  503. parts = DOM.getAttr(e, 'parts', default=None)
  504. use = DOM.getAttr(e, 'use', default=None)
  505. if use is None:
  506. raise WSDLError(
  507. 'Invalid soap:body binding element.'
  508. )
  509. ob = SoapBodyBinding(use, namespace, encstyle, parts)
  510. self.addExtension(ob)
  511. continue
  512. elif ns in DOM.NS_SOAP_BINDING_ALL and name == 'fault':
  513. encstyle = DOM.getAttr(e, 'encodingStyle', default=None)
  514. namespace = DOM.getAttr(e, 'namespace', default=None)
  515. name = DOM.getAttr(e, 'name', default=None)
  516. use = DOM.getAttr(e, 'use', default=None)
  517. if use is None or name is None:
  518. raise WSDLError(
  519. 'Invalid soap:fault binding element.'
  520. )
  521. ob = SoapFaultBinding(name, use, namespace, encstyle)
  522. self.addExtension(ob)
  523. continue
  524. elif ns in DOM.NS_SOAP_BINDING_ALL and name in (
  525. 'header', 'headerfault'
  526. ):
  527. encstyle = DOM.getAttr(e, 'encodingStyle', default=None)
  528. namespace = DOM.getAttr(e, 'namespace', default=None)
  529. message = DOM.getAttr(e, 'message')
  530. part = DOM.getAttr(e, 'part')
  531. use = DOM.getAttr(e, 'use')
  532. if name == 'header':
  533. _class = SoapHeaderBinding
  534. else:
  535. _class = SoapHeaderFaultBinding
  536. ob = _class(message, part, use, namespace, encstyle)
  537. self.addExtension(ob)
  538. continue
  539. elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'urlReplacement':
  540. ob = HttpUrlReplacementBinding()
  541. self.addExtension(ob)
  542. continue
  543. elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'urlEncoded':
  544. ob = HttpUrlEncodedBinding()
  545. self.addExtension(ob)
  546. continue
  547. elif ns in DOM.NS_MIME_BINDING_ALL and name == 'multipartRelated':
  548. ob = MimeMultipartRelatedBinding()
  549. self.addExtension(ob)
  550. ob.load_ex(GetExtensions(e))
  551. continue
  552. elif ns in DOM.NS_MIME_BINDING_ALL and name == 'content':
  553. part = DOM.getAttr(e, 'part', default=None)
  554. type = DOM.getAttr(e, 'type', default=None)
  555. ob = MimeContentBinding(part, type)
  556. self.addExtension(ob)
  557. continue
  558. elif ns in DOM.NS_MIME_BINDING_ALL and name == 'mimeXml':
  559. part = DOM.getAttr(e, 'part', default=None)
  560. ob = MimeXmlBinding(part)
  561. self.addExtension(ob)
  562. continue
  563. else:
  564. self.addExtension(e)
  565. class Service(Element):
  566. def __init__(self, name, documentation=''):
  567. Element.__init__(self, name, documentation)
  568. self.ports = Collection(self)
  569. def getWSDL(self):
  570. return self.parent().parent()
  571. def addPort(self, name, binding, documentation=''):
  572. item = Port(name, binding, documentation)
  573. self.ports[name] = item
  574. return item
  575. def load(self, elements):
  576. for element in elements:
  577. name = DOM.getAttr(element, 'name', default=None)
  578. docs = GetDocumentation(element)
  579. binding = DOM.getAttr(element, 'binding', default=None)
  580. if name is None or binding is None:
  581. raise WSDLError(
  582. 'Invalid port element.'
  583. )
  584. binding = binding.split(':', 1)[-1]
  585. port = self.addPort(name, binding, docs)
  586. port.load_ex(GetExtensions(element))
  587. def load_ex(self, elements):
  588. for e in elements:
  589. self.addExtension(e)
  590. class Port(Element):
  591. def __init__(self, name, binding, documentation=''):
  592. Element.__init__(self, name, documentation)
  593. self.binding = binding
  594. def getService(self):
  595. """Return the Service object associated with this port."""
  596. return self.parent().parent()
  597. def getBinding(self):
  598. """Return the Binding object that is referenced by this port."""
  599. wsdl = self.getService().getWSDL()
  600. return wsdl.bindings[self.binding]
  601. def getPortType(self):
  602. """Return the PortType object that is referenced by this port."""
  603. wsdl = self.getService().getWSDL()
  604. binding = wsdl.bindings[self.binding]
  605. return wsdl.portTypes[binding.type]
  606. def getAddressBinding(self):
  607. """A convenience method to obtain the extension element used
  608. as the address binding for the port, or None if undefined."""
  609. for item in self.extensions:
  610. if isinstance(item, SoapAddressBinding) or \
  611. isinstance(item, HttpAddressBinding):
  612. return item
  613. raise WSDLError(
  614. 'No address binding found in port.'
  615. )
  616. def load_ex(self, elements):
  617. for e in elements:
  618. ns, name = e.namespaceURI, e.localName
  619. if ns in DOM.NS_SOAP_BINDING_ALL and name == 'address':
  620. location = DOM.getAttr(e, 'location', default=None)
  621. ob = SoapAddressBinding(location)
  622. self.addExtension(ob)
  623. continue
  624. elif ns in DOM.NS_HTTP_BINDING_ALL and name == 'address':
  625. location = DOM.getAttr(e, 'location', default=None)
  626. ob = HttpAddressBinding(location)
  627. self.addExtension(ob)
  628. continue
  629. else:
  630. self.addExtension(e)
  631. class SoapBinding:
  632. def __init__(self, transport, style='rpc'):
  633. self.transport = transport
  634. self.style = style
  635. class SoapAddressBinding:
  636. def __init__(self, location):
  637. self.location = location
  638. class SoapOperationBinding:
  639. def __init__(self, soapAction=None, style=None):
  640. self.soapAction = soapAction
  641. self.style = style
  642. class SoapBodyBinding:
  643. def __init__(self, use, namespace=None, encodingStyle=None, parts=None):
  644. if not use in ('literal', 'encoded'):
  645. raise WSDLError(
  646. 'Invalid use attribute value: %s' % use
  647. )
  648. self.encodingStyle = encodingStyle
  649. self.namespace = namespace
  650. if type(parts) in (type(''), type(u'')):
  651. raise WSDLError(
  652. 'The parts argument must be a sequence.'
  653. )
  654. self.parts = parts
  655. self.use = use
  656. class SoapFaultBinding:
  657. def __init__(self, name, use, namespace=None, encodingStyle=None):
  658. if not use in ('literal', 'encoded'):
  659. raise WSDLError(
  660. 'Invalid use attribute value: %s' % use
  661. )
  662. self.encodingStyle = encodingStyle
  663. self.namespace = namespace
  664. self.name = name
  665. self.use = use
  666. class SoapHeaderBinding:
  667. def __init__(self, message, part, use, namespace=None, encodingStyle=None):
  668. if not use in ('literal', 'encoded'):
  669. raise WSDLError(
  670. 'Invalid use attribute value: %s' % use
  671. )
  672. self.encodingStyle = encodingStyle
  673. self.namespace = namespace
  674. self.message = message
  675. self.part = part
  676. self.use = use
  677. tagname = 'header'
  678. class SoapHeaderFaultBinding(SoapHeaderBinding):
  679. tagname = 'headerfault'
  680. class HttpBinding:
  681. def __init__(self, verb):
  682. self.verb = verb
  683. class HttpAddressBinding:
  684. def __init__(self, location):
  685. self.location = location
  686. class HttpOperationBinding:
  687. def __init__(self, location):
  688. self.location = location
  689. class HttpUrlReplacementBinding:
  690. pass
  691. class HttpUrlEncodedBinding:
  692. pass
  693. class MimeContentBinding:
  694. def __init__(self, part=None, type=None):
  695. self.part = part
  696. self.type = type
  697. class MimeXmlBinding:
  698. def __init__(self, part=None):
  699. self.part = part
  700. class MimeMultipartRelatedBinding:
  701. def __init__(self):
  702. self.parts = []
  703. def load_ex(self, elements):
  704. for e in elements:
  705. ns, name = e.namespaceURI, e.localName
  706. if ns in DOM.NS_MIME_BINDING_ALL and name == 'part':
  707. self.parts.append(MimePartBinding())
  708. continue
  709. class MimePartBinding:
  710. def __init__(self):
  711. self.items = []
  712. def load_ex(self, elements):
  713. for e in elements:
  714. ns, name = e.namespaceURI, e.localName
  715. if ns in DOM.NS_MIME_BINDING_ALL and name == 'content':
  716. part = DOM.getAttr(e, 'part', default=None)
  717. type = DOM.getAttr(e, 'type', default=None)
  718. ob = MimeContentBinding(part, type)
  719. self.items.append(ob)
  720. continue
  721. elif ns in DOM.NS_MIME_BINDING_ALL and name == 'mimeXml':
  722. part = DOM.getAttr(e, 'part', default=None)
  723. ob = MimeXmlBinding(part)
  724. self.items.append(ob)
  725. continue
  726. elif ns in DOM.NS_SOAP_BINDING_ALL and name == 'body':
  727. encstyle = DOM.getAttr(e, 'encodingStyle', default=None)
  728. namespace = DOM.getAttr(e, 'namespace', default=None)
  729. parts = DOM.getAttr(e, 'parts', default=None)
  730. use = DOM.getAttr(e, 'use', default=None)
  731. if use is None:
  732. raise WSDLError(
  733. 'Invalid soap:body binding element.'
  734. )
  735. ob = SoapBodyBinding(use, namespace, encstyle, parts)
  736. self.items.append(ob)
  737. continue
  738. class WSDLError(Exception):
  739. pass
  740. def DeclareNSPrefix(writer, prefix, nsuri):
  741. if writer.hasNSPrefix(nsuri):
  742. return
  743. writer.declareNSPrefix(prefix, nsuri)
  744. def ParseTypeRef(value, element):
  745. parts = value.split(':', 1)
  746. if len(parts) == 1:
  747. return (DOM.findTargetNS(element), value)
  748. nsuri = DOM.findNamespaceURI(parts[0], element)
  749. return (nsuri, parts[1])
  750. def ParseQName(value, element):
  751. nameref = value.split(':', 1)
  752. if len(nameref) == 2:
  753. nsuri = DOM.findNamespaceURI(nameref[0], element)
  754. name = nameref[-1]
  755. else:
  756. nsuri = DOM.findTargetNS(element)
  757. name = nameref[-1]
  758. return nsuri, name
  759. def GetDocumentation(element):
  760. docnode = DOM.getElement(element, 'documentation', None, None)
  761. if docnode is not None:
  762. return DOM.getElementText(docnode)
  763. return ''
  764. def GetExtensions(element):
  765. return [ item for item in DOM.getElements(element, None, None)
  766. if item.namespaceURI != DOM.NS_WSDL ]
  767. def FindExtensions(object, kind, t_type=type(())):
  768. if isinstance(kind, t_type):
  769. result = []
  770. namespaceURI, name = kind
  771. return [ item for item in object.extensions
  772. if hasattr(item, 'nodeType') \
  773. and DOM.nsUriMatch(namespaceURI, item.namespaceURI) \
  774. and item.name == name ]
  775. return [ item for item in object.extensions if isinstance(item, kind) ]
  776. def FindExtension(object, kind, t_type=type(())):
  777. if isinstance(kind, t_type):
  778. namespaceURI, name = kind
  779. for item in object.extensions:
  780. if hasattr(item, 'nodeType') \
  781. and DOM.nsUriMatch(namespaceURI, item.namespaceURI) \
  782. and item.name == name:
  783. return item
  784. else:
  785. for item in object.extensions:
  786. if isinstance(item, kind):
  787. return item
  788. return None
  789. class SOAPCallInfo:
  790. """SOAPCallInfo captures the important binding information about a
  791. SOAP operation, in a structure that is easier to work with than
  792. raw WSDL structures."""
  793. def __init__(self, methodName):
  794. self.methodName = methodName
  795. self.inheaders = []
  796. self.outheaders = []
  797. self.inparams = []
  798. self.outparams = []
  799. self.retval = None
  800. encodingStyle = DOM.NS_SOAP_ENC
  801. documentation = ''
  802. soapAction = None
  803. transport = None
  804. namespace = None
  805. location = None
  806. use = 'encoded'
  807. style = 'rpc'
  808. def addInParameter(self, name, type, namespace=None, element_type=0):
  809. """Add an input parameter description to the call info."""
  810. parameter = ParameterInfo(name, type, namespace, element_type)
  811. self.inparams.append(parameter)
  812. return parameter
  813. def addOutParameter(self, name, type, namespace=None, element_type=0):
  814. """Add an output parameter description to the call info."""
  815. parameter = ParameterInfo(name, type, namespace, element_type)
  816. self.outparams.append(parameter)
  817. return parameter
  818. def setReturnParameter(self, name, type, namespace=None, element_type=0):
  819. """Set the return parameter description for the call info."""
  820. parameter = ParameterInfo(name, type, namespace, element_type)
  821. self.retval = parameter
  822. return parameter
  823. def addInHeaderInfo(self, name, type, namespace, element_type=0,
  824. mustUnderstand=0):
  825. """Add an input SOAP header description to the call info."""
  826. headerinfo = HeaderInfo(name, type, namespace, element_type)
  827. if mustUnderstand:
  828. headerinfo.mustUnderstand = 1
  829. self.inheaders.append(headerinfo)
  830. return headerinfo
  831. def addOutHeaderInfo(self, name, type, namespace, element_type=0,
  832. mustUnderstand=0):
  833. """Add an output SOAP header description to the call info."""
  834. headerinfo = HeaderInfo(name, type, namespace, element_type)
  835. if mustUnderstand:
  836. headerinfo.mustUnderstand = 1
  837. self.outheaders.append(headerinfo)
  838. return headerinfo
  839. def getInParameters(self):
  840. """Return a sequence of the in parameters of the method."""
  841. return self.inparams
  842. def getOutParameters(self):
  843. """Return a sequence of the out parameters of the method."""
  844. return self.outparams
  845. def getReturnParameter(self):
  846. """Return param info about the return value of the method."""
  847. return self.retval
  848. def getInHeaders(self):
  849. """Return a sequence of the in headers of the method."""
  850. return self.inheaders
  851. def getOutHeaders(self):
  852. """Return a sequence of the out headers of the method."""
  853. return self.outheaders
  854. class ParameterInfo:
  855. """A ParameterInfo object captures parameter binding information."""
  856. def __init__(self, name, type, namespace=None, element_type=0):
  857. if element_type:
  858. self.element_type = 1
  859. if namespace is not None:
  860. self.namespace = namespace
  861. self.name = name
  862. self.type = type
  863. element_type = 0
  864. namespace = None
  865. default = None
  866. class HeaderInfo(ParameterInfo):
  867. """A HeaderInfo object captures SOAP header binding information."""
  868. def __init__(self, name, type, namespace, element_type=None):
  869. ParameterInfo.__init__(self, name, type, namespace, element_type)
  870. mustUnderstand = 0
  871. actor = None
  872. def callInfoFromWSDL(port, name):
  873. """Return a SOAPCallInfo given a WSDL port and operation name."""
  874. wsdl = port.getService().getWSDL()
  875. binding = port.getBinding()
  876. portType = binding.getPortType()
  877. operation = portType.operations[name]
  878. opbinding = binding.operations[name]
  879. messages = wsdl.messages
  880. callinfo = SOAPCallInfo(name)
  881. addrbinding = port.getAddressBinding()
  882. if not isinstance(addrbinding, SoapAddressBinding):
  883. raise ValueError, 'Unsupported binding type.'
  884. callinfo.location = addrbinding.location
  885. soapbinding = binding.findBinding(SoapBinding)
  886. if soapbinding is None:
  887. raise ValueError, 'Missing soap:binding element.'
  888. callinfo.transport = soapbinding.transport
  889. callinfo.style = soapbinding.style or 'document'
  890. soap_op_binding = opbinding.findBinding(SoapOperationBinding)
  891. if soap_op_binding is not None:
  892. callinfo.soapAction = soap_op_binding.soapAction
  893. callinfo.style = soap_op_binding.style or callinfo.style
  894. parameterOrder = operation.parameterOrder
  895. if operation.input is not None:
  896. message = messages[operation.input.message]
  897. msgrole = opbinding.input
  898. mime = msgrole.findBinding(MimeMultipartRelatedBinding)
  899. if mime is not None:
  900. raise ValueError, 'Mime bindings are not supported.'
  901. else:
  902. for item in msgrole.findBindings(SoapHeaderBinding):
  903. part = messages[item.message].parts[item.part]
  904. header = callinfo.addInHeaderInfo(
  905. part.name,
  906. part.element or part.type,
  907. item.namespace,
  908. element_type = part.element and 1 or 0
  909. )
  910. header.encodingStyle = item.encodingStyle
  911. body = msgrole.findBinding(SoapBodyBinding)
  912. if body is None:
  913. raise ValueError, 'Missing soap:body binding.'
  914. callinfo.encodingStyle = body.encodingStyle
  915. callinfo.namespace = body.namespace
  916. callinfo.use = body.use
  917. if body.parts is not None:
  918. parts = []
  919. for name in body.parts:
  920. parts.append(message.parts[name])
  921. else:
  922. parts = message.parts.values()
  923. for part in parts:
  924. callinfo.addInParameter(
  925. part.name,
  926. part.element or part.type,
  927. element_type = part.element and 1 or 0
  928. )
  929. if operation.output is not None:
  930. message = messages[operation.output.message]
  931. msgrole = opbinding.output
  932. mime = msgrole.findBinding(MimeMultipartRelatedBinding)
  933. if mime is not None:
  934. raise ValueError, 'Mime bindings are not supported.'
  935. else:
  936. for item in msgrole.findBindings(SoapHeaderBinding):
  937. part = messages[item.message].parts[item.part]
  938. header = callinfo.addOutHeaderInfo(
  939. part.name,
  940. part.element or part.type,
  941. item.namespace,
  942. element_type = part.element and 1 or 0
  943. )
  944. header.encodingStyle = item.encodingStyle
  945. body = msgrole.findBinding(SoapBodyBinding)
  946. if body is None:
  947. raise ValueError, 'Missing soap:body binding.'
  948. callinfo.encodingStyle = body.encodingStyle
  949. callinfo.namespace = body.namespace
  950. callinfo.use = body.use
  951. if body.parts is not None:
  952. parts = []
  953. for name in body.parts:
  954. parts.append(message.parts[name])
  955. else:
  956. parts = message.parts.values()
  957. if parts:
  958. callinfo.setReturnParameter(
  959. parts[0].name,
  960. parts[0].element or parts[0].type,
  961. element_type = parts[0].element and 1 or 0
  962. )
  963. for part in parts[1:]:
  964. callinfo.addOutParameter(
  965. part.name,
  966. part.element or part.type,
  967. element_type = part.element and 1 or 0
  968. )
  969. return callinfo