#
# The contents of this file are subject to the AOLserver Public License
# Version 1.1 (the "License"); you may not use this file except in
# compliance with the License. You may obtain a copy of the License at
# http://aolserver.com/.
#
# Software distributed under the License is distributed on an "AS IS"
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
# the License for the specific language governing rights and limitations
# under the License.
#
# The Original Code is AOLserver Code and related documentation
# distributed by AOL.
#
# The Initial Developer of the Original Code is America Online,
# Inc. Portions created by AOL are Copyright (C) 1999 America Online,
# Inc. All Rights Reserved.
#
# Alternatively, the contents of this file may be used under the terms
# of the GNU General Public License (the "GPL"), in which case the
# provisions of GPL are applicable instead of those above. If you wish
# to allow use of your version of this file only under the terms of the
# GPL and not to allow others to use your version of this file under the
# License, indicate your decision by deleting the provisions above and
# replace them with the notice and other provisions required by the GPL.
# If you do not delete the provisions above, a recipient may use your
# version of this file under either the License or the GPL.
#
#
# $Header$
#
#
# xmlrpc.tcl --
#
# Implementation of an XML-RPC client and server mechanism.
#
package require tdom 0.8
proc ns_xmlrpc {option args} {
switch -glob -- $option {
b* - bind {
if {[llength $args] != 3} {
error "wrong # args: should be \"ns_xmlrpc bind url method proc\""
}
error "not yet implemented"
}
c* - call {
if {[llength $args] < 2} {
error "wrong # args: should be \"ns_xmlrpc call ?-timeout timeout? url method ?arg arg ...?\""
}
set timeout 1 ;# default timeout = 1 sec
if {[lindex $args 0] eq "-timeout"} {
set timeout [lindex $args 1]
set args [lrange $args 2 end]
}
set id [eval ns_xmlrpc queue $args]
return [ns_xmlrpc wait $id $timeout]
}
fa* - fault {
if {[llength $args] != 2 && [llength $args] != 4} {
error "wrong # args: should be \"ns_xmlrpc fault ?-indent indent? code string\""
}
set indent none
if {[lindex $args 0] eq "-indent"} {
set indent [lindex $args 1]
set args [lrange $args 2 end]
}
foreach {code string} $args break
set xml {}
dom createDocument methodResponse doc
if {[set res [catch {
$doc documentElement root
$root appendXML {
faultCode
faultString
}
set node [$root selectNodes "//methodResponse/fault/value/struct/member/value/int"]
$doc createTextNode $code child
$node appendChild $child
set node [$root selectNodes "//methodResponse/fault/value/struct/member/value/string"]
$doc createTextNode $string child
$node appendChild $child
set xml "\n[$doc asXML -indent $indent]"
if {$indent eq "none"} {
append xml "\n"
}
} err]]} {
set savedInfo $::errorInfo
}
$doc delete
if {$res} {
error $err
}
return $xml
}
fo* - format {
set indent none
set types yes
while {[llength $args]} {
if {[lindex $args 0] eq "-indent"} {
set indent [lindex $args 1]
set args [lrange $args 2 end]
continue
}
if {[lindex $args 0] eq "-types"} {
set types [lindex $args 1]
set args [lrange $args 2 end]
continue
}
break
}
set xml {}
dom createDocument methodResponse doc
if {[set res [catch {
$doc documentElement root
$doc createElement params params
$root appendChild $params
foreach arg $args {
foreach {type value} $arg {
$doc createElement param paramNode
set node [ns_xmlrpc _format $doc $type $value $types]
$paramNode appendChild $node
$params appendChild $paramNode
}
}
set xml "\n[$doc asXML -indent $indent]"
if {$indent eq "none"} {
append xml "\n"
}
} err]]} {
set savedInfo $::errorInfo
}
$doc delete
if {$res} {
error $err
}
return $xml
}
_f* - _format {
if {[llength $args] != 4} {
error "wrong # args: should be \"ns_xmlrpc _format doc type value types\""
}
foreach {doc type value types} $args break
$doc createElement value valueNode
set childNode $valueNode
if {$types || $type eq "array" || $type eq "struct"} {
$doc createElement $type typeNode
$valueNode appendChild $typeNode
set childNode $typeNode
}
switch -exact -- $type {
array {
$doc createElement data dataNode
$childNode appendChild $dataNode
foreach arg $value {
foreach {_type _value} $arg {
set node [ns_xmlrpc _format $doc $_type $_value $types]
$dataNode appendChild $node
}
}
}
struct {
foreach {name arg} $value {
$doc createElement member memberNode
$childNode appendChild $memberNode
$doc createElement name nameNode
$memberNode appendChild $nameNode
$nameNode appendChild [$doc createTextNode $name]
foreach {_type _value} $arg {
set node [ns_xmlrpc _format $doc $_type $_value $types]
$memberNode appendChild $node
}
}
}
string -
i4 -
int -
double -
base64 -
boolean -
datetime.iso8601 -
default {
$childNode appendChild [$doc createTextNode $value]
}
}
return $valueNode
}
l* - listen {
if {[llength $args] != 1} {
error "wrong # args: should be \"ns_xmlrpc listen xml\""
}
set xml [lindex $args 0]
set method {}
set params {}
dom parse $xml doc
if {[set res [catch {
$doc documentElement root
set method [$root selectNodes "string(//methodCall/methodName)"]
foreach node [$root selectNodes "//methodCall/params/param/value"] {
set child [$node child 1]
if {[llength $child]} {
set node $child
}
lappend params [ns_xmlrpc _parse $node]
}
} err]]} {
set savedInfo $::errorInfo
}
$doc delete
if {$res} {
error $err
}
return [concat $method $params]
}
q* - queue {
if {[llength $args] < 2} {
error "wrong # args: should be \"ns_xmlrpc queue url method ?arg arg ...?\""
}
foreach {url method} $args break
set headers [ns_set create]
ns_set put $headers Content-Type "text/xml"
set body "\n"
## TODO: Escape $method for XML properly. CDATA?
append body "$method"
append body ""
foreach arg [lrange $args 2 end] {
## TODO: Escape $arg for XML properly. CDATA?
append body "$arg"
}
append body "\n"
ns_log notice "body = [list $body]"
return [ns_http queue POST $url $body $headers]
}
w* - wait {
if {[llength $args] != 1 && [llength $args] != 2} {
error "wrong # args: should be \"ns_xmlrpc wait id ?timeout?\""
}
foreach {id timeout} $args break
if {![string length $timeout]} {
set timeout 1 ;# default timeout = 1 sec
}
set code [ns_http wait $id xml $timeout]
ns_log notice "code = $code, xml = [list $xml]"
if {$code != 1} {
return
}
set data [list]
dom parse $xml doc
if {[set res [catch {
$doc documentElement root
foreach node [$root selectNodes "//methodResponse/params/param/value"] {
set child [$node child 1]
if {[llength $child]} {
set node $child
}
lappend data [ns_xmlrpc _parse $node]
}
} err]]} {
set savedInfo $::errorInfo
}
$doc delete
if {$res} {
error $err
}
return $data
}
_p* - _parse {
set node [lindex $args 0]
set type [$node nodeName]
set value [$node text]
# puts "*!* type = $type, value = [list $value], [list [$node asXML]]"
switch -exact -- $type {
array {
set data [list]
foreach node [$node selectNodes "data/value"] {
set child [$node child 1]
if {[llength $child]} {
set node $child
}
lappend data [ns_xmlrpc _parse $node]
}
return $data
}
struct {
set data [list]
foreach node [$node selectNodes "member"] {
set name [ns_xmlrpc _parse [$node child 1]]
set child [$node child 2]
if {[llength [$child childNodes]]} {
set child [$child child 1]
}
set value [ns_xmlrpc _parse $child]
lappend data [list $name $value]
}
return $data
}
string -
i4 -
int -
double -
base64 -
boolean -
datetime.iso8601 -
default {
return $value
}
}
}
default {
error "bad option \"$option\": must be bind, call, fault, format, queue, or wait"
}
}
}