;; @module xmlrpc-client.lsp
;; @description XMLRPC protocol client routines
;; @version 0.3 - comments redone for automatic documentation
;; @version 0.4 - multiple indices with nth redone to be compatible with future versions
;; @version 0.5 - doc changes
;; @version 0.6 - fixed bug in error handler
;; @version 0.61 - fixed doc typo
;; @version 0.7 - check for valid list type in (get-value expr) thanks Kosh
;; @version 0.8 - changed references to /usr/ to /usr/local/
;; @version 0.9 - make system.listMethods more flexible for no of args - thanks Ofoe
;; @author Lutz Mueller 2005-2011, Kosh 2012, Oofoe 2016
;;
;;
Functions for XML-RPC client
;; To use this module include a 'load' statement at the beginning of the program:
;;
;; (load "/usr/local/share/newlisp/modules/xmlrpc-client.lsp")
;; ; or shorter
;; (module "xmlrpc-client.lsp")
;;
;; The script 'xmlrpc.cgi' implements a method 'newLISP.evalString'. This module contains
;; a client side function for this method for testing purposes. The file 'xmlrpc.cgi'
;; can be found in the 'example' directory of the newLISP source distribution.
;;
;; For further information on XML-RPC consult
;; @link http://www.xmlrpc.com/ http://www.xmlrpc.com/ .
;;
;; Whenever a connection could be made, method functions will return a response
;; formatted by the XML-RPC server in XML. If a connection failed the function will
;; return 'nil' and a call to '(XMLRPC:error)' will return an error text.
;;
;; If the XML received cannot be parsed into SXML, the function returns 'nil'
;; and '(XMLRPC:error)' will return an XML error. SXML is XML transformed into
;; LISP S-expressions.
;; If the XML received is syntactically correct but not correctly formatted,
;; XML garbage is returned or 'nil' is returned and an error message in
;; '(XMLRPC:error)'.
;; @syntax (XMLRPC:system.listMethods )
;; @param The URL of the XML-RPC server
;; @return A list of methods supported.
;; The server at returns a list of methods supported.
;; @syntax (XMLRPC:system.methodHelp )
;; @param The URL of the XML-RPC server.
;; @param The name of the method to get help for.
;; @return Help for
;; The server at returns help for the method in
;; @syntax (XMLRPC:system.methodSignatures )
;; @param The URL of the XML-RPC server.
;; @param The name of the method to get the signature for.
;; @return The signature for a server method.
;; Gets the calling parameter conventions (signature) for a method
;; at .
;; @syntax (XMLRPC:execute )
;; @param The URL of the XML-RPC server.
;; @param A XML formatted request.
;; @return XML formatted server response
;; This is a generic method for making XML-RPC requests.
;; The request must be XML formatted correctly by the sender (client).
;; @syntax (XMLRPC:newLISP.evalString )
;; @param The URL of the XML-RPC server.
;; @param The expresssion to be evaluated in a string.
;; @return The result of the expression evaluation.
;; The expression in is encoded in base64 and then
;; transmitted to the remote server.
;; @syntax (XMLRPC:error)
;; @return Error text of last error occured.
(context 'XMLRPC)
(set 'request
[text]
%s
%s
[/text])
(set 'error-msg "")
######### extract value(s) from XML-RPC response XML with #############
; get result data from result structure
;
(define (get-result-data xml)
(if (starts-with xml "ERR:")
(begin
(set 'error-msg xml)
(throw nil)))
(xml-type-tags nil nil nil nil)
(set 'sxml (xml-parse xml (+ 1 2 4)))
(if (not sxml) (throw (format "XML error: %s" (first (xml-error)))))
(if (match '(("methodResponse" ("fault" *))) sxml)
(begin
(set 'error-msg
(let (fault (sxml 0 1 1 1 1 2 1 1)
text (sxml 0 1 1 1 2 2 1 1))
(append "Fault " fault ": " text)))
(throw nil)))
(get-value (sxml 0 1 1 1)))
; get contents from expr = (value ...)
;
(define (get-value expr)
(if
(empty? expr) nil
(list? (expr 1))
(case (expr 1 0)
("i4" (int (expr 1 1)))
("int" (int (expr 1 1)))
("boolean" (if (= "0" (expr 1 1) ) nil true))
("double" (float (expr 1 1)))
("base64" (base64-dec (expr 1 1)))
("dateTime.iso8601" (expr 1 1))
("array" (if (= (expr 1) "array")
"array" ;; if untagged string "array"
(get-array (rest (expr 1 1)))) )
("struct" (get-struct (rest (expr 1))))
("string" (expr 1 1))
(true (expr 1))
) ; end case
true (string (expr 1))
) ; end if
)
; get contents from expr = ((value ...) (value ...) ...)
;
(define (get-array expr)
(if (empty? expr)
'()
(cons (get-value (first expr)) (get-array (rest expr)))))
; get contents from expr = ((member ...) (member) ...)
;
(define (get-struct expr)
(if (empty? expr)
'()
(cons (get-member (first expr)) (get-struct (rest expr)))))
; get contents from expr = (member ...)
;
(define (get-member expr)
(list (expr 1 1) (get-value (last expr))))
################################ standard system methods #######################
# convert to SXML
(xml-type-tags nil nil nil nil)
# ( method /arg.../ -- XML) Compose XML request.
(define (format-request method)
(let ((xml (format
"%s"
method)))
(dolist (value (args))
(push (format "%s" value) xml -1))
(push "\n" xml -1)))
# return method names in a list of strings
#
# (XMLRPC:system.listMethods )
#
(define (system.listMethods url)
(execute url (format-request "system.listMethods")))
# get help for a methodName at url
# return help in a string
#
# (XMLRPC:system.methodHelp )
#
(define (system.methodSignature url methodName)
(execute url (format request "system.methodSignature" methodName) ))
(define (error) error-msg)
# Execute a method on url with XML formatted request
#
# This is a generic method, but with XML formatted by caller.
#
# (XMLRPC:execute )
#
(define (execute url parameter-XML)
(if (not (catch (begin
(set 'error-msg "")
(set 'xml (post-url url parameter-XML "text/xml"))
(get-result-data xml)) 'result))
(begin (set 'error-msg "Wrong format in XML-RPC") nil)
result))
######################### newLISP XML-RPC specific methods #####################
# evaluate a newLISP expression in str at newLISP XML-RPC server at url
# return evaluation result in a string
#
(define (newLISP.evalString url str)
(execute url
(format request
"newLISP.evalString" (append "" (base64-enc str) "")))
)
(context MAIN)
# eof