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.
 
 
 

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