(module protocol mzscheme
(require (planet "xml.ss" ("jim" "webit.plt" 1 3))
(planet "xxexpr.ss" ("lshift" "xxexpr.plt" 1))
(lib "url.ss" "net")
(planet "ssax.ss" ("lizorkin" "ssax.plt" 1 3))
"base.ss"
"serialise.ss")
(provide encode-xmlrpc-call
write-xmlrpc-call
make-xmlrpc-call
read-xmlrpc-response
decode-xmlrpc-response
decode-xmlrpc-call
(struct rpc-call (name args)))
(define (http-200? headers)
(if (regexp-match #rx"^HTTP/[0-9]*\\.[0-9]* 200" headers)
#t
#f))
(define (http-404? headers)
(if (regexp-match #rx"^HTTP/[0-9]*\\.[0-9]* 404" headers)
#t
#f))
(define (encode-xmlrpc-call method-name . args)
`(methodCall
(methodName ,method-name)
(params
,@(map (lambda (val)
`(param ,(serialise val)))
args))))
(define (write-xmlrpc-call call op)
(parameterize
((xml-double-quotes-mode #t))
(let ([result
(pretty-print-xxexpr (list '(*pi* xml (version "1.0"))
call) op)])
result)))
(define (make-xmlrpc-call url call)
(let ((op (open-output-bytes)))
(write-xmlrpc-call call op)
(post-impure-port url
(get-output-bytes op)
'("Content-Type: text/xml"
"User-Agent: PLT Scheme"))))
(define (read-xmlrpc-response ip)
(let ((headers (purify-port ip)))
(cond
[(http-404? headers)
(raise-exn:xmlrpc "Server responded with a 404: File not found")]
[(not (http-200? headers))
(raise-exn:xmlrpc
(format "Server did not respond with an HTTP 200~nHeaders:~n~a~n"
headers))])
(let ([response (ssax:xml->sxml ip '())])
(close-input-port ip)
response) ))
(define (decode-xmlrpc-response ip)
(let ((resp (read-xmlrpc-response ip)))
(xml-match (xml-document-content resp)
[(methodResponse (params (param ,value)))
(deserialise value)]
[(methodResponse (fault ,value))
(let ((h (deserialise value)))
(raise
(make-exn:xmlrpc:fault
(string->immutable-string
(hash-table-get h 'faultString))
(current-continuation-marks)
(hash-table-get h 'faultCode))))]
[,else
(raise-exn:xmlrpc
(format "Received invalid XMLRPC response ~a\n" else))])))
(define (extract-parameter-values param*)
(map (lambda (p)
(xml-match p
[(param ,value) (deserialise value)]
[,else
(raise-exn:xmlrpc
(format "Bad parameter in methodCall: ~a~n" p))]))
param*))
(define (read-xmlrpc-call str)
(let* ([call-ip (open-input-string str)]
[result (ssax:xml->sxml call-ip '())])
(close-input-port call-ip)
result))
(define-struct rpc-call (name args))
(define (decode-xmlrpc-call str)
(let ([docu (read-xmlrpc-call str)])
(xml-match (xml-document-content docu)
[(methodCall (methodName ,name) (params ,param* ...))
(let ([value* (extract-parameter-values param*)])
(make-rpc-call (string->symbol name) value*))]
[,else
(raise-exn:xmlrpc
(format "Cannot parse methodCall: ~a~n" else))])))
)