]> code.delx.au - gnu-emacs/blob - lisp/net/soap-client.el
264a39c18995abe6477f99218a3db3d23c769256
[gnu-emacs] / lisp / net / soap-client.el
1 ;;;; soap-client.el -- Access SOAP web services -*- lexical-binding: t -*-
2
3 ;; Copyright (C) 2009-2015 Free Software Foundation, Inc.
4
5 ;; Author: Alexandru Harsanyi <AlexHarsanyi@gmail.com>
6 ;; Author: Thomas Fitzsimmons <fitzsim@fitzsim.org>
7 ;; Created: December, 2009
8 ;; Version: 3.0.1
9 ;; Keywords: soap, web-services, comm, hypermedia
10 ;; Package: soap-client
11 ;; Homepage: https://github.com/alex-hhh/emacs-soap-client
12
13 ;; This file is part of GNU Emacs.
14
15 ;; GNU Emacs is free software: you can redistribute it and/or modify
16 ;; it under the terms of the GNU General Public License as published by
17 ;; the Free Software Foundation, either version 3 of the License, or
18 ;; (at your option) any later version.
19
20 ;; GNU Emacs is distributed in the hope that it will be useful,
21 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
22 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 ;; GNU General Public License for more details.
24
25 ;; You should have received a copy of the GNU General Public License
26 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
27
28 ;;; Commentary:
29 ;;
30 ;; To use the SOAP client, you first need to load the WSDL document for the
31 ;; service you want to access, using `soap-load-wsdl-from-url'. A WSDL
32 ;; document describes the available operations of the SOAP service, how their
33 ;; parameters and responses are encoded. To invoke operations, you use the
34 ;; `soap-invoke' method passing it the WSDL, the service name, the operation
35 ;; you wish to invoke and any required parameters.
36 ;;
37 ;; Ideally, the service you want to access will have some documentation about
38 ;; the operations it supports. If it does not, you can try using
39 ;; `soap-inspect' to browse the WSDL document and see the available operations
40 ;; and their parameters.
41 ;;
42
43 ;;; Code:
44
45 (eval-when-compile (require 'cl))
46
47 (require 'xml)
48 (require 'xsd-regexp)
49 (require 'rng-xsd)
50 (require 'rng-dt)
51 (require 'warnings)
52 (require 'url)
53 (require 'url-http)
54 (require 'url-util)
55 (require 'url-vars)
56 (require 'mm-decode)
57
58 (defsubst soap-warning (message &rest args)
59 "Display a warning MESSAGE with ARGS, using the 'soap-client warning type."
60 (display-warning 'soap-client (apply #'format-message message args)
61 :warning))
62
63 (defgroup soap-client nil
64 "Access SOAP web services from Emacs."
65 :version "24.1"
66 :group 'tools)
67
68 ;;;; Support for parsing XML documents with namespaces
69
70 ;; XML documents with namespaces are difficult to parse because the names of
71 ;; the nodes depend on what "xmlns" aliases have been defined in the document.
72 ;; To work with such documents, we introduce a translation layer between a
73 ;; "well known" namespace tag and the local namespace tag in the document
74 ;; being parsed.
75
76 (defconst soap-well-known-xmlns
77 '(("apachesoap" . "http://xml.apache.org/xml-soap")
78 ("soapenc" . "http://schemas.xmlsoap.org/soap/encoding/")
79 ("wsdl" . "http://schemas.xmlsoap.org/wsdl/")
80 ("wsdlsoap" . "http://schemas.xmlsoap.org/wsdl/soap/")
81 ("xsd" . "http://www.w3.org/2001/XMLSchema")
82 ("xsi" . "http://www.w3.org/2001/XMLSchema-instance")
83 ("wsa" . "http://www.w3.org/2005/08/addressing")
84 ("wsaw" . "http://www.w3.org/2006/05/addressing/wsdl")
85 ("soap" . "http://schemas.xmlsoap.org/soap/envelope/")
86 ("soap12" . "http://schemas.xmlsoap.org/wsdl/soap12/")
87 ("http" . "http://schemas.xmlsoap.org/wsdl/http/")
88 ("mime" . "http://schemas.xmlsoap.org/wsdl/mime/")
89 ("xml" . "http://www.w3.org/XML/1998/namespace"))
90 "A list of well known xml namespaces and their aliases.")
91
92 (defvar soap-local-xmlns
93 '(("xml" . "http://www.w3.org/XML/1998/namespace"))
94 "A list of local namespace aliases.
95 This is a dynamically bound variable, controlled by
96 `soap-with-local-xmlns'.")
97
98 (defvar soap-default-xmlns nil
99 "The default XML namespaces.
100 Names in this namespace will be unqualified. This is a
101 dynamically bound variable, controlled by
102 `soap-with-local-xmlns'")
103
104 (defvar soap-target-xmlns nil
105 "The target XML namespace.
106 New XSD elements will be defined in this namespace, unless they
107 are fully qualified for a different namespace. This is a
108 dynamically bound variable, controlled by
109 `soap-with-local-xmlns'")
110
111 (defvar soap-current-wsdl nil
112 "The current WSDL document used when decoding the SOAP response.
113 This is a dynamically bound variable.")
114
115 (defun soap-wk2l (well-known-name)
116 "Return local variant of WELL-KNOWN-NAME.
117 This is done by looking up the namespace in the
118 `soap-well-known-xmlns' table and resolving the namespace to
119 the local name based on the current local translation table
120 `soap-local-xmlns'. See also `soap-with-local-xmlns'."
121 (let ((wk-name-1 (if (symbolp well-known-name)
122 (symbol-name well-known-name)
123 well-known-name)))
124 (cond
125 ((string-match "^\\(.*\\):\\(.*\\)$" wk-name-1)
126 (let ((ns (match-string 1 wk-name-1))
127 (name (match-string 2 wk-name-1)))
128 (let ((namespace (cdr (assoc ns soap-well-known-xmlns))))
129 (cond ((equal namespace soap-default-xmlns)
130 ;; Name is unqualified in the default namespace
131 (if (symbolp well-known-name)
132 (intern name)
133 name))
134 (t
135 (let* ((local-ns (car (rassoc namespace soap-local-xmlns)))
136 (local-name (concat local-ns ":" name)))
137 (if (symbolp well-known-name)
138 (intern local-name)
139 local-name)))))))
140 (t well-known-name))))
141
142 (defun soap-l2wk (local-name)
143 "Convert LOCAL-NAME into a well known name.
144 The namespace of LOCAL-NAME is looked up in the
145 `soap-well-known-xmlns' table and a well known namespace tag is
146 used in the name.
147
148 nil is returned if there is no well-known namespace for the
149 namespace of LOCAL-NAME."
150 (let ((l-name-1 (if (symbolp local-name)
151 (symbol-name local-name)
152 local-name))
153 namespace name)
154 (cond
155 ((string-match "^\\(.*\\):\\(.*\\)$" l-name-1)
156 (setq name (match-string 2 l-name-1))
157 (let ((ns (match-string 1 l-name-1)))
158 (setq namespace (cdr (assoc ns soap-local-xmlns)))
159 (unless namespace
160 (error "Soap-l2wk(%s): no namespace for alias %s" local-name ns))))
161 (t
162 (setq name l-name-1)
163 (setq namespace soap-default-xmlns)))
164
165 (if namespace
166 (let ((well-known-ns (car (rassoc namespace soap-well-known-xmlns))))
167 (if well-known-ns
168 (let ((well-known-name (concat well-known-ns ":" name)))
169 (if (symbolp local-name)
170 (intern well-known-name)
171 well-known-name))
172 nil))
173 ;; if no namespace is defined, just return the unqualified name
174 name)))
175
176
177 (defun soap-l2fq (local-name &optional use-tns)
178 "Convert LOCAL-NAME into a fully qualified name.
179 A fully qualified name is a cons of the namespace name and the
180 name of the element itself. For example \"xsd:string\" is
181 converted to (\"http://www.w3.org/2001/XMLSchema\" . \"string\").
182
183 The USE-TNS argument specifies what to do when LOCAL-NAME has no
184 namespace tag. If USE-TNS is non-nil, the `soap-target-xmlns'
185 will be used as the element's namespace, otherwise
186 `soap-default-xmlns' will be used.
187
188 This is needed because different parts of a WSDL document can use
189 different namespace aliases for the same element."
190 (let ((local-name-1 (if (symbolp local-name)
191 (symbol-name local-name)
192 local-name)))
193 (cond ((string-match "^\\(.*\\):\\(.*\\)$" local-name-1)
194 (let ((ns (match-string 1 local-name-1))
195 (name (match-string 2 local-name-1)))
196 (let ((namespace (cdr (assoc ns soap-local-xmlns))))
197 (if namespace
198 (cons namespace name)
199 (error "Soap-l2fq(%s): unknown alias %s" local-name ns)))))
200 (t
201 (cons (if use-tns
202 soap-target-xmlns
203 soap-default-xmlns)
204 local-name-1)))))
205
206 (defun soap-name-p (name)
207 "Return true if NAME is a valid name for XMLSchema types.
208 A valid name is either a string or a cons of (NAMESPACE . NAME)."
209 (or (stringp name)
210 (and (consp name)
211 (stringp (car name))
212 (stringp (cdr name)))))
213
214 (defun soap-extract-xmlns (node &optional xmlns-table)
215 "Return a namespace alias table for NODE by extending XMLNS-TABLE."
216 (let (xmlns default-ns target-ns)
217 (dolist (a (xml-node-attributes node))
218 (let ((name (symbol-name (car a)))
219 (value (cdr a)))
220 (cond ((string= name "targetNamespace")
221 (setq target-ns value))
222 ((string= name "xmlns")
223 (setq default-ns value))
224 ((string-match "^xmlns:\\(.*\\)$" name)
225 (push (cons (match-string 1 name) value) xmlns)))))
226
227 (let ((tns (assoc "tns" xmlns)))
228 (cond ((and tns target-ns)
229 ;; If a tns alias is defined for this node, it must match
230 ;; the target namespace.
231 (unless (equal target-ns (cdr tns))
232 (soap-warning
233 "soap-extract-xmlns(%s): tns alias and targetNamespace mismatch"
234 (xml-node-name node))))
235 ((and tns (not target-ns))
236 (setq target-ns (cdr tns)))))
237
238 (list default-ns target-ns (append xmlns xmlns-table))))
239
240 (defmacro soap-with-local-xmlns (node &rest body)
241 "Install a local alias table from NODE and execute BODY."
242 (declare (debug (form &rest form)) (indent 1))
243 (let ((xmlns (make-symbol "xmlns")))
244 `(let ((,xmlns (soap-extract-xmlns ,node soap-local-xmlns)))
245 (let ((soap-default-xmlns (or (nth 0 ,xmlns) soap-default-xmlns))
246 (soap-target-xmlns (or (nth 1 ,xmlns) soap-target-xmlns))
247 (soap-local-xmlns (nth 2 ,xmlns)))
248 ,@body))))
249
250 (defun soap-get-target-namespace (node)
251 "Return the target namespace of NODE.
252 This is the namespace in which new elements will be defined."
253 (or (xml-get-attribute-or-nil node 'targetNamespace)
254 (cdr (assoc "tns" soap-local-xmlns))
255 soap-target-xmlns))
256
257 (defun soap-xml-get-children1 (node child-name)
258 "Return the children of NODE named CHILD-NAME.
259 This is the same as `xml-get-children', but CHILD-NAME can have
260 namespace tag."
261 (let (result)
262 (dolist (c (xml-node-children node))
263 (when (and (consp c)
264 (soap-with-local-xmlns c
265 ;; We use `ignore-errors' here because we want to silently
266 ;; skip nodes when we cannot convert them to a well-known
267 ;; name.
268 (eq (ignore-errors (soap-l2wk (xml-node-name c)))
269 child-name)))
270 (push c result)))
271 (nreverse result)))
272
273 (defun soap-xml-node-find-matching-child (node set)
274 "Return the first child of NODE whose name is a member of SET."
275 (catch 'found
276 (dolist (child (xml-node-children node))
277 (when (and (consp child)
278 (memq (soap-l2wk (xml-node-name child)) set))
279 (throw 'found child)))))
280
281 (defun soap-xml-get-attribute-or-nil1 (node attribute)
282 "Return the NODE's ATTRIBUTE, or nil if it does not exist.
283 This is the same as `xml-get-attribute-or-nil', but ATTRIBUTE can
284 be tagged with a namespace tag."
285 (catch 'found
286 (soap-with-local-xmlns node
287 (dolist (a (xml-node-attributes node))
288 ;; We use `ignore-errors' here because we want to silently skip
289 ;; attributes for which we cannot convert them to a well-known name.
290 (when (eq (ignore-errors (soap-l2wk (car a))) attribute)
291 (throw 'found (cdr a)))))))
292
293 \f
294 ;;;; XML namespaces
295
296 ;; An element in an XML namespace, "things" stored in soap-xml-namespaces will
297 ;; be derived from this object.
298
299 (defstruct soap-element
300 name
301 ;; The "well-known" namespace tag for the element. For example, while
302 ;; parsing XML documents, we can have different tags for the XMLSchema
303 ;; namespace, but internally all our XMLSchema elements will have the "xsd"
304 ;; tag.
305 namespace-tag)
306
307 (defun soap-element-fq-name (element)
308 "Return a fully qualified name for ELEMENT.
309 A fq name is the concatenation of the namespace tag and the
310 element name."
311 (cond ((soap-element-namespace-tag element)
312 (concat (soap-element-namespace-tag element)
313 ":" (soap-element-name element)))
314 ((soap-element-name element)
315 (soap-element-name element))
316 (t
317 "*unnamed*")))
318
319 ;; a namespace link stores an alias for an object in once namespace to a
320 ;; "target" object possibly in a different namespace
321
322 (defstruct (soap-namespace-link (:include soap-element))
323 target)
324
325 ;; A namespace is a collection of soap-element objects under a name (the name
326 ;; of the namespace).
327
328 (defstruct soap-namespace
329 (name nil :read-only t) ; e.g "http://xml.apache.org/xml-soap"
330 (elements (make-hash-table :test 'equal) :read-only t))
331
332 (defun soap-namespace-put (element ns)
333 "Store ELEMENT in NS.
334 Multiple elements with the same name can be stored in a
335 namespace. When retrieving the element you can specify a
336 discriminant predicate to `soap-namespace-get'"
337 (let ((name (soap-element-name element)))
338 (push element (gethash name (soap-namespace-elements ns)))))
339
340 (defun soap-namespace-put-link (name target ns)
341 "Store a link from NAME to TARGET in NS.
342 TARGET can be either a SOAP-ELEMENT or a string denoting an
343 element name into another namespace.
344
345 If NAME is nil, an element with the same name as TARGET will be
346 added to the namespace."
347
348 (unless (and name (not (equal name "")))
349 ;; if name is nil, use TARGET as a name...
350 (cond ((soap-element-p target)
351 (setq name (soap-element-name target)))
352 ((consp target) ; a fq name: (namespace . name)
353 (setq name (cdr target)))
354 ((stringp target)
355 (cond ((string-match "^\\(.*\\):\\(.*\\)$" target)
356 (setq name (match-string 2 target)))
357 (t
358 (setq name target))))))
359
360 ;; by now, name should be valid
361 (assert (and name (not (equal name "")))
362 nil
363 "Cannot determine name for namespace link")
364 (push (make-soap-namespace-link :name name :target target)
365 (gethash name (soap-namespace-elements ns))))
366
367 (defun soap-namespace-get (name ns &optional discriminant-predicate)
368 "Retrieve an element with NAME from the namespace NS.
369 If multiple elements with the same name exist,
370 DISCRIMINANT-PREDICATE is used to pick one of them. This allows
371 storing elements of different types (like a message type and a
372 binding) but the same name."
373 (assert (stringp name))
374 (let ((elements (gethash name (soap-namespace-elements ns))))
375 (cond (discriminant-predicate
376 (catch 'found
377 (dolist (e elements)
378 (when (funcall discriminant-predicate e)
379 (throw 'found e)))))
380 ((= (length elements) 1) (car elements))
381 ((> (length elements) 1)
382 (error
383 "Soap-namespace-get(%s): multiple elements, discriminant needed"
384 name))
385 (t
386 nil))))
387
388 \f
389 ;;;; XML Schema
390
391 ;; SOAP WSDL documents use XML Schema to define the types that are part of the
392 ;; message exchange. We include here an XML schema model with a parser and
393 ;; serializer/deserialiser.
394
395 (defstruct (soap-xs-type (:include soap-element))
396 id
397 attributes
398 attribute-groups)
399
400 ;;;;; soap-xs-basic-type
401
402 (defstruct (soap-xs-basic-type (:include soap-xs-type))
403 ;; Basic types are "built in" and we know how to handle them directly.
404 ;; Other type definitions reference basic types, so we need to create them
405 ;; in a namespace (see `soap-make-xs-basic-types')
406
407 ;; a symbol of: string, dateTime, long, int, etc
408 kind
409 )
410
411 (defun soap-make-xs-basic-types (namespace-name &optional namespace-tag)
412 "Construct NAMESPACE-NAME containing the XMLSchema basic types.
413 An optional NAMESPACE-TAG can also be specified."
414 (let ((ns (make-soap-namespace :name namespace-name)))
415 (dolist (type '("string" "language" "ID" "IDREF"
416 "dateTime" "time" "date" "boolean"
417 "gYearMonth" "gYear" "gMonthDay" "gDay" "gMonth"
418 "long" "short" "int" "integer" "nonNegativeInteger"
419 "unsignedLong" "unsignedShort" "unsignedInt"
420 "decimal" "duration"
421 "byte" "unsignedByte"
422 "float" "double"
423 "base64Binary" "anyType" "anyURI" "QName" "Array" "byte[]"))
424 (soap-namespace-put
425 (make-soap-xs-basic-type :name type
426 :namespace-tag namespace-tag
427 :kind (intern type))
428 ns))
429 ns))
430
431 (defun soap-encode-xs-basic-type-attributes (value type)
432 "Encode the XML attributes for VALUE according to TYPE.
433 The xsi:type and an optional xsi:nil attributes are added. The
434 attributes are inserted in the current buffer at the current
435 position.
436
437 This is a specialization of `soap-encode-attributes' for
438 `soap-xs-basic-type' objects."
439 (let ((xsi-type (soap-element-fq-name type))
440 (basic-type (soap-xs-basic-type-kind type)))
441 ;; try to classify the type based on the value type and use that type when
442 ;; encoding
443 (when (eq basic-type 'anyType)
444 (cond ((stringp value)
445 (setq xsi-type "xsd:string" basic-type 'string))
446 ((integerp value)
447 (setq xsi-type "xsd:int" basic-type 'int))
448 ((memq value '(t nil))
449 (setq xsi-type "xsd:boolean" basic-type 'boolean))
450 (t
451 (error "Cannot classify anyType value"))))
452
453 (insert " xsi:type=\"" xsi-type "\"")
454 ;; We have some ambiguity here, as a nil value represents "false" when the
455 ;; type is boolean, we will never have a "nil" boolean type...
456 (unless (or value (eq basic-type 'boolean))
457 (insert " xsi:nil=\"true\""))))
458
459 (defun soap-encode-xs-basic-type (value type)
460 "Encode the VALUE according to TYPE.
461 The data is inserted in the current buffer at the current
462 position.
463
464 This is a specialization of `soap-encode-value' for
465 `soap-xs-basic-type' objects."
466 (let ((kind (soap-xs-basic-type-kind type)))
467
468 (when (eq kind 'anyType)
469 (cond ((stringp value)
470 (setq kind 'string))
471 ((integerp value)
472 (setq kind 'int))
473 ((memq value '(t nil))
474 (setq kind 'boolean))
475 (t
476 (error "Cannot classify anyType value"))))
477
478 ;; NOTE: a nil value is not encoded, as an xsi:nil="true" attribute was
479 ;; encoded for it. However, we have some ambiguity here, as a nil value
480 ;; also represents "false" when the type is boolean...
481
482 (when (or value (eq kind 'boolean))
483 (let ((value-string
484 (case kind
485 ((string anyURI QName ID IDREF language)
486 (unless (stringp value)
487 (error "Not a string value: %s" value))
488 (url-insert-entities-in-string value))
489 ((dateTime time date gYearMonth gYear gMonthDay gDay gMonth)
490 (cond ((consp value)
491 ;; Value is a (current-time) style value,
492 ;; convert to the ISO 8601-inspired XSD
493 ;; string format in UTC.
494 (format-time-string
495 (concat
496 (ecase kind
497 (dateTime "%Y-%m-%dT%H:%M:%S")
498 (time "%H:%M:%S")
499 (date "%Y-%m-%d")
500 (gYearMonth "%Y-%m")
501 (gYear "%Y")
502 (gMonthDay "--%m-%d")
503 (gDay "---%d")
504 (gMonth "--%m"))
505 ;; Internal time is always in UTC.
506 "Z")
507 value t))
508 ((stringp value)
509 ;; Value is a string in the ISO 8601-inspired XSD
510 ;; format. Validate it.
511 (soap-decode-date-time value kind)
512 (url-insert-entities-in-string value))
513 (t
514 (error "Invalid date-time format"))))
515 (boolean
516 (unless (memq value '(t nil))
517 (error "Not a boolean value"))
518 (if value "true" "false"))
519
520 ((long short int integer byte unsignedInt unsignedLong
521 unsignedShort nonNegativeInteger decimal duration)
522 (unless (integerp value)
523 (error "Not an integer value"))
524 (when (and (memq kind '(unsignedInt unsignedLong
525 unsignedShort
526 nonNegativeInteger))
527 (< value 0))
528 (error "Not a positive integer"))
529 (number-to-string value))
530
531 ((float double)
532 (unless (numberp value)
533 (error "Not a number"))
534 (number-to-string value))
535
536 (base64Binary
537 (unless (stringp value)
538 (error "Not a string value for base64Binary"))
539 (base64-encode-string value))
540
541 (otherwise
542 (error "Don't know how to encode %s for type %s"
543 value (soap-element-fq-name type))))))
544 (soap-validate-xs-basic-type value-string type)
545 (insert value-string)))))
546
547 ;; Inspired by rng-xsd-convert-date-time.
548 (defun soap-decode-date-time (date-time-string datatype)
549 "Decode DATE-TIME-STRING as DATATYPE.
550 DATE-TIME-STRING should be in ISO 8601 basic or extended format.
551 DATATYPE is one of dateTime, time, date, gYearMonth, gYear,
552 gMonthDay, gDay or gMonth.
553
554 Return a list in a format (SEC MINUTE HOUR DAY MONTH YEAR
555 SEC-FRACTION DATATYPE ZONE). This format is meant to be similar
556 to that returned by `decode-time' (and compatible with
557 `encode-time'). The differences are the DOW (day-of-week) field
558 is replaced with SEC-FRACTION, a float representing the
559 fractional seconds, and the DST (daylight savings time) field is
560 replaced with DATATYPE, a symbol representing the XSD primitive
561 datatype. This symbol can be used to determine which fields
562 apply and which don't when it's not already clear from context.
563 For example a datatype of 'time means the year, month and day
564 fields should be ignored.
565
566 This function will throw an error if DATE-TIME-STRING represents
567 a leap second, since the XML Schema 1.1 standard explicitly
568 disallows them."
569 (let* ((datetime-regexp (cadr (get datatype 'rng-xsd-convert)))
570 (year-sign (progn
571 (string-match datetime-regexp date-time-string)
572 (match-string 1 date-time-string)))
573 (year (match-string 2 date-time-string))
574 (month (match-string 3 date-time-string))
575 (day (match-string 4 date-time-string))
576 (hour (match-string 5 date-time-string))
577 (minute (match-string 6 date-time-string))
578 (second (match-string 7 date-time-string))
579 (second-fraction (match-string 8 date-time-string))
580 (has-time-zone (match-string 9 date-time-string))
581 (time-zone-sign (match-string 10 date-time-string))
582 (time-zone-hour (match-string 11 date-time-string))
583 (time-zone-minute (match-string 12 date-time-string)))
584 (setq year-sign (if year-sign -1 1))
585 (setq year
586 (if year
587 (* year-sign
588 (string-to-number year))
589 ;; By defaulting to the epoch date, a time value can be treated as
590 ;; a relative number of seconds.
591 1970))
592 (setq month
593 (if month (string-to-number month) 1))
594 (setq day
595 (if day (string-to-number day) 1))
596 (setq hour
597 (if hour (string-to-number hour) 0))
598 (setq minute
599 (if minute (string-to-number minute) 0))
600 (setq second
601 (if second (string-to-number second) 0))
602 (setq second-fraction
603 (if second-fraction
604 (float (string-to-number second-fraction))
605 0.0))
606 (setq has-time-zone (and has-time-zone t))
607 (setq time-zone-sign
608 (if (equal time-zone-sign "-") -1 1))
609 (setq time-zone-hour
610 (if time-zone-hour (string-to-number time-zone-hour) 0))
611 (setq time-zone-minute
612 (if time-zone-minute (string-to-number time-zone-minute) 0))
613 (unless (and
614 ;; XSD does not allow year 0.
615 (> year 0)
616 (>= month 1) (<= month 12)
617 (>= day 1) (<= day (rng-xsd-days-in-month year month))
618 (>= hour 0) (<= hour 23)
619 (>= minute 0) (<= minute 59)
620 ;; 60 represents a leap second, but leap seconds are explicitly
621 ;; disallowed by the XML Schema 1.1 specification. This agrees
622 ;; with typical Emacs installations, which don't count leap
623 ;; seconds in time values.
624 (>= second 0) (<= second 59)
625 (>= time-zone-hour 0)
626 (<= time-zone-hour 23)
627 (>= time-zone-minute 0)
628 (<= time-zone-minute 59))
629 (error "Invalid or unsupported time: %s" date-time-string))
630 ;; Return a value in a format similar to that returned by decode-time, and
631 ;; suitable for (apply 'encode-time ...).
632 (list second minute hour day month year second-fraction datatype
633 (if has-time-zone
634 (* (rng-xsd-time-to-seconds
635 time-zone-hour
636 time-zone-minute
637 0)
638 time-zone-sign)
639 ;; UTC.
640 0))))
641
642 (defun soap-decode-xs-basic-type (type node)
643 "Use TYPE, a `soap-xs-basic-type', to decode the contents of NODE.
644 A LISP value is returned based on the contents of NODE and the
645 type-info stored in TYPE.
646
647 This is a specialization of `soap-decode-type' for
648 `soap-xs-basic-type' objects."
649 (let ((contents (xml-node-children node))
650 (kind (soap-xs-basic-type-kind type))
651 (attributes (xml-node-attributes node))
652 (validate-type type)
653 (is-nil nil))
654
655 (dolist (attribute attributes)
656 (let ((attribute-type (soap-l2fq (car attribute)))
657 (attribute-value (cdr attribute)))
658 ;; xsi:type can override an element's expected type.
659 (when (equal attribute-type (soap-l2fq "xsi:type"))
660 (setq validate-type
661 (soap-wsdl-get attribute-value soap-current-wsdl)))
662 ;; xsi:nil can specify that an element is nil in which case we don't
663 ;; validate it.
664 (when (equal attribute-type (soap-l2fq "xsi:nil"))
665 (setq is-nil (string= (downcase attribute-value) "true")))))
666
667 (unless is-nil
668 ;; For validation purposes, when xml-node-children returns nil, treat it
669 ;; as the empty string.
670 (soap-validate-xs-basic-type (car (or contents (list ""))) validate-type))
671
672 (if (null contents)
673 nil
674 (ecase kind
675 ((string anyURI QName ID IDREF language) (car contents))
676 ((dateTime time date gYearMonth gYear gMonthDay gDay gMonth)
677 (car contents))
678 ((long short int integer
679 unsignedInt unsignedLong unsignedShort nonNegativeInteger
680 decimal byte float double duration)
681 (string-to-number (car contents)))
682 (boolean (string= (downcase (car contents)) "true"))
683 (base64Binary (base64-decode-string (car contents)))
684 (anyType (soap-decode-any-type node))
685 (Array (soap-decode-array node))))))
686
687 ;; Register methods for `soap-xs-basic-type'
688 (let ((tag (aref (make-soap-xs-basic-type) 0)))
689 (put tag 'soap-attribute-encoder #'soap-encode-xs-basic-type-attributes)
690 (put tag 'soap-encoder #'soap-encode-xs-basic-type)
691 (put tag 'soap-decoder #'soap-decode-xs-basic-type))
692
693 ;;;;; soap-xs-element
694
695 (defstruct (soap-xs-element (:include soap-element))
696 ;; NOTE: we don't support exact number of occurrences via minOccurs,
697 ;; maxOccurs. Instead we support optional? and multiple?
698
699 id
700 type^ ; note: use soap-xs-element-type to retrieve this member
701 optional?
702 multiple?
703 reference
704 substitution-group
705 ;; contains a list of elements who point to this one via their
706 ;; substitution-group slot
707 alternatives
708 is-group)
709
710 (defun soap-xs-element-type (element)
711 "Retrieve the type of ELEMENT.
712 This is normally stored in the TYPE^ slot, but if this element
713 contains a reference, we retrive the type of the reference."
714 (if (soap-xs-element-reference element)
715 (soap-xs-element-type (soap-xs-element-reference element))
716 (soap-xs-element-type^ element)))
717
718 (defun soap-node-optional (node)
719 "Return t if NODE specifies an optional element."
720 (or (equal (xml-get-attribute-or-nil node 'nillable) "true")
721 (let ((e (xml-get-attribute-or-nil node 'minOccurs)))
722 (and e (equal e "0")))))
723
724 (defun soap-node-multiple (node)
725 "Return t if NODE permits multiple elements."
726 (let* ((e (xml-get-attribute-or-nil node 'maxOccurs)))
727 (and e (not (equal e "1")))))
728
729 (defun soap-xs-parse-element (node)
730 "Construct a `soap-xs-element' from NODE."
731 (let ((name (xml-get-attribute-or-nil node 'name))
732 (id (xml-get-attribute-or-nil node 'id))
733 (type (xml-get-attribute-or-nil node 'type))
734 (optional? (soap-node-optional node))
735 (multiple? (soap-node-multiple node))
736 (ref (xml-get-attribute-or-nil node 'ref))
737 (substitution-group (xml-get-attribute-or-nil node 'substitutionGroup))
738 (node-name (soap-l2wk (xml-node-name node))))
739 (assert (memq node-name '(xsd:element xsd:group))
740 "expecting xsd:element or xsd:group, got %s" node-name)
741
742 (when type
743 (setq type (soap-l2fq type 'tns)))
744
745 (when ref
746 (setq ref (soap-l2fq ref 'tns)))
747
748 (when substitution-group
749 (setq substitution-group (soap-l2fq substitution-group 'tns)))
750
751 (unless (or ref type)
752 ;; no type specified and this is not a reference. Must be a type
753 ;; defined within this node.
754 (let ((simple-type (soap-xml-get-children1 node 'xsd:simpleType)))
755 (if simple-type
756 (setq type (soap-xs-parse-simple-type (car simple-type)))
757 ;; else
758 (let ((complex-type (soap-xml-get-children1 node 'xsd:complexType)))
759 (if complex-type
760 (setq type (soap-xs-parse-complex-type (car complex-type)))
761 ;; else
762 (error "Soap-xs-parse-element: missing type or ref"))))))
763
764 (make-soap-xs-element :name name
765 ;; Use the full namespace name for now, we will
766 ;; convert it to a nstag in
767 ;; `soap-resolve-references-for-xs-element'
768 :namespace-tag soap-target-xmlns
769 :id id :type^ type
770 :optional? optional? :multiple? multiple?
771 :reference ref
772 :substitution-group substitution-group
773 :is-group (eq node-name 'xsd:group))))
774
775 (defun soap-resolve-references-for-xs-element (element wsdl)
776 "Replace names in ELEMENT with the referenced objects in the WSDL.
777 This is a specialization of `soap-resolve-references' for
778 `soap-xs-element' objects.
779
780 See also `soap-wsdl-resolve-references'."
781
782 (let ((namespace (soap-element-namespace-tag element)))
783 (when namespace
784 (let ((nstag (car (rassoc namespace (soap-wsdl-alias-table wsdl)))))
785 (when nstag
786 (setf (soap-element-namespace-tag element) nstag)))))
787
788 (let ((type (soap-xs-element-type^ element)))
789 (cond ((soap-name-p type)
790 (setf (soap-xs-element-type^ element)
791 (soap-wsdl-get type wsdl 'soap-xs-type-p)))
792 ((soap-xs-type-p type)
793 ;; an inline defined type, this will not be reached from anywhere
794 ;; else, so we must resolve references now.
795 (soap-resolve-references type wsdl))))
796 (let ((reference (soap-xs-element-reference element)))
797 (when (and (soap-name-p reference)
798 ;; xsd:group reference nodes will be converted to inline types
799 ;; by soap-resolve-references-for-xs-complex-type, so skip them
800 ;; here.
801 (not (soap-xs-element-is-group element)))
802 (setf (soap-xs-element-reference element)
803 (soap-wsdl-get reference wsdl 'soap-xs-element-p))))
804
805 (let ((subst (soap-xs-element-substitution-group element)))
806 (when (soap-name-p subst)
807 (let ((target (soap-wsdl-get subst wsdl)))
808 (if target
809 (push element (soap-xs-element-alternatives target))
810 (soap-warning "No target found for substitution-group" subst))))))
811
812 (defun soap-encode-xs-element-attributes (value element)
813 "Encode the XML attributes for VALUE according to ELEMENT.
814 Currently no attributes are needed.
815
816 This is a specialization of `soap-encode-attributes' for
817 `soap-xs-basic-type' objects."
818 ;; Use the variables to suppress checkdoc and compiler warnings.
819 (list value element)
820 nil)
821
822 (defun soap-should-encode-value-for-xs-element (value element)
823 "Return t if VALUE should be encoded for ELEMENT, nil otherwise."
824 (cond
825 ;; if value is not nil, attempt to encode it
826 (value)
827
828 ;; value is nil, but the element's type is a boolean, so nil in this case
829 ;; means "false". We need to encode it.
830 ((let ((type (soap-xs-element-type element)))
831 (and (soap-xs-basic-type-p type)
832 (eq (soap-xs-basic-type-kind type) 'boolean))))
833
834 ;; This is not an optional element. Force encoding it (although this
835 ;; might fail at the validation step, but this is what we intend.
836
837 ;; value is nil, but the element's type has some attributes which supply a
838 ;; default value. We need to encode it.
839
840 ((let ((type (soap-xs-element-type element)))
841 (catch 'found
842 (dolist (a (soap-xs-type-attributes type))
843 (when (soap-xs-attribute-default a)
844 (throw 'found t))))))
845
846 ;; otherwise, we don't need to encode it
847 (t nil)))
848
849 (defun soap-type-is-array? (type)
850 "Return t if TYPE defines an ARRAY."
851 (and (soap-xs-complex-type-p type)
852 (eq (soap-xs-complex-type-indicator type) 'array)))
853
854 (defvar soap-encoded-namespaces nil
855 "A list of namespace tags used during encoding a message.
856 This list is populated by `soap-encode-value' and used by
857 `soap-create-envelope' to add aliases for these namespace to the
858 XML request.
859
860 This variable is dynamically bound in `soap-create-envelope'.")
861
862 (defun soap-encode-xs-element (value element)
863 "Encode the VALUE according to ELEMENT.
864 The data is inserted in the current buffer at the current
865 position.
866
867 This is a specialization of `soap-encode-value' for
868 `soap-xs-basic-type' objects."
869 (let ((fq-name (soap-element-fq-name element))
870 (type (soap-xs-element-type element)))
871 ;; Only encode the element if it has a name. NOTE: soap-element-fq-name
872 ;; will return *unnamed* for such elements
873 (if (soap-element-name element)
874 ;; Don't encode this element if value is nil. However, even if value
875 ;; is nil we still want to encode this element if it has any attributes
876 ;; with default values.
877 (when (soap-should-encode-value-for-xs-element value element)
878 (progn
879 (insert "<" fq-name)
880 (soap-encode-attributes value type)
881 ;; If value is nil and type is boolean encode the value as "false".
882 ;; Otherwise don't encode the value.
883 (if (or value (and (soap-xs-basic-type-p type)
884 (eq (soap-xs-basic-type-kind type) 'boolean)))
885 (progn (insert ">")
886 ;; ARRAY's need special treatment, as each element of
887 ;; the array is encoded with the same tag as the
888 ;; current element...
889 (if (soap-type-is-array? type)
890 (let ((new-element (copy-soap-xs-element element)))
891 (when (soap-element-namespace-tag type)
892 (add-to-list 'soap-encoded-namespaces
893 (soap-element-namespace-tag type)))
894 (setf (soap-xs-element-type^ new-element)
895 (soap-xs-complex-type-base type))
896 (loop for i below (length value)
897 do (progn
898 (soap-encode-xs-element (aref value i) new-element)
899 )))
900 (soap-encode-value value type))
901 (insert "</" fq-name ">\n"))
902 ;; else
903 (insert "/>\n"))))
904 (when (soap-should-encode-value-for-xs-element value element)
905 (soap-encode-value value type)))))
906
907 (defun soap-decode-xs-element (element node)
908 "Use ELEMENT, a `soap-xs-element', to decode the contents of NODE.
909 A LISP value is returned based on the contents of NODE and the
910 type-info stored in ELEMENT.
911
912 This is a specialization of `soap-decode-type' for
913 `soap-xs-basic-type' objects."
914 (let ((type (soap-xs-element-type element)))
915 (soap-decode-type type node)))
916
917 ;; Register methods for `soap-xs-element'
918 (let ((tag (aref (make-soap-xs-element) 0)))
919 (put tag 'soap-resolve-references #'soap-resolve-references-for-xs-element)
920 (put tag 'soap-attribute-encoder #'soap-encode-xs-element-attributes)
921 (put tag 'soap-encoder #'soap-encode-xs-element)
922 (put tag 'soap-decoder #'soap-decode-xs-element))
923
924 ;;;;; soap-xs-attribute
925
926 (defstruct (soap-xs-attribute (:include soap-element))
927 type ; a simple type or basic type
928 default ; the default value, if any
929 reference)
930
931 (defstruct (soap-xs-attribute-group (:include soap-xs-type))
932 reference)
933
934 (defun soap-xs-parse-attribute (node)
935 "Construct a `soap-xs-attribute' from NODE."
936 (assert (eq (soap-l2wk (xml-node-name node)) 'xsd:attribute)
937 "expecting xsd:attribute, got %s" (soap-l2wk (xml-node-name node)))
938 (let* ((name (xml-get-attribute-or-nil node 'name))
939 (type (soap-l2fq (xml-get-attribute-or-nil node 'type)))
940 (default (xml-get-attribute-or-nil node 'fixed))
941 (attribute (xml-get-attribute-or-nil node 'ref))
942 (ref (when attribute (soap-l2fq attribute))))
943 (unless (or type ref)
944 (setq type (soap-xs-parse-simple-type
945 (soap-xml-node-find-matching-child
946 node '(xsd:restriction xsd:list xsd:union)))))
947 (make-soap-xs-attribute
948 :name name :type type :default default :reference ref)))
949
950 (defun soap-xs-parse-attribute-group (node)
951 "Construct a `soap-xs-attribute-group' from NODE."
952 (let ((node-name (soap-l2wk (xml-node-name node))))
953 (assert (eq node-name 'xsd:attributeGroup)
954 "expecting xsd:attributeGroup, got %s" node-name)
955 (let ((name (xml-get-attribute-or-nil node 'name))
956 (id (xml-get-attribute-or-nil node 'id))
957 (ref (xml-get-attribute-or-nil node 'ref))
958 attribute-group)
959 (when (and name ref)
960 (soap-warning "name and ref set for attribute group %s" node-name))
961 (setq attribute-group
962 (make-soap-xs-attribute-group :id id
963 :name name
964 :reference (and ref (soap-l2fq ref))))
965 (when (not ref)
966 (dolist (child (xml-node-children node))
967 ;; Ignore whitespace.
968 (unless (stringp child)
969 ;; Ignore optional annotation.
970 ;; Ignore anyAttribute nodes.
971 (case (soap-l2wk (xml-node-name child))
972 (xsd:attribute
973 (push (soap-xs-parse-attribute child)
974 (soap-xs-type-attributes attribute-group)))
975 (xsd:attributeGroup
976 (push (soap-xs-parse-attribute-group child)
977 (soap-xs-attribute-group-attribute-groups
978 attribute-group)))))))
979 attribute-group)))
980
981 (defun soap-resolve-references-for-xs-attribute (attribute wsdl)
982 "Replace names in ATTRIBUTE with the referenced objects in the WSDL.
983 This is a specialization of `soap-resolve-references' for
984 `soap-xs-attribute' objects.
985
986 See also `soap-wsdl-resolve-references'."
987 (let* ((type (soap-xs-attribute-type attribute))
988 (reference (soap-xs-attribute-reference attribute))
989 (predicate 'soap-xs-element-p)
990 (xml-reference
991 (and (soap-name-p reference)
992 (equal (car reference) "http://www.w3.org/XML/1998/namespace"))))
993 (cond (xml-reference
994 ;; Convert references to attributes defined by the XML
995 ;; schema (xml:base, xml:lang, xml:space and xml:id) to
996 ;; xsd:string, to avoid needing to bundle and parse
997 ;; xml.xsd.
998 (setq reference '("http://www.w3.org/2001/XMLSchema" . "string"))
999 (setq predicate 'soap-xs-basic-type-p))
1000 ((soap-name-p type)
1001 (setf (soap-xs-attribute-type attribute)
1002 (soap-wsdl-get type wsdl
1003 (lambda (type)
1004 (or (soap-xs-basic-type-p type)
1005 (soap-xs-simple-type-p type))))))
1006 ((soap-xs-type-p type)
1007 ;; an inline defined type, this will not be reached from anywhere
1008 ;; else, so we must resolve references now.
1009 (soap-resolve-references type wsdl)))
1010 (when (soap-name-p reference)
1011 (setf (soap-xs-attribute-reference attribute)
1012 (soap-wsdl-get reference wsdl predicate)))))
1013
1014 (put (aref (make-soap-xs-attribute) 0)
1015 'soap-resolve-references #'soap-resolve-references-for-xs-attribute)
1016
1017 (defun soap-resolve-references-for-xs-attribute-group (attribute-group wsdl)
1018 "Set slots in ATTRIBUTE-GROUP to the referenced objects in the WSDL.
1019 This is a specialization of `soap-resolve-references' for
1020 `soap-xs-attribute-group' objects.
1021
1022 See also `soap-wsdl-resolve-references'."
1023 (let ((reference (soap-xs-attribute-group-reference attribute-group)))
1024 (when (soap-name-p reference)
1025 (let ((resolved (soap-wsdl-get reference wsdl
1026 'soap-xs-attribute-group-p)))
1027 (dolist (attribute (soap-xs-attribute-group-attributes resolved))
1028 (soap-resolve-references attribute wsdl))
1029 (setf (soap-xs-attribute-group-name attribute-group)
1030 (soap-xs-attribute-group-name resolved))
1031 (setf (soap-xs-attribute-group-id attribute-group)
1032 (soap-xs-attribute-group-id resolved))
1033 (setf (soap-xs-attribute-group-reference attribute-group) nil)
1034 (setf (soap-xs-attribute-group-attributes attribute-group)
1035 (soap-xs-attribute-group-attributes resolved))
1036 (setf (soap-xs-attribute-group-attribute-groups attribute-group)
1037 (soap-xs-attribute-group-attribute-groups resolved))))))
1038
1039 (put (aref (make-soap-xs-attribute-group) 0)
1040 'soap-resolve-references #'soap-resolve-references-for-xs-attribute-group)
1041
1042 ;;;;; soap-xs-simple-type
1043
1044 (defstruct (soap-xs-simple-type (:include soap-xs-type))
1045 ;; A simple type is an extension on the basic type to which some
1046 ;; restrictions can be added. For example we can define a simple type based
1047 ;; off "string" with the restrictions that only the strings "one", "two" and
1048 ;; "three" are valid values (this is an enumeration).
1049
1050 base ; can be a single type, or a list of types for union types
1051 enumeration ; nil, or list of permitted values for the type
1052 pattern ; nil, or value must match this pattern
1053 length-range ; a cons of (min . max) length, inclusive range.
1054 ; For exact length, use (l, l).
1055 ; nil means no range,
1056 ; (nil . l) means no min range,
1057 ; (l . nil) means no max range.
1058 integer-range ; a pair of (min, max) integer values, inclusive range,
1059 ; same meaning as `length-range'
1060 is-list ; t if this is an xs:list, nil otherwise
1061 )
1062
1063 (defun soap-xs-parse-simple-type (node)
1064 "Construct an `soap-xs-simple-type' object from the XML NODE."
1065 (assert (memq (soap-l2wk (xml-node-name node))
1066 '(xsd:simpleType xsd:simpleContent))
1067 nil
1068 "expecting xsd:simpleType or xsd:simpleContent node, got %s"
1069 (soap-l2wk (xml-node-name node)))
1070
1071 ;; NOTE: name can be nil for inline types. Such types cannot be added to a
1072 ;; namespace.
1073 (let ((name (xml-get-attribute-or-nil node 'name))
1074 (id (xml-get-attribute-or-nil node 'id)))
1075
1076 (let ((type (make-soap-xs-simple-type
1077 :name name :namespace-tag soap-target-xmlns :id id))
1078 (def (soap-xml-node-find-matching-child
1079 node '(xsd:restriction xsd:extension xsd:union xsd:list))))
1080 (ecase (soap-l2wk (xml-node-name def))
1081 (xsd:restriction (soap-xs-add-restriction def type))
1082 (xsd:extension (soap-xs-add-extension def type))
1083 (xsd:union (soap-xs-add-union def type))
1084 (xsd:list (soap-xs-add-list def type)))
1085
1086 type)))
1087
1088 (defun soap-xs-add-restriction (node type)
1089 "Add restrictions defined in XML NODE to TYPE, an `soap-xs-simple-type'."
1090
1091 (assert (eq (soap-l2wk (xml-node-name node)) 'xsd:restriction)
1092 nil
1093 "expecting xsd:restriction node, got %s"
1094 (soap-l2wk (xml-node-name node)))
1095
1096 (setf (soap-xs-simple-type-base type)
1097 (soap-l2fq (xml-get-attribute node 'base)))
1098
1099 (dolist (r (xml-node-children node))
1100 (unless (stringp r) ; skip the white space
1101 (let ((value (xml-get-attribute r 'value)))
1102 (case (soap-l2wk (xml-node-name r))
1103 (xsd:enumeration
1104 (push value (soap-xs-simple-type-enumeration type)))
1105 (xsd:pattern
1106 (setf (soap-xs-simple-type-pattern type)
1107 (concat "\\`" (xsdre-translate value) "\\'")))
1108 (xsd:length
1109 (let ((value (string-to-number value)))
1110 (setf (soap-xs-simple-type-length-range type)
1111 (cons value value))))
1112 (xsd:minLength
1113 (let ((value (string-to-number value)))
1114 (setf (soap-xs-simple-type-length-range type)
1115 (if (soap-xs-simple-type-length-range type)
1116 (cons value
1117 (cdr (soap-xs-simple-type-length-range type)))
1118 ;; else
1119 (cons value nil)))))
1120 (xsd:maxLength
1121 (let ((value (string-to-number value)))
1122 (setf (soap-xs-simple-type-length-range type)
1123 (if (soap-xs-simple-type-length-range type)
1124 (cons (car (soap-xs-simple-type-length-range type))
1125 value)
1126 ;; else
1127 (cons nil value)))))
1128 (xsd:minExclusive
1129 (let ((value (string-to-number value)))
1130 (setf (soap-xs-simple-type-integer-range type)
1131 (if (soap-xs-simple-type-integer-range type)
1132 (cons (1+ value)
1133 (cdr (soap-xs-simple-type-integer-range type)))
1134 ;; else
1135 (cons (1+ value) nil)))))
1136 (xsd:maxExclusive
1137 (let ((value (string-to-number value)))
1138 (setf (soap-xs-simple-type-integer-range type)
1139 (if (soap-xs-simple-type-integer-range type)
1140 (cons (car (soap-xs-simple-type-integer-range type))
1141 (1- value))
1142 ;; else
1143 (cons nil (1- value))))))
1144 (xsd:minInclusive
1145 (let ((value (string-to-number value)))
1146 (setf (soap-xs-simple-type-integer-range type)
1147 (if (soap-xs-simple-type-integer-range type)
1148 (cons value
1149 (cdr (soap-xs-simple-type-integer-range type)))
1150 ;; else
1151 (cons value nil)))))
1152 (xsd:maxInclusive
1153 (let ((value (string-to-number value)))
1154 (setf (soap-xs-simple-type-integer-range type)
1155 (if (soap-xs-simple-type-integer-range type)
1156 (cons (car (soap-xs-simple-type-integer-range type))
1157 value)
1158 ;; else
1159 (cons nil value))))))))))
1160
1161 (defun soap-xs-add-union (node type)
1162 "Add union members defined in XML NODE to TYPE, an `soap-xs-simple-type'."
1163 (assert (eq (soap-l2wk (xml-node-name node)) 'xsd:union)
1164 nil
1165 "expecting xsd:union node, got %s" (soap-l2wk (xml-node-name node)))
1166
1167 (setf (soap-xs-simple-type-base type)
1168 (mapcar 'soap-l2fq
1169 (split-string
1170 (or (xml-get-attribute-or-nil node 'memberTypes) ""))))
1171
1172 ;; Additional simple types can be defined inside the union node. Add them
1173 ;; to the base list. The "memberTypes" members will have to be resolved by
1174 ;; the "resolve-references" method, the inline types will not.
1175 (let (result)
1176 (dolist (simple-type (soap-xml-get-children1 node 'xsd:simpleType))
1177 (push (soap-xs-parse-simple-type simple-type) result))
1178 (setf (soap-xs-simple-type-base type)
1179 (append (soap-xs-simple-type-base type) (nreverse result)))))
1180
1181 (defun soap-xs-add-list (node type)
1182 "Add list defined in XML NODE to TYPE, a `soap-xs-simple-type'."
1183 (assert (eq (soap-l2wk (xml-node-name node)) 'xsd:list)
1184 nil
1185 "expecting xsd:list node, got %s" (soap-l2wk (xml-node-name node)))
1186
1187 ;; A simple type can be defined inline inside the list node or referenced by
1188 ;; the itemType attribute, in which case it will be resolved by the
1189 ;; resolve-references method.
1190 (let* ((item-type (xml-get-attribute-or-nil node 'itemType))
1191 (children (soap-xml-get-children1 node 'xsd:simpleType)))
1192 (if item-type
1193 (if (= (length children) 0)
1194 (setf (soap-xs-simple-type-base type) (soap-l2fq item-type))
1195 (soap-warning
1196 "xsd:list node with itemType has more than zero children: %s"
1197 (soap-xs-type-name type)))
1198 (if (= (length children) 1)
1199 (setf (soap-xs-simple-type-base type)
1200 (soap-xs-parse-simple-type
1201 (car (soap-xml-get-children1 node 'xsd:simpleType))))
1202 (soap-warning "xsd:list node has more than one child %s"
1203 (soap-xs-type-name type))))
1204 (setf (soap-xs-simple-type-is-list type) t)))
1205
1206 (defun soap-xs-add-extension (node type)
1207 "Add the extended type defined in XML NODE to TYPE, an `soap-xs-simple-type'."
1208 (setf (soap-xs-simple-type-base type)
1209 (soap-l2fq (xml-get-attribute node 'base)))
1210 (dolist (attribute (soap-xml-get-children1 node 'xsd:attribute))
1211 (push (soap-xs-parse-attribute attribute)
1212 (soap-xs-type-attributes type)))
1213 (dolist (attribute-group (soap-xml-get-children1 node 'xsd:attributeGroup))
1214 (push (soap-xs-parse-attribute-group attribute-group)
1215 (soap-xs-type-attribute-groups type))))
1216
1217 (defun soap-validate-xs-basic-type (value type)
1218 "Validate VALUE against the basic type TYPE."
1219 (let* ((kind (soap-xs-basic-type-kind type)))
1220 (case kind
1221 ((anyType Array byte[])
1222 value)
1223 (t
1224 (let ((convert (get kind 'rng-xsd-convert)))
1225 (if convert
1226 (if (rng-dt-make-value convert value)
1227 value
1228 (error "Invalid %s: %s" (symbol-name kind) value))
1229 (error "Don't know how to convert %s" kind)))))))
1230
1231 (defun soap-validate-xs-simple-type (value type)
1232 "Validate VALUE against the restrictions of TYPE."
1233
1234 (let* ((base-type (soap-xs-simple-type-base type))
1235 (messages nil))
1236 (if (listp base-type)
1237 (catch 'valid
1238 (dolist (base base-type)
1239 (condition-case error-object
1240 (cond ((soap-xs-simple-type-p base)
1241 (throw 'valid
1242 (soap-validate-xs-simple-type value base)))
1243 ((soap-xs-basic-type-p base)
1244 (throw 'valid
1245 (soap-validate-xs-basic-type value base))))
1246 (error (push (cadr error-object) messages))))
1247 (when messages
1248 (error (mapconcat 'identity (nreverse messages) "; and: "))))
1249 (cl-flet ((fail-with-message (format value)
1250 (push (format format value) messages)
1251 (throw 'invalid nil)))
1252 (catch 'invalid
1253 (let ((enumeration (soap-xs-simple-type-enumeration type)))
1254 (when (and (> (length enumeration) 1)
1255 (not (member value enumeration)))
1256 (fail-with-message "bad value, should be one of %s" enumeration)))
1257
1258 (let ((pattern (soap-xs-simple-type-pattern type)))
1259 (when (and pattern (not (string-match-p pattern value)))
1260 (fail-with-message "bad value, should match pattern %s" pattern)))
1261
1262 (let ((length-range (soap-xs-simple-type-length-range type)))
1263 (when length-range
1264 (unless (stringp value)
1265 (fail-with-message
1266 "bad value, should be a string with length range %s"
1267 length-range))
1268 (when (car length-range)
1269 (unless (>= (length value) (car length-range))
1270 (fail-with-message "short string, should be at least %s chars"
1271 (car length-range))))
1272 (when (cdr length-range)
1273 (unless (<= (length value) (cdr length-range))
1274 (fail-with-message "long string, should be at most %s chars"
1275 (cdr length-range))))))
1276
1277 (let ((integer-range (soap-xs-simple-type-integer-range type)))
1278 (when integer-range
1279 (unless (numberp value)
1280 (fail-with-message "bad value, should be a number with range %s"
1281 integer-range))
1282 (when (car integer-range)
1283 (unless (>= value (car integer-range))
1284 (fail-with-message "small value, should be at least %s"
1285 (car integer-range))))
1286 (when (cdr integer-range)
1287 (unless (<= value (cdr integer-range))
1288 (fail-with-message "big value, should be at most %s"
1289 (cdr integer-range))))))))
1290 (when messages
1291 (error "Xs-simple-type(%s, %s): %s"
1292 value (or (soap-xs-type-name type) (soap-xs-type-id type))
1293 (car messages)))))
1294 ;; Return the validated value.
1295 value)
1296
1297 (defun soap-resolve-references-for-xs-simple-type (type wsdl)
1298 "Replace names in TYPE with the referenced objects in the WSDL.
1299 This is a specialization of `soap-resolve-references' for
1300 `soap-xs-simple-type' objects.
1301
1302 See also `soap-wsdl-resolve-references'."
1303
1304 (let ((namespace (soap-element-namespace-tag type)))
1305 (when namespace
1306 (let ((nstag (car (rassoc namespace (soap-wsdl-alias-table wsdl)))))
1307 (when nstag
1308 (setf (soap-element-namespace-tag type) nstag)))))
1309
1310 (let ((base (soap-xs-simple-type-base type)))
1311 (cond
1312 ((soap-name-p base)
1313 (setf (soap-xs-simple-type-base type)
1314 (soap-wsdl-get base wsdl 'soap-xs-type-p)))
1315 ((soap-xs-type-p base)
1316 (soap-resolve-references base wsdl))
1317 ((listp base)
1318 (setf (soap-xs-simple-type-base type)
1319 (mapcar (lambda (type)
1320 (cond ((soap-name-p type)
1321 (soap-wsdl-get type wsdl 'soap-xs-type-p))
1322 ((soap-xs-type-p type)
1323 (soap-resolve-references type wsdl)
1324 type)
1325 (t ; signal an error?
1326 type)))
1327 base)))
1328 (t (error "Oops"))))
1329 (dolist (attribute (soap-xs-type-attributes type))
1330 (soap-resolve-references attribute wsdl))
1331 (dolist (attribute-group (soap-xs-type-attribute-groups type))
1332 (soap-resolve-references attribute-group wsdl)))
1333
1334 (defun soap-encode-xs-simple-type-attributes (value type)
1335 "Encode the XML attributes for VALUE according to TYPE.
1336 The xsi:type and an optional xsi:nil attributes are added. The
1337 attributes are inserted in the current buffer at the current
1338 position.
1339
1340 This is a specialization of `soap-encode-attributes' for
1341 `soap-xs-simple-type' objects."
1342 (insert " xsi:type=\"" (soap-element-fq-name type) "\"")
1343 (unless value (insert " xsi:nil=\"true\"")))
1344
1345 (defun soap-encode-xs-simple-type (value type)
1346 "Encode the VALUE according to TYPE.
1347 The data is inserted in the current buffer at the current
1348 position.
1349
1350 This is a specialization of `soap-encode-value' for
1351 `soap-xs-simple-type' objects."
1352 (soap-validate-xs-simple-type value type)
1353 (if (soap-xs-simple-type-is-list type)
1354 (progn
1355 (dolist (v (butlast value))
1356 (soap-encode-value v (soap-xs-simple-type-base type))
1357 (insert " "))
1358 (soap-encode-value (car (last value)) (soap-xs-simple-type-base type)))
1359 (soap-encode-value value (soap-xs-simple-type-base type))))
1360
1361 (defun soap-decode-xs-simple-type (type node)
1362 "Use TYPE, a `soap-xs-simple-type', to decode the contents of NODE.
1363 A LISP value is returned based on the contents of NODE and the
1364 type-info stored in TYPE.
1365
1366 This is a specialization of `soap-decode-type' for
1367 `soap-xs-simple-type' objects."
1368 (if (soap-xs-simple-type-is-list type)
1369 ;; Technically, we could construct fake XML NODEs and pass them to
1370 ;; soap-decode-value...
1371 (split-string (car (xml-node-children node)))
1372 (let ((value (soap-decode-type (soap-xs-simple-type-base type) node)))
1373 (soap-validate-xs-simple-type value type))))
1374
1375 ;; Register methods for `soap-xs-simple-type'
1376 (let ((tag (aref (make-soap-xs-simple-type) 0)))
1377 (put tag 'soap-resolve-references
1378 #'soap-resolve-references-for-xs-simple-type)
1379 (put tag 'soap-attribute-encoder #'soap-encode-xs-simple-type-attributes)
1380 (put tag 'soap-encoder #'soap-encode-xs-simple-type)
1381 (put tag 'soap-decoder #'soap-decode-xs-simple-type))
1382
1383 ;;;;; soap-xs-complex-type
1384
1385 (defstruct (soap-xs-complex-type (:include soap-xs-type))
1386 indicator ; sequence, choice, all, array
1387 base
1388 elements
1389 optional?
1390 multiple?
1391 is-group)
1392
1393 (defun soap-xs-parse-complex-type (node)
1394 "Construct a `soap-xs-complex-type' by parsing the XML NODE."
1395 (let ((name (xml-get-attribute-or-nil node 'name))
1396 (id (xml-get-attribute-or-nil node 'id))
1397 (node-name (soap-l2wk (xml-node-name node)))
1398 type
1399 attributes
1400 attribute-groups)
1401 (assert (memq node-name '(xsd:complexType xsd:complexContent xsd:group))
1402 nil "unexpected node: %s" node-name)
1403
1404 (dolist (def (xml-node-children node))
1405 (when (consp def) ; skip text nodes
1406 (case (soap-l2wk (xml-node-name def))
1407 (xsd:attribute (push (soap-xs-parse-attribute def) attributes))
1408 (xsd:attributeGroup
1409 (push (soap-xs-parse-attribute-group def)
1410 attribute-groups))
1411 (xsd:simpleContent (setq type (soap-xs-parse-simple-type def)))
1412 ((xsd:sequence xsd:all xsd:choice)
1413 (setq type (soap-xs-parse-sequence def)))
1414 (xsd:complexContent
1415 (dolist (def (xml-node-children def))
1416 (when (consp def)
1417 (case (soap-l2wk (xml-node-name def))
1418 (xsd:attribute
1419 (push (soap-xs-parse-attribute def) attributes))
1420 (xsd:attributeGroup
1421 (push (soap-xs-parse-attribute-group def)
1422 attribute-groups))
1423 ((xsd:extension xsd:restriction)
1424 (setq type
1425 (soap-xs-parse-extension-or-restriction def)))
1426 ((xsd:sequence xsd:all xsd:choice)
1427 (soap-xs-parse-sequence def)))))))))
1428 (unless type
1429 ;; the type has not been built, this is a shortcut for a simpleContent
1430 ;; node
1431 (setq type (make-soap-xs-complex-type)))
1432
1433 (setf (soap-xs-type-name type) name)
1434 (setf (soap-xs-type-namespace-tag type) soap-target-xmlns)
1435 (setf (soap-xs-type-id type) id)
1436 (setf (soap-xs-type-attributes type)
1437 (append attributes (soap-xs-type-attributes type)))
1438 (setf (soap-xs-type-attribute-groups type)
1439 (append attribute-groups (soap-xs-type-attribute-groups type)))
1440 (when (soap-xs-complex-type-p type)
1441 (setf (soap-xs-complex-type-is-group type)
1442 (eq node-name 'xsd:group)))
1443 type))
1444
1445 (defun soap-xs-parse-sequence (node)
1446 "Parse a sequence definition from XML NODE.
1447 Returns a `soap-xs-complex-type'"
1448 (assert (memq (soap-l2wk (xml-node-name node))
1449 '(xsd:sequence xsd:choice xsd:all))
1450 nil
1451 "unexpected node: %s" (soap-l2wk (xml-node-name node)))
1452
1453 (let ((type (make-soap-xs-complex-type)))
1454
1455 (setf (soap-xs-complex-type-indicator type)
1456 (ecase (soap-l2wk (xml-node-name node))
1457 (xsd:sequence 'sequence)
1458 (xsd:all 'all)
1459 (xsd:choice 'choice)))
1460
1461 (setf (soap-xs-complex-type-optional? type) (soap-node-optional node))
1462 (setf (soap-xs-complex-type-multiple? type) (soap-node-multiple node))
1463
1464 (dolist (r (xml-node-children node))
1465 (unless (stringp r) ; skip the white space
1466 (case (soap-l2wk (xml-node-name r))
1467 ((xsd:element xsd:group)
1468 (push (soap-xs-parse-element r)
1469 (soap-xs-complex-type-elements type)))
1470 ((xsd:sequence xsd:choice xsd:all)
1471 ;; an inline sequence, choice or all node
1472 (let ((choice (soap-xs-parse-sequence r)))
1473 (push (make-soap-xs-element :name nil :type^ choice)
1474 (soap-xs-complex-type-elements type))))
1475 (xsd:attribute
1476 (push (soap-xs-parse-attribute r)
1477 (soap-xs-type-attributes type)))
1478 (xsd:attributeGroup
1479 (push (soap-xs-parse-attribute-group r)
1480 (soap-xs-type-attribute-groups type))))))
1481
1482 (setf (soap-xs-complex-type-elements type)
1483 (nreverse (soap-xs-complex-type-elements type)))
1484
1485 type))
1486
1487 (defun soap-xs-parse-extension-or-restriction (node)
1488 "Parse an extension or restriction definition from XML NODE.
1489 Return a `soap-xs-complex-type'."
1490 (assert (memq (soap-l2wk (xml-node-name node))
1491 '(xsd:extension xsd:restriction))
1492 nil
1493 "unexpected node: %s" (soap-l2wk (xml-node-name node)))
1494 (let (type
1495 attributes
1496 attribute-groups
1497 array?
1498 (base (xml-get-attribute-or-nil node 'base)))
1499
1500 ;; Array declarations are recognized specially, it is unclear to me how
1501 ;; they could be treated generally...
1502 (setq array?
1503 (and (eq (soap-l2wk (xml-node-name node)) 'xsd:restriction)
1504 (equal base (soap-wk2l "soapenc:Array"))))
1505
1506 (dolist (def (xml-node-children node))
1507 (when (consp def) ; skip text nodes
1508 (case (soap-l2wk (xml-node-name def))
1509 ((xsd:sequence xsd:choice xsd:all)
1510 (setq type (soap-xs-parse-sequence def)))
1511 (xsd:attribute
1512 (if array?
1513 (let ((array-type
1514 (soap-xml-get-attribute-or-nil1 def 'wsdl:arrayType)))
1515 (when (and array-type
1516 (string-match "^\\(.*\\)\\[\\]$" array-type))
1517 ;; Override
1518 (setq base (match-string 1 array-type))))
1519 ;; else
1520 (push (soap-xs-parse-attribute def) attributes)))
1521 (xsd:attributeGroup
1522 (push (soap-xs-parse-attribute-group def) attribute-groups)))))
1523
1524 (unless type
1525 (setq type (make-soap-xs-complex-type))
1526 (when array?
1527 (setf (soap-xs-complex-type-indicator type) 'array)))
1528
1529 (setf (soap-xs-complex-type-base type) (soap-l2fq base))
1530 (setf (soap-xs-complex-type-attributes type) attributes)
1531 (setf (soap-xs-complex-type-attribute-groups type) attribute-groups)
1532 type))
1533
1534 (defun soap-resolve-references-for-xs-complex-type (type wsdl)
1535 "Replace names in TYPE with the referenced objects in the WSDL.
1536 This is a specialization of `soap-resolve-references' for
1537 `soap-xs-complex-type' objects.
1538
1539 See also `soap-wsdl-resolve-references'."
1540
1541 (let ((namespace (soap-element-namespace-tag type)))
1542 (when namespace
1543 (let ((nstag (car (rassoc namespace (soap-wsdl-alias-table wsdl)))))
1544 (when nstag
1545 (setf (soap-element-namespace-tag type) nstag)))))
1546
1547 (let ((base (soap-xs-complex-type-base type)))
1548 (cond ((soap-name-p base)
1549 (setf (soap-xs-complex-type-base type)
1550 (soap-wsdl-get base wsdl 'soap-xs-type-p)))
1551 ((soap-xs-type-p base)
1552 (soap-resolve-references base wsdl))))
1553 (let (all-elements)
1554 (dolist (element (soap-xs-complex-type-elements type))
1555 (if (soap-xs-element-is-group element)
1556 ;; This is an xsd:group element that references an xsd:group node,
1557 ;; which we treat as a complex type. We replace the reference
1558 ;; element by inlining the elements of the referenced xsd:group
1559 ;; (complex type) node.
1560 (let ((type (soap-wsdl-get
1561 (soap-xs-element-reference element)
1562 wsdl (lambda (type)
1563 (and
1564 (soap-xs-complex-type-p type)
1565 (soap-xs-complex-type-is-group type))))))
1566 (dolist (element (soap-xs-complex-type-elements type))
1567 (soap-resolve-references element wsdl)
1568 (push element all-elements)))
1569 ;; This is a non-xsd:group node so just add it directly.
1570 (soap-resolve-references element wsdl)
1571 (push element all-elements)))
1572 (setf (soap-xs-complex-type-elements type) (nreverse all-elements)))
1573 (dolist (attribute (soap-xs-type-attributes type))
1574 (soap-resolve-references attribute wsdl))
1575 (dolist (attribute-group (soap-xs-type-attribute-groups type))
1576 (soap-resolve-references attribute-group wsdl)))
1577
1578 (defun soap-encode-xs-complex-type-attributes (value type)
1579 "Encode the XML attributes for encoding VALUE according to TYPE.
1580 The xsi:type and optional xsi:nil attributes are added, plus
1581 additional attributes needed for arrays types, if applicable. The
1582 attributes are inserted in the current buffer at the current
1583 position.
1584
1585 This is a specialization of `soap-encode-attributes' for
1586 `soap-xs-complex-type' objects."
1587 (if (eq (soap-xs-complex-type-indicator type) 'array)
1588 (let ((element-type (soap-xs-complex-type-base type)))
1589 (insert " xsi:type=\"soapenc:Array\"")
1590 (insert " soapenc:arrayType=\""
1591 (soap-element-fq-name element-type)
1592 "[" (format "%s" (length value)) "]" "\""))
1593 ;; else
1594 (progn
1595 (dolist (a (soap-get-xs-attributes type))
1596 (let ((element-name (soap-element-name a)))
1597 (if (soap-xs-attribute-default a)
1598 (insert " " element-name
1599 "=\"" (soap-xs-attribute-default a) "\"")
1600 (dolist (value-pair value)
1601 (when (equal element-name (symbol-name (car value-pair)))
1602 (insert " " element-name
1603 "=\"" (cdr value-pair) "\""))))))
1604 ;; If this is not an empty type, and we have no value, mark it as nil
1605 (when (and (soap-xs-complex-type-indicator type) (null value))
1606 (insert " xsi:nil=\"true\"")))))
1607
1608 (defun soap-get-candidate-elements (element)
1609 "Return a list of elements that are compatible with ELEMENT.
1610 The returned list includes ELEMENT's references and
1611 alternatives."
1612 (let ((reference (soap-xs-element-reference element)))
1613 ;; If the element is a reference, append the reference and its
1614 ;; alternatives...
1615 (if reference
1616 (append (list reference)
1617 (soap-xs-element-alternatives reference))
1618 ;; ...otherwise append the element itself and its alternatives.
1619 (append (list element)
1620 (soap-xs-element-alternatives element)))))
1621
1622 (defun soap-encode-xs-complex-type (value type)
1623 "Encode the VALUE according to TYPE.
1624 The data is inserted in the current buffer at the current
1625 position.
1626
1627 This is a specialization of `soap-encode-value' for
1628 `soap-xs-complex-type' objects."
1629 (case (soap-xs-complex-type-indicator type)
1630 (array
1631 (error "soap-encode-xs-complex-type arrays are handled elsewhere"))
1632 ((sequence choice all nil)
1633 (let ((type-list (list type)))
1634
1635 ;; Collect all base types
1636 (let ((base (soap-xs-complex-type-base type)))
1637 (while base
1638 (push base type-list)
1639 (setq base (soap-xs-complex-type-base base))))
1640
1641 (dolist (type type-list)
1642 (dolist (element (soap-xs-complex-type-elements type))
1643 (catch 'done
1644 (let ((instance-count 0))
1645 (dolist (candidate (soap-get-candidate-elements element))
1646 (let ((e-name (soap-xs-element-name candidate)))
1647 (if e-name
1648 (let ((e-name (intern e-name)))
1649 (dolist (v value)
1650 (when (equal (car v) e-name)
1651 (incf instance-count)
1652 (soap-encode-value (cdr v) candidate))))
1653 (if (soap-xs-complex-type-indicator type)
1654 (let ((current-point (point)))
1655 ;; Check if encoding happened by checking if
1656 ;; characters were inserted in the buffer.
1657 (soap-encode-value value candidate)
1658 (when (not (equal current-point (point)))
1659 (incf instance-count)))
1660 (dolist (v value)
1661 (let ((current-point (point)))
1662 (soap-encode-value v candidate)
1663 (when (not (equal current-point (point)))
1664 (incf instance-count))))))))
1665 ;; Do some sanity checking
1666 (let* ((indicator (soap-xs-complex-type-indicator type))
1667 (element-type (soap-xs-element-type element))
1668 (reference (soap-xs-element-reference element))
1669 (e-name (or (soap-xs-element-name element)
1670 (and reference
1671 (soap-xs-element-name reference)))))
1672 (cond ((and (eq indicator 'choice)
1673 (> instance-count 0))
1674 ;; This was a choice node and we encoded
1675 ;; one instance.
1676 (throw 'done t))
1677 ((and (not (eq indicator 'choice))
1678 (= instance-count 0)
1679 (not (soap-xs-element-optional? element))
1680 (and (soap-xs-complex-type-p element-type)
1681 (not (soap-xs-complex-type-optional-p
1682 element-type))))
1683 (soap-warning
1684 "While encoding %s: missing non-nillable slot %s"
1685 value e-name))
1686 ((and (> instance-count 1)
1687 (not (soap-xs-element-multiple? element))
1688 (and (soap-xs-complex-type-p element-type)
1689 (not (soap-xs-complex-type-multiple-p
1690 element-type))))
1691 (soap-warning
1692 (concat "While encoding %s: expected single,"
1693 " found multiple elements for slot %s")
1694 value e-name))))))))))
1695 (t
1696 (error "Don't know how to encode complex type: %s"
1697 (soap-xs-complex-type-indicator type)))))
1698
1699 (defun soap-xml-get-children-fq (node child-name)
1700 "Return the children of NODE named CHILD-NAME.
1701 This is the same as `xml-get-children1', but NODE's local
1702 namespace is used to resolve the children's namespace tags."
1703 (let (result)
1704 (dolist (c (xml-node-children node))
1705 (when (and (consp c)
1706 (soap-with-local-xmlns node
1707 ;; We use `ignore-errors' here because we want to silently
1708 ;; skip nodes for which we cannot convert them to a
1709 ;; well-known name.
1710 (equal (ignore-errors
1711 (soap-l2fq (xml-node-name c)))
1712 child-name)))
1713 (push c result)))
1714 (nreverse result)))
1715
1716 (defun soap-xs-element-get-fq-name (element wsdl)
1717 "Return ELEMENT's fully-qualified name using WSDL's alias table.
1718 Return nil if ELEMENT does not have a name."
1719 (let* ((ns-aliases (soap-wsdl-alias-table wsdl))
1720 (ns-name (cdr (assoc
1721 (soap-element-namespace-tag element)
1722 ns-aliases))))
1723 (when ns-name
1724 (cons ns-name (soap-element-name element)))))
1725
1726 (defun soap-xs-complex-type-optional-p (type)
1727 "Return t if TYPE or any of TYPE's ancestor types is optional.
1728 Return nil otherwise."
1729 (when type
1730 (or (soap-xs-complex-type-optional? type)
1731 (and (soap-xs-complex-type-p type)
1732 (soap-xs-complex-type-optional-p
1733 (soap-xs-complex-type-base type))))))
1734
1735 (defun soap-xs-complex-type-multiple-p (type)
1736 "Return t if TYPE or any of TYPE's ancestor types permits multiple elements.
1737 Return nil otherwise."
1738 (when type
1739 (or (soap-xs-complex-type-multiple? type)
1740 (and (soap-xs-complex-type-p type)
1741 (soap-xs-complex-type-multiple-p
1742 (soap-xs-complex-type-base type))))))
1743
1744 (defun soap-get-xs-attributes-from-groups (attribute-groups)
1745 "Return a list of attributes from all ATTRIBUTE-GROUPS."
1746 (let (attributes)
1747 (dolist (group attribute-groups)
1748 (let ((sub-groups (soap-xs-attribute-group-attribute-groups group)))
1749 (setq attributes (append attributes
1750 (soap-get-xs-attributes-from-groups sub-groups)
1751 (soap-xs-attribute-group-attributes group)))))
1752 attributes))
1753
1754 (defun soap-get-xs-attributes (type)
1755 "Return a list of all of TYPE's and TYPE's ancestors' attributes."
1756 (let* ((base (and (soap-xs-complex-type-p type)
1757 (soap-xs-complex-type-base type)))
1758 (attributes (append (soap-xs-type-attributes type)
1759 (soap-get-xs-attributes-from-groups
1760 (soap-xs-type-attribute-groups type)))))
1761 (if base
1762 (append attributes (soap-get-xs-attributes base))
1763 attributes)))
1764
1765 (defun soap-decode-xs-attributes (type node)
1766 "Use TYPE, a `soap-xs-complex-type', to decode the attributes of NODE."
1767 (let (result)
1768 (dolist (attribute (soap-get-xs-attributes type))
1769 (let* ((name (soap-xs-attribute-name attribute))
1770 (attribute-type (soap-xs-attribute-type attribute))
1771 (symbol (intern name))
1772 (value (xml-get-attribute-or-nil node symbol)))
1773 ;; We don't support attribute uses: required, optional, prohibited.
1774 (cond
1775 ((soap-xs-basic-type-p attribute-type)
1776 ;; Basic type values are validated by xml.el.
1777 (when value
1778 (push (cons symbol
1779 ;; Create a fake XML node to satisfy the
1780 ;; soap-decode-xs-basic-type API.
1781 (soap-decode-xs-basic-type attribute-type
1782 (list symbol nil value)))
1783 result)))
1784 ((soap-xs-simple-type-p attribute-type)
1785 (when value
1786 (push (cons symbol
1787 (soap-validate-xs-simple-type value attribute-type))
1788 result)))
1789 (t
1790 (error (concat "Attribute %s is of type %s which is"
1791 " not a basic or simple type")
1792 name (soap-name-p attribute))))))
1793 result))
1794
1795 (defun soap-decode-xs-complex-type (type node)
1796 "Use TYPE, a `soap-xs-complex-type', to decode the contents of NODE.
1797 A LISP value is returned based on the contents of NODE and the
1798 type-info stored in TYPE.
1799
1800 This is a specialization of `soap-decode-type' for
1801 `soap-xs-basic-type' objects."
1802 (case (soap-xs-complex-type-indicator type)
1803 (array
1804 (let ((result nil)
1805 (element-type (soap-xs-complex-type-base type)))
1806 (dolist (node (xml-node-children node))
1807 (when (consp node)
1808 (push (soap-decode-type element-type node) result)))
1809 (nreverse result)))
1810 ((sequence choice all nil)
1811 (let ((result nil)
1812 (base (soap-xs-complex-type-base type)))
1813 (when base
1814 (setq result (nreverse (soap-decode-type base node))))
1815 (catch 'done
1816 (dolist (element (soap-xs-complex-type-elements type))
1817 (let* ((instance-count 0)
1818 (e-name (soap-xs-element-name element))
1819 ;; Heuristic: guess if we need to decode using local
1820 ;; namespaces.
1821 (use-fq-names (string-match ":" (symbol-name (car node))))
1822 (children (if e-name
1823 (if use-fq-names
1824 ;; Find relevant children
1825 ;; using local namespaces by
1826 ;; searching for the element's
1827 ;; fully-qualified name.
1828 (soap-xml-get-children-fq
1829 node
1830 (soap-xs-element-get-fq-name
1831 element soap-current-wsdl))
1832 ;; No local namespace resolution
1833 ;; needed so use the element's
1834 ;; name unqualified.
1835 (xml-get-children node (intern e-name)))
1836 ;; e-name is nil so a) we don't know which
1837 ;; children to operate on, and b) we want to
1838 ;; re-use soap-decode-xs-complex-type, which
1839 ;; expects a node argument with a complex
1840 ;; type; therefore we need to operate on the
1841 ;; entire node. We wrap node in a list so
1842 ;; that it will carry through as "node" in the
1843 ;; loop below.
1844 ;;
1845 ;; For example:
1846 ;;
1847 ;; Element Type:
1848 ;; <xs:complexType name="A">
1849 ;; <xs:sequence>
1850 ;; <xs:element name="B" type="t:BType"/>
1851 ;; <xs:choice>
1852 ;; <xs:element name="C" type="xs:string"/>
1853 ;; <xs:element name="D" type="t:DType"/>
1854 ;; </xs:choice>
1855 ;; </xs:sequence>
1856 ;; </xs:complexType>
1857 ;;
1858 ;; Node:
1859 ;; <t:A>
1860 ;; <t:B tag="b"/>
1861 ;; <t:C>1</C>
1862 ;; </t:A>
1863 ;;
1864 ;; soap-decode-type will be called below with:
1865 ;;
1866 ;; element =
1867 ;; <xs:choice>
1868 ;; <xs:element name="C" type="xs:string"/>
1869 ;; <xs:element name="D" type="t:DType"/>
1870 ;; </xs:choice>
1871 ;; node =
1872 ;; <t:A>
1873 ;; <t:B tag="b"/>
1874 ;; <t:C>1</C>
1875 ;; </t:A>
1876 (list node)))
1877 (element-type (soap-xs-element-type element)))
1878 (dolist (node children)
1879 (incf instance-count)
1880 (let* ((attributes
1881 (soap-decode-xs-attributes element-type node))
1882 ;; Attributes may specify xsi:type override.
1883 (element-type
1884 (if (soap-xml-get-attribute-or-nil1 node 'xsi:type)
1885 (soap-wsdl-get
1886 (soap-l2fq
1887 (soap-xml-get-attribute-or-nil1 node
1888 'xsi:type))
1889 soap-current-wsdl 'soap-xs-type-p t)
1890 element-type))
1891 (decoded-child (soap-decode-type element-type node)))
1892 (if e-name
1893 (push (cons (intern e-name)
1894 (append attributes decoded-child)) result)
1895 ;; When e-name is nil we don't want to introduce an extra
1896 ;; level of nesting, so we splice the decoding into
1897 ;; result.
1898 (setq result (append decoded-child result)))))
1899 (cond ((and (eq (soap-xs-complex-type-indicator type) 'choice)
1900 ;; Choices can allow multiple values.
1901 (not (soap-xs-complex-type-multiple-p type))
1902 (> instance-count 0))
1903 ;; This was a choice node, and we decoded one value.
1904 (throw 'done t))
1905
1906 ;; Do some sanity checking
1907 ((and (not (eq (soap-xs-complex-type-indicator type)
1908 'choice))
1909 (= instance-count 0)
1910 (not (soap-xs-element-optional? element))
1911 (and (soap-xs-complex-type-p element-type)
1912 (not (soap-xs-complex-type-optional-p
1913 element-type))))
1914 (soap-warning "missing non-nillable slot %s" e-name))
1915 ((and (> instance-count 1)
1916 (not (soap-xs-complex-type-multiple-p type))
1917 (not (soap-xs-element-multiple? element))
1918 (and (soap-xs-complex-type-p element-type)
1919 (not (soap-xs-complex-type-multiple-p
1920 element-type))))
1921 (soap-warning "expected single %s slot, found multiple"
1922 e-name))))))
1923 (nreverse result)))
1924 (t
1925 (error "Don't know how to decode complex type: %s"
1926 (soap-xs-complex-type-indicator type)))))
1927
1928 ;; Register methods for `soap-xs-complex-type'
1929 (let ((tag (aref (make-soap-xs-complex-type) 0)))
1930 (put tag 'soap-resolve-references
1931 #'soap-resolve-references-for-xs-complex-type)
1932 (put tag 'soap-attribute-encoder #'soap-encode-xs-complex-type-attributes)
1933 (put tag 'soap-encoder #'soap-encode-xs-complex-type)
1934 (put tag 'soap-decoder #'soap-decode-xs-complex-type))
1935
1936 ;;;; WSDL documents
1937 ;;;;; WSDL document elements
1938
1939
1940 (defstruct (soap-message (:include soap-element))
1941 parts ; ALIST of NAME => WSDL-TYPE name
1942 )
1943
1944 (defstruct (soap-operation (:include soap-element))
1945 parameter-order
1946 input ; (NAME . MESSAGE)
1947 output ; (NAME . MESSAGE)
1948 faults ; a list of (NAME . MESSAGE)
1949 input-action ; WS-addressing action string
1950 output-action) ; WS-addressing action string
1951
1952 (defstruct (soap-port-type (:include soap-element))
1953 operations) ; a namespace of operations
1954
1955 ;; A bound operation is an operation which has a soap action and a use
1956 ;; method attached -- these are attached as part of a binding and we
1957 ;; can have different bindings for the same operations.
1958 (defstruct soap-bound-operation
1959 operation ; SOAP-OPERATION
1960 soap-action ; value for SOAPAction HTTP header
1961 soap-headers ; list of (message part use)
1962 soap-body ; message parts present in the body
1963 use ; 'literal or 'encoded, see
1964 ; http://www.w3.org/TR/wsdl#_soap:body
1965 )
1966
1967 (defstruct (soap-binding (:include soap-element))
1968 port-type
1969 (operations (make-hash-table :test 'equal) :readonly t))
1970
1971 (defstruct (soap-port (:include soap-element))
1972 service-url
1973 binding)
1974
1975
1976 ;;;;; The WSDL document
1977
1978 ;; The WSDL data structure used for encoding/decoding SOAP messages
1979 (defstruct (soap-wsdl
1980 ;; NOTE: don't call this constructor, see `soap-make-wsdl'
1981 (:constructor soap-make-wsdl^)
1982 (:copier soap-copy-wsdl))
1983 origin ; file or URL from which this wsdl was loaded
1984 current-file ; most-recently fetched file or URL
1985 xmlschema-imports ; a list of schema imports
1986 ports ; a list of SOAP-PORT instances
1987 alias-table ; a list of namespace aliases
1988 namespaces ; a list of namespaces
1989 )
1990
1991 (defun soap-make-wsdl (origin)
1992 "Create a new WSDL document, loaded from ORIGIN, and intialize it."
1993 (let ((wsdl (soap-make-wsdl^ :origin origin)))
1994
1995 ;; Add the XSD types to the wsdl document
1996 (let ((ns (soap-make-xs-basic-types
1997 "http://www.w3.org/2001/XMLSchema" "xsd")))
1998 (soap-wsdl-add-namespace ns wsdl)
1999 (soap-wsdl-add-alias "xsd" (soap-namespace-name ns) wsdl))
2000
2001 ;; Add the soapenc types to the wsdl document
2002 (let ((ns (soap-make-xs-basic-types
2003 "http://schemas.xmlsoap.org/soap/encoding/" "soapenc")))
2004 (soap-wsdl-add-namespace ns wsdl)
2005 (soap-wsdl-add-alias "soapenc" (soap-namespace-name ns) wsdl))
2006
2007 wsdl))
2008
2009 (defun soap-wsdl-add-alias (alias name wsdl)
2010 "Add a namespace ALIAS for NAME to the WSDL document."
2011 (let ((existing (assoc alias (soap-wsdl-alias-table wsdl))))
2012 (if existing
2013 (unless (equal (cdr existing) name)
2014 (warn "Redefining alias %s from %s to %s"
2015 alias (cdr existing) name)
2016 (push (cons alias name) (soap-wsdl-alias-table wsdl)))
2017 (push (cons alias name) (soap-wsdl-alias-table wsdl)))))
2018
2019 (defun soap-wsdl-find-namespace (name wsdl)
2020 "Find a namespace by NAME in the WSDL document."
2021 (catch 'found
2022 (dolist (ns (soap-wsdl-namespaces wsdl))
2023 (when (equal name (soap-namespace-name ns))
2024 (throw 'found ns)))))
2025
2026 (defun soap-wsdl-add-namespace (ns wsdl)
2027 "Add the namespace NS to the WSDL document.
2028 If a namespace by this name already exists in WSDL, individual
2029 elements will be added to it."
2030 (let ((existing (soap-wsdl-find-namespace (soap-namespace-name ns) wsdl)))
2031 (if existing
2032 ;; Add elements from NS to EXISTING, replacing existing values.
2033 (maphash (lambda (_key value)
2034 (dolist (v value)
2035 (soap-namespace-put v existing)))
2036 (soap-namespace-elements ns))
2037 (push ns (soap-wsdl-namespaces wsdl)))))
2038
2039 (defun soap-wsdl-get (name wsdl &optional predicate use-local-alias-table)
2040 "Retrieve element NAME from the WSDL document.
2041
2042 PREDICATE is used to differentiate between elements when NAME
2043 refers to multiple elements. A typical value for this would be a
2044 structure predicate for the type of element you want to retrieve.
2045 For example, to retrieve a message named \"foo\" when other
2046 elements named \"foo\" exist in the WSDL you could use:
2047
2048 (soap-wsdl-get \"foo\" WSDL \\='soap-message-p)
2049
2050 If USE-LOCAL-ALIAS-TABLE is not nil, `soap-local-xmlns' will be
2051 used to resolve the namespace alias."
2052 (let ((alias-table (soap-wsdl-alias-table wsdl))
2053 namespace element-name element)
2054
2055 (when (symbolp name)
2056 (setq name (symbol-name name)))
2057
2058 (when use-local-alias-table
2059 (setq alias-table (append soap-local-xmlns alias-table)))
2060
2061 (cond ((consp name) ; a fully qualified name, as returned by `soap-l2fq'
2062 (setq element-name (cdr name))
2063 (when (symbolp element-name)
2064 (setq element-name (symbol-name element-name)))
2065 (setq namespace (soap-wsdl-find-namespace (car name) wsdl))
2066 (unless namespace
2067 (error "Soap-wsdl-get(%s): unknown namespace: %s" name namespace)))
2068
2069 ((string-match "^\\(.*\\):\\(.*\\)$" name)
2070 (setq element-name (match-string 2 name))
2071
2072 (let* ((ns-alias (match-string 1 name))
2073 (ns-name (cdr (assoc ns-alias alias-table))))
2074 (unless ns-name
2075 (error "Soap-wsdl-get(%s): cannot find namespace alias %s"
2076 name ns-alias))
2077
2078 (setq namespace (soap-wsdl-find-namespace ns-name wsdl))
2079 (unless namespace
2080 (error
2081 "Soap-wsdl-get(%s): unknown namespace %s, referenced as %s"
2082 name ns-name ns-alias))))
2083 (t
2084 (error "Soap-wsdl-get(%s): bad name" name)))
2085
2086 (setq element (soap-namespace-get
2087 element-name namespace
2088 (if predicate
2089 (lambda (e)
2090 (or (funcall 'soap-namespace-link-p e)
2091 (funcall predicate e)))
2092 nil)))
2093
2094 (unless element
2095 (error "Soap-wsdl-get(%s): cannot find element" name))
2096
2097 (if (soap-namespace-link-p element)
2098 ;; NOTE: don't use the local alias table here
2099 (soap-wsdl-get (soap-namespace-link-target element) wsdl predicate)
2100 element)))
2101
2102 ;;;;; soap-parse-schema
2103
2104 (defun soap-parse-schema (node wsdl)
2105 "Parse a schema NODE, placing the results in WSDL.
2106 Return a SOAP-NAMESPACE containing the elements."
2107 (soap-with-local-xmlns node
2108 (assert (eq (soap-l2wk (xml-node-name node)) 'xsd:schema)
2109 nil
2110 "expecting an xsd:schema node, got %s"
2111 (soap-l2wk (xml-node-name node)))
2112
2113 (let ((ns (make-soap-namespace :name (soap-get-target-namespace node))))
2114
2115 (dolist (def (xml-node-children node))
2116 (unless (stringp def) ; skip text nodes
2117 (case (soap-l2wk (xml-node-name def))
2118 (xsd:import
2119 ;; Imports will be processed later
2120 ;; NOTE: we should expand the location now!
2121 (let ((location (or
2122 (xml-get-attribute-or-nil def 'schemaLocation)
2123 (xml-get-attribute-or-nil def 'location))))
2124 (when location
2125 (push location (soap-wsdl-xmlschema-imports wsdl)))))
2126 (xsd:element
2127 (soap-namespace-put (soap-xs-parse-element def) ns))
2128 (xsd:attribute
2129 (soap-namespace-put (soap-xs-parse-attribute def) ns))
2130 (xsd:attributeGroup
2131 (soap-namespace-put (soap-xs-parse-attribute-group def) ns))
2132 (xsd:simpleType
2133 (soap-namespace-put (soap-xs-parse-simple-type def) ns))
2134 ((xsd:complexType xsd:group)
2135 (soap-namespace-put (soap-xs-parse-complex-type def) ns)))))
2136 ns)))
2137
2138 ;;;;; Resolving references for wsdl types
2139
2140 ;; See `soap-wsdl-resolve-references', which is the main entry point for
2141 ;; resolving references
2142
2143 (defun soap-resolve-references (element wsdl)
2144 "Replace names in ELEMENT with the referenced objects in the WSDL.
2145 This is a generic function which invokes a specific resolver
2146 function depending on the type of the ELEMENT.
2147
2148 If ELEMENT has no resolver function, it is silently ignored."
2149 (let ((resolver (get (aref element 0) 'soap-resolve-references)))
2150 (when resolver
2151 (funcall resolver element wsdl))))
2152
2153 (defun soap-resolve-references-for-message (message wsdl)
2154 "Replace names in MESSAGE with the referenced objects in the WSDL.
2155 This is a generic function, called by `soap-resolve-references',
2156 you should use that function instead.
2157
2158 See also `soap-wsdl-resolve-references'."
2159 (let (resolved-parts)
2160 (dolist (part (soap-message-parts message))
2161 (let ((name (car part))
2162 (element (cdr part)))
2163 (when (stringp name)
2164 (setq name (intern name)))
2165 (if (soap-name-p element)
2166 (setq element (soap-wsdl-get
2167 element wsdl
2168 (lambda (x)
2169 (or (soap-xs-type-p x) (soap-xs-element-p x)))))
2170 ;; else, inline element, resolve recursively, as the element
2171 ;; won't be reached.
2172 (soap-resolve-references element wsdl)
2173 (unless (soap-element-namespace-tag element)
2174 (setf (soap-element-namespace-tag element)
2175 (soap-element-namespace-tag message))))
2176 (push (cons name element) resolved-parts)))
2177 (setf (soap-message-parts message) (nreverse resolved-parts))))
2178
2179 (defun soap-resolve-references-for-operation (operation wsdl)
2180 "Resolve references for an OPERATION type using the WSDL document.
2181 See also `soap-resolve-references' and
2182 `soap-wsdl-resolve-references'"
2183
2184 (let ((namespace (soap-element-namespace-tag operation)))
2185 (when namespace
2186 (let ((nstag (car (rassoc namespace (soap-wsdl-alias-table wsdl)))))
2187 (when nstag
2188 (setf (soap-element-namespace-tag operation) nstag)))))
2189
2190 (let ((input (soap-operation-input operation))
2191 (counter 0))
2192 (let ((name (car input))
2193 (message (cdr input)))
2194 ;; Name this part if it was not named
2195 (when (or (null name) (equal name ""))
2196 (setq name (format "in%d" (incf counter))))
2197 (when (soap-name-p message)
2198 (setf (soap-operation-input operation)
2199 (cons (intern name)
2200 (soap-wsdl-get message wsdl 'soap-message-p))))))
2201
2202 (let ((output (soap-operation-output operation))
2203 (counter 0))
2204 (let ((name (car output))
2205 (message (cdr output)))
2206 (when (or (null name) (equal name ""))
2207 (setq name (format "out%d" (incf counter))))
2208 (when (soap-name-p message)
2209 (setf (soap-operation-output operation)
2210 (cons (intern name)
2211 (soap-wsdl-get message wsdl 'soap-message-p))))))
2212
2213 (let ((resolved-faults nil)
2214 (counter 0))
2215 (dolist (fault (soap-operation-faults operation))
2216 (let ((name (car fault))
2217 (message (cdr fault)))
2218 (when (or (null name) (equal name ""))
2219 (setq name (format "fault%d" (incf counter))))
2220 (if (soap-name-p message)
2221 (push (cons (intern name)
2222 (soap-wsdl-get message wsdl 'soap-message-p))
2223 resolved-faults)
2224 (push fault resolved-faults))))
2225 (setf (soap-operation-faults operation) resolved-faults))
2226
2227 (when (= (length (soap-operation-parameter-order operation)) 0)
2228 (setf (soap-operation-parameter-order operation)
2229 (mapcar 'car (soap-message-parts
2230 (cdr (soap-operation-input operation))))))
2231
2232 (setf (soap-operation-parameter-order operation)
2233 (mapcar (lambda (p)
2234 (if (stringp p)
2235 (intern p)
2236 p))
2237 (soap-operation-parameter-order operation))))
2238
2239 (defun soap-resolve-references-for-binding (binding wsdl)
2240 "Resolve references for a BINDING type using the WSDL document.
2241 See also `soap-resolve-references' and
2242 `soap-wsdl-resolve-references'"
2243 (when (soap-name-p (soap-binding-port-type binding))
2244 (setf (soap-binding-port-type binding)
2245 (soap-wsdl-get (soap-binding-port-type binding)
2246 wsdl 'soap-port-type-p)))
2247
2248 (let ((port-ops (soap-port-type-operations (soap-binding-port-type binding))))
2249 (maphash (lambda (k v)
2250 (setf (soap-bound-operation-operation v)
2251 (soap-namespace-get k port-ops 'soap-operation-p))
2252 (let (resolved-headers)
2253 (dolist (h (soap-bound-operation-soap-headers v))
2254 (push (list (soap-wsdl-get (nth 0 h) wsdl)
2255 (intern (nth 1 h))
2256 (nth 2 h))
2257 resolved-headers))
2258 (setf (soap-bound-operation-soap-headers v)
2259 (nreverse resolved-headers))))
2260 (soap-binding-operations binding))))
2261
2262 (defun soap-resolve-references-for-port (port wsdl)
2263 "Replace names in PORT with the referenced objects in the WSDL.
2264 This is a generic function, called by `soap-resolve-references',
2265 you should use that function instead.
2266
2267 See also `soap-wsdl-resolve-references'."
2268 (when (soap-name-p (soap-port-binding port))
2269 (setf (soap-port-binding port)
2270 (soap-wsdl-get (soap-port-binding port) wsdl 'soap-binding-p))))
2271
2272 ;; Install resolvers for our types
2273 (progn
2274 (put (aref (make-soap-message) 0) 'soap-resolve-references
2275 'soap-resolve-references-for-message)
2276 (put (aref (make-soap-operation) 0) 'soap-resolve-references
2277 'soap-resolve-references-for-operation)
2278 (put (aref (make-soap-binding) 0) 'soap-resolve-references
2279 'soap-resolve-references-for-binding)
2280 (put (aref (make-soap-port) 0) 'soap-resolve-references
2281 'soap-resolve-references-for-port))
2282
2283 (defun soap-wsdl-resolve-references (wsdl)
2284 "Resolve all references inside the WSDL structure.
2285
2286 When the WSDL elements are created from the XML document, they
2287 refer to each other by name. For example, the ELEMENT-TYPE slot
2288 of an SOAP-ARRAY-TYPE will contain the name of the element and
2289 the user would have to call `soap-wsdl-get' to obtain the actual
2290 element.
2291
2292 After the entire document is loaded, we resolve all these
2293 references to the actual elements they refer to so that at
2294 runtime, we don't have to call `soap-wsdl-get' each time we
2295 traverse an element tree."
2296 (let ((nprocessed 0)
2297 (nstag-id 0)
2298 (alias-table (soap-wsdl-alias-table wsdl)))
2299 (dolist (ns (soap-wsdl-namespaces wsdl))
2300 (let ((nstag (car-safe (rassoc (soap-namespace-name ns) alias-table))))
2301 (unless nstag
2302 ;; If this namespace does not have an alias, create one for it.
2303 (catch 'done
2304 (while t
2305 (setq nstag (format "ns%d" (incf nstag-id)))
2306 (unless (assoc nstag alias-table)
2307 (soap-wsdl-add-alias nstag (soap-namespace-name ns) wsdl)
2308 (throw 'done t)))))
2309
2310 (maphash (lambda (_name element)
2311 (cond ((soap-element-p element) ; skip links
2312 (incf nprocessed)
2313 (soap-resolve-references element wsdl))
2314 ((listp element)
2315 (dolist (e element)
2316 (when (soap-element-p e)
2317 (incf nprocessed)
2318 (soap-resolve-references e wsdl))))))
2319 (soap-namespace-elements ns)))))
2320 wsdl)
2321
2322 ;;;;; Loading WSDL from XML documents
2323
2324 (defun soap-parse-server-response ()
2325 "Error-check and parse the XML contents of the current buffer."
2326 (let ((mime-part (mm-dissect-buffer t t)))
2327 (unless mime-part
2328 (error "Failed to decode response from server"))
2329 (unless (equal (car (mm-handle-type mime-part)) "text/xml")
2330 (error "Server response is not an XML document"))
2331 (with-temp-buffer
2332 (mm-insert-part mime-part)
2333 (prog1
2334 (car (xml-parse-region (point-min) (point-max)))
2335 (kill-buffer)
2336 (mm-destroy-part mime-part)))))
2337
2338 (defun soap-fetch-xml-from-url (url wsdl)
2339 "Load an XML document from URL and return it.
2340 The previously parsed URL is read from WSDL."
2341 (message "Fetching from %s" url)
2342 (let ((current-file (url-expand-file-name url (soap-wsdl-current-file wsdl)))
2343 (url-request-method "GET")
2344 (url-package-name "soap-client.el")
2345 (url-package-version "1.0")
2346 (url-mime-charset-string "utf-8;q=1, iso-8859-1;q=0.5")
2347 (url-http-attempt-keepalives t))
2348 (setf (soap-wsdl-current-file wsdl) current-file)
2349 (let ((buffer (url-retrieve-synchronously current-file)))
2350 (with-current-buffer buffer
2351 (declare (special url-http-response-status))
2352 (if (> url-http-response-status 299)
2353 (error "Error retrieving WSDL: %s" url-http-response-status))
2354 (soap-parse-server-response)))))
2355
2356 (defun soap-fetch-xml-from-file (file wsdl)
2357 "Load an XML document from FILE and return it.
2358 The previously parsed file is read from WSDL."
2359 (let* ((current-file (soap-wsdl-current-file wsdl))
2360 (expanded-file (expand-file-name file
2361 (if current-file
2362 (file-name-directory current-file)
2363 default-directory))))
2364 (setf (soap-wsdl-current-file wsdl) expanded-file)
2365 (with-temp-buffer
2366 (insert-file-contents expanded-file)
2367 (car (xml-parse-region (point-min) (point-max))))))
2368
2369 (defun soap-fetch-xml (file-or-url wsdl)
2370 "Load an XML document from FILE-OR-URL and return it.
2371 The previously parsed file or URL is read from WSDL."
2372 (let ((current-file (or (soap-wsdl-current-file wsdl) file-or-url)))
2373 (if (or (and current-file (file-exists-p current-file))
2374 (file-exists-p file-or-url))
2375 (soap-fetch-xml-from-file file-or-url wsdl)
2376 (soap-fetch-xml-from-url file-or-url wsdl))))
2377
2378 (defun soap-load-wsdl (file-or-url &optional wsdl)
2379 "Load a document from FILE-OR-URL and return it.
2380 Build on WSDL if it is provided."
2381 (let* ((wsdl (or wsdl (soap-make-wsdl file-or-url)))
2382 (xml (soap-fetch-xml file-or-url wsdl)))
2383 (soap-wsdl-resolve-references (soap-parse-wsdl xml wsdl))
2384 wsdl))
2385
2386 (defalias 'soap-load-wsdl-from-url 'soap-load-wsdl)
2387
2388 (defun soap-parse-wsdl-phase-validate-node (node)
2389 "Assert that NODE is valid."
2390 (soap-with-local-xmlns node
2391 (let ((node-name (soap-l2wk (xml-node-name node))))
2392 (assert (eq node-name 'wsdl:definitions)
2393 nil
2394 "expecting wsdl:definitions node, got %s" node-name))))
2395
2396 (defun soap-parse-wsdl-phase-fetch-imports (node wsdl)
2397 "Fetch and load files imported by NODE into WSDL."
2398 (soap-with-local-xmlns node
2399 (dolist (node (soap-xml-get-children1 node 'wsdl:import))
2400 (let ((location (xml-get-attribute-or-nil node 'location)))
2401 (when location
2402 (soap-load-wsdl location wsdl))))))
2403
2404 (defun soap-parse-wsdl-phase-parse-schema (node wsdl)
2405 "Load types found in NODE into WSDL."
2406 (soap-with-local-xmlns node
2407 ;; Find all the 'xsd:schema nodes which are children of wsdl:types nodes and
2408 ;; build our type-library.
2409 (let ((types (car (soap-xml-get-children1 node 'wsdl:types))))
2410 (dolist (node (xml-node-children types))
2411 ;; We cannot use (xml-get-children node (soap-wk2l 'xsd:schema)) because
2412 ;; each node can install its own alias type so the schema nodes might
2413 ;; have a different prefix.
2414 (when (consp node)
2415 (soap-with-local-xmlns
2416 node
2417 (when (eq (soap-l2wk (xml-node-name node)) 'xsd:schema)
2418 (soap-wsdl-add-namespace (soap-parse-schema node wsdl)
2419 wsdl))))))))
2420
2421 (defun soap-parse-wsdl-phase-fetch-schema (node wsdl)
2422 "Fetch and load schema imports defined by NODE into WSDL."
2423 (soap-with-local-xmlns node
2424 (while (soap-wsdl-xmlschema-imports wsdl)
2425 (let* ((import (pop (soap-wsdl-xmlschema-imports wsdl)))
2426 (xml (soap-fetch-xml import wsdl)))
2427 (soap-wsdl-add-namespace (soap-parse-schema xml wsdl) wsdl)))))
2428
2429 (defun soap-parse-wsdl-phase-finish-parsing (node wsdl)
2430 "Finish parsing NODE into WSDL."
2431 (soap-with-local-xmlns node
2432 (let ((ns (make-soap-namespace :name (soap-get-target-namespace node))))
2433 (dolist (node (soap-xml-get-children1 node 'wsdl:message))
2434 (soap-namespace-put (soap-parse-message node) ns))
2435
2436 (dolist (node (soap-xml-get-children1 node 'wsdl:portType))
2437 (let ((port-type (soap-parse-port-type node)))
2438 (soap-namespace-put port-type ns)
2439 (soap-wsdl-add-namespace
2440 (soap-port-type-operations port-type) wsdl)))
2441
2442 (dolist (node (soap-xml-get-children1 node 'wsdl:binding))
2443 (soap-namespace-put (soap-parse-binding node) ns))
2444
2445 (dolist (node (soap-xml-get-children1 node 'wsdl:service))
2446 (dolist (node (soap-xml-get-children1 node 'wsdl:port))
2447 (let ((name (xml-get-attribute node 'name))
2448 (binding (xml-get-attribute node 'binding))
2449 (url (let ((n (car (soap-xml-get-children1
2450 node 'wsdlsoap:address))))
2451 (xml-get-attribute n 'location))))
2452 (let ((port (make-soap-port
2453 :name name :binding (soap-l2fq binding 'tns)
2454 :service-url url)))
2455 (soap-namespace-put port ns)
2456 (push port (soap-wsdl-ports wsdl))))))
2457
2458 (soap-wsdl-add-namespace ns wsdl))))
2459
2460 (defun soap-parse-wsdl (node wsdl)
2461 "Construct from NODE a WSDL structure, which is an XML document."
2462 ;; Break this into phases to allow for asynchronous parsing.
2463 (soap-parse-wsdl-phase-validate-node node)
2464 ;; Makes synchronous calls.
2465 (soap-parse-wsdl-phase-fetch-imports node wsdl)
2466 (soap-parse-wsdl-phase-parse-schema node wsdl)
2467 ;; Makes synchronous calls.
2468 (soap-parse-wsdl-phase-fetch-schema node wsdl)
2469 (soap-parse-wsdl-phase-finish-parsing node wsdl)
2470 wsdl)
2471
2472 (defun soap-parse-message (node)
2473 "Parse NODE as a wsdl:message and return the corresponding type."
2474 (assert (eq (soap-l2wk (xml-node-name node)) 'wsdl:message)
2475 nil
2476 "expecting wsdl:message node, got %s"
2477 (soap-l2wk (xml-node-name node)))
2478 (let ((name (xml-get-attribute-or-nil node 'name))
2479 parts)
2480 (dolist (p (soap-xml-get-children1 node 'wsdl:part))
2481 (let ((name (xml-get-attribute-or-nil p 'name))
2482 (type (xml-get-attribute-or-nil p 'type))
2483 (element (xml-get-attribute-or-nil p 'element)))
2484
2485 (when type
2486 (setq type (soap-l2fq type 'tns)))
2487
2488 (if element
2489 (setq element (soap-l2fq element 'tns))
2490 ;; else
2491 (setq element (make-soap-xs-element
2492 :name name
2493 :namespace-tag soap-target-xmlns
2494 :type^ type)))
2495
2496 (push (cons name element) parts)))
2497 (make-soap-message :name name :parts (nreverse parts))))
2498
2499 (defun soap-parse-port-type (node)
2500 "Parse NODE as a wsdl:portType and return the corresponding port."
2501 (assert (eq (soap-l2wk (xml-node-name node)) 'wsdl:portType)
2502 nil
2503 "expecting wsdl:portType node got %s"
2504 (soap-l2wk (xml-node-name node)))
2505 (let* ((soap-target-xmlns (concat "urn:" (xml-get-attribute node 'name)))
2506 (ns (make-soap-namespace :name soap-target-xmlns)))
2507 (dolist (node (soap-xml-get-children1 node 'wsdl:operation))
2508 (let ((o (soap-parse-operation node)))
2509
2510 (let ((other-operation (soap-namespace-get
2511 (soap-element-name o) ns 'soap-operation-p)))
2512 (if other-operation
2513 ;; Unfortunately, the Confluence WSDL defines two operations
2514 ;; named "search" which differ only in parameter names...
2515 (soap-warning "Discarding duplicate operation: %s"
2516 (soap-element-name o))
2517
2518 (progn
2519 (soap-namespace-put o ns)
2520
2521 ;; link all messages from this namespace, as this namespace
2522 ;; will be used for decoding the response.
2523 (destructuring-bind (name . message) (soap-operation-input o)
2524 (soap-namespace-put-link name message ns))
2525
2526 (destructuring-bind (name . message) (soap-operation-output o)
2527 (soap-namespace-put-link name message ns))
2528
2529 (dolist (fault (soap-operation-faults o))
2530 (destructuring-bind (name . message) fault
2531 (soap-namespace-put-link name message ns)))
2532
2533 )))))
2534
2535 (make-soap-port-type :name (xml-get-attribute node 'name)
2536 :operations ns)))
2537
2538 (defun soap-parse-operation (node)
2539 "Parse NODE as a wsdl:operation and return the corresponding type."
2540 (assert (eq (soap-l2wk (xml-node-name node)) 'wsdl:operation)
2541 nil
2542 "expecting wsdl:operation node, got %s"
2543 (soap-l2wk (xml-node-name node)))
2544 (let ((name (xml-get-attribute node 'name))
2545 (parameter-order (split-string
2546 (xml-get-attribute node 'parameterOrder)))
2547 input output faults input-action output-action)
2548 (dolist (n (xml-node-children node))
2549 (when (consp n) ; skip string nodes which are whitespace
2550 (let ((node-name (soap-l2wk (xml-node-name n))))
2551 (cond
2552 ((eq node-name 'wsdl:input)
2553 (let ((message (xml-get-attribute n 'message))
2554 (name (xml-get-attribute n 'name))
2555 (action (soap-xml-get-attribute-or-nil1 n 'wsaw:Action)))
2556 (setq input (cons name (soap-l2fq message 'tns)))
2557 (setq input-action action)))
2558 ((eq node-name 'wsdl:output)
2559 (let ((message (xml-get-attribute n 'message))
2560 (name (xml-get-attribute n 'name))
2561 (action (soap-xml-get-attribute-or-nil1 n 'wsaw:Action)))
2562 (setq output (cons name (soap-l2fq message 'tns)))
2563 (setq output-action action)))
2564 ((eq node-name 'wsdl:fault)
2565 (let ((message (xml-get-attribute n 'message))
2566 (name (xml-get-attribute n 'name)))
2567 (push (cons name (soap-l2fq message 'tns)) faults)))))))
2568 (make-soap-operation
2569 :name name
2570 :namespace-tag soap-target-xmlns
2571 :parameter-order parameter-order
2572 :input input
2573 :output output
2574 :faults (nreverse faults)
2575 :input-action input-action
2576 :output-action output-action)))
2577
2578 (defun soap-parse-binding (node)
2579 "Parse NODE as a wsdl:binding and return the corresponding type."
2580 (assert (eq (soap-l2wk (xml-node-name node)) 'wsdl:binding)
2581 nil
2582 "expecting wsdl:binding node, got %s"
2583 (soap-l2wk (xml-node-name node)))
2584 (let ((name (xml-get-attribute node 'name))
2585 (type (xml-get-attribute node 'type)))
2586 (let ((binding (make-soap-binding :name name
2587 :port-type (soap-l2fq type 'tns))))
2588 (dolist (wo (soap-xml-get-children1 node 'wsdl:operation))
2589 (let ((name (xml-get-attribute wo 'name))
2590 soap-action
2591 soap-headers
2592 soap-body
2593 use)
2594 (dolist (so (soap-xml-get-children1 wo 'wsdlsoap:operation))
2595 (setq soap-action (xml-get-attribute-or-nil so 'soapAction)))
2596
2597 ;; Search a wsdlsoap:body node and find a "use" tag. The
2598 ;; same use tag is assumed to be present for both input and
2599 ;; output types (although the WDSL spec allows separate
2600 ;; "use"-s for each of them...
2601
2602 (dolist (i (soap-xml-get-children1 wo 'wsdl:input))
2603
2604 ;; There can be multiple headers ...
2605 (dolist (h (soap-xml-get-children1 i 'wsdlsoap:header))
2606 (let ((message (soap-l2fq (xml-get-attribute-or-nil h 'message)))
2607 (part (xml-get-attribute-or-nil h 'part))
2608 (use (xml-get-attribute-or-nil h 'use)))
2609 (when (and message part)
2610 (push (list message part use) soap-headers))))
2611
2612 ;; ... but only one body
2613 (let ((body (car (soap-xml-get-children1 i 'wsdlsoap:body))))
2614 (setq soap-body (xml-get-attribute-or-nil body 'parts))
2615 (when soap-body
2616 (setq soap-body
2617 (mapcar #'intern (split-string soap-body
2618 nil
2619 'omit-nulls))))
2620 (setq use (xml-get-attribute-or-nil body 'use))))
2621
2622 (unless use
2623 (dolist (i (soap-xml-get-children1 wo 'wsdl:output))
2624 (dolist (b (soap-xml-get-children1 i 'wsdlsoap:body))
2625 (setq use (or use
2626 (xml-get-attribute-or-nil b 'use))))))
2627
2628 (puthash name (make-soap-bound-operation
2629 :operation name
2630 :soap-action soap-action
2631 :soap-headers (nreverse soap-headers)
2632 :soap-body soap-body
2633 :use (and use (intern use)))
2634 (soap-binding-operations binding))))
2635 binding)))
2636
2637 ;;;; SOAP type decoding
2638
2639 (defvar soap-multi-refs nil
2640 "The list of multi-ref nodes in the current SOAP response.
2641 This is a dynamically bound variable used during decoding the
2642 SOAP response.")
2643
2644 (defvar soap-decoded-multi-refs nil
2645 "List of decoded multi-ref nodes in the current SOAP response.
2646 This is a dynamically bound variable used during decoding the
2647 SOAP response.")
2648
2649 (defun soap-decode-type (type node)
2650 "Use TYPE (an xsd type) to decode the contents of NODE.
2651
2652 NODE is an XML node, representing some SOAP encoded value or a
2653 reference to another XML node (a multiRef). This function will
2654 resolve the multiRef reference, if any, than call a TYPE specific
2655 decode function to perform the actual decoding."
2656 (let ((href (xml-get-attribute-or-nil node 'href)))
2657 (cond (href
2658 (catch 'done
2659 ;; NODE is actually a HREF, find the target and decode that.
2660 ;; Check first if we already decoded this multiref.
2661
2662 (let ((decoded (cdr (assoc href soap-decoded-multi-refs))))
2663 (when decoded
2664 (throw 'done decoded)))
2665
2666 (unless (string-match "^#\\(.*\\)$" href)
2667 (error "Invalid multiRef: %s" href))
2668
2669 (let ((id (match-string 1 href)))
2670 (dolist (mr soap-multi-refs)
2671 (let ((mrid (xml-get-attribute mr 'id)))
2672 (when (equal id mrid)
2673 ;; recurse here, in case there are multiple HREF's
2674 (let ((decoded (soap-decode-type type mr)))
2675 (push (cons href decoded) soap-decoded-multi-refs)
2676 (throw 'done decoded)))))
2677 (error "Cannot find href %s" href))))
2678 (t
2679 (soap-with-local-xmlns node
2680 (if (equal (soap-xml-get-attribute-or-nil1 node 'xsi:nil) "true")
2681 nil
2682 ;; Handle union types.
2683 (cond ((listp type)
2684 (catch 'done
2685 (dolist (union-member type)
2686 (let* ((decoder (get (aref union-member 0)
2687 'soap-decoder))
2688 (result (ignore-errors
2689 (funcall decoder
2690 union-member node))))
2691 (when result (throw 'done result))))))
2692 (t
2693 (let ((decoder (get (aref type 0) 'soap-decoder)))
2694 (assert decoder nil
2695 "no soap-decoder for %s type" (aref type 0))
2696 (funcall decoder type node))))))))))
2697
2698 (defun soap-decode-any-type (node)
2699 "Decode NODE using type information inside it."
2700 ;; If the NODE has type information, we use that...
2701 (let ((type (soap-xml-get-attribute-or-nil1 node 'xsi:type)))
2702 (when type
2703 (setq type (soap-l2fq type)))
2704 (if type
2705 (let ((wtype (soap-wsdl-get type soap-current-wsdl 'soap-xs-type-p)))
2706 (if wtype
2707 (soap-decode-type wtype node)
2708 ;; The node has type info encoded in it, but we don't know how
2709 ;; to decode it...
2710 (error "Node has unknown type: %s" type)))
2711
2712 ;; No type info in the node...
2713
2714 (let ((contents (xml-node-children node)))
2715 (if (and (= (length contents) 1) (stringp (car contents)))
2716 ;; contents is just a string
2717 (car contents)
2718
2719 ;; we assume the NODE is a sequence with every element a
2720 ;; structure name
2721 (let (result)
2722 (dolist (element contents)
2723 ;; skip any string contents, assume they are whitespace
2724 (unless (stringp element)
2725 (let ((key (xml-node-name element))
2726 (value (soap-decode-any-type element)))
2727 (push (cons key value) result))))
2728 (nreverse result)))))))
2729
2730 (defun soap-decode-array (node)
2731 "Decode NODE as an Array using type information inside it."
2732 (let ((type (soap-xml-get-attribute-or-nil1 node 'soapenc:arrayType))
2733 (wtype nil)
2734 (contents (xml-node-children node))
2735 result)
2736 (when type
2737 ;; Type is in the format "someType[NUM]" where NUM is the number of
2738 ;; elements in the array. We discard the [NUM] part.
2739 (setq type (replace-regexp-in-string "\\[[0-9]+\\]\\'" "" type))
2740 (setq wtype (soap-wsdl-get (soap-l2fq type)
2741 soap-current-wsdl 'soap-xs-type-p))
2742 (unless wtype
2743 ;; The node has type info encoded in it, but we don't know how to
2744 ;; decode it...
2745 (error "Soap-decode-array: node has unknown type: %s" type)))
2746 (dolist (e contents)
2747 (when (consp e)
2748 (push (if wtype
2749 (soap-decode-type wtype e)
2750 (soap-decode-any-type e))
2751 result)))
2752 (nreverse result)))
2753
2754 ;;;; Soap Envelope parsing
2755
2756 (define-error 'soap-error "SOAP error")
2757
2758 (defun soap-parse-envelope (node operation wsdl)
2759 "Parse the SOAP envelope in NODE and return the response.
2760 OPERATION is the WSDL operation for which we expect the response,
2761 WSDL is used to decode the NODE"
2762 (soap-with-local-xmlns node
2763 (assert (eq (soap-l2wk (xml-node-name node)) 'soap:Envelope)
2764 nil
2765 "expecting soap:Envelope node, got %s"
2766 (soap-l2wk (xml-node-name node)))
2767 (let ((headers (soap-xml-get-children1 node 'soap:Header))
2768 (body (car (soap-xml-get-children1 node 'soap:Body))))
2769
2770 (let ((fault (car (soap-xml-get-children1 body 'soap:Fault))))
2771 (when fault
2772 (let ((fault-code (let ((n (car (xml-get-children
2773 fault 'faultcode))))
2774 (car-safe (xml-node-children n))))
2775 (fault-string (let ((n (car (xml-get-children
2776 fault 'faultstring))))
2777 (car-safe (xml-node-children n))))
2778 (detail (xml-get-children fault 'detail)))
2779 (while t
2780 (signal 'soap-error (list fault-code fault-string detail))))))
2781
2782 ;; First (non string) element of the body is the root node of he
2783 ;; response
2784 (let ((response (if (eq (soap-bound-operation-use operation) 'literal)
2785 ;; For 'literal uses, the response is the actual body
2786 body
2787 ;; ...otherwise the first non string element
2788 ;; of the body is the response
2789 (catch 'found
2790 (dolist (n (xml-node-children body))
2791 (when (consp n)
2792 (throw 'found n)))))))
2793 (soap-parse-response response operation wsdl headers body)))))
2794
2795 (defun soap-parse-response (response-node operation wsdl soap-headers soap-body)
2796 "Parse RESPONSE-NODE and return the result as a LISP value.
2797 OPERATION is the WSDL operation for which we expect the response,
2798 WSDL is used to decode the NODE.
2799
2800 SOAP-HEADERS is a list of the headers of the SOAP envelope or nil
2801 if there are no headers.
2802
2803 SOAP-BODY is the body of the SOAP envelope (of which
2804 RESPONSE-NODE is a sub-node). It is used in case RESPONSE-NODE
2805 reference multiRef parts which are external to RESPONSE-NODE."
2806 (let* ((soap-current-wsdl wsdl)
2807 (op (soap-bound-operation-operation operation))
2808 (use (soap-bound-operation-use operation))
2809 (message (cdr (soap-operation-output op))))
2810
2811 (soap-with-local-xmlns response-node
2812
2813 (when (eq use 'encoded)
2814 (let* ((received-message-name (soap-l2fq (xml-node-name response-node)))
2815 (received-message (soap-wsdl-get
2816 received-message-name wsdl 'soap-message-p)))
2817 (unless (eq received-message message)
2818 (error "Unexpected message: got %s, expecting %s"
2819 received-message-name
2820 (soap-element-name message)))))
2821
2822 (let ((decoded-parts nil)
2823 (soap-multi-refs (xml-get-children soap-body 'multiRef))
2824 (soap-decoded-multi-refs nil))
2825
2826 (dolist (part (soap-message-parts message))
2827 (let ((tag (car part))
2828 (type (cdr part))
2829 node)
2830
2831 (setq node
2832 (cond
2833 ((eq use 'encoded)
2834 (car (xml-get-children response-node tag)))
2835
2836 ((eq use 'literal)
2837 (catch 'found
2838 (let* ((ns-aliases (soap-wsdl-alias-table wsdl))
2839 (ns-name (cdr (assoc
2840 (soap-element-namespace-tag type)
2841 ns-aliases)))
2842 (fqname (cons ns-name (soap-element-name type))))
2843 (dolist (c (append (mapcar (lambda (header)
2844 (car (xml-node-children
2845 header)))
2846 soap-headers)
2847 (xml-node-children response-node)))
2848 (when (consp c)
2849 (soap-with-local-xmlns c
2850 (when (equal (soap-l2fq (xml-node-name c))
2851 fqname)
2852 (throw 'found c))))))))))
2853
2854 (unless node
2855 (error "Soap-parse-response(%s): cannot find message part %s"
2856 (soap-element-name op) tag))
2857 (let ((decoded-value (soap-decode-type type node)))
2858 (when decoded-value
2859 (push decoded-value decoded-parts)))))
2860
2861 decoded-parts))))
2862
2863 ;;;; SOAP type encoding
2864
2865 (defun soap-encode-attributes (value type)
2866 "Encode XML attributes for VALUE according to TYPE.
2867 This is a generic function which determines the attribute encoder
2868 for the type and calls that specialized function to do the work.
2869
2870 Attributes are inserted in the current buffer at the current
2871 position."
2872 (let ((attribute-encoder (get (aref type 0) 'soap-attribute-encoder)))
2873 (assert attribute-encoder nil
2874 "no soap-attribute-encoder for %s type" (aref type 0))
2875 (funcall attribute-encoder value type)))
2876
2877 (defun soap-encode-value (value type)
2878 "Encode the VALUE using TYPE.
2879 The resulting XML data is inserted in the current buffer
2880 at (point)/
2881
2882 TYPE is one of the soap-*-type structures which defines how VALUE
2883 is to be encoded. This is a generic function which finds an
2884 encoder function based on TYPE and calls that encoder to do the
2885 work."
2886 (let ((encoder (get (aref type 0) 'soap-encoder)))
2887 (assert encoder nil "no soap-encoder for %s type" (aref type 0))
2888 (funcall encoder value type))
2889 (when (soap-element-namespace-tag type)
2890 (add-to-list 'soap-encoded-namespaces (soap-element-namespace-tag type))))
2891
2892 (defun soap-encode-body (operation parameters &optional service-url)
2893 "Create the body of a SOAP request for OPERATION in the current buffer.
2894 PARAMETERS is a list of parameters supplied to the OPERATION.
2895
2896 The OPERATION and PARAMETERS are encoded according to the WSDL
2897 document. SERVICE-URL should be provided when WS-Addressing is
2898 being used."
2899 (let* ((op (soap-bound-operation-operation operation))
2900 (use (soap-bound-operation-use operation))
2901 (message (cdr (soap-operation-input op)))
2902 (parameter-order (soap-operation-parameter-order op))
2903 (param-table (loop for formal in parameter-order
2904 for value in parameters
2905 collect (cons formal value))))
2906
2907 (unless (= (length parameter-order) (length parameters))
2908 (error "Wrong number of parameters for %s: expected %d, got %s"
2909 (soap-element-name op)
2910 (length parameter-order)
2911 (length parameters)))
2912
2913 (let ((headers (soap-bound-operation-soap-headers operation))
2914 (input-action (soap-operation-input-action op)))
2915 (when headers
2916 (insert "<soap:Header>\n")
2917 (when input-action
2918 (add-to-list 'soap-encoded-namespaces "wsa")
2919 (insert "<wsa:Action>" input-action "</wsa:Action>\n")
2920 (insert "<wsa:To>" service-url "</wsa:To>\n"))
2921 (dolist (h headers)
2922 (let* ((message (nth 0 h))
2923 (part (assq (nth 1 h) (soap-message-parts message)))
2924 (value (cdr (assoc (car part) (car parameters))))
2925 (use (nth 2 h))
2926 (element (cdr part)))
2927 (when (eq use 'encoded)
2928 (when (soap-element-namespace-tag element)
2929 (add-to-list 'soap-encoded-namespaces
2930 (soap-element-namespace-tag element)))
2931 (insert "<" (soap-element-fq-name element) ">\n"))
2932 (soap-encode-value value element)
2933 (when (eq use 'encoded)
2934 (insert "</" (soap-element-fq-name element) ">\n"))))
2935 (insert "</soap:Header>\n")))
2936
2937 (insert "<soap:Body>\n")
2938 (when (eq use 'encoded)
2939 (when (soap-element-namespace-tag op)
2940 (add-to-list 'soap-encoded-namespaces (soap-element-namespace-tag op)))
2941 (insert "<" (soap-element-fq-name op) ">\n"))
2942
2943 (dolist (part (soap-message-parts message))
2944 (let* ((param-name (car part))
2945 (element (cdr part))
2946 (value (cdr (assoc param-name param-table))))
2947 (when (or (null (soap-bound-operation-soap-body operation))
2948 (member param-name
2949 (soap-bound-operation-soap-body operation)))
2950 (soap-encode-value value element))))
2951
2952 (when (eq use 'encoded)
2953 (insert "</" (soap-element-fq-name op) ">\n"))
2954 (insert "</soap:Body>\n")))
2955
2956 (defun soap-create-envelope (operation parameters wsdl &optional service-url)
2957 "Create a SOAP request envelope for OPERATION using PARAMETERS.
2958 WSDL is the wsdl document used to encode the PARAMETERS.
2959 SERVICE-URL should be provided when WS-Addressing is being used."
2960 (with-temp-buffer
2961 (let ((soap-encoded-namespaces '("xsi" "soap" "soapenc"))
2962 (use (soap-bound-operation-use operation)))
2963
2964 ;; Create the request body
2965 (soap-encode-body operation parameters service-url)
2966
2967 ;; Put the envelope around the body
2968 (goto-char (point-min))
2969 (insert "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<soap:Envelope\n")
2970 (when (eq use 'encoded)
2971 (insert " soapenc:encodingStyle=\"\
2972 http://schemas.xmlsoap.org/soap/encoding/\"\n"))
2973 (dolist (nstag soap-encoded-namespaces)
2974 (insert " xmlns:" nstag "=\"")
2975 (let ((nsname (cdr (assoc nstag soap-well-known-xmlns))))
2976 (unless nsname
2977 (setq nsname (cdr (assoc nstag (soap-wsdl-alias-table wsdl)))))
2978 (insert nsname)
2979 (insert "\"\n")))
2980 (insert ">\n")
2981 (goto-char (point-max))
2982 (insert "</soap:Envelope>\n"))
2983
2984 (buffer-string)))
2985
2986 ;;;; invoking soap methods
2987
2988 (defcustom soap-debug nil
2989 "When t, enable some debugging facilities."
2990 :type 'boolean
2991 :group 'soap-client)
2992
2993 (defun soap-invoke-internal (callback cbargs wsdl service operation-name
2994 &rest parameters)
2995 "Implement `soap-invoke' and `soap-invoke-async'.
2996 If CALLBACK is non-nil, operate asynchronously, then call CALLBACK as (apply
2997 CALLBACK RESPONSE CBARGS), where RESPONSE is the SOAP invocation result.
2998 If CALLBACK is nil, operate synchronously. WSDL, SERVICE,
2999 OPERATION-NAME and PARAMETERS are as described in `soap-invoke'."
3000 (let ((port (catch 'found
3001 (dolist (p (soap-wsdl-ports wsdl))
3002 (when (equal service (soap-element-name p))
3003 (throw 'found p))))))
3004 (unless port
3005 (error "Unknown SOAP service: %s" service))
3006
3007 (let* ((binding (soap-port-binding port))
3008 (operation (gethash operation-name
3009 (soap-binding-operations binding))))
3010 (unless operation
3011 (error "No operation %s for SOAP service %s" operation-name service))
3012
3013 (let ((url-request-method "POST")
3014 (url-package-name "soap-client.el")
3015 (url-package-version "1.0")
3016 (url-request-data
3017 ;; url-request-data expects a unibyte string already encoded...
3018 (encode-coding-string
3019 (soap-create-envelope operation parameters wsdl
3020 (soap-port-service-url port))
3021 'utf-8))
3022 (url-mime-charset-string "utf-8;q=1, iso-8859-1;q=0.5")
3023 (url-http-attempt-keepalives t)
3024 (url-request-extra-headers
3025 (list
3026 (cons "SOAPAction"
3027 (concat "\"" (soap-bound-operation-soap-action
3028 operation) "\""))
3029 (cons "Content-Type"
3030 "text/xml; charset=utf-8"))))
3031 (if callback
3032 (url-retrieve
3033 (soap-port-service-url port)
3034 (lambda (status)
3035 (let ((data-buffer (current-buffer)))
3036 (unwind-protect
3037 (let ((error-status (plist-get status :error)))
3038 (if error-status
3039 (signal (car error-status) (cdr error-status))
3040 (apply callback
3041 (soap-parse-envelope
3042 (soap-parse-server-response)
3043 operation wsdl)
3044 cbargs)))
3045 ;; Ensure the url-retrieve buffer is not leaked.
3046 (and (buffer-live-p data-buffer)
3047 (kill-buffer data-buffer))))))
3048 (let ((buffer (url-retrieve-synchronously
3049 (soap-port-service-url port))))
3050 (condition-case err
3051 (with-current-buffer buffer
3052 (declare (special url-http-response-status))
3053 (if (null url-http-response-status)
3054 (error "No HTTP response from server"))
3055 (if (and soap-debug (> url-http-response-status 299))
3056 ;; This is a warning because some SOAP errors come
3057 ;; back with a HTTP response 500 (internal server
3058 ;; error)
3059 (warn "Error in SOAP response: HTTP code %s"
3060 url-http-response-status))
3061 (soap-parse-envelope (soap-parse-server-response)
3062 operation wsdl))
3063 (soap-error
3064 ;; Propagate soap-errors -- they are error replies of the
3065 ;; SOAP protocol and don't indicate a communication
3066 ;; problem or a bug in this code.
3067 (signal (car err) (cdr err)))
3068 (error
3069 (when soap-debug
3070 (pop-to-buffer buffer))
3071 (error (error-message-string err))))))))))
3072
3073 (defun soap-invoke (wsdl service operation-name &rest parameters)
3074 "Invoke a SOAP operation and return the result.
3075
3076 WSDL is used for encoding the request and decoding the response.
3077 It also contains information about the WEB server address that
3078 will service the request.
3079
3080 SERVICE is the SOAP service to invoke.
3081
3082 OPERATION-NAME is the operation to invoke.
3083
3084 PARAMETERS -- the remaining parameters are used as parameters for
3085 the SOAP request.
3086
3087 NOTE: The SOAP service provider should document the available
3088 operations and their parameters for the service. You can also
3089 use the `soap-inspect' function to browse the available
3090 operations in a WSDL document."
3091 (apply #'soap-invoke-internal nil nil wsdl service operation-name parameters))
3092
3093 (defun soap-invoke-async (callback cbargs wsdl service operation-name
3094 &rest parameters)
3095 "Like `soap-invoke', but call CALLBACK asynchronously with response.
3096 CALLBACK is called as (apply CALLBACK RESPONSE CBARGS), where
3097 RESPONSE is the SOAP invocation result. WSDL, SERVICE,
3098 OPERATION-NAME and PARAMETERS are as described in `soap-invoke'."
3099 (unless callback
3100 (error "Callback argument is nil"))
3101 (apply #'soap-invoke-internal callback cbargs wsdl service operation-name
3102 parameters))
3103
3104 (provide 'soap-client)
3105
3106 \f
3107 ;; Local Variables:
3108 ;; eval: (outline-minor-mode 1)
3109 ;; outline-regexp: ";;;;+"
3110 ;; End:
3111
3112 ;;; soap-client.el ends here