2011-02-16 19:33:35 +00:00
|
|
|
|
;;;; soap-client.el -- Access SOAP web services from Emacs
|
2011-02-16 09:25:37 +00:00
|
|
|
|
|
2012-01-05 09:46:05 +00:00
|
|
|
|
;; Copyright (C) 2009-2012 Free Software Foundation, Inc.
|
2011-02-16 09:25:37 +00:00
|
|
|
|
|
2011-03-05 10:32:10 +00:00
|
|
|
|
;; Author: Alexandru Harsanyi <AlexHarsanyi@gmail.com>
|
2011-02-17 04:41:29 +00:00
|
|
|
|
;; Created: December, 2009
|
|
|
|
|
;; Keywords: soap, web-services, comm, hypermedia
|
2011-03-05 10:32:10 +00:00
|
|
|
|
;; Package: soap-client
|
2011-02-17 04:41:29 +00:00
|
|
|
|
;; Homepage: http://code.google.com/p/emacs-soap-client
|
|
|
|
|
|
|
|
|
|
;; This file is part of GNU Emacs.
|
|
|
|
|
|
|
|
|
|
;; GNU Emacs is free software: you can redistribute it and/or modify
|
2011-02-16 09:25:37 +00:00
|
|
|
|
;; it under the terms of the GNU General Public License as published by
|
|
|
|
|
;; the Free Software Foundation, either version 3 of the License, or
|
|
|
|
|
;; (at your option) any later version.
|
|
|
|
|
|
2011-02-17 04:41:29 +00:00
|
|
|
|
;; GNU Emacs is distributed in the hope that it will be useful,
|
2011-02-16 09:25:37 +00:00
|
|
|
|
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
;; GNU General Public License for more details.
|
|
|
|
|
|
|
|
|
|
;; You should have received a copy of the GNU General Public License
|
2011-02-17 04:41:29 +00:00
|
|
|
|
;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
|
2011-02-16 09:25:37 +00:00
|
|
|
|
|
|
|
|
|
;;; Commentary:
|
2011-02-16 19:33:35 +00:00
|
|
|
|
;;
|
2011-02-16 09:25:37 +00:00
|
|
|
|
;; To use the SOAP client, you first need to load the WSDL document for the
|
|
|
|
|
;; service you want to access, using `soap-load-wsdl-from-url'. A WSDL
|
|
|
|
|
;; document describes the available operations of the SOAP service, how their
|
|
|
|
|
;; parameters and responses are encoded. To invoke operations, you use the
|
|
|
|
|
;; `soap-invoke' method passing it the WSDL, the service name, the operation
|
|
|
|
|
;; you wish to invoke and any required parameters.
|
|
|
|
|
;;
|
2011-11-13 07:48:23 +00:00
|
|
|
|
;; Ideally, the service you want to access will have some documentation about
|
2011-02-16 09:25:37 +00:00
|
|
|
|
;; the operations it supports. If it does not, you can try using
|
|
|
|
|
;; `soap-inspect' to browse the WSDL document and see the available operations
|
|
|
|
|
;; and their parameters.
|
|
|
|
|
;;
|
|
|
|
|
|
|
|
|
|
;;; Code:
|
|
|
|
|
|
|
|
|
|
(eval-when-compile (require 'cl))
|
|
|
|
|
|
|
|
|
|
(require 'xml)
|
|
|
|
|
(require 'warnings)
|
|
|
|
|
(require 'url)
|
|
|
|
|
(require 'url-http)
|
|
|
|
|
(require 'url-util)
|
|
|
|
|
(require 'mm-decode)
|
|
|
|
|
|
|
|
|
|
(defsubst soap-warning (message &rest args)
|
|
|
|
|
"Display a warning MESSAGE with ARGS, using the 'soap-client warning type."
|
|
|
|
|
(display-warning 'soap-client (apply 'format message args) :warning))
|
|
|
|
|
|
|
|
|
|
(defgroup soap-client nil
|
|
|
|
|
"Access SOAP web services from Emacs."
|
Add missing :version tags to new defgroups and defcustoms
* window.el (window-sides-slots):
* tool-bar.el (tool-bar-position):
* term/xterm.el (xterm-extra-capabilities):
* ses.el (ses-self-reference-early-detection):
* progmodes/verilog-mode.el (verilog-auto-declare-nettype)
(verilog-auto-wire-type)
(verilog-auto-delete-trailing-whitespace)
(verilog-auto-reset-blocking-in-non, verilog-auto-inst-sort)
(verilog-auto-tieoff-declaration):
* progmodes/sql.el (sql-login-hook, sql-ansi-statement-starters)
(sql-oracle-statement-starters, sql-oracle-scan-on):
* progmodes/prolog.el (prolog-align-comments-flag)
(prolog-indent-mline-comments-flag, prolog-object-end-to-0-flag)
(prolog-left-indent-regexp, prolog-paren-indent-p)
(prolog-paren-indent, prolog-parse-mode, prolog-keywords)
(prolog-types, prolog-mode-specificators)
(prolog-determinism-specificators, prolog-directives)
(prolog-electric-newline-flag, prolog-hungry-delete-key-flag)
(prolog-electric-dot-flag)
(prolog-electric-dot-full-predicate-template)
(prolog-electric-underscore-flag, prolog-electric-tab-flag)
(prolog-electric-if-then-else-flag, prolog-electric-colon-flag)
(prolog-electric-dash-flag, prolog-old-sicstus-keys-flag)
(prolog-program-switches, prolog-prompt-regexp)
(prolog-debug-on-string, prolog-debug-off-string)
(prolog-trace-on-string, prolog-trace-off-string)
(prolog-zip-on-string, prolog-zip-off-string)
(prolog-use-standard-consult-compile-method-flag)
(prolog-use-prolog-tokenizer-flag, prolog-imenu-flag)
(prolog-imenu-max-lines, prolog-info-predicate-index)
(prolog-underscore-wordchar-flag, prolog-use-sicstus-sd)
(prolog-char-quote-workaround):
* progmodes/cc-vars.el (c-defun-tactic):
* net/tramp.el (tramp-encoding-command-interactive)
(tramp-local-end-of-line):
* net/soap-client.el (soap-client):
* net/netrc.el (netrc-file):
* net/gnutls.el (gnutls):
* minibuffer.el (completion-category-overrides)
(completion-cycle-threshold)
(completion-pcm-complete-word-inserts-delimiters):
* man.el (Man-name-local-regexp):
* mail/feedmail.el (feedmail-display-full-frame):
* international/characters.el (glyphless-char-display-control):
* eshell/em-ls.el (eshell-ls-date-format):
* emacs-lisp/cl-indent.el (lisp-lambda-list-keyword-alignment)
(lisp-lambda-list-keyword-parameter-indentation)
(lisp-lambda-list-keyword-parameter-alignment):
* doc-view.el (doc-view-image-width, doc-view-unoconv-program):
* dired-x.el (dired-omit-verbose):
* cus-theme.el (custom-theme-allow-multiple-selections):
* calc/calc.el (calc-highlight-selections-with-faces)
(calc-lu-field-reference, calc-lu-power-reference)
(calc-note-threshold):
* battery.el (battery-mode-line-limit):
* arc-mode.el (archive-7z-extract, archive-7z-expunge)
(archive-7z-update):
* allout.el (allout-prefixed-keybindings)
(allout-unprefixed-keybindings)
(allout-inhibit-auto-fill-on-headline)
(allout-flattened-numbering-abbreviation):
* allout-widgets.el (allout-widgets-auto-activation)
(allout-widgets-icons-dark-subdir)
(allout-widgets-icons-light-subdir, allout-widgets-icon-types)
(allout-widgets-theme-dark-background)
(allout-widgets-theme-light-background)
(allout-widgets-item-image-properties-emacs)
(allout-widgets-item-image-properties-xemacs)
(allout-widgets-run-unit-tests-on-load)
(allout-widgets-time-decoration-activity)
(allout-widgets-hook-error-post-time)
(allout-widgets-track-decoration):
* gnus/sieve-manage.el (sieve-manage-default-stream):
* gnus/shr.el (shr):
* gnus/nnir.el (nnir-ignored-newsgroups, nnir-summary-line-format)
(nnir-retrieve-headers-override-function)
(nnir-imap-default-search-key, nnir-notmuch-program)
(nnir-notmuch-additional-switches, nnir-notmuch-remove-prefix)
(nnir-method-default-engines):
* gnus/message.el (message-cite-reply-position):
* gnus/gssapi.el (gssapi-program):
* gnus/gravatar.el (gravatar):
* gnus/gnus-sum.el (gnus-refer-thread-use-nnir):
* gnus/gnus-registry.el (gnus-registry-unfollowed-addresses)
(gnus-registry-max-pruned-entries):
* gnus/gnus-picon.el (gnus-picon-inhibit-top-level-domains):
* gnus/gnus-int.el (gnus-after-set-mark-hook)
(gnus-before-update-mark-hook):
* gnus/gnus-async.el (gnus-async-post-fetch-function):
* gnus/auth-source.el (auth-source-cache-expiry):
Add missing :version tags to new defcustoms and defgroups.
2012-02-11 22:13:29 +00:00
|
|
|
|
:version "24.1"
|
2011-02-16 09:25:37 +00:00
|
|
|
|
:group 'tools)
|
|
|
|
|
|
|
|
|
|
;;;; Support for parsing XML documents with namespaces
|
|
|
|
|
|
|
|
|
|
;; XML documents with namespaces are difficult to parse because the names of
|
|
|
|
|
;; the nodes depend on what "xmlns" aliases have been defined in the document.
|
|
|
|
|
;; To work with such documents, we introduce a translation layer between a
|
|
|
|
|
;; "well known" namespace tag and the local namespace tag in the document
|
|
|
|
|
;; being parsed.
|
|
|
|
|
|
2011-02-16 19:56:31 +00:00
|
|
|
|
(defconst soap-well-known-xmlns
|
2011-02-16 09:25:37 +00:00
|
|
|
|
'(("apachesoap" . "http://xml.apache.org/xml-soap")
|
|
|
|
|
("soapenc" . "http://schemas.xmlsoap.org/soap/encoding/")
|
|
|
|
|
("wsdl" . "http://schemas.xmlsoap.org/wsdl/")
|
|
|
|
|
("wsdlsoap" . "http://schemas.xmlsoap.org/wsdl/soap/")
|
|
|
|
|
("xsd" . "http://www.w3.org/2001/XMLSchema")
|
|
|
|
|
("xsi" . "http://www.w3.org/2001/XMLSchema-instance")
|
|
|
|
|
("soap" . "http://schemas.xmlsoap.org/soap/envelope/")
|
|
|
|
|
("soap12" . "http://schemas.xmlsoap.org/wsdl/soap12/")
|
|
|
|
|
("http" . "http://schemas.xmlsoap.org/wsdl/http/")
|
|
|
|
|
("mime" . "http://schemas.xmlsoap.org/wsdl/mime/"))
|
|
|
|
|
"A list of well known xml namespaces and their aliases.")
|
|
|
|
|
|
2011-02-16 19:56:31 +00:00
|
|
|
|
(defvar soap-local-xmlns nil
|
2011-02-16 09:25:37 +00:00
|
|
|
|
"A list of local namespace aliases.
|
|
|
|
|
This is a dynamically bound variable, controlled by
|
|
|
|
|
`soap-with-local-xmlns'.")
|
|
|
|
|
|
2011-02-16 19:56:31 +00:00
|
|
|
|
(defvar soap-default-xmlns nil
|
2011-02-16 09:25:37 +00:00
|
|
|
|
"The default XML namespaces.
|
|
|
|
|
Names in this namespace will be unqualified. This is a
|
|
|
|
|
dynamically bound variable, controlled by
|
|
|
|
|
`soap-with-local-xmlns'")
|
|
|
|
|
|
2011-02-16 19:56:31 +00:00
|
|
|
|
(defvar soap-target-xmlns nil
|
2011-02-16 09:25:37 +00:00
|
|
|
|
"The target XML namespace.
|
|
|
|
|
New XSD elements will be defined in this namespace, unless they
|
|
|
|
|
are fully qualified for a different namespace. This is a
|
|
|
|
|
dynamically bound variable, controlled by
|
|
|
|
|
`soap-with-local-xmlns'")
|
|
|
|
|
|
|
|
|
|
(defun soap-wk2l (well-known-name)
|
|
|
|
|
"Return local variant of WELL-KNOWN-NAME.
|
|
|
|
|
This is done by looking up the namespace in the
|
2011-02-16 19:56:31 +00:00
|
|
|
|
`soap-well-known-xmlns' table and resolving the namespace to
|
2011-02-16 09:25:37 +00:00
|
|
|
|
the local name based on the current local translation table
|
2011-02-16 19:56:31 +00:00
|
|
|
|
`soap-local-xmlns'. See also `soap-with-local-xmlns'."
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(let ((wk-name-1 (if (symbolp well-known-name)
|
|
|
|
|
(symbol-name well-known-name)
|
|
|
|
|
well-known-name)))
|
|
|
|
|
(cond
|
|
|
|
|
((string-match "^\\(.*\\):\\(.*\\)$" wk-name-1)
|
|
|
|
|
(let ((ns (match-string 1 wk-name-1))
|
|
|
|
|
(name (match-string 2 wk-name-1)))
|
2011-02-16 19:56:31 +00:00
|
|
|
|
(let ((namespace (cdr (assoc ns soap-well-known-xmlns))))
|
|
|
|
|
(cond ((equal namespace soap-default-xmlns)
|
2011-02-16 09:25:37 +00:00
|
|
|
|
;; Name is unqualified in the default namespace
|
|
|
|
|
(if (symbolp well-known-name)
|
|
|
|
|
(intern name)
|
|
|
|
|
name))
|
|
|
|
|
(t
|
2011-02-16 19:56:31 +00:00
|
|
|
|
(let* ((local-ns (car (rassoc namespace soap-local-xmlns)))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(local-name (concat local-ns ":" name)))
|
|
|
|
|
(if (symbolp well-known-name)
|
|
|
|
|
(intern local-name)
|
|
|
|
|
local-name)))))))
|
|
|
|
|
(t well-known-name))))
|
|
|
|
|
|
|
|
|
|
(defun soap-l2wk (local-name)
|
|
|
|
|
"Convert LOCAL-NAME into a well known name.
|
|
|
|
|
The namespace of LOCAL-NAME is looked up in the
|
2011-02-16 19:56:31 +00:00
|
|
|
|
`soap-well-known-xmlns' table and a well known namespace tag is
|
2011-02-16 09:25:37 +00:00
|
|
|
|
used in the name.
|
|
|
|
|
|
|
|
|
|
nil is returned if there is no well-known namespace for the
|
|
|
|
|
namespace of LOCAL-NAME."
|
|
|
|
|
(let ((l-name-1 (if (symbolp local-name)
|
|
|
|
|
(symbol-name local-name)
|
|
|
|
|
local-name))
|
|
|
|
|
namespace name)
|
|
|
|
|
(cond
|
|
|
|
|
((string-match "^\\(.*\\):\\(.*\\)$" l-name-1)
|
|
|
|
|
(setq name (match-string 2 l-name-1))
|
|
|
|
|
(let ((ns (match-string 1 l-name-1)))
|
2011-02-16 19:56:31 +00:00
|
|
|
|
(setq namespace (cdr (assoc ns soap-local-xmlns)))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(unless namespace
|
|
|
|
|
(error "Soap-l2wk(%s): no namespace for alias %s" local-name ns))))
|
|
|
|
|
(t
|
|
|
|
|
(setq name l-name-1)
|
2011-02-16 19:56:31 +00:00
|
|
|
|
(setq namespace soap-default-xmlns)))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
|
|
|
|
|
(if namespace
|
2011-02-16 19:56:31 +00:00
|
|
|
|
(let ((well-known-ns (car (rassoc namespace soap-well-known-xmlns))))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(if well-known-ns
|
|
|
|
|
(let ((well-known-name (concat well-known-ns ":" name)))
|
|
|
|
|
(if (symbol-name local-name)
|
|
|
|
|
(intern well-known-name)
|
|
|
|
|
well-known-name))
|
|
|
|
|
(progn
|
|
|
|
|
;; (soap-warning "soap-l2wk(%s): namespace %s has no well-known tag"
|
|
|
|
|
;; local-name namespace)
|
|
|
|
|
nil)))
|
|
|
|
|
;; if no namespace is defined, just return the unqualified name
|
|
|
|
|
name)))
|
2011-02-16 19:33:35 +00:00
|
|
|
|
|
2011-02-16 09:25:37 +00:00
|
|
|
|
|
|
|
|
|
(defun soap-l2fq (local-name &optional use-tns)
|
|
|
|
|
"Convert LOCAL-NAME into a fully qualified name.
|
|
|
|
|
A fully qualified name is a cons of the namespace name and the
|
|
|
|
|
name of the element itself. For example \"xsd:string\" is
|
2011-02-16 19:33:35 +00:00
|
|
|
|
converted to \(\"http://www.w3.org/2001/XMLSchema\" . \"string\"\).
|
2011-02-16 09:25:37 +00:00
|
|
|
|
|
|
|
|
|
The USE-TNS argument specifies what to do when LOCAL-NAME has no
|
2011-02-16 19:56:31 +00:00
|
|
|
|
namespace tag. If USE-TNS is non-nil, the `soap-target-xmlns'
|
2011-02-16 09:25:37 +00:00
|
|
|
|
will be used as the element's namespace, otherwise
|
2011-02-16 19:56:31 +00:00
|
|
|
|
`soap-default-xmlns' will be used.
|
2011-02-16 09:25:37 +00:00
|
|
|
|
|
|
|
|
|
This is needed because different parts of a WSDL document can use
|
|
|
|
|
different namespace aliases for the same element."
|
|
|
|
|
(let ((local-name-1 (if (symbolp local-name)
|
|
|
|
|
(symbol-name local-name)
|
|
|
|
|
local-name)))
|
|
|
|
|
(cond ((string-match "^\\(.*\\):\\(.*\\)$" local-name-1)
|
|
|
|
|
(let ((ns (match-string 1 local-name-1))
|
|
|
|
|
(name (match-string 2 local-name-1)))
|
2011-02-16 19:56:31 +00:00
|
|
|
|
(let ((namespace (cdr (assoc ns soap-local-xmlns))))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(if namespace
|
|
|
|
|
(cons namespace name)
|
|
|
|
|
(error "Soap-l2fq(%s): unknown alias %s" local-name ns)))))
|
|
|
|
|
(t
|
|
|
|
|
(cons (if use-tns
|
2011-02-16 19:56:31 +00:00
|
|
|
|
soap-target-xmlns
|
|
|
|
|
soap-default-xmlns)
|
2011-02-16 09:25:37 +00:00
|
|
|
|
local-name)))))
|
|
|
|
|
|
|
|
|
|
(defun soap-extract-xmlns (node &optional xmlns-table)
|
|
|
|
|
"Return a namespace alias table for NODE by extending XMLNS-TABLE."
|
|
|
|
|
(let (xmlns default-ns target-ns)
|
|
|
|
|
(dolist (a (xml-node-attributes node))
|
|
|
|
|
(let ((name (symbol-name (car a)))
|
|
|
|
|
(value (cdr a)))
|
|
|
|
|
(cond ((string= name "targetNamespace")
|
|
|
|
|
(setq target-ns value))
|
|
|
|
|
((string= name "xmlns")
|
|
|
|
|
(setq default-ns value))
|
|
|
|
|
((string-match "^xmlns:\\(.*\\)$" name)
|
|
|
|
|
(push (cons (match-string 1 name) value) xmlns)))))
|
2011-02-16 19:33:35 +00:00
|
|
|
|
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(let ((tns (assoc "tns" xmlns)))
|
|
|
|
|
(cond ((and tns target-ns)
|
2011-02-16 19:33:35 +00:00
|
|
|
|
;; If a tns alias is defined for this node, it must match
|
|
|
|
|
;; the target namespace.
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(unless (equal target-ns (cdr tns))
|
2011-02-16 19:33:35 +00:00
|
|
|
|
(soap-warning
|
|
|
|
|
"soap-extract-xmlns(%s): tns alias and targetNamespace mismatch"
|
|
|
|
|
(xml-node-name node))))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
((and tns (not target-ns))
|
|
|
|
|
(setq target-ns (cdr tns)))
|
|
|
|
|
((and (not tns) target-ns)
|
|
|
|
|
;; a tns alias was not defined in this node. See if the node has
|
|
|
|
|
;; a "targetNamespace" attribute and add an alias to this. Note
|
|
|
|
|
;; that we might override an existing tns alias in XMLNS-TABLE,
|
|
|
|
|
;; but that is intended.
|
|
|
|
|
(push (cons "tns" target-ns) xmlns))))
|
2011-02-16 19:33:35 +00:00
|
|
|
|
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(list default-ns target-ns (append xmlns xmlns-table))))
|
|
|
|
|
|
|
|
|
|
(defmacro soap-with-local-xmlns (node &rest body)
|
|
|
|
|
"Install a local alias table from NODE and execute BODY."
|
|
|
|
|
(declare (debug (form &rest form)) (indent 1))
|
|
|
|
|
(let ((xmlns (make-symbol "xmlns")))
|
2011-02-16 19:56:31 +00:00
|
|
|
|
`(let ((,xmlns (soap-extract-xmlns ,node soap-local-xmlns)))
|
|
|
|
|
(let ((soap-default-xmlns (or (nth 0 ,xmlns) soap-default-xmlns))
|
|
|
|
|
(soap-target-xmlns (or (nth 1 ,xmlns) soap-target-xmlns))
|
|
|
|
|
(soap-local-xmlns (nth 2 ,xmlns)))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
,@body))))
|
|
|
|
|
|
|
|
|
|
(defun soap-get-target-namespace (node)
|
|
|
|
|
"Return the target namespace of NODE.
|
|
|
|
|
This is the namespace in which new elements will be defined."
|
|
|
|
|
(or (xml-get-attribute-or-nil node 'targetNamespace)
|
2011-02-16 19:56:31 +00:00
|
|
|
|
(cdr (assoc "tns" soap-local-xmlns))
|
|
|
|
|
soap-target-xmlns))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
|
|
|
|
|
(defun soap-xml-get-children1 (node child-name)
|
|
|
|
|
"Return the children of NODE named CHILD-NAME.
|
|
|
|
|
This is the same as `xml-get-children', but CHILD-NAME can have
|
|
|
|
|
namespace tag."
|
|
|
|
|
(let (result)
|
|
|
|
|
(dolist (c (xml-node-children node))
|
|
|
|
|
(when (and (consp c)
|
|
|
|
|
(soap-with-local-xmlns c
|
|
|
|
|
;; We use `ignore-errors' here because we want to silently
|
|
|
|
|
;; skip nodes for which we cannot convert them to a
|
|
|
|
|
;; well-known name.
|
2011-02-16 19:33:35 +00:00
|
|
|
|
(eq (ignore-errors (soap-l2wk (xml-node-name c)))
|
|
|
|
|
child-name)))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(push c result)))
|
|
|
|
|
(nreverse result)))
|
|
|
|
|
|
|
|
|
|
(defun soap-xml-get-attribute-or-nil1 (node attribute)
|
|
|
|
|
"Return the NODE's ATTRIBUTE, or nil if it does not exist.
|
|
|
|
|
This is the same as `xml-get-attribute-or-nil', but ATTRIBUTE can
|
|
|
|
|
be tagged with a namespace tag."
|
|
|
|
|
(catch 'found
|
|
|
|
|
(soap-with-local-xmlns node
|
|
|
|
|
(dolist (a (xml-node-attributes node))
|
|
|
|
|
;; We use `ignore-errors' here because we want to silently skip
|
|
|
|
|
;; attributes for which we cannot convert them to a well-known name.
|
|
|
|
|
(when (eq (ignore-errors (soap-l2wk (car a))) attribute)
|
|
|
|
|
(throw 'found (cdr a)))))))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;;;; XML namespaces
|
|
|
|
|
|
|
|
|
|
;; An element in an XML namespace, "things" stored in soap-xml-namespaces will
|
|
|
|
|
;; be derived from this object.
|
|
|
|
|
|
|
|
|
|
(defstruct soap-element
|
|
|
|
|
name
|
|
|
|
|
;; The "well-known" namespace tag for the element. For example, while
|
|
|
|
|
;; parsing XML documents, we can have different tags for the XMLSchema
|
|
|
|
|
;; namespace, but internally all our XMLSchema elements will have the "xsd"
|
|
|
|
|
;; tag.
|
|
|
|
|
namespace-tag)
|
|
|
|
|
|
|
|
|
|
(defun soap-element-fq-name (element)
|
|
|
|
|
"Return a fully qualified name for ELEMENT.
|
|
|
|
|
A fq name is the concatenation of the namespace tag and the
|
|
|
|
|
element name."
|
|
|
|
|
(concat (soap-element-namespace-tag element)
|
|
|
|
|
":" (soap-element-name element)))
|
|
|
|
|
|
|
|
|
|
;; a namespace link stores an alias for an object in once namespace to a
|
|
|
|
|
;; "target" object possibly in a different namespace
|
|
|
|
|
|
|
|
|
|
(defstruct (soap-namespace-link (:include soap-element))
|
|
|
|
|
target)
|
|
|
|
|
|
|
|
|
|
;; A namespace is a collection of soap-element objects under a name (the name
|
|
|
|
|
;; of the namespace).
|
|
|
|
|
|
|
|
|
|
(defstruct soap-namespace
|
|
|
|
|
(name nil :read-only t) ; e.g "http://xml.apache.org/xml-soap"
|
|
|
|
|
(elements (make-hash-table :test 'equal) :read-only t))
|
|
|
|
|
|
|
|
|
|
(defun soap-namespace-put (element ns)
|
|
|
|
|
"Store ELEMENT in NS.
|
|
|
|
|
Multiple elements with the same name can be stored in a
|
|
|
|
|
namespace. When retrieving the element you can specify a
|
|
|
|
|
discriminant predicate to `soap-namespace-get'"
|
|
|
|
|
(let ((name (soap-element-name element)))
|
|
|
|
|
(push element (gethash name (soap-namespace-elements ns)))))
|
|
|
|
|
|
|
|
|
|
(defun soap-namespace-put-link (name target ns &optional replace)
|
|
|
|
|
"Store a link from NAME to TARGET in NS.
|
|
|
|
|
An error will be signaled if an element by the same name is
|
|
|
|
|
already present in NS, unless REPLACE is non nil.
|
|
|
|
|
|
|
|
|
|
TARGET can be either a SOAP-ELEMENT or a string denoting an
|
|
|
|
|
element name into another namespace.
|
|
|
|
|
|
|
|
|
|
If NAME is nil, an element with the same name as TARGET will be
|
|
|
|
|
added to the namespace."
|
|
|
|
|
|
|
|
|
|
(unless (and name (not (equal name "")))
|
|
|
|
|
;; if name is nil, use TARGET as a name...
|
|
|
|
|
(cond ((soap-element-p target)
|
|
|
|
|
(setq name (soap-element-name target)))
|
2011-03-05 10:32:10 +00:00
|
|
|
|
((consp target) ; a fq name: (namespace . name)
|
|
|
|
|
(setq name (cdr target)))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
((stringp target)
|
|
|
|
|
(cond ((string-match "^\\(.*\\):\\(.*\\)$" target)
|
|
|
|
|
(setq name (match-string 2 target)))
|
|
|
|
|
(t
|
|
|
|
|
(setq name target))))))
|
|
|
|
|
|
2011-03-05 10:32:10 +00:00
|
|
|
|
;; by now, name should be valid
|
|
|
|
|
(assert (and name (not (equal name "")))
|
|
|
|
|
nil
|
|
|
|
|
"Cannot determine name for namespace link")
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(push (make-soap-namespace-link :name name :target target)
|
|
|
|
|
(gethash name (soap-namespace-elements ns))))
|
|
|
|
|
|
|
|
|
|
(defun soap-namespace-get (name ns &optional discriminant-predicate)
|
|
|
|
|
"Retrieve an element with NAME from the namespace NS.
|
|
|
|
|
If multiple elements with the same name exist,
|
|
|
|
|
DISCRIMINANT-PREDICATE is used to pick one of them. This allows
|
|
|
|
|
storing elements of different types (like a message type and a
|
|
|
|
|
binding) but the same name."
|
|
|
|
|
(assert (stringp name))
|
|
|
|
|
(let ((elements (gethash name (soap-namespace-elements ns))))
|
|
|
|
|
(cond (discriminant-predicate
|
|
|
|
|
(catch 'found
|
|
|
|
|
(dolist (e elements)
|
|
|
|
|
(when (funcall discriminant-predicate e)
|
|
|
|
|
(throw 'found e)))))
|
|
|
|
|
((= (length elements) 1) (car elements))
|
|
|
|
|
((> (length elements) 1)
|
2011-02-16 19:33:35 +00:00
|
|
|
|
(error
|
|
|
|
|
"Soap-namespace-get(%s): multiple elements, discriminant needed"
|
|
|
|
|
name))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(t
|
|
|
|
|
nil))))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;;;; WSDL documents
|
|
|
|
|
;;;;; WSDL document elements
|
|
|
|
|
|
|
|
|
|
(defstruct (soap-basic-type (:include soap-element))
|
|
|
|
|
kind ; a symbol of: string, dateTime, long, int
|
|
|
|
|
)
|
|
|
|
|
|
2012-04-25 10:28:29 +00:00
|
|
|
|
(defstruct (soap-simple-type (:include soap-basic-type))
|
|
|
|
|
enumeration)
|
|
|
|
|
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(defstruct soap-sequence-element
|
|
|
|
|
name type nillable? multiple?)
|
|
|
|
|
|
|
|
|
|
(defstruct (soap-sequence-type (:include soap-element))
|
|
|
|
|
parent ; OPTIONAL WSDL-TYPE name
|
2011-11-14 20:23:26 +00:00
|
|
|
|
elements ; LIST of SOAP-SEQUENCE-ELEMENT
|
2011-02-16 09:25:37 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
(defstruct (soap-array-type (:include soap-element))
|
|
|
|
|
element-type ; WSDL-TYPE of the array elements
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
(defstruct (soap-message (:include soap-element))
|
|
|
|
|
parts ; ALIST of NAME => WSDL-TYPE name
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
(defstruct (soap-operation (:include soap-element))
|
|
|
|
|
parameter-order
|
|
|
|
|
input ; (NAME . MESSAGE)
|
|
|
|
|
output ; (NAME . MESSAGE)
|
|
|
|
|
faults) ; a list of (NAME . MESSAGE)
|
|
|
|
|
|
|
|
|
|
(defstruct (soap-port-type (:include soap-element))
|
|
|
|
|
operations) ; a namespace of operations
|
|
|
|
|
|
|
|
|
|
;; A bound operation is an operation which has a soap action and a use
|
|
|
|
|
;; method attached -- these are attached as part of a binding and we
|
|
|
|
|
;; can have different bindings for the same operations.
|
|
|
|
|
(defstruct soap-bound-operation
|
|
|
|
|
operation ; SOAP-OPERATION
|
|
|
|
|
soap-action ; value for SOAPAction HTTP header
|
2011-02-16 19:33:35 +00:00
|
|
|
|
use ; 'literal or 'encoded, see
|
|
|
|
|
; http://www.w3.org/TR/wsdl#_soap:body
|
2011-02-16 09:25:37 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
(defstruct (soap-binding (:include soap-element))
|
|
|
|
|
port-type
|
|
|
|
|
(operations (make-hash-table :test 'equal) :readonly t))
|
|
|
|
|
|
|
|
|
|
(defstruct (soap-port (:include soap-element))
|
|
|
|
|
service-url
|
|
|
|
|
binding)
|
|
|
|
|
|
|
|
|
|
(defun soap-default-xsd-types ()
|
|
|
|
|
"Return a namespace containing some of the XMLSchema types."
|
|
|
|
|
(let ((ns (make-soap-namespace :name "http://www.w3.org/2001/XMLSchema")))
|
2012-04-25 10:28:29 +00:00
|
|
|
|
(dolist (type '("string" "dateTime" "boolean"
|
|
|
|
|
"long" "int" "integer" "unsignedInt" "byte" "float" "double"
|
|
|
|
|
"base64Binary" "anyType" "anyURI" "Array" "byte[]"))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(soap-namespace-put
|
|
|
|
|
(make-soap-basic-type :name type :kind (intern type))
|
|
|
|
|
ns))
|
|
|
|
|
ns))
|
|
|
|
|
|
|
|
|
|
(defun soap-default-soapenc-types ()
|
|
|
|
|
"Return a namespace containing some of the SOAPEnc types."
|
2011-02-16 19:33:35 +00:00
|
|
|
|
(let ((ns (make-soap-namespace
|
2012-04-25 10:28:29 +00:00
|
|
|
|
:name "http://schemas.xmlsoap.org/soap/encoding/")))
|
|
|
|
|
(dolist (type '("string" "dateTime" "boolean"
|
|
|
|
|
"long" "int" "integer" "unsignedInt" "byte" "float" "double"
|
|
|
|
|
"base64Binary" "anyType" "anyURI" "Array" "byte[]"))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(soap-namespace-put
|
|
|
|
|
(make-soap-basic-type :name type :kind (intern type))
|
|
|
|
|
ns))
|
|
|
|
|
ns))
|
|
|
|
|
|
|
|
|
|
(defun soap-type-p (element)
|
|
|
|
|
"Return t if ELEMENT is a SOAP data type (basic or complex)."
|
|
|
|
|
(or (soap-basic-type-p element)
|
|
|
|
|
(soap-sequence-type-p element)
|
|
|
|
|
(soap-array-type-p element)))
|
2011-02-16 19:33:35 +00:00
|
|
|
|
|
2011-02-16 09:25:37 +00:00
|
|
|
|
|
|
|
|
|
;;;;; The WSDL document
|
|
|
|
|
|
|
|
|
|
;; The WSDL data structure used for encoding/decoding SOAP messages
|
|
|
|
|
(defstruct soap-wsdl
|
|
|
|
|
origin ; file or URL from which this wsdl was loaded
|
|
|
|
|
ports ; a list of SOAP-PORT instances
|
|
|
|
|
alias-table ; a list of namespace aliases
|
|
|
|
|
namespaces ; a list of namespaces
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
(defun soap-wsdl-add-alias (alias name wsdl)
|
|
|
|
|
"Add a namespace ALIAS for NAME to the WSDL document."
|
|
|
|
|
(push (cons alias name) (soap-wsdl-alias-table wsdl)))
|
|
|
|
|
|
|
|
|
|
(defun soap-wsdl-find-namespace (name wsdl)
|
|
|
|
|
"Find a namespace by NAME in the WSDL document."
|
|
|
|
|
(catch 'found
|
|
|
|
|
(dolist (ns (soap-wsdl-namespaces wsdl))
|
|
|
|
|
(when (equal name (soap-namespace-name ns))
|
|
|
|
|
(throw 'found ns)))))
|
|
|
|
|
|
|
|
|
|
(defun soap-wsdl-add-namespace (ns wsdl)
|
|
|
|
|
"Add the namespace NS to the WSDL document.
|
|
|
|
|
If a namespace by this name already exists in WSDL, individual
|
|
|
|
|
elements will be added to it."
|
|
|
|
|
(let ((existing (soap-wsdl-find-namespace (soap-namespace-name ns) wsdl)))
|
|
|
|
|
(if existing
|
|
|
|
|
;; Add elements from NS to EXISTING, replacing existing values.
|
|
|
|
|
(maphash (lambda (key value)
|
|
|
|
|
(dolist (v value)
|
|
|
|
|
(soap-namespace-put v existing)))
|
|
|
|
|
(soap-namespace-elements ns))
|
|
|
|
|
(push ns (soap-wsdl-namespaces wsdl)))))
|
|
|
|
|
|
|
|
|
|
(defun soap-wsdl-get (name wsdl &optional predicate use-local-alias-table)
|
|
|
|
|
"Retrieve element NAME from the WSDL document.
|
|
|
|
|
|
|
|
|
|
PREDICATE is used to differentiate between elements when NAME
|
|
|
|
|
refers to multiple elements. A typical value for this would be a
|
|
|
|
|
structure predicate for the type of element you want to retrieve.
|
|
|
|
|
For example, to retrieve a message named \"foo\" when other
|
|
|
|
|
elements named \"foo\" exist in the WSDL you could use:
|
|
|
|
|
|
|
|
|
|
(soap-wsdl-get \"foo\" WSDL 'soap-message-p)
|
|
|
|
|
|
2011-02-16 19:56:31 +00:00
|
|
|
|
If USE-LOCAL-ALIAS-TABLE is not nil, `soap-local-xmlns` will be
|
2011-02-16 09:25:37 +00:00
|
|
|
|
used to resolve the namespace alias."
|
|
|
|
|
(let ((alias-table (soap-wsdl-alias-table wsdl))
|
|
|
|
|
namespace element-name element)
|
|
|
|
|
|
|
|
|
|
(when (symbolp name)
|
|
|
|
|
(setq name (symbol-name name)))
|
|
|
|
|
|
|
|
|
|
(when use-local-alias-table
|
2011-02-16 19:56:31 +00:00
|
|
|
|
(setq alias-table (append soap-local-xmlns alias-table)))
|
2011-02-16 19:33:35 +00:00
|
|
|
|
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(cond ((consp name) ; a fully qualified name, as returned by `soap-l2fq'
|
|
|
|
|
(setq element-name (cdr name))
|
|
|
|
|
(when (symbolp element-name)
|
|
|
|
|
(setq element-name (symbol-name element-name)))
|
|
|
|
|
(setq namespace (soap-wsdl-find-namespace (car name) wsdl))
|
|
|
|
|
(unless namespace
|
|
|
|
|
(error "Soap-wsdl-get(%s): unknown namespace: %s" name namespace)))
|
2011-02-16 19:33:35 +00:00
|
|
|
|
|
2011-02-16 09:25:37 +00:00
|
|
|
|
((string-match "^\\(.*\\):\\(.*\\)$" name)
|
|
|
|
|
(setq element-name (match-string 2 name))
|
|
|
|
|
|
|
|
|
|
(let* ((ns-alias (match-string 1 name))
|
|
|
|
|
(ns-name (cdr (assoc ns-alias alias-table))))
|
|
|
|
|
(unless ns-name
|
2011-02-16 19:33:35 +00:00
|
|
|
|
(error "Soap-wsdl-get(%s): cannot find namespace alias %s"
|
|
|
|
|
name ns-alias))
|
|
|
|
|
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(setq namespace (soap-wsdl-find-namespace ns-name wsdl))
|
|
|
|
|
(unless namespace
|
2011-02-16 19:33:35 +00:00
|
|
|
|
(error
|
|
|
|
|
"Soap-wsdl-get(%s): unknown namespace %s, referenced by alias %s"
|
|
|
|
|
name ns-name ns-alias))))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(t
|
|
|
|
|
(error "Soap-wsdl-get(%s): bad name" name)))
|
|
|
|
|
|
|
|
|
|
(setq element (soap-namespace-get
|
|
|
|
|
element-name namespace
|
|
|
|
|
(if predicate
|
|
|
|
|
(lambda (e)
|
|
|
|
|
(or (funcall 'soap-namespace-link-p e)
|
|
|
|
|
(funcall predicate e)))
|
|
|
|
|
nil)))
|
2011-02-16 19:33:35 +00:00
|
|
|
|
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(unless element
|
|
|
|
|
(error "Soap-wsdl-get(%s): cannot find element" name))
|
2011-02-16 19:33:35 +00:00
|
|
|
|
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(if (soap-namespace-link-p element)
|
|
|
|
|
;; NOTE: don't use the local alias table here
|
|
|
|
|
(soap-wsdl-get (soap-namespace-link-target element) wsdl predicate)
|
|
|
|
|
element)))
|
|
|
|
|
|
|
|
|
|
;;;;; Resolving references for wsdl types
|
|
|
|
|
|
|
|
|
|
;; See `soap-wsdl-resolve-references', which is the main entry point for
|
|
|
|
|
;; resolving references
|
|
|
|
|
|
|
|
|
|
(defun soap-resolve-references-for-element (element wsdl)
|
|
|
|
|
"Resolve references in ELEMENT using the WSDL document.
|
|
|
|
|
This is a generic function which invokes a specific function
|
|
|
|
|
depending on the element type.
|
|
|
|
|
|
|
|
|
|
If ELEMENT has no resolver function, it is silently ignored.
|
|
|
|
|
|
|
|
|
|
All references are resolved in-place, that is the ELEMENT is
|
|
|
|
|
updated."
|
|
|
|
|
(let ((resolver (get (aref element 0) 'soap-resolve-references)))
|
|
|
|
|
(when resolver
|
|
|
|
|
(funcall resolver element wsdl))))
|
|
|
|
|
|
2012-04-25 10:28:29 +00:00
|
|
|
|
(defun soap-resolve-references-for-simple-type (type wsdl)
|
|
|
|
|
"Resolve the base type for the simple TYPE using the WSDL
|
|
|
|
|
document."
|
|
|
|
|
(let ((kind (soap-basic-type-kind type)))
|
|
|
|
|
(unless (symbolp kind)
|
|
|
|
|
(let ((basic-type (soap-wsdl-get kind wsdl 'soap-basic-type-p)))
|
|
|
|
|
(setf (soap-basic-type-kind type)
|
|
|
|
|
(soap-basic-type-kind basic-type))))))
|
|
|
|
|
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(defun soap-resolve-references-for-sequence-type (type wsdl)
|
|
|
|
|
"Resolve references for a sequence TYPE using WSDL document.
|
|
|
|
|
See also `soap-resolve-references-for-element' and
|
|
|
|
|
`soap-wsdl-resolve-references'"
|
|
|
|
|
(let ((parent (soap-sequence-type-parent type)))
|
|
|
|
|
(when (or (consp parent) (stringp parent))
|
|
|
|
|
(setf (soap-sequence-type-parent type)
|
2012-04-25 10:28:29 +00:00
|
|
|
|
(soap-wsdl-get
|
|
|
|
|
parent wsdl
|
|
|
|
|
;; Prevent self references, see Bug#9
|
|
|
|
|
(lambda (e) (and (not (eq e type)) (soap-type-p e)))))))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(dolist (element (soap-sequence-type-elements type))
|
|
|
|
|
(let ((element-type (soap-sequence-element-type element)))
|
|
|
|
|
(cond ((or (consp element-type) (stringp element-type))
|
|
|
|
|
(setf (soap-sequence-element-type element)
|
2012-04-25 10:28:29 +00:00
|
|
|
|
(soap-wsdl-get
|
|
|
|
|
element-type wsdl
|
|
|
|
|
;; Prevent self references, see Bug#9
|
|
|
|
|
(lambda (e) (and (not (eq e type)) (soap-type-p e))))))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
((soap-element-p element-type)
|
|
|
|
|
;; since the element already has a child element, it
|
|
|
|
|
;; could be an inline structure. we must resolve
|
|
|
|
|
;; references in it, because it might not be reached by
|
|
|
|
|
;; scanning the wsdl names.
|
|
|
|
|
(soap-resolve-references-for-element element-type wsdl))))))
|
|
|
|
|
|
|
|
|
|
(defun soap-resolve-references-for-array-type (type wsdl)
|
|
|
|
|
"Resolve references for an array TYPE using WSDL.
|
|
|
|
|
See also `soap-resolve-references-for-element' and
|
|
|
|
|
`soap-wsdl-resolve-references'"
|
|
|
|
|
(let ((element-type (soap-array-type-element-type type)))
|
|
|
|
|
(when (or (consp element-type) (stringp element-type))
|
|
|
|
|
(setf (soap-array-type-element-type type)
|
2012-04-25 10:28:29 +00:00
|
|
|
|
(soap-wsdl-get
|
|
|
|
|
element-type wsdl
|
|
|
|
|
;; Prevent self references, see Bug#9
|
|
|
|
|
(lambda (e) (and (not (eq e type)) (soap-type-p e))))))))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
|
|
|
|
|
(defun soap-resolve-references-for-message (message wsdl)
|
|
|
|
|
"Resolve references for a MESSAGE type using the WSDL document.
|
|
|
|
|
See also `soap-resolve-references-for-element' and
|
|
|
|
|
`soap-wsdl-resolve-references'"
|
|
|
|
|
(let (resolved-parts)
|
|
|
|
|
(dolist (part (soap-message-parts message))
|
|
|
|
|
(let ((name (car part))
|
|
|
|
|
(type (cdr part)))
|
|
|
|
|
(when (stringp name)
|
|
|
|
|
(setq name (intern name)))
|
|
|
|
|
(when (or (consp type) (stringp type))
|
|
|
|
|
(setq type (soap-wsdl-get type wsdl 'soap-type-p)))
|
|
|
|
|
(push (cons name type) resolved-parts)))
|
|
|
|
|
(setf (soap-message-parts message) (nreverse resolved-parts))))
|
|
|
|
|
|
|
|
|
|
(defun soap-resolve-references-for-operation (operation wsdl)
|
|
|
|
|
"Resolve references for an OPERATION type using the WSDL document.
|
|
|
|
|
See also `soap-resolve-references-for-element' and
|
|
|
|
|
`soap-wsdl-resolve-references'"
|
|
|
|
|
(let ((input (soap-operation-input operation))
|
|
|
|
|
(counter 0))
|
|
|
|
|
(let ((name (car input))
|
|
|
|
|
(message (cdr input)))
|
|
|
|
|
;; Name this part if it was not named
|
|
|
|
|
(when (or (null name) (equal name ""))
|
|
|
|
|
(setq name (format "in%d" (incf counter))))
|
|
|
|
|
(when (or (consp message) (stringp message))
|
|
|
|
|
(setf (soap-operation-input operation)
|
2011-02-16 19:33:35 +00:00
|
|
|
|
(cons (intern name)
|
|
|
|
|
(soap-wsdl-get message wsdl 'soap-message-p))))))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
|
|
|
|
|
(let ((output (soap-operation-output operation))
|
|
|
|
|
(counter 0))
|
|
|
|
|
(let ((name (car output))
|
|
|
|
|
(message (cdr output)))
|
|
|
|
|
(when (or (null name) (equal name ""))
|
|
|
|
|
(setq name (format "out%d" (incf counter))))
|
|
|
|
|
(when (or (consp message) (stringp message))
|
|
|
|
|
(setf (soap-operation-output operation)
|
2011-02-16 19:33:35 +00:00
|
|
|
|
(cons (intern name)
|
|
|
|
|
(soap-wsdl-get message wsdl 'soap-message-p))))))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
|
|
|
|
|
(let ((resolved-faults nil)
|
|
|
|
|
(counter 0))
|
|
|
|
|
(dolist (fault (soap-operation-faults operation))
|
|
|
|
|
(let ((name (car fault))
|
|
|
|
|
(message (cdr fault)))
|
|
|
|
|
(when (or (null name) (equal name ""))
|
|
|
|
|
(setq name (format "fault%d" (incf counter))))
|
|
|
|
|
(if (or (consp message) (stringp message))
|
2011-02-16 19:33:35 +00:00
|
|
|
|
(push (cons (intern name)
|
|
|
|
|
(soap-wsdl-get message wsdl 'soap-message-p))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
resolved-faults)
|
|
|
|
|
(push fault resolved-faults))))
|
|
|
|
|
(setf (soap-operation-faults operation) resolved-faults))
|
|
|
|
|
|
|
|
|
|
(when (= (length (soap-operation-parameter-order operation)) 0)
|
|
|
|
|
(setf (soap-operation-parameter-order operation)
|
|
|
|
|
(mapcar 'car (soap-message-parts
|
|
|
|
|
(cdr (soap-operation-input operation))))))
|
2011-02-16 19:33:35 +00:00
|
|
|
|
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(setf (soap-operation-parameter-order operation)
|
|
|
|
|
(mapcar (lambda (p)
|
|
|
|
|
(if (stringp p)
|
|
|
|
|
(intern p)
|
|
|
|
|
p))
|
|
|
|
|
(soap-operation-parameter-order operation))))
|
|
|
|
|
|
|
|
|
|
(defun soap-resolve-references-for-binding (binding wsdl)
|
|
|
|
|
"Resolve references for a BINDING type using the WSDL document.
|
|
|
|
|
See also `soap-resolve-references-for-element' and
|
|
|
|
|
`soap-wsdl-resolve-references'"
|
|
|
|
|
(when (or (consp (soap-binding-port-type binding))
|
|
|
|
|
(stringp (soap-binding-port-type binding)))
|
|
|
|
|
(setf (soap-binding-port-type binding)
|
2011-02-16 19:33:35 +00:00
|
|
|
|
(soap-wsdl-get (soap-binding-port-type binding)
|
|
|
|
|
wsdl 'soap-port-type-p)))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
|
|
|
|
|
(let ((port-ops (soap-port-type-operations (soap-binding-port-type binding))))
|
|
|
|
|
(maphash (lambda (k v)
|
|
|
|
|
(setf (soap-bound-operation-operation v)
|
|
|
|
|
(soap-namespace-get k port-ops 'soap-operation-p)))
|
|
|
|
|
(soap-binding-operations binding))))
|
|
|
|
|
|
|
|
|
|
(defun soap-resolve-references-for-port (port wsdl)
|
|
|
|
|
"Resolve references for a PORT type using the WSDL document.
|
|
|
|
|
See also `soap-resolve-references-for-element' and
|
|
|
|
|
`soap-wsdl-resolve-references'"
|
|
|
|
|
(when (or (consp (soap-port-binding port))
|
|
|
|
|
(stringp (soap-port-binding port)))
|
|
|
|
|
(setf (soap-port-binding port)
|
|
|
|
|
(soap-wsdl-get (soap-port-binding port) wsdl 'soap-binding-p))))
|
|
|
|
|
|
|
|
|
|
;; Install resolvers for our types
|
|
|
|
|
(progn
|
2012-04-25 10:28:29 +00:00
|
|
|
|
(put (aref (make-soap-simple-type) 0) 'soap-resolve-references
|
|
|
|
|
'soap-resolve-references-for-simple-type)
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(put (aref (make-soap-sequence-type) 0) 'soap-resolve-references
|
|
|
|
|
'soap-resolve-references-for-sequence-type)
|
|
|
|
|
(put (aref (make-soap-array-type) 0) 'soap-resolve-references
|
|
|
|
|
'soap-resolve-references-for-array-type)
|
|
|
|
|
(put (aref (make-soap-message) 0) 'soap-resolve-references
|
|
|
|
|
'soap-resolve-references-for-message)
|
|
|
|
|
(put (aref (make-soap-operation) 0) 'soap-resolve-references
|
|
|
|
|
'soap-resolve-references-for-operation)
|
|
|
|
|
(put (aref (make-soap-binding) 0) 'soap-resolve-references
|
|
|
|
|
'soap-resolve-references-for-binding)
|
|
|
|
|
(put (aref (make-soap-port) 0) 'soap-resolve-references
|
|
|
|
|
'soap-resolve-references-for-port))
|
|
|
|
|
|
|
|
|
|
(defun soap-wsdl-resolve-references (wsdl)
|
|
|
|
|
"Resolve all references inside the WSDL structure.
|
|
|
|
|
|
|
|
|
|
When the WSDL elements are created from the XML document, they
|
|
|
|
|
refer to each other by name. For example, the ELEMENT-TYPE slot
|
|
|
|
|
of an SOAP-ARRAY-TYPE will contain the name of the element and
|
|
|
|
|
the user would have to call `soap-wsdl-get' to obtain the actual
|
|
|
|
|
element.
|
|
|
|
|
|
|
|
|
|
After the entire document is loaded, we resolve all these
|
|
|
|
|
references to the actual elements they refer to so that at
|
|
|
|
|
runtime, we don't have to call `soap-wsdl-get' each time we
|
|
|
|
|
traverse an element tree."
|
|
|
|
|
(let ((nprocessed 0)
|
|
|
|
|
(nstag-id 0)
|
|
|
|
|
(alias-table (soap-wsdl-alias-table wsdl)))
|
|
|
|
|
(dolist (ns (soap-wsdl-namespaces wsdl))
|
|
|
|
|
(let ((nstag (car-safe (rassoc (soap-namespace-name ns) alias-table))))
|
|
|
|
|
(unless nstag
|
|
|
|
|
;; If this namespace does not have an alias, create one for it.
|
|
|
|
|
(catch 'done
|
|
|
|
|
(while t
|
|
|
|
|
(setq nstag (format "ns%d" (incf nstag-id)))
|
|
|
|
|
(unless (assoc nstag alias-table)
|
|
|
|
|
(soap-wsdl-add-alias nstag (soap-namespace-name ns) wsdl)
|
|
|
|
|
(throw 'done t)))))
|
|
|
|
|
|
|
|
|
|
(maphash (lambda (name element)
|
|
|
|
|
(cond ((soap-element-p element) ; skip links
|
|
|
|
|
(incf nprocessed)
|
|
|
|
|
(soap-resolve-references-for-element element wsdl)
|
|
|
|
|
(setf (soap-element-namespace-tag element) nstag))
|
|
|
|
|
((listp element)
|
|
|
|
|
(dolist (e element)
|
|
|
|
|
(when (soap-element-p e)
|
|
|
|
|
(incf nprocessed)
|
|
|
|
|
(soap-resolve-references-for-element e wsdl)
|
|
|
|
|
(setf (soap-element-namespace-tag e) nstag))))))
|
2011-06-30 23:15:02 +00:00
|
|
|
|
(soap-namespace-elements ns)))))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
wsdl)
|
|
|
|
|
|
|
|
|
|
;;;;; Loading WSDL from XML documents
|
|
|
|
|
|
|
|
|
|
(defun soap-load-wsdl-from-url (url)
|
|
|
|
|
"Load a WSDL document from URL and return it.
|
|
|
|
|
The returned WSDL document needs to be used for `soap-invoke'
|
|
|
|
|
calls."
|
|
|
|
|
(let ((url-request-method "GET")
|
|
|
|
|
(url-package-name "soap-client.el")
|
|
|
|
|
(url-package-version "1.0")
|
|
|
|
|
(url-mime-charset-string "utf-8;q=1, iso-8859-1;q=0.5")
|
|
|
|
|
(url-request-coding-system 'utf-8)
|
|
|
|
|
(url-http-attempt-keepalives nil))
|
|
|
|
|
(let ((buffer (url-retrieve-synchronously url)))
|
|
|
|
|
(with-current-buffer buffer
|
|
|
|
|
(declare (special url-http-response-status))
|
|
|
|
|
(if (> url-http-response-status 299)
|
|
|
|
|
(error "Error retrieving WSDL: %s" url-http-response-status))
|
|
|
|
|
(let ((mime-part (mm-dissect-buffer t t)))
|
|
|
|
|
(unless mime-part
|
|
|
|
|
(error "Failed to decode response from server"))
|
|
|
|
|
(unless (equal (car (mm-handle-type mime-part)) "text/xml")
|
|
|
|
|
(error "Server response is not an XML document"))
|
|
|
|
|
(with-temp-buffer
|
|
|
|
|
(mm-insert-part mime-part)
|
|
|
|
|
(let ((wsdl-xml (car (xml-parse-region (point-min) (point-max)))))
|
|
|
|
|
(prog1
|
|
|
|
|
(let ((wsdl (soap-parse-wsdl wsdl-xml)))
|
|
|
|
|
(setf (soap-wsdl-origin wsdl) url)
|
|
|
|
|
wsdl)
|
|
|
|
|
(kill-buffer buffer)))))))))
|
|
|
|
|
|
|
|
|
|
(defun soap-load-wsdl (file)
|
|
|
|
|
"Load a WSDL document from FILE and return it."
|
|
|
|
|
(with-temp-buffer
|
|
|
|
|
(insert-file-contents file)
|
|
|
|
|
(let ((xml (car (xml-parse-region (point-min) (point-max)))))
|
|
|
|
|
(let ((wsdl (soap-parse-wsdl xml)))
|
|
|
|
|
(setf (soap-wsdl-origin wsdl) file)
|
|
|
|
|
wsdl))))
|
|
|
|
|
|
|
|
|
|
(defun soap-parse-wsdl (node)
|
|
|
|
|
"Construct a WSDL structure from NODE, which is an XML document."
|
|
|
|
|
(soap-with-local-xmlns node
|
|
|
|
|
|
|
|
|
|
(assert (eq (soap-l2wk (xml-node-name node)) 'wsdl:definitions)
|
|
|
|
|
nil
|
|
|
|
|
"soap-parse-wsdl: expecting wsdl:definitions node, got %s"
|
|
|
|
|
(soap-l2wk (xml-node-name node)))
|
|
|
|
|
|
|
|
|
|
(let ((wsdl (make-soap-wsdl)))
|
|
|
|
|
|
|
|
|
|
;; Add the local alias table to the wsdl document -- it will be used for
|
|
|
|
|
;; all types in this document even after we finish parsing it.
|
2011-02-16 19:56:31 +00:00
|
|
|
|
(setf (soap-wsdl-alias-table wsdl) soap-local-xmlns)
|
2011-02-16 09:25:37 +00:00
|
|
|
|
|
|
|
|
|
;; Add the XSD types to the wsdl document
|
|
|
|
|
(let ((ns (soap-default-xsd-types)))
|
|
|
|
|
(soap-wsdl-add-namespace ns wsdl)
|
|
|
|
|
(soap-wsdl-add-alias "xsd" (soap-namespace-name ns) wsdl))
|
|
|
|
|
|
|
|
|
|
;; Add the soapenc types to the wsdl document
|
|
|
|
|
(let ((ns (soap-default-soapenc-types)))
|
|
|
|
|
(soap-wsdl-add-namespace ns wsdl)
|
|
|
|
|
(soap-wsdl-add-alias "soapenc" (soap-namespace-name ns) wsdl))
|
|
|
|
|
|
|
|
|
|
;; Find all the 'xsd:schema nodes which are children of wsdl:types nodes
|
|
|
|
|
;; and build our type-library
|
|
|
|
|
|
|
|
|
|
(let ((types (car (soap-xml-get-children1 node 'wsdl:types))))
|
|
|
|
|
(dolist (node (xml-node-children types))
|
|
|
|
|
;; We cannot use (xml-get-children node (soap-wk2l 'xsd:schema))
|
|
|
|
|
;; because each node can install its own alias type so the schema
|
|
|
|
|
;; nodes might have a different prefix.
|
|
|
|
|
(when (consp node)
|
|
|
|
|
(soap-with-local-xmlns node
|
|
|
|
|
(when (eq (soap-l2wk (xml-node-name node)) 'xsd:schema)
|
|
|
|
|
(soap-wsdl-add-namespace (soap-parse-schema node) wsdl))))))
|
|
|
|
|
|
|
|
|
|
(let ((ns (make-soap-namespace :name (soap-get-target-namespace node))))
|
|
|
|
|
(dolist (node (soap-xml-get-children1 node 'wsdl:message))
|
|
|
|
|
(soap-namespace-put (soap-parse-message node) ns))
|
|
|
|
|
|
|
|
|
|
(dolist (node (soap-xml-get-children1 node 'wsdl:portType))
|
|
|
|
|
(let ((port-type (soap-parse-port-type node)))
|
|
|
|
|
(soap-namespace-put port-type ns)
|
2011-02-16 19:33:35 +00:00
|
|
|
|
(soap-wsdl-add-namespace
|
|
|
|
|
(soap-port-type-operations port-type) wsdl)))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
|
|
|
|
|
(dolist (node (soap-xml-get-children1 node 'wsdl:binding))
|
|
|
|
|
(soap-namespace-put (soap-parse-binding node) ns))
|
|
|
|
|
|
|
|
|
|
(dolist (node (soap-xml-get-children1 node 'wsdl:service))
|
|
|
|
|
(dolist (node (soap-xml-get-children1 node 'wsdl:port))
|
|
|
|
|
(let ((name (xml-get-attribute node 'name))
|
|
|
|
|
(binding (xml-get-attribute node 'binding))
|
2011-02-16 19:33:35 +00:00
|
|
|
|
(url (let ((n (car (soap-xml-get-children1
|
|
|
|
|
node 'wsdlsoap:address))))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(xml-get-attribute n 'location))))
|
|
|
|
|
(let ((port (make-soap-port
|
2011-02-16 19:33:35 +00:00
|
|
|
|
:name name :binding (soap-l2fq binding 'tns)
|
|
|
|
|
:service-url url)))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(soap-namespace-put port ns)
|
|
|
|
|
(push port (soap-wsdl-ports wsdl))))))
|
|
|
|
|
|
|
|
|
|
(soap-wsdl-add-namespace ns wsdl))
|
|
|
|
|
|
|
|
|
|
(soap-wsdl-resolve-references wsdl)
|
|
|
|
|
|
|
|
|
|
wsdl)))
|
|
|
|
|
|
|
|
|
|
(defun soap-parse-schema (node)
|
|
|
|
|
"Parse a schema NODE.
|
|
|
|
|
Return a SOAP-NAMESPACE containing the elements."
|
|
|
|
|
(soap-with-local-xmlns node
|
|
|
|
|
(assert (eq (soap-l2wk (xml-node-name node)) 'xsd:schema)
|
|
|
|
|
nil
|
|
|
|
|
"soap-parse-schema: expecting an xsd:schema node, got %s"
|
|
|
|
|
(soap-l2wk (xml-node-name node)))
|
|
|
|
|
(let ((ns (make-soap-namespace :name (soap-get-target-namespace node))))
|
|
|
|
|
;; NOTE: we only extract the complexTypes from the schema, we wouldn't
|
|
|
|
|
;; know how to handle basic types beyond the built in ones anyway.
|
2012-04-25 10:28:29 +00:00
|
|
|
|
(dolist (node (soap-xml-get-children1 node 'xsd:simpleType))
|
|
|
|
|
(soap-namespace-put (soap-parse-simple-type node) ns))
|
|
|
|
|
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(dolist (node (soap-xml-get-children1 node 'xsd:complexType))
|
|
|
|
|
(soap-namespace-put (soap-parse-complex-type node) ns))
|
|
|
|
|
|
|
|
|
|
(dolist (node (soap-xml-get-children1 node 'xsd:element))
|
|
|
|
|
(soap-namespace-put (soap-parse-schema-element node) ns))
|
|
|
|
|
|
|
|
|
|
ns)))
|
|
|
|
|
|
2012-04-25 10:28:29 +00:00
|
|
|
|
(defun soap-parse-simple-type (node)
|
|
|
|
|
"Parse NODE and construct a simple type from it."
|
|
|
|
|
(assert (eq (soap-l2wk (xml-node-name node)) 'xsd:simpleType)
|
|
|
|
|
nil
|
|
|
|
|
"soap-parse-complex-type: expecting xsd:simpleType node, got %s"
|
|
|
|
|
(soap-l2wk (xml-node-name node)))
|
|
|
|
|
(let ((name (xml-get-attribute-or-nil node 'name))
|
|
|
|
|
type
|
|
|
|
|
enumeration
|
|
|
|
|
(restriction (car-safe
|
|
|
|
|
(soap-xml-get-children1 node 'xsd:restriction))))
|
|
|
|
|
(unless restriction
|
|
|
|
|
(error "simpleType %s has no base type" name))
|
|
|
|
|
|
|
|
|
|
(setq type (xml-get-attribute-or-nil restriction 'base))
|
|
|
|
|
(dolist (e (soap-xml-get-children1 restriction 'xsd:enumeration))
|
|
|
|
|
(push (xml-get-attribute e 'value) enumeration))
|
|
|
|
|
|
|
|
|
|
(make-soap-simple-type :name name :kind type :enumeration enumeration)))
|
|
|
|
|
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(defun soap-parse-schema-element (node)
|
|
|
|
|
"Parse NODE and construct a schema element from it."
|
|
|
|
|
(assert (eq (soap-l2wk (xml-node-name node)) 'xsd:element)
|
|
|
|
|
nil
|
|
|
|
|
"soap-parse-schema-element: expecting xsd:element node, got %s"
|
|
|
|
|
(soap-l2wk (xml-node-name node)))
|
|
|
|
|
(let ((name (xml-get-attribute-or-nil node 'name))
|
|
|
|
|
type)
|
|
|
|
|
;; A schema element that contains an inline complex type --
|
|
|
|
|
;; construct the actual complex type for it.
|
|
|
|
|
(let ((type-node (soap-xml-get-children1 node 'xsd:complexType)))
|
|
|
|
|
(when (> (length type-node) 0)
|
2011-02-16 19:33:35 +00:00
|
|
|
|
(assert (= (length type-node) 1)) ; only one complex type
|
|
|
|
|
; definition per element
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(setq type (soap-parse-complex-type (car type-node)))))
|
|
|
|
|
(setf (soap-element-name type) name)
|
|
|
|
|
type))
|
|
|
|
|
|
|
|
|
|
(defun soap-parse-complex-type (node)
|
|
|
|
|
"Parse NODE and construct a complex type from it."
|
|
|
|
|
(assert (eq (soap-l2wk (xml-node-name node)) 'xsd:complexType)
|
|
|
|
|
nil
|
|
|
|
|
"soap-parse-complex-type: expecting xsd:complexType node, got %s"
|
|
|
|
|
(soap-l2wk (xml-node-name node)))
|
|
|
|
|
(let ((name (xml-get-attribute-or-nil node 'name))
|
|
|
|
|
;; Use a dummy type for the complex type, it will be replaced
|
|
|
|
|
;; with the real type below, except when the complex type node
|
|
|
|
|
;; is empty...
|
|
|
|
|
(type (make-soap-sequence-type :elements nil)))
|
|
|
|
|
(dolist (c (xml-node-children node))
|
|
|
|
|
(when (consp c) ; skip string nodes, which are whitespace
|
|
|
|
|
(let ((node-name (soap-l2wk (xml-node-name c))))
|
|
|
|
|
(cond
|
2011-03-05 10:32:10 +00:00
|
|
|
|
;; The difference between xsd:all and xsd:sequence is that fields
|
|
|
|
|
;; in xsd:all are not ordered and they can occur only once. We
|
|
|
|
|
;; don't care about that difference in soap-client.el
|
|
|
|
|
((or (eq node-name 'xsd:sequence)
|
|
|
|
|
(eq node-name 'xsd:all))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(setq type (soap-parse-complex-type-sequence c)))
|
|
|
|
|
((eq node-name 'xsd:complexContent)
|
|
|
|
|
(setq type (soap-parse-complex-type-complex-content c)))
|
|
|
|
|
((eq node-name 'xsd:attribute)
|
|
|
|
|
;; The name of this node comes from an attribute tag
|
|
|
|
|
(let ((n (xml-get-attribute-or-nil c 'name)))
|
|
|
|
|
(setq name n)))
|
|
|
|
|
(t
|
|
|
|
|
(error "Unknown node type %s" node-name))))))
|
|
|
|
|
(setf (soap-element-name type) name)
|
|
|
|
|
type))
|
|
|
|
|
|
|
|
|
|
(defun soap-parse-sequence (node)
|
|
|
|
|
"Parse NODE and a list of sequence elements that it defines.
|
|
|
|
|
NODE is assumed to be an xsd:sequence node. In that case, each
|
|
|
|
|
of its children is assumed to be a sequence element. Each
|
|
|
|
|
sequence element is parsed constructing the corresponding type.
|
|
|
|
|
A list of these types is returned."
|
2011-03-05 10:32:10 +00:00
|
|
|
|
(assert (let ((n (soap-l2wk (xml-node-name node))))
|
|
|
|
|
(memq n '(xsd:sequence xsd:all)))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
nil
|
2011-03-05 10:32:10 +00:00
|
|
|
|
"soap-parse-sequence: expecting xsd:sequence or xsd:all node, got %s"
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(soap-l2wk (xml-node-name node)))
|
|
|
|
|
(let (elements)
|
|
|
|
|
(dolist (e (soap-xml-get-children1 node 'xsd:element))
|
|
|
|
|
(let ((name (xml-get-attribute-or-nil e 'name))
|
|
|
|
|
(type (xml-get-attribute-or-nil e 'type))
|
|
|
|
|
(nillable? (or (equal (xml-get-attribute-or-nil e 'nillable) "true")
|
|
|
|
|
(let ((e (xml-get-attribute-or-nil e 'minOccurs)))
|
|
|
|
|
(and e (equal e "0")))))
|
|
|
|
|
(multiple? (let ((e (xml-get-attribute-or-nil e 'maxOccurs)))
|
|
|
|
|
(and e (not (equal e "1"))))))
|
|
|
|
|
(if type
|
|
|
|
|
(setq type (soap-l2fq type 'tns))
|
|
|
|
|
|
|
|
|
|
;; The node does not have a type, maybe it has a complexType
|
|
|
|
|
;; defined inline...
|
|
|
|
|
(let ((type-node (soap-xml-get-children1 e 'xsd:complexType)))
|
|
|
|
|
(when (> (length type-node) 0)
|
|
|
|
|
(assert (= (length type-node) 1)
|
|
|
|
|
nil
|
|
|
|
|
"only one complex type definition per element supported")
|
|
|
|
|
(setq type (soap-parse-complex-type (car type-node))))))
|
|
|
|
|
|
|
|
|
|
(push (make-soap-sequence-element
|
2011-02-16 19:33:35 +00:00
|
|
|
|
:name (intern name) :type type :nillable? nillable?
|
|
|
|
|
:multiple? multiple?)
|
2011-02-16 09:25:37 +00:00
|
|
|
|
elements)))
|
|
|
|
|
(nreverse elements)))
|
|
|
|
|
|
|
|
|
|
(defun soap-parse-complex-type-sequence (node)
|
|
|
|
|
"Parse NODE as a sequence type."
|
|
|
|
|
(let ((elements (soap-parse-sequence node)))
|
|
|
|
|
(make-soap-sequence-type :elements elements)))
|
|
|
|
|
|
|
|
|
|
(defun soap-parse-complex-type-complex-content (node)
|
|
|
|
|
"Parse NODE as a xsd:complexContent node.
|
|
|
|
|
A sequence or an array type is returned depending on the actual
|
|
|
|
|
contents."
|
|
|
|
|
(assert (eq (soap-l2wk (xml-node-name node)) 'xsd:complexContent)
|
|
|
|
|
nil
|
|
|
|
|
"soap-parse-complex-type-complex-content: expecting xsd:complexContent node, got %s"
|
|
|
|
|
(soap-l2wk (xml-node-name node)))
|
|
|
|
|
(let (array? parent elements)
|
|
|
|
|
(let ((extension (car-safe (soap-xml-get-children1 node 'xsd:extension)))
|
2011-02-16 19:33:35 +00:00
|
|
|
|
(restriction (car-safe
|
|
|
|
|
(soap-xml-get-children1 node 'xsd:restriction))))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
;; a complex content node is either an extension or a restriction
|
|
|
|
|
(cond (extension
|
|
|
|
|
(setq parent (xml-get-attribute-or-nil extension 'base))
|
|
|
|
|
(setq elements (soap-parse-sequence
|
2011-02-16 19:33:35 +00:00
|
|
|
|
(car (soap-xml-get-children1
|
|
|
|
|
extension 'xsd:sequence)))))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(restriction
|
|
|
|
|
(let ((base (xml-get-attribute-or-nil restriction 'base)))
|
2012-04-25 10:28:29 +00:00
|
|
|
|
(assert (equal base (soap-wk2l "soapenc:Array"))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
nil
|
|
|
|
|
"restrictions supported only for soapenc:Array types, this is a %s"
|
|
|
|
|
base))
|
|
|
|
|
(setq array? t)
|
2011-02-16 19:33:35 +00:00
|
|
|
|
(let ((attribute (car (soap-xml-get-children1
|
|
|
|
|
restriction 'xsd:attribute))))
|
|
|
|
|
(let ((array-type (soap-xml-get-attribute-or-nil1
|
|
|
|
|
attribute 'wsdl:arrayType)))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(when (string-match "^\\(.*\\)\\[\\]$" array-type)
|
|
|
|
|
(setq parent (match-string 1 array-type))))))
|
|
|
|
|
|
|
|
|
|
(t
|
|
|
|
|
(error "Unknown complex type"))))
|
|
|
|
|
|
|
|
|
|
(if parent
|
|
|
|
|
(setq parent (soap-l2fq parent 'tns)))
|
2011-02-16 19:33:35 +00:00
|
|
|
|
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(if array?
|
|
|
|
|
(make-soap-array-type :element-type parent)
|
|
|
|
|
(make-soap-sequence-type :parent parent :elements elements))))
|
|
|
|
|
|
|
|
|
|
(defun soap-parse-message (node)
|
|
|
|
|
"Parse NODE as a wsdl:message and return the corresponding type."
|
|
|
|
|
(assert (eq (soap-l2wk (xml-node-name node)) 'wsdl:message)
|
|
|
|
|
nil
|
|
|
|
|
"soap-parse-message: expecting wsdl:message node, got %s"
|
|
|
|
|
(soap-l2wk (xml-node-name node)))
|
|
|
|
|
(let ((name (xml-get-attribute-or-nil node 'name))
|
|
|
|
|
parts)
|
|
|
|
|
(dolist (p (soap-xml-get-children1 node 'wsdl:part))
|
|
|
|
|
(let ((name (xml-get-attribute-or-nil p 'name))
|
|
|
|
|
(type (xml-get-attribute-or-nil p 'type))
|
|
|
|
|
(element (xml-get-attribute-or-nil p 'element)))
|
|
|
|
|
|
|
|
|
|
(when type
|
|
|
|
|
(setq type (soap-l2fq type 'tns)))
|
|
|
|
|
|
|
|
|
|
(when element
|
|
|
|
|
(setq element (soap-l2fq element 'tns)))
|
|
|
|
|
|
|
|
|
|
(push (cons name (or type element)) parts)))
|
|
|
|
|
(make-soap-message :name name :parts (nreverse parts))))
|
|
|
|
|
|
|
|
|
|
(defun soap-parse-port-type (node)
|
|
|
|
|
"Parse NODE as a wsdl:portType and return the corresponding port."
|
|
|
|
|
(assert (eq (soap-l2wk (xml-node-name node)) 'wsdl:portType)
|
|
|
|
|
nil
|
|
|
|
|
"soap-parse-port-type: expecting wsdl:portType node got %s"
|
|
|
|
|
(soap-l2wk (xml-node-name node)))
|
|
|
|
|
(let ((ns (make-soap-namespace
|
|
|
|
|
:name (concat "urn:" (xml-get-attribute node 'name)))))
|
|
|
|
|
(dolist (node (soap-xml-get-children1 node 'wsdl:operation))
|
|
|
|
|
(let ((o (soap-parse-operation node)))
|
|
|
|
|
|
2011-02-16 19:33:35 +00:00
|
|
|
|
(let ((other-operation (soap-namespace-get
|
|
|
|
|
(soap-element-name o) ns 'soap-operation-p)))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(if other-operation
|
|
|
|
|
;; Unfortunately, the Confluence WSDL defines two operations
|
|
|
|
|
;; named "search" which differ only in parameter names...
|
2011-02-16 19:33:35 +00:00
|
|
|
|
(soap-warning "Discarding duplicate operation: %s"
|
|
|
|
|
(soap-element-name o))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
|
|
|
|
|
(progn
|
|
|
|
|
(soap-namespace-put o ns)
|
|
|
|
|
|
|
|
|
|
;; link all messages from this namespace, as this namespace
|
|
|
|
|
;; will be used for decoding the response.
|
|
|
|
|
(destructuring-bind (name . message) (soap-operation-input o)
|
|
|
|
|
(soap-namespace-put-link name message ns))
|
|
|
|
|
|
|
|
|
|
(destructuring-bind (name . message) (soap-operation-output o)
|
|
|
|
|
(soap-namespace-put-link name message ns))
|
|
|
|
|
|
|
|
|
|
(dolist (fault (soap-operation-faults o))
|
|
|
|
|
(destructuring-bind (name . message) fault
|
|
|
|
|
(soap-namespace-put-link name message ns 'replace)))
|
|
|
|
|
|
|
|
|
|
)))))
|
|
|
|
|
|
|
|
|
|
(make-soap-port-type :name (xml-get-attribute node 'name)
|
|
|
|
|
:operations ns)))
|
|
|
|
|
|
|
|
|
|
(defun soap-parse-operation (node)
|
|
|
|
|
"Parse NODE as a wsdl:operation and return the corresponding type."
|
|
|
|
|
(assert (eq (soap-l2wk (xml-node-name node)) 'wsdl:operation)
|
|
|
|
|
nil
|
|
|
|
|
"soap-parse-operation: expecting wsdl:operation node, got %s"
|
|
|
|
|
(soap-l2wk (xml-node-name node)))
|
|
|
|
|
(let ((name (xml-get-attribute node 'name))
|
2011-02-16 19:33:35 +00:00
|
|
|
|
(parameter-order (split-string
|
|
|
|
|
(xml-get-attribute node 'parameterOrder)))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
input output faults)
|
|
|
|
|
(dolist (n (xml-node-children node))
|
|
|
|
|
(when (consp n) ; skip string nodes which are whitespace
|
|
|
|
|
(let ((node-name (soap-l2wk (xml-node-name n))))
|
|
|
|
|
(cond
|
|
|
|
|
((eq node-name 'wsdl:input)
|
|
|
|
|
(let ((message (xml-get-attribute n 'message))
|
|
|
|
|
(name (xml-get-attribute n 'name)))
|
|
|
|
|
(setq input (cons name (soap-l2fq message 'tns)))))
|
|
|
|
|
((eq node-name 'wsdl:output)
|
|
|
|
|
(let ((message (xml-get-attribute n 'message))
|
|
|
|
|
(name (xml-get-attribute n 'name)))
|
|
|
|
|
(setq output (cons name (soap-l2fq message 'tns)))))
|
|
|
|
|
((eq node-name 'wsdl:fault)
|
|
|
|
|
(let ((message (xml-get-attribute n 'message))
|
|
|
|
|
(name (xml-get-attribute n 'name)))
|
|
|
|
|
(push (cons name (soap-l2fq message 'tns)) faults)))))))
|
|
|
|
|
(make-soap-operation
|
|
|
|
|
:name name
|
|
|
|
|
:parameter-order parameter-order
|
|
|
|
|
:input input
|
|
|
|
|
:output output
|
|
|
|
|
:faults (nreverse faults))))
|
|
|
|
|
|
|
|
|
|
(defun soap-parse-binding (node)
|
|
|
|
|
"Parse NODE as a wsdl:binding and return the corresponding type."
|
|
|
|
|
(assert (eq (soap-l2wk (xml-node-name node)) 'wsdl:binding)
|
|
|
|
|
nil
|
|
|
|
|
"soap-parse-binding: expecting wsdl:binding node, got %s"
|
|
|
|
|
(soap-l2wk (xml-node-name node)))
|
|
|
|
|
(let ((name (xml-get-attribute node 'name))
|
|
|
|
|
(type (xml-get-attribute node 'type)))
|
2011-02-16 19:33:35 +00:00
|
|
|
|
(let ((binding (make-soap-binding :name name
|
|
|
|
|
:port-type (soap-l2fq type 'tns))))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(dolist (wo (soap-xml-get-children1 node 'wsdl:operation))
|
|
|
|
|
(let ((name (xml-get-attribute wo 'name))
|
|
|
|
|
soap-action
|
|
|
|
|
use)
|
|
|
|
|
(dolist (so (soap-xml-get-children1 wo 'wsdlsoap:operation))
|
|
|
|
|
(setq soap-action (xml-get-attribute-or-nil so 'soapAction)))
|
|
|
|
|
|
|
|
|
|
;; Search a wsdlsoap:body node and find a "use" tag. The
|
|
|
|
|
;; same use tag is assumed to be present for both input and
|
|
|
|
|
;; output types (although the WDSL spec allows separate
|
|
|
|
|
;; "use"-s for each of them...
|
|
|
|
|
|
|
|
|
|
(dolist (i (soap-xml-get-children1 wo 'wsdl:input))
|
|
|
|
|
(dolist (b (soap-xml-get-children1 i 'wsdlsoap:body))
|
|
|
|
|
(setq use (or use
|
|
|
|
|
(xml-get-attribute-or-nil b 'use)))))
|
|
|
|
|
|
|
|
|
|
(unless use
|
|
|
|
|
(dolist (i (soap-xml-get-children1 wo 'wsdl:output))
|
|
|
|
|
(dolist (b (soap-xml-get-children1 i 'wsdlsoap:body))
|
|
|
|
|
(setq use (or use
|
|
|
|
|
(xml-get-attribute-or-nil b 'use))))))
|
|
|
|
|
|
|
|
|
|
(puthash name (make-soap-bound-operation :operation name
|
|
|
|
|
:soap-action soap-action
|
|
|
|
|
:use (and use (intern use)))
|
|
|
|
|
(soap-binding-operations binding))))
|
|
|
|
|
binding)))
|
|
|
|
|
|
|
|
|
|
;;;; SOAP type decoding
|
|
|
|
|
|
2011-02-16 19:56:31 +00:00
|
|
|
|
(defvar soap-multi-refs nil
|
2011-02-16 09:25:37 +00:00
|
|
|
|
"The list of multi-ref nodes in the current SOAP response.
|
|
|
|
|
This is a dynamically bound variable used during decoding the
|
|
|
|
|
SOAP response.")
|
|
|
|
|
|
2011-02-16 19:56:31 +00:00
|
|
|
|
(defvar soap-decoded-multi-refs nil
|
2011-02-16 09:25:37 +00:00
|
|
|
|
"List of decoded multi-ref nodes in the current SOAP response.
|
|
|
|
|
This is a dynamically bound variable used during decoding the
|
|
|
|
|
SOAP response.")
|
|
|
|
|
|
2011-02-16 19:56:31 +00:00
|
|
|
|
(defvar soap-current-wsdl nil
|
2011-02-16 09:25:37 +00:00
|
|
|
|
"The current WSDL document used when decoding the SOAP response.
|
|
|
|
|
This is a dynamically bound variable.")
|
|
|
|
|
|
|
|
|
|
(defun soap-decode-type (type node)
|
|
|
|
|
"Use TYPE (an xsd type) to decode the contents of NODE.
|
|
|
|
|
|
|
|
|
|
NODE is an XML node, representing some SOAP encoded value or a
|
|
|
|
|
reference to another XML node (a multiRef). This function will
|
|
|
|
|
resolve the multiRef reference, if any, than call a TYPE specific
|
|
|
|
|
decode function to perform the actual decoding."
|
|
|
|
|
(let ((href (xml-get-attribute-or-nil node 'href)))
|
|
|
|
|
(cond (href
|
|
|
|
|
(catch 'done
|
|
|
|
|
;; NODE is actually a HREF, find the target and decode that.
|
|
|
|
|
;; Check first if we already decoded this multiref.
|
|
|
|
|
|
2011-02-16 19:56:31 +00:00
|
|
|
|
(let ((decoded (cdr (assoc href soap-decoded-multi-refs))))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(when decoded
|
|
|
|
|
(throw 'done decoded)))
|
|
|
|
|
|
|
|
|
|
(string-match "^#\\(.*\\)$" href) ; TODO: check that it matched
|
|
|
|
|
|
|
|
|
|
(let ((id (match-string 1 href)))
|
2011-02-16 19:56:31 +00:00
|
|
|
|
(dolist (mr soap-multi-refs)
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(let ((mrid (xml-get-attribute mr 'id)))
|
|
|
|
|
(when (equal id mrid)
|
|
|
|
|
;; recurse here, in case there are multiple HREF's
|
|
|
|
|
(let ((decoded (soap-decode-type type mr)))
|
2011-02-16 19:56:31 +00:00
|
|
|
|
(push (cons href decoded) soap-decoded-multi-refs)
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(throw 'done decoded)))))
|
|
|
|
|
(error "Cannot find href %s" href))))
|
|
|
|
|
(t
|
|
|
|
|
(soap-with-local-xmlns node
|
|
|
|
|
(if (equal (soap-xml-get-attribute-or-nil1 node 'xsi:nil) "true")
|
|
|
|
|
nil
|
|
|
|
|
(let ((decoder (get (aref type 0) 'soap-decoder)))
|
2011-02-16 19:33:35 +00:00
|
|
|
|
(assert decoder nil "no soap-decoder for %s type"
|
|
|
|
|
(aref type 0))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(funcall decoder type node))))))))
|
|
|
|
|
|
|
|
|
|
(defun soap-decode-any-type (node)
|
|
|
|
|
"Decode NODE using type information inside it."
|
|
|
|
|
;; If the NODE has type information, we use that...
|
|
|
|
|
(let ((type (soap-xml-get-attribute-or-nil1 node 'xsi:type)))
|
|
|
|
|
(if type
|
2011-02-16 19:56:31 +00:00
|
|
|
|
(let ((wtype (soap-wsdl-get type soap-current-wsdl 'soap-type-p)))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(if wtype
|
|
|
|
|
(soap-decode-type wtype node)
|
|
|
|
|
;; The node has type info encoded in it, but we don't know how
|
|
|
|
|
;; to decode it...
|
|
|
|
|
(error "Soap-decode-any-type: node has unknown type: %s" type)))
|
|
|
|
|
|
|
|
|
|
;; No type info in the node...
|
|
|
|
|
|
|
|
|
|
(let ((contents (xml-node-children node)))
|
|
|
|
|
(if (and (= (length contents) 1) (stringp (car contents)))
|
|
|
|
|
;; contents is just a string
|
|
|
|
|
(car contents)
|
|
|
|
|
|
|
|
|
|
;; we assume the NODE is a sequence with every element a
|
|
|
|
|
;; structure name
|
|
|
|
|
(let (result)
|
|
|
|
|
(dolist (element contents)
|
|
|
|
|
(let ((key (xml-node-name element))
|
|
|
|
|
(value (soap-decode-any-type element)))
|
|
|
|
|
(push (cons key value) result)))
|
|
|
|
|
(nreverse result)))))))
|
|
|
|
|
|
|
|
|
|
(defun soap-decode-array (node)
|
|
|
|
|
"Decode NODE as an Array using type information inside it."
|
|
|
|
|
(let ((type (soap-xml-get-attribute-or-nil1 node 'soapenc:arrayType))
|
|
|
|
|
(wtype nil)
|
|
|
|
|
(contents (xml-node-children node))
|
|
|
|
|
result)
|
|
|
|
|
(when type
|
|
|
|
|
;; Type is in the format "someType[NUM]" where NUM is the number of
|
|
|
|
|
;; elements in the array. We discard the [NUM] part.
|
|
|
|
|
(setq type (replace-regexp-in-string "\\[[0-9]+\\]\\'" "" type))
|
2011-02-16 19:56:31 +00:00
|
|
|
|
(setq wtype (soap-wsdl-get type soap-current-wsdl 'soap-type-p))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(unless wtype
|
|
|
|
|
;; The node has type info encoded in it, but we don't know how to
|
|
|
|
|
;; decode it...
|
|
|
|
|
(error "Soap-decode-array: node has unknown type: %s" type)))
|
|
|
|
|
(dolist (e contents)
|
|
|
|
|
(when (consp e)
|
|
|
|
|
(push (if wtype
|
|
|
|
|
(soap-decode-type wtype e)
|
|
|
|
|
(soap-decode-any-type e))
|
|
|
|
|
result)))
|
|
|
|
|
(nreverse result)))
|
|
|
|
|
|
|
|
|
|
(defun soap-decode-basic-type (type node)
|
|
|
|
|
"Use TYPE to decode the contents of NODE.
|
|
|
|
|
TYPE is a `soap-basic-type' struct, and NODE is an XML document.
|
|
|
|
|
A LISP value is returned based on the contents of NODE and the
|
|
|
|
|
type-info stored in TYPE."
|
|
|
|
|
(let ((contents (xml-node-children node))
|
|
|
|
|
(type-kind (soap-basic-type-kind type)))
|
|
|
|
|
|
|
|
|
|
(if (null contents)
|
|
|
|
|
nil
|
|
|
|
|
(ecase type-kind
|
2012-04-25 10:28:29 +00:00
|
|
|
|
((string anyURI) (car contents))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(dateTime (car contents)) ; TODO: convert to a date time
|
2012-04-25 10:28:29 +00:00
|
|
|
|
((long int integer unsignedInt byte float double) (string-to-number (car contents)))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(boolean (string= (downcase (car contents)) "true"))
|
|
|
|
|
(base64Binary (base64-decode-string (car contents)))
|
|
|
|
|
(anyType (soap-decode-any-type node))
|
|
|
|
|
(Array (soap-decode-array node))))))
|
|
|
|
|
|
|
|
|
|
(defun soap-decode-sequence-type (type node)
|
|
|
|
|
"Use TYPE to decode the contents of NODE.
|
|
|
|
|
TYPE is assumed to be a sequence type and an ALIST with the
|
|
|
|
|
contents of the NODE is returned."
|
|
|
|
|
(let ((result nil)
|
|
|
|
|
(parent (soap-sequence-type-parent type)))
|
|
|
|
|
(when parent
|
|
|
|
|
(setq result (nreverse (soap-decode-type parent node))))
|
|
|
|
|
(dolist (element (soap-sequence-type-elements type))
|
|
|
|
|
(let ((instance-count 0)
|
|
|
|
|
(e-name (soap-sequence-element-name element))
|
|
|
|
|
(e-type (soap-sequence-element-type element)))
|
|
|
|
|
(dolist (node (xml-get-children node e-name))
|
|
|
|
|
(incf instance-count)
|
|
|
|
|
(push (cons e-name (soap-decode-type e-type node)) result))
|
|
|
|
|
;; Do some sanity checking
|
|
|
|
|
(cond ((and (= instance-count 0)
|
|
|
|
|
(not (soap-sequence-element-nillable? element)))
|
|
|
|
|
(soap-warning "While decoding %s: missing non-nillable slot %s"
|
|
|
|
|
(soap-element-name type) e-name))
|
|
|
|
|
((and (> instance-count 1)
|
|
|
|
|
(not (soap-sequence-element-multiple? element)))
|
|
|
|
|
(soap-warning "While decoding %s: multiple slots named %s"
|
|
|
|
|
(soap-element-name type) e-name)))))
|
|
|
|
|
(nreverse result)))
|
|
|
|
|
|
|
|
|
|
(defun soap-decode-array-type (type node)
|
|
|
|
|
"Use TYPE to decode the contents of NODE.
|
|
|
|
|
TYPE is assumed to be an array type. Arrays are decoded as lists.
|
|
|
|
|
This is because it is easier to work with list results in LISP."
|
|
|
|
|
(let ((result nil)
|
|
|
|
|
(element-type (soap-array-type-element-type type)))
|
|
|
|
|
(dolist (node (xml-node-children node))
|
|
|
|
|
(when (consp node)
|
|
|
|
|
(push (soap-decode-type element-type node) result)))
|
|
|
|
|
(nreverse result)))
|
|
|
|
|
|
|
|
|
|
(progn
|
|
|
|
|
(put (aref (make-soap-basic-type) 0)
|
|
|
|
|
'soap-decoder 'soap-decode-basic-type)
|
2012-04-25 10:28:29 +00:00
|
|
|
|
;; just use the basic type decoder for the simple type -- we accept any
|
|
|
|
|
;; value and don't do any validation on it.
|
|
|
|
|
(put (aref (make-soap-simple-type) 0)
|
|
|
|
|
'soap-decoder 'soap-decode-basic-type)
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(put (aref (make-soap-sequence-type) 0)
|
|
|
|
|
'soap-decoder 'soap-decode-sequence-type)
|
|
|
|
|
(put (aref (make-soap-array-type) 0)
|
|
|
|
|
'soap-decoder 'soap-decode-array-type))
|
|
|
|
|
|
|
|
|
|
;;;; Soap Envelope parsing
|
|
|
|
|
|
|
|
|
|
(put 'soap-error
|
|
|
|
|
'error-conditions
|
|
|
|
|
'(error soap-error))
|
|
|
|
|
(put 'soap-error 'error-message "SOAP error")
|
|
|
|
|
|
|
|
|
|
(defun soap-parse-envelope (node operation wsdl)
|
|
|
|
|
"Parse the SOAP envelope in NODE and return the response.
|
|
|
|
|
OPERATION is the WSDL operation for which we expect the response,
|
|
|
|
|
WSDL is used to decode the NODE"
|
|
|
|
|
(soap-with-local-xmlns node
|
|
|
|
|
(assert (eq (soap-l2wk (xml-node-name node)) 'soap:Envelope)
|
|
|
|
|
nil
|
|
|
|
|
"soap-parse-envelope: expecting soap:Envelope node, got %s"
|
|
|
|
|
(soap-l2wk (xml-node-name node)))
|
|
|
|
|
(let ((body (car (soap-xml-get-children1 node 'soap:Body))))
|
|
|
|
|
|
|
|
|
|
(let ((fault (car (soap-xml-get-children1 body 'soap:Fault))))
|
|
|
|
|
(when fault
|
2011-02-16 19:33:35 +00:00
|
|
|
|
(let ((fault-code (let ((n (car (xml-get-children
|
|
|
|
|
fault 'faultcode))))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(car-safe (xml-node-children n))))
|
2011-02-16 19:33:35 +00:00
|
|
|
|
(fault-string (let ((n (car (xml-get-children
|
2012-04-25 10:28:29 +00:00
|
|
|
|
fault 'faultstring))))
|
|
|
|
|
(car-safe (xml-node-children n))))
|
|
|
|
|
(detail (xml-get-children fault 'detail)))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(while t
|
2012-04-25 10:28:29 +00:00
|
|
|
|
(signal 'soap-error (list fault-code fault-string detail))))))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
|
|
|
|
|
;; First (non string) element of the body is the root node of he
|
|
|
|
|
;; response
|
|
|
|
|
(let ((response (if (eq (soap-bound-operation-use operation) 'literal)
|
|
|
|
|
;; For 'literal uses, the response is the actual body
|
|
|
|
|
body
|
|
|
|
|
;; ...otherwise the first non string element
|
|
|
|
|
;; of the body is the response
|
|
|
|
|
(catch 'found
|
|
|
|
|
(dolist (n (xml-node-children body))
|
|
|
|
|
(when (consp n)
|
|
|
|
|
(throw 'found n)))))))
|
|
|
|
|
(soap-parse-response response operation wsdl body)))))
|
|
|
|
|
|
|
|
|
|
(defun soap-parse-response (response-node operation wsdl soap-body)
|
|
|
|
|
"Parse RESPONSE-NODE and return the result as a LISP value.
|
|
|
|
|
OPERATION is the WSDL operation for which we expect the response,
|
|
|
|
|
WSDL is used to decode the NODE.
|
|
|
|
|
|
|
|
|
|
SOAP-BODY is the body of the SOAP envelope (of which
|
|
|
|
|
RESPONSE-NODE is a sub-node). It is used in case RESPONSE-NODE
|
|
|
|
|
reference multiRef parts which are external to RESPONSE-NODE."
|
2011-02-16 19:56:31 +00:00
|
|
|
|
(let* ((soap-current-wsdl wsdl)
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(op (soap-bound-operation-operation operation))
|
|
|
|
|
(use (soap-bound-operation-use operation))
|
|
|
|
|
(message (cdr (soap-operation-output op))))
|
|
|
|
|
|
|
|
|
|
(soap-with-local-xmlns response-node
|
|
|
|
|
|
|
|
|
|
(when (eq use 'encoded)
|
|
|
|
|
(let* ((received-message-name (soap-l2fq (xml-node-name response-node)))
|
2011-02-16 19:33:35 +00:00
|
|
|
|
(received-message (soap-wsdl-get
|
|
|
|
|
received-message-name wsdl 'soap-message-p)))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(unless (eq received-message message)
|
|
|
|
|
(error "Unexpected message: got %s, expecting %s"
|
|
|
|
|
received-message-name
|
|
|
|
|
(soap-element-name message)))))
|
|
|
|
|
|
|
|
|
|
(let ((decoded-parts nil)
|
2011-02-16 19:56:31 +00:00
|
|
|
|
(soap-multi-refs (xml-get-children soap-body 'multiRef))
|
|
|
|
|
(soap-decoded-multi-refs nil))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
|
|
|
|
|
(dolist (part (soap-message-parts message))
|
|
|
|
|
(let ((tag (car part))
|
|
|
|
|
(type (cdr part))
|
|
|
|
|
node)
|
|
|
|
|
|
|
|
|
|
(setq node
|
|
|
|
|
(cond
|
|
|
|
|
((eq use 'encoded)
|
|
|
|
|
(car (xml-get-children response-node tag)))
|
|
|
|
|
|
|
|
|
|
((eq use 'literal)
|
|
|
|
|
(catch 'found
|
|
|
|
|
(let* ((ns-aliases (soap-wsdl-alias-table wsdl))
|
2011-02-16 19:33:35 +00:00
|
|
|
|
(ns-name (cdr (assoc
|
|
|
|
|
(soap-element-namespace-tag type)
|
|
|
|
|
ns-aliases)))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(fqname (cons ns-name (soap-element-name type))))
|
|
|
|
|
(dolist (c (xml-node-children response-node))
|
|
|
|
|
(when (consp c)
|
|
|
|
|
(soap-with-local-xmlns c
|
2011-02-16 19:33:35 +00:00
|
|
|
|
(when (equal (soap-l2fq (xml-node-name c))
|
|
|
|
|
fqname)
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(throw 'found c))))))))))
|
|
|
|
|
|
|
|
|
|
(unless node
|
|
|
|
|
(error "Soap-parse-response(%s): cannot find message part %s"
|
|
|
|
|
(soap-element-name op) tag))
|
|
|
|
|
(push (soap-decode-type type node) decoded-parts)))
|
|
|
|
|
|
|
|
|
|
decoded-parts))))
|
|
|
|
|
|
|
|
|
|
;;;; SOAP type encoding
|
|
|
|
|
|
2011-02-16 19:56:31 +00:00
|
|
|
|
(defvar soap-encoded-namespaces nil
|
2011-02-16 09:25:37 +00:00
|
|
|
|
"A list of namespace tags used during encoding a message.
|
|
|
|
|
This list is populated by `soap-encode-value' and used by
|
|
|
|
|
`soap-create-envelope' to add aliases for these namespace to the
|
|
|
|
|
XML request.
|
|
|
|
|
|
|
|
|
|
This variable is dynamically bound in `soap-create-envelope'.")
|
|
|
|
|
|
|
|
|
|
(defun soap-encode-value (xml-tag value type)
|
|
|
|
|
"Encode inside an XML-TAG the VALUE using TYPE.
|
|
|
|
|
The resulting XML data is inserted in the current buffer
|
|
|
|
|
at (point)/
|
|
|
|
|
|
|
|
|
|
TYPE is one of the soap-*-type structures which defines how VALUE
|
|
|
|
|
is to be encoded. This is a generic function which finds an
|
|
|
|
|
encoder function based on TYPE and calls that encoder to do the
|
|
|
|
|
work."
|
|
|
|
|
(let ((encoder (get (aref type 0) 'soap-encoder)))
|
|
|
|
|
(assert encoder nil "no soap-encoder for %s type" (aref type 0))
|
|
|
|
|
;; XML-TAG can be a string or a symbol, but we pass only string's to the
|
|
|
|
|
;; encoders
|
|
|
|
|
(when (symbolp xml-tag)
|
|
|
|
|
(setq xml-tag (symbol-name xml-tag)))
|
|
|
|
|
(funcall encoder xml-tag value type))
|
2011-02-16 19:56:31 +00:00
|
|
|
|
(add-to-list 'soap-encoded-namespaces (soap-element-namespace-tag type)))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
|
|
|
|
|
(defun soap-encode-basic-type (xml-tag value type)
|
|
|
|
|
"Encode inside XML-TAG the LISP VALUE according to TYPE.
|
|
|
|
|
Do not call this function directly, use `soap-encode-value'
|
|
|
|
|
instead."
|
|
|
|
|
(let ((xsi-type (soap-element-fq-name type))
|
|
|
|
|
(basic-type (soap-basic-type-kind type)))
|
|
|
|
|
|
|
|
|
|
;; try to classify the type based on the value type and use that type when
|
|
|
|
|
;; encoding
|
|
|
|
|
(when (eq basic-type 'anyType)
|
|
|
|
|
(cond ((stringp value)
|
|
|
|
|
(setq xsi-type "xsd:string" basic-type 'string))
|
|
|
|
|
((integerp value)
|
|
|
|
|
(setq xsi-type "xsd:int" basic-type 'int))
|
|
|
|
|
((memq value '(t nil))
|
|
|
|
|
(setq xsi-type "xsd:boolean" basic-type 'boolean))
|
|
|
|
|
(t
|
2011-02-16 19:33:35 +00:00
|
|
|
|
(error
|
|
|
|
|
"Soap-encode-basic-type(%s, %s, %s): cannot classify anyType value"
|
|
|
|
|
xml-tag value xsi-type))))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
|
|
|
|
|
(insert "<" xml-tag " xsi:type=\"" xsi-type "\"")
|
|
|
|
|
|
|
|
|
|
;; We have some ambiguity here, as a nil value represents "false" when the
|
|
|
|
|
;; type is boolean, we will never have a "nil" boolean type...
|
|
|
|
|
|
|
|
|
|
(if (or value (eq basic-type 'boolean))
|
|
|
|
|
(progn
|
|
|
|
|
(insert ">")
|
|
|
|
|
(case basic-type
|
2012-04-25 10:28:29 +00:00
|
|
|
|
((string anyURI)
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(unless (stringp value)
|
|
|
|
|
(error "Soap-encode-basic-type(%s, %s, %s): not a string value"
|
|
|
|
|
xml-tag value xsi-type))
|
|
|
|
|
(insert (url-insert-entities-in-string value)))
|
|
|
|
|
|
|
|
|
|
(dateTime
|
|
|
|
|
(cond ((and (consp value) ; is there a time-value-p ?
|
|
|
|
|
(>= (length value) 2)
|
|
|
|
|
(numberp (nth 0 value))
|
|
|
|
|
(numberp (nth 1 value)))
|
2011-02-16 19:33:35 +00:00
|
|
|
|
;; Value is a (current-time) style value, convert
|
|
|
|
|
;; to a string
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(insert (format-time-string "%Y-%m-%dT%H:%M:%S" value)))
|
|
|
|
|
((stringp value)
|
|
|
|
|
(insert (url-insert-entities-in-string value)))
|
|
|
|
|
(t
|
2011-02-16 19:33:35 +00:00
|
|
|
|
(error
|
|
|
|
|
"Soap-encode-basic-type(%s, %s, %s): not a dateTime value"
|
|
|
|
|
xml-tag value xsi-type))))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
|
|
|
|
|
(boolean
|
|
|
|
|
(unless (memq value '(t nil))
|
|
|
|
|
(error "Soap-encode-basic-type(%s, %s, %s): not a boolean value"
|
|
|
|
|
xml-tag value xsi-type))
|
|
|
|
|
(insert (if value "true" "false")))
|
|
|
|
|
|
2012-04-25 10:28:29 +00:00
|
|
|
|
((long int integer byte unsignedInt)
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(unless (integerp value)
|
|
|
|
|
(error "Soap-encode-basic-type(%s, %s, %s): not an integer value"
|
|
|
|
|
xml-tag value xsi-type))
|
2012-04-25 10:28:29 +00:00
|
|
|
|
(when (and (eq basic-type 'unsignedInt) (< value 0))
|
|
|
|
|
(error "Soap-encode-basic-type(%s, %s, %s): not a positive integer"
|
|
|
|
|
xml-tag value xsi-type))
|
|
|
|
|
(insert (number-to-string value)))
|
|
|
|
|
|
|
|
|
|
((float double)
|
|
|
|
|
(unless (numberp value)
|
|
|
|
|
(error "Soap-encode-basic-type(%s, %s, %s): not a number"
|
|
|
|
|
xml-tag value xsi-type))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(insert (number-to-string value)))
|
2011-02-16 19:33:35 +00:00
|
|
|
|
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(base64Binary
|
|
|
|
|
(unless (stringp value)
|
|
|
|
|
(error "Soap-encode-basic-type(%s, %s, %s): not a string value"
|
|
|
|
|
xml-tag value xsi-type))
|
|
|
|
|
(insert (base64-encode-string value)))
|
|
|
|
|
|
|
|
|
|
(otherwise
|
2011-02-16 19:33:35 +00:00
|
|
|
|
(error
|
|
|
|
|
"Soap-encode-basic-type(%s, %s, %s): don't know how to encode"
|
|
|
|
|
xml-tag value xsi-type))))
|
|
|
|
|
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(insert " xsi:nil=\"true\">"))
|
|
|
|
|
(insert "</" xml-tag ">\n")))
|
|
|
|
|
|
2012-04-25 10:28:29 +00:00
|
|
|
|
(defun soap-encode-simple-type (xml-tag value type)
|
|
|
|
|
"Encode inside XML-TAG the LISP VALUE according to TYPE."
|
|
|
|
|
|
2012-05-29 06:16:49 +00:00
|
|
|
|
;; Validate VALUE against the simple type's enumeration, than just encode it
|
2012-04-25 10:28:29 +00:00
|
|
|
|
;; using `soap-encode-basic-type'
|
|
|
|
|
|
|
|
|
|
(let ((enumeration (soap-simple-type-enumeration type)))
|
|
|
|
|
(unless (and (> (length enumeration) 1)
|
|
|
|
|
(member value enumeration))
|
|
|
|
|
(error "soap-encode-simple-type(%s, %s, %s): bad value, should be one of %s"
|
|
|
|
|
xml-tag value (soap-element-fq-name type) enumeration)))
|
|
|
|
|
|
|
|
|
|
(soap-encode-basic-type xml-tag value type))
|
|
|
|
|
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(defun soap-encode-sequence-type (xml-tag value type)
|
|
|
|
|
"Encode inside XML-TAG the LISP VALUE according to TYPE.
|
|
|
|
|
Do not call this function directly, use `soap-encode-value'
|
|
|
|
|
instead."
|
|
|
|
|
(let ((xsi-type (soap-element-fq-name type)))
|
|
|
|
|
(insert "<" xml-tag " xsi:type=\"" xsi-type "\"")
|
|
|
|
|
(if value
|
|
|
|
|
(progn
|
|
|
|
|
(insert ">\n")
|
|
|
|
|
(let ((parents (list type))
|
|
|
|
|
(parent (soap-sequence-type-parent type)))
|
|
|
|
|
|
|
|
|
|
(while parent
|
|
|
|
|
(push parent parents)
|
|
|
|
|
(setq parent (soap-sequence-type-parent parent)))
|
|
|
|
|
|
|
|
|
|
(dolist (type parents)
|
|
|
|
|
(dolist (element (soap-sequence-type-elements type))
|
|
|
|
|
(let ((instance-count 0)
|
|
|
|
|
(e-name (soap-sequence-element-name element))
|
|
|
|
|
(e-type (soap-sequence-element-type element)))
|
|
|
|
|
(dolist (v value)
|
|
|
|
|
(when (equal (car v) e-name)
|
|
|
|
|
(incf instance-count)
|
|
|
|
|
(soap-encode-value e-name (cdr v) e-type)))
|
|
|
|
|
|
|
|
|
|
;; Do some sanity checking
|
|
|
|
|
(cond ((and (= instance-count 0)
|
|
|
|
|
(not (soap-sequence-element-nillable? element)))
|
2011-02-16 19:33:35 +00:00
|
|
|
|
(soap-warning
|
|
|
|
|
"While encoding %s: missing non-nillable slot %s"
|
|
|
|
|
(soap-element-name type) e-name))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
((and (> instance-count 1)
|
|
|
|
|
(not (soap-sequence-element-multiple? element)))
|
2011-02-16 19:33:35 +00:00
|
|
|
|
(soap-warning
|
|
|
|
|
"While encoding %s: multiple slots named %s"
|
|
|
|
|
(soap-element-name type) e-name))))))))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(insert " xsi:nil=\"true\">"))
|
|
|
|
|
(insert "</" xml-tag ">\n")))
|
|
|
|
|
|
|
|
|
|
(defun soap-encode-array-type (xml-tag value type)
|
|
|
|
|
"Encode inside XML-TAG the LISP VALUE according to TYPE.
|
|
|
|
|
Do not call this function directly, use `soap-encode-value'
|
|
|
|
|
instead."
|
|
|
|
|
(unless (vectorp value)
|
|
|
|
|
(error "Soap-encode: %s(%s) expects a vector, got: %s"
|
|
|
|
|
xml-tag (soap-element-fq-name type) value))
|
|
|
|
|
(let* ((element-type (soap-array-type-element-type type))
|
|
|
|
|
(array-type (concat (soap-element-fq-name element-type)
|
|
|
|
|
"[" (format "%s" (length value)) "]")))
|
|
|
|
|
(insert "<" xml-tag
|
|
|
|
|
" soapenc:arrayType=\"" array-type "\" "
|
|
|
|
|
" xsi:type=\"soapenc:Array\">\n")
|
|
|
|
|
(loop for i below (length value)
|
|
|
|
|
do (soap-encode-value xml-tag (aref value i) element-type))
|
|
|
|
|
(insert "</" xml-tag ">\n")))
|
|
|
|
|
|
|
|
|
|
(progn
|
|
|
|
|
(put (aref (make-soap-basic-type) 0)
|
|
|
|
|
'soap-encoder 'soap-encode-basic-type)
|
2012-04-25 10:28:29 +00:00
|
|
|
|
(put (aref (make-soap-simple-type) 0)
|
|
|
|
|
'soap-encoder 'soap-encode-simple-type)
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(put (aref (make-soap-sequence-type) 0)
|
|
|
|
|
'soap-encoder 'soap-encode-sequence-type)
|
|
|
|
|
(put (aref (make-soap-array-type) 0)
|
|
|
|
|
'soap-encoder 'soap-encode-array-type))
|
|
|
|
|
|
|
|
|
|
(defun soap-encode-body (operation parameters wsdl)
|
|
|
|
|
"Create the body of a SOAP request for OPERATION in the current buffer.
|
|
|
|
|
PARAMETERS is a list of parameters supplied to the OPERATION.
|
|
|
|
|
|
|
|
|
|
The OPERATION and PARAMETERS are encoded according to the WSDL
|
|
|
|
|
document."
|
|
|
|
|
(let* ((op (soap-bound-operation-operation operation))
|
|
|
|
|
(use (soap-bound-operation-use operation))
|
|
|
|
|
(message (cdr (soap-operation-input op)))
|
|
|
|
|
(parameter-order (soap-operation-parameter-order op)))
|
|
|
|
|
|
|
|
|
|
(unless (= (length parameter-order) (length parameters))
|
|
|
|
|
(error "Wrong number of parameters for %s: expected %d, got %s"
|
|
|
|
|
(soap-element-name op)
|
|
|
|
|
(length parameter-order)
|
|
|
|
|
(length parameters)))
|
|
|
|
|
|
|
|
|
|
(insert "<soap:Body>\n")
|
|
|
|
|
(when (eq use 'encoded)
|
2011-02-16 19:56:31 +00:00
|
|
|
|
(add-to-list 'soap-encoded-namespaces (soap-element-namespace-tag op))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(insert "<" (soap-element-fq-name op) ">\n"))
|
|
|
|
|
|
|
|
|
|
(let ((param-table (loop for formal in parameter-order
|
|
|
|
|
for value in parameters
|
|
|
|
|
collect (cons formal value))))
|
|
|
|
|
(dolist (part (soap-message-parts message))
|
|
|
|
|
(let* ((param-name (car part))
|
|
|
|
|
(type (cdr part))
|
|
|
|
|
(tag-name (if (eq use 'encoded)
|
|
|
|
|
param-name
|
|
|
|
|
(soap-element-name type)))
|
|
|
|
|
(value (cdr (assoc param-name param-table)))
|
|
|
|
|
(start-pos (point)))
|
|
|
|
|
(soap-encode-value tag-name value type)
|
|
|
|
|
(when (eq use 'literal)
|
|
|
|
|
;; hack: add the xmlns attribute to the tag, the only way
|
|
|
|
|
;; ASP.NET web services recognize the namespace of the
|
|
|
|
|
;; element itself...
|
|
|
|
|
(save-excursion
|
|
|
|
|
(goto-char start-pos)
|
|
|
|
|
(when (re-search-forward " ")
|
|
|
|
|
(let* ((ns (soap-element-namespace-tag type))
|
2011-02-16 19:33:35 +00:00
|
|
|
|
(namespace (cdr (assoc ns
|
|
|
|
|
(soap-wsdl-alias-table wsdl)))))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(when namespace
|
|
|
|
|
(insert "xmlns=\"" namespace "\" ")))))))))
|
|
|
|
|
|
|
|
|
|
(when (eq use 'encoded)
|
|
|
|
|
(insert "</" (soap-element-fq-name op) ">\n"))
|
|
|
|
|
(insert "</soap:Body>\n")))
|
|
|
|
|
|
|
|
|
|
(defun soap-create-envelope (operation parameters wsdl)
|
|
|
|
|
"Create a SOAP request envelope for OPERATION using PARAMETERS.
|
|
|
|
|
WSDL is the wsdl document used to encode the PARAMETERS."
|
|
|
|
|
(with-temp-buffer
|
2011-02-16 19:56:31 +00:00
|
|
|
|
(let ((soap-encoded-namespaces '("xsi" "soap" "soapenc"))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(use (soap-bound-operation-use operation)))
|
|
|
|
|
|
|
|
|
|
;; Create the request body
|
|
|
|
|
(soap-encode-body operation parameters wsdl)
|
|
|
|
|
|
|
|
|
|
;; Put the envelope around the body
|
|
|
|
|
(goto-char (point-min))
|
|
|
|
|
(insert "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<soap:Envelope\n")
|
|
|
|
|
(when (eq use 'encoded)
|
|
|
|
|
(insert " soapenc:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"\n"))
|
2011-02-16 19:56:31 +00:00
|
|
|
|
(dolist (nstag soap-encoded-namespaces)
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(insert " xmlns:" nstag "=\"")
|
2011-02-16 19:56:31 +00:00
|
|
|
|
(let ((nsname (cdr (assoc nstag soap-well-known-xmlns))))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(unless nsname
|
|
|
|
|
(setq nsname (cdr (assoc nstag (soap-wsdl-alias-table wsdl)))))
|
|
|
|
|
(insert nsname)
|
|
|
|
|
(insert "\"\n")))
|
|
|
|
|
(insert ">\n")
|
|
|
|
|
(goto-char (point-max))
|
|
|
|
|
(insert "</soap:Envelope>\n"))
|
|
|
|
|
|
|
|
|
|
(buffer-string)))
|
|
|
|
|
|
|
|
|
|
;;;; invoking soap methods
|
|
|
|
|
|
|
|
|
|
(defcustom soap-debug nil
|
|
|
|
|
"When t, enable some debugging facilities."
|
|
|
|
|
:type 'boolean
|
|
|
|
|
:group 'soap-client)
|
|
|
|
|
|
|
|
|
|
(defun soap-invoke (wsdl service operation-name &rest parameters)
|
|
|
|
|
"Invoke a SOAP operation and return the result.
|
|
|
|
|
|
|
|
|
|
WSDL is used for encoding the request and decoding the response.
|
|
|
|
|
It also contains information about the WEB server address that
|
|
|
|
|
will service the request.
|
|
|
|
|
|
|
|
|
|
SERVICE is the SOAP service to invoke.
|
|
|
|
|
|
|
|
|
|
OPERATION-NAME is the operation to invoke.
|
|
|
|
|
|
|
|
|
|
PARAMETERS -- the remaining parameters are used as parameters for
|
|
|
|
|
the SOAP request.
|
|
|
|
|
|
|
|
|
|
NOTE: The SOAP service provider should document the available
|
|
|
|
|
operations and their parameters for the service. You can also
|
|
|
|
|
use the `soap-inspect' function to browse the available
|
|
|
|
|
operations in a WSDL document."
|
|
|
|
|
(let ((port (catch 'found
|
|
|
|
|
(dolist (p (soap-wsdl-ports wsdl))
|
|
|
|
|
(when (equal service (soap-element-name p))
|
|
|
|
|
(throw 'found p))))))
|
|
|
|
|
(unless port
|
|
|
|
|
(error "Unknown SOAP service: %s" service))
|
|
|
|
|
|
|
|
|
|
(let* ((binding (soap-port-binding port))
|
2011-02-16 19:33:35 +00:00
|
|
|
|
(operation (gethash operation-name
|
|
|
|
|
(soap-binding-operations binding))))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(unless operation
|
|
|
|
|
(error "No operation %s for SOAP service %s" operation-name service))
|
|
|
|
|
|
|
|
|
|
(let ((url-request-method "POST")
|
|
|
|
|
(url-package-name "soap-client.el")
|
|
|
|
|
(url-package-version "1.0")
|
|
|
|
|
(url-http-version "1.0")
|
|
|
|
|
(url-request-data (soap-create-envelope operation parameters wsdl))
|
|
|
|
|
(url-mime-charset-string "utf-8;q=1, iso-8859-1;q=0.5")
|
|
|
|
|
(url-request-coding-system 'utf-8)
|
|
|
|
|
(url-http-attempt-keepalives t)
|
|
|
|
|
(url-request-extra-headers (list
|
2011-02-16 19:33:35 +00:00
|
|
|
|
(cons "SOAPAction"
|
|
|
|
|
(soap-bound-operation-soap-action
|
|
|
|
|
operation))
|
|
|
|
|
(cons "Content-Type"
|
|
|
|
|
"text/xml; charset=utf-8"))))
|
|
|
|
|
(let ((buffer (url-retrieve-synchronously
|
|
|
|
|
(soap-port-service-url port))))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(condition-case err
|
|
|
|
|
(with-current-buffer buffer
|
|
|
|
|
(declare (special url-http-response-status))
|
|
|
|
|
(if (null url-http-response-status)
|
|
|
|
|
(error "No HTTP response from server"))
|
|
|
|
|
(if (and soap-debug (> url-http-response-status 299))
|
|
|
|
|
;; This is a warning because some SOAP errors come
|
|
|
|
|
;; back with a HTTP response 500 (internal server
|
|
|
|
|
;; error)
|
2011-02-16 19:33:35 +00:00
|
|
|
|
(warn "Error in SOAP response: HTTP code %s"
|
|
|
|
|
url-http-response-status))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(let ((mime-part (mm-dissect-buffer t t)))
|
|
|
|
|
(unless mime-part
|
|
|
|
|
(error "Failed to decode response from server"))
|
|
|
|
|
(unless (equal (car (mm-handle-type mime-part)) "text/xml")
|
|
|
|
|
(error "Server response is not an XML document"))
|
|
|
|
|
(with-temp-buffer
|
|
|
|
|
(mm-insert-part mime-part)
|
2011-02-16 19:33:35 +00:00
|
|
|
|
(let ((response (car (xml-parse-region
|
|
|
|
|
(point-min) (point-max)))))
|
2011-02-16 09:25:37 +00:00
|
|
|
|
(prog1
|
|
|
|
|
(soap-parse-envelope response operation wsdl)
|
|
|
|
|
(kill-buffer buffer)
|
|
|
|
|
(mm-destroy-part mime-part))))))
|
|
|
|
|
(soap-error
|
|
|
|
|
;; Propagate soap-errors -- they are error replies of the
|
|
|
|
|
;; SOAP protocol and don't indicate a communication
|
|
|
|
|
;; problem or a bug in this code.
|
|
|
|
|
(signal (car err) (cdr err)))
|
|
|
|
|
(error
|
|
|
|
|
(when soap-debug
|
|
|
|
|
(pop-to-buffer buffer))
|
|
|
|
|
(error (error-message-string err)))))))))
|
|
|
|
|
|
|
|
|
|
(provide 'soap-client)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
;;; Local Variables:
|
2011-06-09 20:22:06 +00:00
|
|
|
|
;;; eval: (outline-minor-mode 1)
|
2011-02-16 09:25:37 +00:00
|
|
|
|
;;; outline-regexp: ";;;;+"
|
|
|
|
|
;;; End:
|
|
|
|
|
|
|
|
|
|
;;; soap-client.el ends here
|