diff options
Diffstat (limited to 'third-party/s-xml-rpc')
-rw-r--r-- | third-party/s-xml-rpc/.clbuild-skip-update | 0 | ||||
-rw-r--r-- | third-party/s-xml-rpc/ChangeLog | 63 | ||||
-rw-r--r-- | third-party/s-xml-rpc/Makefile | 33 | ||||
-rw-r--r-- | third-party/s-xml-rpc/s-xml-rpc.asd | 32 | ||||
-rw-r--r-- | third-party/s-xml-rpc/src/aserve.lisp | 79 | ||||
-rw-r--r-- | third-party/s-xml-rpc/src/define-xmlrpc-method.lisp | 30 | ||||
-rw-r--r-- | third-party/s-xml-rpc/src/extensions.lisp | 107 | ||||
-rw-r--r-- | third-party/s-xml-rpc/src/package.lisp | 49 | ||||
-rw-r--r-- | third-party/s-xml-rpc/src/validator1-client.lisp | 182 | ||||
-rw-r--r-- | third-party/s-xml-rpc/src/validator1-server.lisp | 90 | ||||
-rw-r--r-- | third-party/s-xml-rpc/src/xml-rpc.lisp | 586 | ||||
-rw-r--r-- | third-party/s-xml-rpc/test/all-tests.lisp | 17 | ||||
-rw-r--r-- | third-party/s-xml-rpc/test/test-base64.lisp | 123 | ||||
-rw-r--r-- | third-party/s-xml-rpc/test/test-extensions.lisp | 53 | ||||
-rw-r--r-- | third-party/s-xml-rpc/test/test-xml-rpc.lisp | 176 | ||||
-rw-r--r-- | third-party/s-xml-rpc/test/test.b64 | 1 |
16 files changed, 1621 insertions, 0 deletions
diff --git a/third-party/s-xml-rpc/.clbuild-skip-update b/third-party/s-xml-rpc/.clbuild-skip-update new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/third-party/s-xml-rpc/.clbuild-skip-update diff --git a/third-party/s-xml-rpc/ChangeLog b/third-party/s-xml-rpc/ChangeLog new file mode 100644 index 0000000..d6b2ab1 --- /dev/null +++ b/third-party/s-xml-rpc/ChangeLog @@ -0,0 +1,63 @@ +2006-04-19 Sven Van Caekenberghe <svc@mac.com> + + * changes due to reporting and initial fixes by Alain Picard + * added support for whitespace handling + * iso8601->universal-time now accepts leading & trailing whitespace + * encode-xml-rpc-value now encodes t and nil correctly as boolean 1 and 0 + * parsing doubles (using read-from-string) with reader macros disabled for security + * decode-xml-rpc now handles whitespace more correctly in <data> and <value> tags + * added several test cases and fixed older stop-server problem + +2005-02-11 + + * ported to clisp 2.32 (sysdeps) + * changed end-of-header test to accept empty lines as well + * changed usage to princ to write-string where possible + * fixed a test (added import, unintern code to/from s-xml-rpc-exports) + +2005-01-22 Sven Van Caekenberghe <svc@mac.com> + + * fixed a performance issue in base64 decoding + +2004-10-26 Rudi Schlatte <rudi@constantly.at> + + * src/sysdeps.lisp (with-open-socket-stream, run-process) + (start-standard-server, stop-server): Port to cmucl. + +2004-06-17 Rudi Schlatte <rudi@constantly.at> + + * src/package.lisp: Add system.getCapabilities. + + * src/extensions.lisp: Create, move server extensions from + xml-rpc.lisp here. + (do-one-multicall): Raise standard fault codes. + (|system.getCapabilities|): Implement. + + * src/xml-rpc.lisp: Remove server extensions. + (encode-xml-rpc-value): Encode symbols as strings + (execute-xml-rpc-call, handle-xml-rpc-call): Raise standard fault + codes. + +2004-06-13 Rudi Schlatte <rudi@constantly.at> + + * src/xml-rpc.lisp (xml-literal): new datatype for unescaped + strings (used by system.multicall to pass back encoded fault structs) + (encode-xml-rpc-value): handle it. + (encode-xml-rpc-fault-value, encode-xml-rpc-fault): separate + encoding of fault and methodResponse for system.multicall + (do-one-multicall, |system.multicall|): Implement system.multicall. + + * src/package.lisp (s-xml-rpc-exports): New package -- don't + export the whole common-lisp package by default ;) + + * src/xml-rpc.lisp (*xml-rpc-package*): ... use it. + + * src/xml-rpc.lisp (|system.listMethods|) + (|system.methodSignature|, |system.methodHelp|): Added + introspection methods, to be imported in *xml-rpc-package*. + + * src/package.lisp (s-xml-rpc): ... export them, and also + |system.multicall| + + * src/xml-rpc.lisp: Some indentation frobs. + diff --git a/third-party/s-xml-rpc/Makefile b/third-party/s-xml-rpc/Makefile new file mode 100644 index 0000000..2c79e22 --- /dev/null +++ b/third-party/s-xml-rpc/Makefile @@ -0,0 +1,33 @@ +default: + @echo Possible targets: + @echo clean-openmcl --- remove all '*.dfsl' recursively + @echo clean-lw --- remove all '*.nfasl' recursively + @echo clean-emacs --- remove all '*~' recursively + @echo clean --- all of the above + +clean-openmcl: + find . -name "*.dfsl" | xargs rm + +clean-lw: + find . -name "*.nfasl" | xargs rm + +clean-emacs: + find . -name "*~" | xargs rm + +clean: clean-openmcl clean-lw clean-emacs + +# +# This can obviously only be done by a specific person in a very specific context ;-) +# + +PRJ=s-xml-rpc +ACCOUNT=scaekenberghe +CVSRT=:ext:$(ACCOUNT)@common-lisp.net:/project/$(PRJ)/cvsroot + +release: + rm -rf /tmp/$(PRJ) /tmp/public_html /tmp/$(PRJ).tgz /tmp/$(PRJ).tgz.asc + cd /tmp; cvs -d$(CVSRT) export -r HEAD $(PRJ); cvs -d$(CVSRT) export -r HEAD public_html + mv /tmp/public_html /tmp/$(PRJ)/doc + cd /tmp; gnutar cvfz $(PRJ).tgz $(PRJ); gpg -a -b $(PRJ).tgz + scp /tmp/$(PRJ).tgz $(ACCOUNT)@common-lisp.net:/project/$(PRJ)/public_html + scp /tmp/$(PRJ).tgz.asc $(ACCOUNT)@common-lisp.net:/project/$(PRJ)/public_html diff --git a/third-party/s-xml-rpc/s-xml-rpc.asd b/third-party/s-xml-rpc/s-xml-rpc.asd new file mode 100644 index 0000000..8ff2538 --- /dev/null +++ b/third-party/s-xml-rpc/s-xml-rpc.asd @@ -0,0 +1,32 @@ +;;;; -*- Mode: LISP -*- +;;;; +;;;; $Id: s-xml-rpc.asd,v 1.3 2006-01-09 19:33:47 scaekenberghe Exp $ +;;;; +;;;; The S-XML-RPC ASDF system definition +;;;; +;;;; Copyright (C) 2002, 2004 Sven Van Caekenberghe, Beta Nine BVBA. +;;;; +;;;; You are granted the rights to distribute and use this software +;;;; as governed by the terms of the Lisp Lesser General Public License +;;;; (http://opensource.franz.com/preamble.html), also known as the LLGPL. + +(in-package :asdf) + +(defsystem :s-xml-rpc + :name "S-XML-RPC" + :author "Sven Van Caekenberghe <svc@mac.com>" + :version "7" + :maintainer "Sven Van Caekenberghe <svc@mac.com>, Brian Mastenbrook <>, Rudi Schlatte <>" + :licence "Lesser Lisp General Public License (LLGPL)" + :description "Common Lisp XML-RPC Package" + :long-description "s-xml-rpc is a Common Lisp implementation of the XML-RPC procotol for both client and server" + + :components + ((:module + :src + :components ((:file "package") + (:file "xml-rpc" :depends-on ("package")) + (:file "extensions" :depends-on ("package" "xml-rpc"))))) + :depends-on (:s-xml :s-sysdeps :s-base64)) + +;;;; eof diff --git a/third-party/s-xml-rpc/src/aserve.lisp b/third-party/s-xml-rpc/src/aserve.lisp new file mode 100644 index 0000000..ecd9073 --- /dev/null +++ b/third-party/s-xml-rpc/src/aserve.lisp @@ -0,0 +1,79 @@ +;;;; -*- mode: lisp -*- +;;;; +;;;; $Id: aserve.lisp,v 1.1.1.1 2004-06-09 09:02:39 scaekenberghe Exp $ +;;;; +;;;; This file implements XML-RPC client and server networking based +;;;; on (Portable) AllegroServe (see http://opensource.franz.com/aserve/ +;;;; or http://sourceforge.net/projects/portableaserve/), which you have +;;;; to install first. +;;;; +;;;; Copyright (C) 2002, 2004 Sven Van Caekenberghe. +;;;; +;;;; You are granted the rights to distribute and use this software +;;;; as governed by the terms of the Lisp Lesser GNU Public License +;;;; (http://opensource.franz.com/preamble.html), also known as the LLGPL. + +(defpackage xml-rpc-aserve + (:use common-lisp net.aserve.client net.aserve xml-rpc) + (:export + "XML-RPC-CALL" + "START-XML-RPC-ASERVE" + "PUBLISH-ASERVE-XML-RPC-HANDLER")) + +(in-package :xml-rpc-aserve) + +(defun xml-rpc-call-aserve (encoded &key + (url *xml-rpc-url*) + (agent *xml-rpc-agent*) + (host *xml-rpc-host*) + (port *xml-rpc-port*) + (basic-autorization *xml-rpc-authorization*) + (proxy)) + (let ((xml (print-xml-string encoded))) + (multiple-value-bind (response response-code headers uri) + (do-http-request + (format nil "http://~a:~d~a" host port url) + :method :post + :protocol :http/1.0 + :user-agent agent + :content-type "text/xml" + :basic-authorization basic-autorization + :content xml + :proxy proxy) + (declare (ignore headers uri)) + (if (= response-code 200) + (let ((result (decode-xml-rpc (make-string-input-stream response)))) + (if (typep result 'xml-rpc-fault) + (error result) + (car result))) + (error "http-error:~d" response-code))))) + +(defun start-xml-rpc-aserve (&key (port *xml-rpc-port*)) + (process-run-function "aserve-xml-rpc" + #'(lambda () + (start :port port + :listeners 4 + :chunking nil + :keep-alive nil)))) + +(defun publish-aserve-xml-rpc-handler (&key (url *xml-rpc-url*) (agent *xml-rpc-agent*)) + (declare (ignore agent)) + (publish :path url + :content-type "text/xml" + :function #'aserve-xml-rpc-handler)) + +(defun aserve-xml-rpc-handler (request entity) + (with-http-response (request + entity + :response (if (eq :post (request-method request)) + *response-ok* + *response-bad-request*)) + (with-http-body (request entity) + (let ((body (get-request-body request)) + (id (process-name *current-process*))) + (with-input-from-string (in body) + (let ((xml (handle-xml-rpc-call in id))) + (format-debug t "~d sending ~a~%" id xml) + (princ xml *html-stream*))))))) + +;;;; eof diff --git a/third-party/s-xml-rpc/src/define-xmlrpc-method.lisp b/third-party/s-xml-rpc/src/define-xmlrpc-method.lisp new file mode 100644 index 0000000..74a3bc3 --- /dev/null +++ b/third-party/s-xml-rpc/src/define-xmlrpc-method.lisp @@ -0,0 +1,30 @@ +;;;; -*- mode: lisp -*- +;;;; +;;;; $Id: define-xmlrpc-method.lisp,v 1.1 2004-07-08 19:45:25 scaekenberghe Exp $ +;;;; +;;;; The code in this file adds a very handly define-xmlrpc-method macro. +;;;; +;;;; (define-xmlrpc-method get-state-name (state) +;;;; :url #u"http://betty.userland.com/RPC2" +;;;; :method "examples.getStateName") +;;;; +;;;; (define-xmlrpc-method get-time () +;;;; :url #u"http://time.xmlrpc.com/RPC2" +;;;; :method "currentTime.getCurrentTime") +;;;; +;;;; It require the PURI package. +;;;; +;;;; Copyright (C) 2004 Frederic Brunel. +;;;; +;;;; You are granted the rights to distribute and use this software +;;;; as governed by the terms of the Lisp Lesser GNU Public License +;;;; (http://opensource.franz.com/preamble.html), also known as the LLGPL. + +(defmacro define-xmlrpc-method (name args &key url method) + `(defun ,name ,args + (xml-rpc-call (encode-xml-rpc-call ,method ,@args) + :url ,(puri:uri-path url) + :host ,(puri:uri-host url) + :port ,(cond ((puri:uri-port url)) (t 80))))) + +;;;; eof diff --git a/third-party/s-xml-rpc/src/extensions.lisp b/third-party/s-xml-rpc/src/extensions.lisp new file mode 100644 index 0000000..fa961e2 --- /dev/null +++ b/third-party/s-xml-rpc/src/extensions.lisp @@ -0,0 +1,107 @@ +;;;; -*- mode: lisp -*- +;;;; +;;;; $Id: extensions.lisp,v 1.1 2004-06-17 19:43:11 rschlatte Exp $ +;;;; +;;;; Extensions for xml-rpc: +;;;; +;;;; Server introspection: +;;;; http://xmlrpc.usefulinc.com/doc/reserved.html +;;;; +;;;; Multicall: +;;;; http://www.xmlrpc.com/discuss/msgReader$1208 +;;;; +;;;; Capabilities: +;;;; http://groups.yahoo.com/group/xml-rpc/message/2897 +;;;; +;;;; +;;;; Copyright (C) 2004 Rudi Schlatte +;;;; +;;;; You are granted the rights to distribute and use this software +;;;; as governed by the terms of the Lisp Lesser General Public License +;;;; (http://opensource.franz.com/preamble.html), also known as the LLGPL. + +(in-package :s-xml-rpc) + +;;; Introspection + +(defun |system.listMethods| () + "List the methods that are available on this server." + (let ((result nil)) + (do-symbols (sym *xml-rpc-package* (sort result #'string-lessp)) + (when (and (fboundp sym) (valid-xml-rpc-method-name-p (symbol-name sym))) + (push (symbol-name sym) result))))) + +(defun |system.methodSignature| (method-name) + "Dummy system.methodSignature implementation. There's no way + to get (and no concept of) required argument types in Lisp, so + this function always returns nil or errors." + (let ((method (find-xml-rpc-method method-name))) + (if method + ;; http://xmlrpc.usefulinc.com/doc/sysmethodsig.html says to + ;; return a non-array if the signature is not available + "n/a" + (error "Method ~A not found." method-name)))) + +(defun |system.methodHelp| (method-name) + "Returns the function documentation for the given method." + (let ((method (find-xml-rpc-method method-name))) + (if method + (or (documentation method 'function) "") + (error "Method ~A not found." method-name)))) + +;;; system.multicall + +(defun do-one-multicall (call-struct) + (let ((name (get-xml-rpc-struct-member call-struct :|methodName|)) + (params (get-xml-rpc-struct-member call-struct :|params|))) + (handler-bind + ((xml-rpc-fault + #'(lambda (c) + (format-debug (or *xml-rpc-debug-stream* t) + "Call to ~A in system.multicall failed with ~a~%" + name c) + (return-from do-one-multicall + (xml-literal + (encode-xml-rpc-fault-value (xml-rpc-fault-string c) + (xml-rpc-fault-code c)))))) + (error + #'(lambda (c) + (format-debug + (or *xml-rpc-debug-stream* t) + "Call to ~A in system.multicall failed with ~a~%" name c) + (return-from do-one-multicall + (xml-literal + (encode-xml-rpc-fault-value + ;; -32603 ---> server error. internal xml-rpc error + (format nil "~a" c) -32603)))))) + (format-debug (or *xml-rpc-debug-stream* t) + "system.multicall calling ~a with ~s~%" name params) + (let ((result (apply *xml-rpc-call-hook* name params))) + (list result))))) + +(defun |system.multicall| (calls) + "Implement system.multicall; see http://www.xmlrpc.com/discuss/msgReader$1208 + for the specification." + (mapcar #'do-one-multicall calls)) + +;;; system.getCapabilities + +(defun |system.getCapabilities| () + "Get a list of supported capabilities; see + http://groups.yahoo.com/group/xml-rpc/message/2897 for the + specification." + (let ((capabilities + '("xmlrpc" ("specUrl" "http://www.xmlrpc.com/spec" + "specVersion" 1) + "introspect" ("specUrl" "http://xmlrpc.usefulinc.com/doc/reserved.html" + "specVersion" 1) + "multicall" ("specUrl" "http://www.xmlrpc.com/discuss/msgReader$1208" + "specVersion" 1) + "faults_interop" ("specUrl" "http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php" + "specVersion" 20010516)))) + (apply #'xml-rpc-struct + (loop for (name description) on capabilities by #'cddr + collecting name + collecting (apply #'xml-rpc-struct description))))) + +;;;; eof diff --git a/third-party/s-xml-rpc/src/package.lisp b/third-party/s-xml-rpc/src/package.lisp new file mode 100644 index 0000000..e3d2568 --- /dev/null +++ b/third-party/s-xml-rpc/src/package.lisp @@ -0,0 +1,49 @@ +;;;; -*- mode: lisp -*- +;;;; +;;;; $Id: package.lisp,v 1.4 2004-06-17 19:43:11 rschlatte Exp $ +;;;; +;;;; S-XML-RPC package definition +;;;; +;;;; Copyright (C) 2002, 2004 Sven Van Caekenberghe, Beta Nine BVBA. +;;;; +;;;; You are granted the rights to distribute and use this software +;;;; as governed by the terms of the Lisp Lesser GNU Public License +;;;; (http://opensource.franz.com/preamble.html), also known as the LLGPL. + +(defpackage s-xml-rpc + (:use + common-lisp + #+ccl ccl + #+lispworks mp + #+lispworks comm + s-xml + s-base64) + (:export + #:xml-rpc-call + #:encode-xml-rpc-call + #:call-xml-rpc-server + #:xml-rpc-condition + #:xml-rpc-fault #:xml-rpc-fault-code #:xml-rpc-fault-string + #:xml-rpc-error #:xml-rpc-error-place #:xml-rpc-error-data + #:start-xml-rpc-server + #:xml-rpc-time #:xml-rpc-time-p + #:xml-rpc-time-universal-time + #:xml-rpc-struct #:xml-rpc-struct-p + #:xml-rpc-struct-alist #:get-xml-rpc-struct-member #:xml-rpc-struct-equal + #:*xml-rpc-host* #:*xml-rpc-port* #:*xml-rpc-url* #:*xml-rpc-agent* + #:*xml-rpc-proxy-host* #:*xml-rpc-proxy-port* #:*xml-rpc-authorization* + #:*xml-rpc-debug* #:*xml-rpc-debug-stream* + #:*xml-rpc-package* #:*xml-rpc-call-hook* + #:execute-xml-rpc-call #:stop-server + #:|system.listMethods| #:|system.methodSignature| #:|system.methodHelp| + #:|system.multicall| #:|system.getCapabilities|) + (:documentation "An implementation of the standard XML-RPC protocol for both client and server")) + +(defpackage s-xml-rpc-exports + (:use) + (:import-from :s-xml-rpc #:|system.listMethods| #:|system.methodSignature| + #:|system.methodHelp| #:|system.multicall| + #:|system.getCapabilities|) + (:documentation "This package contains the functions callable via xml-rpc.")) + +;;;; eof diff --git a/third-party/s-xml-rpc/src/validator1-client.lisp b/third-party/s-xml-rpc/src/validator1-client.lisp new file mode 100644 index 0000000..8800671 --- /dev/null +++ b/third-party/s-xml-rpc/src/validator1-client.lisp @@ -0,0 +1,182 @@ +;;;; -*- mode: lisp -*- +;;;; +;;;; $Id: validator1-client.lisp,v 1.1 2004-06-14 20:11:55 scaekenberghe Exp $ +;;;; +;;;; This is a Common Lisp implementation of the XML-RPC 'validator1' +;;;; server test suite, as live testable from the website +;;;; http://validator.xmlrpc.com and documented on the web page +;;;; http://www.xmlrpc.com/validator1Docs +;;;; +;;;; Copyright (C) 2002, 2004 Sven Van Caekenberghe, Beta Nine BVBA. +;;;; +;;;; You are granted the rights to distribute and use this software +;;;; as governed by the terms of the Lisp Lesser General Public License +;;;; (http://opensource.franz.com/preamble.html), also known as the LLGPL. + +(in-package :s-xml-rpc) + +(defun random-string (&optional (length 8)) + (with-output-to-string (stream) + (dotimes (i (random length)) + (write-char (code-char (+ 32 (random 95))) + stream)))) + +(defun echo-struct-test () + (let* ((struct (xml-rpc-struct :|foo| (random 1000000) + :|bar| (random-string) + :|fooBar| (list (random 100) (random 100)))) + (result (xml-rpc-call (encode-xml-rpc-call :|validator1.echoStructTest| + struct)))) + (format t "validator1.echoStructTest(~s)=~s~%" struct result) + (assert (xml-rpc-struct-equal struct result)))) + +(defun easy-struct-test () + (let* ((moe (random 1000)) + (larry (random 1000)) + (curry (random 1000)) + (struct (xml-rpc-struct :|moe| moe + :|larry| larry + :|curly| curry)) + (result (xml-rpc-call (encode-xml-rpc-call :|validator1.easyStructTest| + struct)))) + (format t "validator1.easyStructTest(~s)=~s~%" struct result) + (assert (= (+ moe larry curry) result)))) + +(defun count-the-entities () + (let* ((string (random-string 512)) + (left-angle-brackets (count #\< string)) + (right-angle-brackets (count #\> string)) + (apostrophes (count #\' string)) + (quotes (count #\" string)) + (ampersands (count #\& string)) + (result (xml-rpc-call (encode-xml-rpc-call :|validator1.countTheEntities| + string)))) + (format t "validator1.countTheEntitities(~s)=~s~%" string result) + (assert + (and (xml-rpc-struct-p result) + (= left-angle-brackets + (get-xml-rpc-struct-member result :|ctLeftAngleBrackets|)) + (= right-angle-brackets + (get-xml-rpc-struct-member result :|ctRightAngleBrackets|)) + (= apostrophes + (get-xml-rpc-struct-member result :|ctApostrophes|)) + (= quotes + (get-xml-rpc-struct-member result :|ctQuotes|)) + (= ampersands + (get-xml-rpc-struct-member result :|ctAmpersands|)))))) + +(defun array-of-structs-test () + (let ((array (make-array (random 32))) + (sum 0)) + (dotimes (i (length array)) + (setf (aref array i) + (xml-rpc-struct :|moe| (random 1000) + :|larry| (random 1000) + :|curly| (random 1000))) + (incf sum (get-xml-rpc-struct-member (aref array i) + :|curly|))) + (let ((result (xml-rpc-call (encode-xml-rpc-call :|validator1.arrayOfStructsTest| + array)))) + (format t "validator1.arrayOfStructsTest(~s)=~s~%" array result) + (assert (= result sum))))) + +(defun random-bytes (&optional (length 16)) + (let ((bytes (make-array (random length) :element-type '(unsigned-byte 8)))) + (dotimes (i (length bytes) bytes) + (setf (aref bytes i) (random 256))))) + +(defun many-types-test () + (let* ((integer (random 10000)) + (boolean (if (zerop (random 2)) t nil)) + (string (random-string)) + (double (random 10000.0)) + (dateTime (xml-rpc-time)) + (base64 (random-bytes)) + (result (xml-rpc-call (encode-xml-rpc-call :|validator1.manyTypesTest| + integer + boolean + string + double + dateTime + base64)))) + (format t + "validator1.manyTypesTest(~s,~s,~s,~s,~s,~s)=~s~%" + integer + boolean + string + double + dateTime + base64 + result) + (assert (equal integer (elt result 0))) + (assert (equal boolean (elt result 1))) + (assert (equal string (elt result 2))) + (assert (equal double (elt result 3))) + (assert (equal (xml-rpc-time-universal-time dateTime) + (xml-rpc-time-universal-time (elt result 4)))) + (assert (reduce #'(lambda (x y) (and x y)) + (map 'list #'= base64 (elt result 5)) + :initial-value t)))) + +(defun simple-struct-return-test () + (let* ((number (random 1000)) + (result (xml-rpc-call (encode-xml-rpc-call :|validator1.simpleStructReturnTest| number)))) + (format t "validator1.simpleStructReturnTest(~s)=~s~%" number result) + (assert + (and (= (* number 10) + (get-xml-rpc-struct-member result :|times10|)) + (= (* number 100) + (get-xml-rpc-struct-member result :|times100|)) + (= (* number 1000) + (get-xml-rpc-struct-member result :|times1000|)))))) + +(defun moderate-size-array-check () + (let ((array (make-array (+ 100 (random 100)) + :element-type 'string))) + (dotimes (i (length array)) + (setf (aref array i) (random-string))) + (let ((result (xml-rpc-call (encode-xml-rpc-call :|validator1.moderateSizeArrayCheck| + array)))) + (format t "validator1.moderateSizeArrayCheck(~s)=~s~%" array result) + (assert + (equal (concatenate 'string (elt array 0) (elt array (1- (length array)))) + result))))) + +(defun nested-struct-test () + (let* ((moe (random 1000)) + (larry (random 1000)) + (curry (random 1000)) + (struct (xml-rpc-struct :|moe| moe + :|larry| larry + :|curly| curry)) + (first (xml-rpc-struct :\01 struct)) + (april (xml-rpc-struct :\04 first)) + (year (xml-rpc-struct :\2000 april)) + (result (xml-rpc-call (encode-xml-rpc-call :|validator1.nestedStructTest| + year)))) + (format t "validator1.nestedStructTest(~s)=~s~%" year result) + (assert (= (+ moe larry curry) result)))) + +(defun test-run (&optional (runs 1)) + (dotimes (i runs t) + (echo-struct-test) + (easy-struct-test) + (count-the-entities) + (array-of-structs-test) + (many-types-test) + (simple-struct-return-test) + (moderate-size-array-check) + (nested-struct-test))) + +(defun timed-test-run (&optional (runs 1)) + (dotimes (i runs t) + (time (echo-struct-test)) + (time (easy-struct-test)) + (time (count-the-entities)) + (time (array-of-structs-test)) + (time (many-types-test)) + (time (simple-struct-return-test)) + (time (moderate-size-array-check)) + (time (nested-struct-test)))) + +;;;; eof diff --git a/third-party/s-xml-rpc/src/validator1-server.lisp b/third-party/s-xml-rpc/src/validator1-server.lisp new file mode 100644 index 0000000..2833b8d --- /dev/null +++ b/third-party/s-xml-rpc/src/validator1-server.lisp @@ -0,0 +1,90 @@ +;;;; -*- mode: lisp -*- +;;;; +;;;; $Id: validator1-server.lisp,v 1.1 2004-06-14 20:11:55 scaekenberghe Exp $ +;;;; +;;;; This is a Common Lisp implementation of the XML-RPC 'validator1' +;;;; server test suite, as live testable from the website +;;;; http://validator.xmlrpc.com and documented on the web page +;;;; http://www.xmlrpc.com/validator1Docs +;;;; +;;;; Copyright (C) 2002, 2004 Sven Van Caekenberghe, Beta Nine BVBA. +;;;; +;;;; You are granted the rights to distribute and use this software +;;;; as governed by the terms of the Lisp Lesser General Public License +;;;; (http://opensource.franz.com/preamble.html), also known as the LLGPL. + +(in-package :s-xml-rpc) + +(defun |validator1.echoStructTest| (struct) + (assert (xml-rpc-struct-p struct)) + struct) + +(defun |validator1.easyStructTest| (struct) + (assert (xml-rpc-struct-p struct)) + (+ (get-xml-rpc-struct-member struct :|moe|) + (get-xml-rpc-struct-member struct :|larry|) + (get-xml-rpc-struct-member struct :|curly|))) + +(defun |validator1.countTheEntities| (string) + (assert (stringp string)) + (let ((left-angle-brackets (count #\< string)) + (right-angle-brackets (count #\> string)) + (apostrophes (count #\' string)) + (quotes (count #\" string)) + (ampersands (count #\& string))) + (xml-rpc-struct :|ctLeftAngleBrackets| left-angle-brackets + :|ctRightAngleBrackets| right-angle-brackets + :|ctApostrophes| apostrophes + :|ctQuotes| quotes + :|ctAmpersands| ampersands))) + +(defun |validator1.manyTypesTest| (number boolean string double dateTime base64) + (assert + (and (integerp number) + (or (null boolean) (eq boolean t)) + (stringp string) + (floatp double) + (xml-rpc-time-p dateTime) + (and (arrayp base64) + (= (array-rank base64) 1) + (subtypep (array-element-type base64) + '(unsigned-byte 8))))) + (list number boolean string double dateTime base64)) + +(defun |validator1.arrayOfStructsTest| (array) + (assert (listp array)) + (reduce #'+ + (mapcar #'(lambda (struct) + (assert (xml-rpc-struct-p struct)) + (get-xml-rpc-struct-member struct :|curly|)) + array) + :initial-value 0)) + +(defun |validator1.simpleStructReturnTest| (number) + (assert (integerp number)) + (xml-rpc-struct :|times10| (* number 10) + :|times100| (* number 100) + :|times1000| (* number 1000))) + +(defun |validator1.moderateSizeArrayCheck| (array) + (assert (listp array)) + (concatenate 'string (first array) (first (last array)))) + +(defun |validator1.nestedStructTest| (struct) + (assert (xml-rpc-struct-p struct)) + (let* ((year (get-xml-rpc-struct-member struct :\2000)) + (april (get-xml-rpc-struct-member year :\04)) + (first (get-xml-rpc-struct-member april :\01))) + (|validator1.easyStructTest| first))) + +(import '(|validator1.echoStructTest| + |validator1.easyStructTest| + |validator1.countTheEntities| + |validator1.manyTypesTest| + |validator1.arrayOfStructsTest| + |validator1.simpleStructReturnTest| + |validator1.moderateSizeArrayCheck| + |validator1.nestedStructTest|) + :s-xml-rpc-exports) + +;;;; eof diff --git a/third-party/s-xml-rpc/src/xml-rpc.lisp b/third-party/s-xml-rpc/src/xml-rpc.lisp new file mode 100644 index 0000000..b65d2c0 --- /dev/null +++ b/third-party/s-xml-rpc/src/xml-rpc.lisp @@ -0,0 +1,586 @@ +;;;; -*- mode: lisp -*- +;;;; +;;;; $Id: xml-rpc.lisp,v 1.11 2008-02-15 15:42:40 scaekenberghe Exp $ +;;;; +;;;; This is a Common Lisp implementation of the XML-RPC protocol, +;;;; as documented on the website http://www.xmlrpc.com +;;;; This implementation includes both a client and server part. +;;;; A Base64 encoder/decoder and a minimal XML parser are required. +;;;; +;;;; Copyright (C) 2002, 2004 Sven Van Caekenberghe, Beta Nine BVBA. +;;;; +;;;; You are granted the rights to distribute and use this software +;;;; as governed by the terms of the Lisp Lesser General Public License +;;;; (http://opensource.franz.com/preamble.html), also known as the LLGPL. + +(in-package :s-xml-rpc) + +;;; conditions + +(define-condition xml-rpc-condition (error) + () + (:documentation "Parent condition for all conditions thrown by the XML-RPC package")) + +(define-condition xml-rpc-fault (xml-rpc-condition) + ((code :initarg :code :reader xml-rpc-fault-code) + (string :initarg :string :reader xml-rpc-fault-string)) + (:report (lambda (condition stream) + (format stream + "XML-RPC fault with message '~a' and code ~d." + (xml-rpc-fault-string condition) + (xml-rpc-fault-code condition)))) + (:documentation "This condition is thrown when the XML-RPC server returns a fault")) + +(setf (documentation 'xml-rpc-fault-code 'function) "Get the code from an XML-RPC fault") +(setf (documentation 'xml-rpc-fault-string 'function) "Get the string from an XML-RPC fault") + +(define-condition xml-rpc-error (xml-rpc-condition) + ((place :initarg :code :reader xml-rpc-error-place) + (data :initarg :data :reader xml-rpc-error-data)) + (:report (lambda (condition stream) + (format stream + "XML-RPC error ~a at ~a." + (xml-rpc-error-data condition) + (xml-rpc-error-place condition)))) + (:documentation "This condition is thrown when an XML-RPC protocol error occurs")) + +(setf (documentation 'xml-rpc-error-place 'function) + "Get the place from an XML-RPC error" + (documentation 'xml-rpc-error-data 'function) + "Get the data from an XML-RPC error") + +;;; whitespace handling support + +(defparameter +whitespace-characters+ + '(#\Tab #\Space #\Page #\Return #\Newline #\Linefeed) + "The list of characters that we consider as whitespace") + +(defun whitespace-char? (char) + "Return t when char is considered whitespace" + (member char +whitespace-characters+ :test #'char=)) + +(defun whitespace-string? (str) + "Return t when str consists of nothing but whitespace characters" + (every #'whitespace-char? str)) + +;;; iso8601 support (the xml-rpc variant) + +(defun universal-time->iso8601 (time &optional (stream nil)) + "Convert a Common Lisp universal time to a string in the XML-RPC variant of ISO8601" + (multiple-value-bind (second minute hour date month year) + (decode-universal-time time) + (format stream + "~d~2,'0d~2,'0dT~2,'0d:~2,'0d:~2,'0d" + year + month + date + hour + minute + second))) + +(defun iso8601->universal-time (string) + "Convert string in the XML-RPC variant of ISO8601 to a Common Lisp universal time" + (let (year month date (hour 0) (minute 0) (second 0)) + (setf string (string-trim +whitespace-characters+ string)) + (when (< (length string) 9) + (error "~s is to short to represent an iso8601" string)) + (setf year (parse-integer string :start 0 :end 4) + month (parse-integer string :start 4 :end 6) + date (parse-integer string :start 6 :end 8)) + (when (and (>= (length string) 17) (char= #\T (char string 8))) + (setf hour (parse-integer string :start 9 :end 11) + minute (parse-integer string :start 12 :end 14) + second (parse-integer string :start 15 :end 17))) + (encode-universal-time second minute hour date month year))) + +(defstruct (xml-rpc-time (:print-function print-xml-rpc-time)) + "A wrapper around a Common Lisp universal time to be interpreted as an XML-RPC-TIME" + universal-time) + +(setf (documentation 'xml-rpc-time-p 'function) + "Return T when the argument is an XML-RPC time" + (documentation 'xml-rpc-time-universal-time 'function) + "Return the universal time from an XML-RPC time") + +(defun print-xml-rpc-time (xml-rpc-time stream depth) + (declare (ignore depth)) + (format stream + "#<XML-RPC-TIME ~a>" + (universal-time->iso8601 (xml-rpc-time-universal-time xml-rpc-time)))) + +(defun xml-rpc-time (&optional (universal-time (get-universal-time))) + "Create a new XML-RPC-TIME struct with the universal time specified, defaulting to now" + (make-xml-rpc-time :universal-time universal-time)) + +;;; a wrapper for literal strings, where escaping #\< and #\& is not +;;; desired + +(defstruct (xml-literal (:print-function print-xml-literal)) + "A wrapper around a Common Lisp string that will be sent over + the wire unescaped" + content) + +(setf (documentation 'xml-literal-p 'function) + "Return T when the argument is an unescaped xml string" + (documentation 'xml-literal-content 'function) + "Return the content of a literal xml string") + +(defun print-xml-literal (xml-literal stream depth) + (declare (ignore depth)) + (format stream + "#<XML-LITERAL \"~a\" >" + (xml-literal-content xml-literal))) + +(defun xml-literal (content) + "Create a new XML-LITERAL struct with the specified content." + (make-xml-literal :content content)) + +;;; an extra datatype for xml-rpc structures (associative maps) + +(defstruct (xml-rpc-struct (:print-function print-xml-rpc-struct)) + "An XML-RPC-STRUCT is an associative map of member names and values" + alist) + +(setf (documentation 'xml-rpc-struct-p 'function) + "Return T when the argument is an XML-RPC struct" + (documentation 'xml-rpc-struct-alist 'function) + "Return the alist of member names and values from an XML-RPC struct") + +(defun print-xml-rpc-struct (xml-element stream depth) + (declare (ignore depth)) + (format stream "#<XML-RPC-STRUCT~{ ~S~}>" (xml-rpc-struct-alist xml-element))) + +(defun get-xml-rpc-struct-member (struct member) + "Get the value of a specific member of an XML-RPC-STRUCT" + (cdr (assoc member (xml-rpc-struct-alist struct)))) + +(defun (setf get-xml-rpc-struct-member) (value struct member) + "Set the value of a specific member of an XML-RPC-STRUCT" + (let ((pair (assoc member (xml-rpc-struct-alist struct)))) + (if pair + (rplacd pair value) + (push (cons member value) (xml-rpc-struct-alist struct))) + value)) + +(defun xml-rpc-struct (&rest args) + "Create a new XML-RPC-STRUCT from the arguments: alternating member names and values" + (unless (evenp (length args)) + (error "~s must contain an even number of elements" args)) + (let (alist) + (loop + (if (null args) + (return) + (push (cons (pop args) (pop args)) alist))) + (make-xml-rpc-struct :alist alist))) + +(defun xml-rpc-struct-equal (struct1 struct2) + "Compare two XML-RPC-STRUCTs for equality" + (if (and (xml-rpc-struct-p struct1) + (xml-rpc-struct-p struct2) + (= (length (xml-rpc-struct-alist struct1)) + (length (xml-rpc-struct-alist struct2)))) + (dolist (assoc (xml-rpc-struct-alist struct1) t) + (unless (equal (get-xml-rpc-struct-member struct2 (car assoc)) + (cdr assoc)) + (return-from xml-rpc-struct-equal nil))) + nil)) + +;;; encoding support + +(defun encode-xml-rpc-struct (struct stream) + (write-string "<struct>" stream) + (dolist (member (xml-rpc-struct-alist struct)) + (write-string "<member>" stream) + (format stream "<name>~a</name>" (car member)) ; assuming name contains no special characters + (encode-xml-rpc-value (cdr member) stream) + (write-string "</member>" stream)) + (write-string "</struct>" stream)) + +(defun encode-xml-rpc-array (sequence stream) + (write-string "<array><data>" stream) + (map 'nil #'(lambda (element) (encode-xml-rpc-value element stream)) sequence) + (write-string "</data></array>" stream)) + +(defun encode-xml-rpc-value (arg stream) + (write-string "<value>" stream) + (cond ((or (null arg) (eql arg t)) + (write-string "<boolean>" stream) + (write-string (if arg "1" "0") stream) + (write-string "</boolean>" stream)) + ((or (stringp arg) (symbolp arg)) + (write-string "<string>" stream) + (print-string-xml (string arg) stream) + (write-string "</string>" stream)) + ((integerp arg) (format stream "<int>~d</int>" arg)) + ((floatp arg) (format stream "<double>~f</double>" arg)) + ((and (arrayp arg) + (= (array-rank arg) 1) + (subtypep (array-element-type arg) + '(unsigned-byte 8))) + (write-string "<base64>" stream) + (encode-base64-bytes arg stream) + (write-string "</base64>" stream)) + ((xml-rpc-time-p arg) + (write-string "<dateTime.iso8601>" stream) + (universal-time->iso8601 (xml-rpc-time-universal-time arg) stream) + (write-string "</dateTime.iso8601>" stream)) + ((xml-literal-p arg) + (write-string (xml-literal-content arg) stream)) + ((or (listp arg) (vectorp arg)) (encode-xml-rpc-array arg stream)) + ((xml-rpc-struct-p arg) (encode-xml-rpc-struct arg stream)) + ;; add generic method call + (t (error "cannot encode ~s" arg))) + (write-string "</value>" stream)) + +(defun encode-xml-rpc-args (args stream) + (write-string "<params>" stream) + (dolist (arg args) + (write-string "<param>" stream) + (encode-xml-rpc-value arg stream) + (write-string "</param>" stream)) + (write-string "</params>" stream)) + +(defun encode-xml-rpc-call (name &rest args) + "Encode an XML-RPC call with name and args as an XML string" + (with-output-to-string (stream) + (write-string "<methodCall>" stream) + ;; Spec says: The string may only contain identifier characters, + ;; upper and lower-case A-Z, the numeric characters, 0-9, + ;; underscore, dot, colon and slash. + (format stream "<methodName>~a</methodName>" (string name)) ; assuming name contains no special characters + (when args + (encode-xml-rpc-args args stream)) + (write-string "</methodCall>" stream))) + +(defun encode-xml-rpc-result (value) + (with-output-to-string (stream) + (write-string "<methodResponse>" stream) + (encode-xml-rpc-args (list value) stream) + (write-string "</methodResponse>" stream))) + +(defun encode-xml-rpc-fault-value (fault-string &optional (fault-code 0)) + ;; for system.multicall + (with-output-to-string (stream) + (write-string "<struct>" stream) + (format stream "<member><name>faultCode</name><value><int>~d</int></value></member>" fault-code) + (write-string "<member><name>faultString</name><value><string>" stream) + (print-string-xml fault-string stream) + (write-string "</string></value></member>" stream) + (write-string "</struct>" stream))) + +(defun encode-xml-rpc-fault (fault-string &optional (fault-code 0)) + (with-output-to-string (stream) + (write-string "<methodResponse><fault><value>" stream) + (write-string (encode-xml-rpc-fault-value fault-string fault-code) stream) + (write-string "</value></fault></methodResponse>" stream))) + +;;; decoding support + +(defun decode-xml-rpc-new-element (name attributes seed) + (declare (ignore seed name attributes)) + '()) + +(defun decode-xml-rpc-finish-element (name attributes parent-seed seed) + (declare (ignore attributes)) + (cons (case name + ((:|int| :|i4|) (parse-integer seed)) + (:|double| (let ((*read-eval* nil) + (*read-default-float-format* 'double-float)) + (read-from-string seed))) + (:|boolean| (= 1 (parse-integer seed))) + (:|string| (if (null seed) "" seed)) + (:|dateTime.iso8601| (xml-rpc-time (iso8601->universal-time seed))) + (:|base64| (if (null seed) + (make-array 0 :element-type '(unsigned-byte 8)) + (with-input-from-string (in seed) + (decode-base64-bytes in)))) + (:|array| (car seed)) + (:|data| (unless (stringp seed) (nreverse seed))) + (:|value| (cond ((stringp seed) seed) + ((null seed) "") + (t (car seed)))) + (:|struct| (make-xml-rpc-struct :alist seed)) + (:|member| (cons (cadr seed) (car seed))) + (:|name| (intern seed :keyword)) + (:|params| (nreverse seed)) + (:|param| (car seed)) + (:|fault| (make-condition 'xml-rpc-fault + :string (get-xml-rpc-struct-member (car seed) :|faultString|) + :code (get-xml-rpc-struct-member (car seed) :|faultCode|))) + (:|methodName| seed) + (:|methodCall| (let ((pair (nreverse seed))) + (cons (car pair) (cadr pair)))) + (:|methodResponse| (car seed))) + parent-seed)) + +(defun decode-xml-rpc-text (string seed) + (declare (ignore seed)) + string) + +(defun decode-xml-rpc (stream) + (car (start-parse-xml stream + (make-instance 'xml-parser-state + :new-element-hook #'decode-xml-rpc-new-element + :finish-element-hook #'decode-xml-rpc-finish-element + :text-hook #'decode-xml-rpc-text)))) + +;;; networking basics + +(defparameter *xml-rpc-host* "localhost" + "String naming the default XML-RPC host to use") + +(defparameter *xml-rpc-port* 80 + "Integer specifying the default XML-RPC port to use") + +(defparameter *xml-rpc-url* "/RPC2" + "String specifying the default XML-RPC URL to use") + +(defparameter *xml-rpc-agent* (concatenate 'string + (lisp-implementation-type) + " " + (lisp-implementation-version)) + "String specifying the default XML-RPC agent to include in server responses") + +(defvar *xml-rpc-debug* nil + "When T the XML-RPC client and server part will be more verbose about their protocol") + +(defvar *xml-rpc-debug-stream* nil + "When not nil it specifies where debugging output should be written to") + +(defparameter *xml-rpc-proxy-host* nil + "When not null, a string naming the XML-RPC proxy host to use") + +(defparameter *xml-rpc-proxy-port* nil + "When not null, an integer specifying the XML-RPC proxy port to use") + +(defparameter *xml-rpc-package* (find-package :s-xml-rpc-exports) + "Package for XML-RPC callable functions") + +(defparameter *xml-rpc-authorization* nil + "When not null, a string to be used as Authorization header") + +(defun format-debug (&rest args) + (when *xml-rpc-debug* + (apply #'format args))) + +(defparameter +crlf+ (make-array 2 + :element-type 'character + :initial-contents '(#\return #\linefeed))) + +(defun tokens (string &key (start 0) (separators (list #\space #\return #\linefeed #\tab))) + (if (= start (length string)) + '() + (let ((p (position-if #'(lambda (char) (find char separators)) string :start start))) + (if p + (if (= p start) + (tokens string :start (1+ start) :separators separators) + (cons (subseq string start p) + (tokens string :start (1+ p) :separators separators))) + (list (subseq string start)))))) + +(defun format-header (stream headers) + (mapc #'(lambda (header) + (cond ((null (rest header)) (write-string (first header) stream) (write-string +crlf+ stream)) + ((second header) (apply #'format stream header) (write-string +crlf+ stream)))) + headers) + (write-string +crlf+ stream)) + +(defun debug-stream (in) + (if *xml-rpc-debug* + (make-echo-stream in *standard-output*) + in)) + +;;; client API + +(defun xml-rpc-call (encoded &key + (url *xml-rpc-url*) + (agent *xml-rpc-agent*) + (host *xml-rpc-host*) + (port *xml-rpc-port*) + (authorization *xml-rpc-authorization*) + (proxy-host *xml-rpc-proxy-host*) + (proxy-port *xml-rpc-proxy-port*)) + "Execute an already encoded XML-RPC call and return the decoded result" + (let ((uri (if proxy-host (format nil "http://~a:~d~a" host port url) url))) + (with-open-stream (connection (s-sysdeps:open-socket-stream (if proxy-host proxy-host host) + (if proxy-port proxy-port port))) + (format-debug (or *xml-rpc-debug-stream* t) "POST ~a HTTP/1.0~%Host: ~a:~d~%" uri host port) + (format-header connection `(("POST ~a HTTP/1.0" ,uri) + ("User-Agent: ~a" ,agent) + ("Host: ~a:~d" ,host ,port) + ("Authorization: ~a" ,authorization) + ("Content-Type: text/xml") + ("Content-Length: ~d" ,(length encoded)))) + (write-string encoded connection) + (finish-output connection) + (format-debug (or *xml-rpc-debug-stream* t) "Sending ~a~%~%" encoded) + (let ((header (read-line connection nil nil))) + (when (null header) (error "no response from server")) + (format-debug (or *xml-rpc-debug-stream* t) "~a~%" header) + (setf header (tokens header)) + (unless (and (>= (length header) 3) + (string-equal (second header) "200") + (string-equal (third header) "OK")) + (error "http-error:~{ ~a~}" header))) + (do ((line (read-line connection nil nil) + (read-line connection nil nil))) + ((or (null line) (<= (length line) 1))) + (format-debug (or *xml-rpc-debug-stream* t) "~a~%" line)) + (let ((result (decode-xml-rpc (debug-stream connection)))) + (if (typep result 'xml-rpc-fault) + (error result) + (car result)))))) + +(defun call-xml-rpc-server (server-keywords name &rest args) + "Encode and execute an XML-RPC call with name and args, using the list of server-keywords" + (apply #'xml-rpc-call + (cons (apply #'encode-xml-rpc-call (cons name args)) + server-keywords))) + +(defun describe-server (&key (host *xml-rpc-host*) (port *xml-rpc-port*) (url *xml-rpc-url*)) + "Tries to describe a remote server using system.* methods" + (dolist (method (xml-rpc-call (encode-xml-rpc-call "system.listMethods") + :host host + :port port + :url url)) + (format t + "Method ~a ~a~%~a~%~%" + method + (xml-rpc-call (encode-xml-rpc-call "system.methodSignature" method) + :host host + :port port + :url url) + (xml-rpc-call (encode-xml-rpc-call "system.methodHelp" method) + :host host + :port port + :url url)))) + + +;;; server API + +(defvar *xml-rpc-call-hook* 'execute-xml-rpc-call + "A function to execute the xml-rpc call and return the result, accepting a method-name string and a optional argument list") + +(defparameter +xml-rpc-method-characters+ + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_.:/") + +(defun valid-xml-rpc-method-name-p (method-name) + (not (find-if-not (lambda (c) (find c +xml-rpc-method-characters+)) + method-name))) + +(defun find-xml-rpc-method (method-name) + "Looks for a method with the given name in *xml-rpc-package*. + Returns the symbol named METHOD-NAME if it exists and is + fbound, or NIL if not." + (let ((sym (find-symbol method-name *xml-rpc-package*))) + (if (fboundp sym) sym nil))) + +(defun execute-xml-rpc-call (method-name &rest arguments) + "Execute method METHOD-NAME on ARGUMENTS, or raise an error if + no such method exists in *XML-RPC-PACKAGE*" + (let ((method (find-xml-rpc-method method-name))) + (if method + (apply method arguments) + ;; http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php + ;; -32601 ---> server error. requested method not found + (error 'xml-rpc-fault :code -32601 + :string (format nil "Method ~A not found." method-name))))) + +(defun handle-xml-rpc-call (in id) + "Handle an actual call, reading XML from in and returning the + XML-encoded result." + ;; Try to conform to + ;; http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php + (handler-bind ((s-xml:xml-parser-error + #'(lambda (c) + (format-debug (or *xml-rpc-debug-stream* t) + "~a request parsing failed with ~a~%" + id c) + (return-from handle-xml-rpc-call + ;; -32700 ---> parse error. not well formed + (encode-xml-rpc-fault (format nil "~a" c) -32700)))) + (xml-rpc-fault + #'(lambda (c) + (format-debug (or *xml-rpc-debug-stream* t) + "~a call failed with ~a~%" id c) + (return-from handle-xml-rpc-call + (encode-xml-rpc-fault (xml-rpc-fault-string c) + (xml-rpc-fault-code c))))) + (error + #'(lambda (c) + (format-debug (or *xml-rpc-debug-stream* t) + "~a call failed with ~a~%" id c) + (return-from handle-xml-rpc-call + ;; -32603 ---> server error. internal xml-rpc error + (encode-xml-rpc-fault (format nil "~a" c) -32603))))) + (let ((call (decode-xml-rpc (debug-stream in)))) + (format-debug (or *xml-rpc-debug-stream* t) "~a received call ~s~%" id call) + (let ((result (apply *xml-rpc-call-hook* + (first call) + (rest call)))) + (format-debug (or *xml-rpc-debug-stream* t) "~a call result is ~s~%" id result) + (encode-xml-rpc-result result))))) + +(defun xml-rpc-implementation-version () + "Identify ourselves" + (concatenate 'string + "$Id: xml-rpc.lisp,v 1.11 2008-02-15 15:42:40 scaekenberghe Exp $" + " " + (lisp-implementation-type) + " " + (lisp-implementation-version))) + +(defun xml-rpc-server-connection-handler (connection id agent url) + "Handle an incoming connection, doing both all HTTP and XML-RPC stuff" + (handler-bind ((error #'(lambda (c) + (format-debug (or *xml-rpc-debug-stream* t) + "xml-rpc server connection handler failed with ~a~%" c) + (error c) + (return-from xml-rpc-server-connection-handler nil)))) + (let ((header (read-line connection nil nil))) + (when (null header) (error "no request from client")) + (setf header (tokens header)) + (if (and (>= (length header) 3) + (string-equal (first header) "POST") + (string-equal (second header) url)) + (progn + (do ((line (read-line connection nil nil) + (read-line connection nil nil))) + ((or (null line) (<= (length line) 1))) + (format-debug (or *xml-rpc-debug-stream* t) "~d ~a~%" id line)) + (let ((xml (handle-xml-rpc-call connection id))) + (format-header connection + `(("HTTP/1.0 200 OK") + ("Server: ~a" ,agent) + ("Connection: close") + ("Content-Type: text/xml") + ("Content-Length: ~d" ,(length xml)))) + (write-string xml connection) + (format-debug (or *xml-rpc-debug-stream* t) "~d sending ~a~%" id xml))) + (progn + (format-header connection + `(("HTTP/1.0 400 Bad Request") + ("Server: ~a" ,agent) + ("Connection: close"))) + (format-debug (or *xml-rpc-debug-stream* t) "~d got a bad request~%" id))) + (force-output connection) + (close connection)))) + +(defparameter *counter* 0 "Unique ID for incoming connections") + +(defun start-xml-rpc-server (&key (port *xml-rpc-port*) (url *xml-rpc-url*) (agent *xml-rpc-agent*)) + "Start an XML-RPC server in a separate process" + (s-sysdeps:start-standard-server + :name (format nil "xml-rpc server ~a:~d" url port) + :port port + :connection-handler #'(lambda (client-stream) + (let ((id (incf *counter*))) + (format-debug (or *xml-rpc-debug-stream* t) "spawned connection handler ~d~%" id) + (s-sysdeps:run-process (format nil "xml-rpc-server-connection-handler-~d" id) + #'xml-rpc-server-connection-handler + client-stream + id + agent + url))))) + +;;;; eof diff --git a/third-party/s-xml-rpc/test/all-tests.lisp b/third-party/s-xml-rpc/test/all-tests.lisp new file mode 100644 index 0000000..3b24e2a --- /dev/null +++ b/third-party/s-xml-rpc/test/all-tests.lisp @@ -0,0 +1,17 @@ +;;;; -*- mode: lisp -*- +;;;; +;;;; $Id: all-tests.lisp,v 1.2 2004-06-17 19:43:11 rschlatte Exp $ +;;;; +;;;; Load and execute all unit and functional tests +;;;; +;;;; Copyright (C) 2002, 2004 Sven Van Caekenberghe, Beta Nine BVBA. +;;;; +;;;; You are granted the rights to distribute and use this software +;;;; as governed by the terms of the Lisp Lesser General Public License +;;;; (http://opensource.franz.com/preamble.html), also known as the LLGPL. + +(load (merge-pathnames "test-base64" *load-pathname*) :verbose t) +(load (merge-pathnames "test-xml-rpc" *load-pathname*) :verbose t) +(load (merge-pathnames "test-extensions" *load-pathname*) :verbose t) + +;;;; eof diff --git a/third-party/s-xml-rpc/test/test-base64.lisp b/third-party/s-xml-rpc/test/test-base64.lisp new file mode 100644 index 0000000..c263788 --- /dev/null +++ b/third-party/s-xml-rpc/test/test-base64.lisp @@ -0,0 +1,123 @@ +;;;; -*- mode: lisp -*- +;;;; +;;;; $Id: test-base64.lisp,v 1.1.1.1 2004-06-09 09:02:41 scaekenberghe Exp $ +;;;; +;;;; Unit and functional tests for base64.lisp +;;;; +;;;; Copyright (C) 2002, 2004 Sven Van Caekenberghe, Beta Nine BVBA. +;;;; +;;;; You are granted the rights to distribute and use this software +;;;; as governed by the terms of the Lisp Lesser General Public License +;;;; (http://opensource.franz.com/preamble.html), also known as the LLGPL. + +(in-package :s-base64) + +(assert + (equal (multiple-value-list (core-encode-base64 0 0 0)) + (list #\A #\A #\A #\A))) + +(assert + (equal (multiple-value-list (core-encode-base64 255 255 255)) + (list #\/ #\/ #\/ #\/))) + +(assert + (equal (multiple-value-list (core-encode-base64 1 2 3)) + (list #\A #\Q #\I #\D))) + +(assert + (equal (multiple-value-list (core-encode-base64 10 20 30)) + (list #\C #\h #\Q #\e))) + +(assert + (equal (multiple-value-list (core-decode-base64 #\A #\A #\A #\A)) + (list 0 0 0))) + +(assert + (equal (multiple-value-list (core-decode-base64 #\/ #\/ #\/ #\/)) + (list 255 255 255))) + +(assert + (equal (multiple-value-list (core-decode-base64 #\A #\Q #\I #\D)) + (list 1 2 3))) + +(assert + (equal (multiple-value-list (core-decode-base64 #\C #\h #\Q #\e)) + (list 10 20 30))) + +(assert + (let* ((string "Hello World!") + (bytes (map 'vector #'char-code string)) + encoded + decoded) + (setf encoded (with-output-to-string (out) + (encode-base64-bytes bytes out))) + (setf decoded (with-input-from-string (in encoded) + (decode-base64-bytes in))) + (equal string + (map 'string #'code-char decoded)))) + +;;; this is more of a functional test + +(defun same-character-file (file1 file2) + (with-open-file (a file1 :direction :input) + (with-open-file (b file2 :direction :input) + (loop + (let ((char-a (read-char a nil nil nil)) + (char-b (read-char b nil nil nil))) + (cond ((not (or (and (null char-a) (null char-b)) + (and char-a char-b))) + (return-from same-character-file nil)) + ((null char-a) + (return-from same-character-file t)) + ((char/= char-a char-b) + (return-from same-character-file nil)))))))) + +(defun same-binary-file (file1 file2) + (with-open-file (a file1 :direction :input :element-type 'unsigned-byte) + (with-open-file (b file2 :direction :input :element-type 'unsigned-byte) + (loop + (let ((byte-a (read-byte a nil nil)) + (byte-b (read-byte b nil nil))) + (cond ((not (or (and (null byte-a) (null byte-b)) + (and byte-a byte-b))) + (return-from same-binary-file nil)) + ((null byte-a) + (return-from same-binary-file t)) + ((/= byte-a byte-b) + (return-from same-binary-file nil)))))))) + +(let ((original (merge-pathnames "test.b64" *load-pathname*)) + (first-gif (merge-pathnames "test.gif" *load-pathname*)) + (b64 (merge-pathnames "test2.b64" *load-pathname*)) + (second-gif (merge-pathnames "test2.gif" *load-pathname*))) + (with-open-file (in original + :direction :input) + (with-open-file (out first-gif + :direction :output + :element-type 'unsigned-byte + :if-does-not-exist :create + :if-exists :supersede) + (decode-base64 in out))) + (with-open-file (in first-gif + :direction :input + :element-type 'unsigned-byte) + (with-open-file (out b64 + :direction :output + :if-does-not-exist :create + :if-exists :supersede) + (encode-base64 in out nil))) + (assert (same-character-file original b64)) + (with-open-file (in b64 + :direction :input) + (with-open-file (out second-gif + :direction :output + :element-type 'unsigned-byte + :if-does-not-exist :create + :if-exists :supersede) + (decode-base64 in out))) + (assert (same-binary-file first-gif second-gif)) + (delete-file first-gif) + (delete-file b64) + (delete-file second-gif)) + +;;;; eof
\ No newline at end of file diff --git a/third-party/s-xml-rpc/test/test-extensions.lisp b/third-party/s-xml-rpc/test/test-extensions.lisp new file mode 100644 index 0000000..a3e3eb0 --- /dev/null +++ b/third-party/s-xml-rpc/test/test-extensions.lisp @@ -0,0 +1,53 @@ +;;;; -*- mode: lisp -*- +;;;; +;;;; $Id: test-extensions.lisp,v 1.2 2006-04-19 10:22:31 scaekenberghe Exp $ +;;;; +;;;; Unit and functional tests for xml-rpc.lisp +;;;; +;;;; Copyright (C) 2004 Rudi Schlatte +;;;; +;;;; You are granted the rights to distribute and use this software +;;;; as governed by the terms of the Lisp Lesser General Public License +;;;; (http://opensource.franz.com/preamble.html), also known as the LLGPL. + +(in-package :s-xml-rpc) + +(let* ((server-port 8080) + (server-process (start-xml-rpc-server :port server-port)) + (server-args `(:port ,server-port)) + (*xml-rpc-package* (make-package (gensym))) + (symbols '(|system.listMethods| |system.methodSignature| + |system.methodHelp| |system.multicall| + |system.getCapabilities|))) + (import symbols *xml-rpc-package*) + (sleep 1) ; give the server some time to come up ;-) + (unwind-protect + (progn + (assert + (equal (sort (call-xml-rpc-server server-args "system.listMethods") + #'string<) + (sort (mapcar #'string symbols) #'string<))) + (assert + (every #'string= + (mapcar (lambda (name) + (call-xml-rpc-server server-args "system.methodHelp" + name)) + symbols) + (mapcar (lambda (name) + (or (documentation name 'function) "")) + symbols))) + (assert + (= 2 + (length (call-xml-rpc-server + server-args "system.multicall" + (list + (xml-rpc-struct "methodName" + "system.listMethods") + (xml-rpc-struct "methodName" + "system.methodHelp" + "params" + (list "system.multicall")))))))) + (s-sysdeps:kill-process server-process) + (delete-package *xml-rpc-package*))) + +;;;; eof
\ No newline at end of file diff --git a/third-party/s-xml-rpc/test/test-xml-rpc.lisp b/third-party/s-xml-rpc/test/test-xml-rpc.lisp new file mode 100644 index 0000000..3933a88 --- /dev/null +++ b/third-party/s-xml-rpc/test/test-xml-rpc.lisp @@ -0,0 +1,176 @@ +;;;; -*- mode: lisp -*- +;;;; +;;;; $Id: test-xml-rpc.lisp,v 1.4 2008-02-15 15:42:40 scaekenberghe Exp $ +;;;; +;;;; Unit and functional tests for xml-rpc.lisp +;;;; +;;;; Copyright (C) 2002, 2004 Sven Van Caekenberghe, Beta Nine BVBA. +;;;; +;;;; You are granted the rights to distribute and use this software +;;;; as governed by the terms of the Lisp Lesser General Public License +;;;; (http://opensource.franz.com/preamble.html), also known as the LLGPL. + +(in-package :s-xml-rpc) + +(assert + (let ((now (get-universal-time))) + (equal (iso8601->universal-time (universal-time->iso8601 now)) + now))) + +(assert + (equal (with-input-from-string (in (encode-xml-rpc-call "add" 1 2)) + (decode-xml-rpc in)) + '("add" 1 2))) + +(assert + (equal (with-input-from-string (in (encode-xml-rpc-result '(1 2))) + (car (decode-xml-rpc in))) + '(1 2))) + +(let ((condition (with-input-from-string (in (encode-xml-rpc-fault "Fatal Error" 100)) + (decode-xml-rpc in)))) + (assert (typep condition 'xml-rpc-fault)) + (assert (equal (xml-rpc-fault-string condition) "Fatal Error")) + (assert (equal (xml-rpc-fault-code condition) 100))) + +(assert + (xml-rpc-time-p (xml-rpc-call (encode-xml-rpc-call "currentTime.getCurrentTime") + :host "time.xmlrpc.com"))) + +(assert + (equal (xml-rpc-call (encode-xml-rpc-call "examples.getStateName" 41) + :host "betty.userland.com") + "South Dakota")) + +(assert + (equal (call-xml-rpc-server '(:host "betty.userland.com") "examples.getStateName" 41) + "South Dakota")) + +#-clisp +(assert + (let ((server-process (start-xml-rpc-server :port 8080))) + (import 's-xml-rpc::xml-rpc-implementation-version :s-xml-rpc-exports) + (sleep 1) ; give the server some time to come up ;-) + (unwind-protect + (equal (xml-rpc-call (encode-xml-rpc-call "XML-RPC-IMPLEMENTATION-VERSION") :port 8080) + (xml-rpc-implementation-version)) + (s-sysdeps:kill-process server-process) + (unintern 's-xml-rpc::xml-rpc-implementation-version :s-xml-rpc-exports)))) + +(assert + (let* ((struct-in (xml-rpc-struct :foo 100 :bar "")) + (xml (with-output-to-string (out) + (encode-xml-rpc-value struct-in out))) + (struct-out (with-input-from-string (in xml) + (decode-xml-rpc in)))) + (xml-rpc-struct-equal struct-in struct-out))) + +;; testing whitespace handling + +(assert (null (decode-xml-rpc (make-string-input-stream +"<array> + <data> + </data> +</array>")))) + +(assert (equalp (decode-xml-rpc (make-string-input-stream +"<params> + <param> + <value> + foo + </value> + </param> + <param> + <value> + <array> + <data> + <value><i4>12</i4></value> + <value><string>Egypt</string></value> + <value><boolean>1</boolean></value> + <value> <string> </string> </value> + <value> </value> + <value> fgo </value> + <value><i4>-31</i4></value> + <value></value> + <double> -12.214 </double> + <dateTime.iso8601> + 19980717T14:08:55 </dateTime.iso8601> + <base64>eW91IGNhbid0IHJlYWQgdGhpcyE=</base64> + </data> + </array> + </value> + </param> +</params>")) +`(" + foo + " + (12 + "Egypt" + T + " " + " " + " fgo " + -31 + "" + -12.214D0 + ,(xml-rpc-time (iso8601->universal-time "19980717T14:08:55")) + #(121 111 117 32 99 97 110 39 116 32 114 101 97 100 32 116 104 105 115 33))))) + +(assert (equalp (decode-xml-rpc (make-string-input-stream +"<array> + <data> + <value></value> + </data> +</array>")) +'(""))) + +(assert (equalp (decode-xml-rpc (make-string-input-stream +"<array> + <data> + <value> + <string>XYZ</string> + </value> + </data> +</array>")) +'("XYZ"))) + +;; double decoding + +(assert (< (abs (- (decode-xml-rpc (make-string-input-stream "<value><double>3.141592653589793</double></value>")) + pi)) + 0.000000000001D0)) + +;; string decoding + +(assert (equal (decode-xml-rpc (make-string-input-stream "<value><string>foo</string></value>")) + "foo")) + +(assert (equal (decode-xml-rpc (make-string-input-stream "<value>foo</value>")) + "foo")) + +(assert (equal (decode-xml-rpc (make-string-input-stream "<value><string></string></value>")) + "")) + +(assert (equal (decode-xml-rpc (make-string-input-stream "<value></value>")) + "")) + +;; boolean encoding + +(assert (equal (with-output-to-string (out) + (encode-xml-rpc-value t out)) + "<value><boolean>1</boolean></value>")) + +(assert (equal (with-output-to-string (out) + (encode-xml-rpc-value nil out)) + "<value><boolean>0</boolean></value>")) + + +;; boolean decoding + +(assert (equal (decode-xml-rpc (make-string-input-stream "<value><boolean>1</boolean></value>")) + t)) + +(assert (equal (decode-xml-rpc (make-string-input-stream "<value><boolean>0</boolean></value>")) + nil)) + +;;;; eof diff --git a/third-party/s-xml-rpc/test/test.b64 b/third-party/s-xml-rpc/test/test.b64 new file mode 100644 index 0000000..55445dd --- /dev/null +++ b/third-party/s-xml-rpc/test/test.b64 @@ -0,0 +1 @@ +R0lGODlhNABYAMQAAP////vi5etreuM0SN8ZL+dOYPCIk+56hPi/xPGapPjP1N4EGvHr9PSstMzI6riw4IY+kHsie3lvyVtMu0Y4tN3b8Y+CzX1ast2ftskNNNBdfqGb2so0WeHL4E8spLA0aiH5BAAAAAAALAAAAAA0AFgAQAX/ICCOZGmeaKqeBUEc5WAATbECQnCfyiAWsFIBgfMJFoTOYcAQERYN0SK4KxEUp4FPC/ABXN/oKOCtmh0OxGPj+UAgHmKhIZiZHJLJhMKXNKsMFQAMFnp6EogWFxEWOAsCgw4VfyN5IhKNABMPZhAUe3yhoYiYFmsWiISYlCoMeaJ8ehR5hoektLCjEhCcJGgQqSkKC1BmxscBxEQkBAlOxSMDCzY/ZQAICyPEOwnbCgXgcyQCBeQFWCIHVDXhJN/hkMc0DR0iA80oPXbyI+Vd6CO+RdOxjAWVYz367bPXzciVHjKwGCAwg4AOfiUOEBgg4ICAe08WYCFgQ0aKBtaM/xWSEMuQKA+1WHrIhYpCJgB4KDzYA4BPFQejLAgdSvSB0QoOHgCT4GAEKqSfUuT8ZIHVCAYbWOaaQErSiqoYw4oVkWxBwRETt41RW2KBNWluq5QteCAbiQJxSRATQIDVRisLnJmQ5sLi2MNhP24cUIDxRcRjCAsG0HgHnbEaFRAwAURExxsJHh8jB2CJiQZBOALokHIEgXiqjQks3frcFyRdcKfzonGBaGMDxFzTIgDBMBd4GVN7sQSdAWpiuZQIsJHkCwNQqKOgCBmF2wZu5xj4XUL4MUJcMTlowmANhAgXhGoIIMA5NUKwQIWCcANNIkEMMPDBAWgUmAEGIgwDQ/97u2ByCQVNTdCIBRTssEEfVpUWjwgMpIHKTQCg0oQnguhhBgObPCBBLys4gIoEJVTA0gSCGIPUegBolV8tomwSVgUTiEBhehZsgMB6AZ4Q4JJLcjhIh1ntAeMITamw0ycSbDDJHx0aRdSLuPSIyQM1rnAEMcRc0R0/c7EADUppLgRAAoRtuE0ABvBF3lpmlTCMI9A5shkz1vB1G0AqtNkPEoWx9cwMGllRWEitmTCMSAlCw4ymAFxaz6KWOuqmnGuWaqqpHQTw6almbmSAAqoG8M0AB3XXA0mC/bWCbYjxNQ0JdHY6WQoJZDgaYymhJEICgaKQg1hXgDMYOuKooMD/hvJQx4BmlsYT7GyWGoZRcJRhm44z3HaaFzOD4mXeDglRVitlWMjgghYgJaCZuE8gWkW86ZJgxCOA0iBtp1royk8Zmi1UGAAT4WmSZwREYUClZhzQ7MX3MDqFri4kIK7BY12cxRW+4dNCuSaQNpYW74KHhALgbXawpQP4ixGzWoiD3UZ9HkAqCUKfysA9zx1AJznvrlkBmCuu10AGCFSgDg5Y0CFaIbLAAuIJF86CyAZGbWDKAxwcoEACFbzhgZEWwHEBV0H5oSNLX5eAipSkcDWBB3MjsocHeUhwwQdEoFgmABX4ZCJLrSylCwQfoFHB5RpwIAJ4BehIwQY49tTI/wMVArDTDu1JKRSL2GBBXdMkFNIE5JpMeQOKNYl9i+CFk9KLJIQ0gUpTURmTR+67J5/IA2iQQEhOthvjgH5crT7UUrZsMMIDNa04Vt9i75Ee+KNUiRF6sTDFIfU8toTIWUqm0PgmE5i/QpLyXGh/Ja/4AQHUsciFAGExEzGBhUOIMEF7JDfAUMykgFv5RB/CZ4sHGAsFeABFTJSXvCKFjlUgnA5cMBbCUPXpBBtCQB3yNCwa1OFqIkAADK5lgAOsygQdUMYJiPGWX5mgLo/BRmACooIcnpAZvjLPE2rVjVUxoGKiSoER4beysrQFU2TBohNgUBcSkmCK7piCAS62LlR12QUAC1hIC8aIHU6hAIzaGNYThtWNzbwGWGc04w0uVRAwhDGPufFhplqYnBUIMQB4WiKiREYMG2pjUJ0607A60MaJneBnJayCbjK5HR5y8pNiCQEAOw==
\ No newline at end of file |