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.
 
 
 

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