2003-07-19 14:05:54 +04:00
|
|
|
#
|
|
|
|
# Creates XML-RPC call/response documents
|
|
|
|
#
|
|
|
|
# Copyright (C) 2001, 2002, 2003 by Michael Neumann (mneumann@ntecs.de)
|
|
|
|
#
|
|
|
|
# $Id$
|
|
|
|
#
|
|
|
|
|
|
|
|
require "date"
|
|
|
|
require "xmlrpc/base64"
|
|
|
|
|
|
|
|
module XMLRPC
|
|
|
|
|
|
|
|
module XMLWriter
|
|
|
|
|
|
|
|
class Abstract
|
|
|
|
def ele(name, *children)
|
|
|
|
element(name, nil, *children)
|
|
|
|
end
|
|
|
|
|
|
|
|
def tag(name, txt)
|
|
|
|
element(name, nil, text(txt))
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
class Simple < Abstract
|
|
|
|
|
|
|
|
def document_to_str(doc)
|
|
|
|
doc
|
|
|
|
end
|
|
|
|
|
|
|
|
def document(*params)
|
|
|
|
params.join("")
|
|
|
|
end
|
|
|
|
|
|
|
|
def pi(name, *params)
|
|
|
|
"<?#{name} " + params.join(" ") + " ?>"
|
|
|
|
end
|
|
|
|
|
|
|
|
def element(name, attrs, *children)
|
|
|
|
raise "attributes not yet implemented" unless attrs.nil?
|
|
|
|
if children.empty?
|
|
|
|
"<#{name}/>"
|
|
|
|
else
|
|
|
|
"<#{name}>" + children.join("") + "</#{name}>"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def text(txt)
|
|
|
|
cleaned = txt.dup
|
|
|
|
cleaned.gsub!(/&/, '&')
|
|
|
|
cleaned.gsub!(/</, '<')
|
|
|
|
cleaned.gsub!(/>/, '>')
|
|
|
|
cleaned
|
|
|
|
end
|
|
|
|
|
|
|
|
end # class Simple
|
|
|
|
|
|
|
|
|
|
|
|
class XMLParser < Abstract
|
|
|
|
|
|
|
|
def initialize
|
|
|
|
require "xmltreebuilder"
|
|
|
|
end
|
|
|
|
|
|
|
|
def document_to_str(doc)
|
|
|
|
doc.to_s
|
|
|
|
end
|
|
|
|
|
|
|
|
def document(*params)
|
|
|
|
XML::SimpleTree::Document.new(*params)
|
|
|
|
end
|
|
|
|
|
|
|
|
def pi(name, *params)
|
|
|
|
XML::SimpleTree::ProcessingInstruction.new(name, *params)
|
|
|
|
end
|
|
|
|
|
|
|
|
def element(name, attrs, *children)
|
|
|
|
XML::SimpleTree::Element.new(name, attrs, *children)
|
|
|
|
end
|
|
|
|
|
|
|
|
def text(txt)
|
|
|
|
XML::SimpleTree::Text.new(txt)
|
|
|
|
end
|
|
|
|
|
|
|
|
end # class XMLParser
|
|
|
|
|
2004-11-16 02:26:20 +03:00
|
|
|
Classes = [Simple, XMLParser]
|
|
|
|
|
|
|
|
# yields an instance of each installed XML writer
|
|
|
|
def self.each_installed_writer
|
|
|
|
XMLRPC::XMLWriter::Classes.each do |klass|
|
|
|
|
begin
|
|
|
|
yield klass.new
|
|
|
|
rescue LoadError
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2003-07-19 14:05:54 +04:00
|
|
|
end # module XMLWriter
|
|
|
|
|
|
|
|
class Create
|
|
|
|
|
|
|
|
def initialize(xml_writer = nil)
|
|
|
|
@writer = xml_writer || Config::DEFAULT_WRITER.new
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
def methodCall(name, *params)
|
|
|
|
name = name.to_s
|
|
|
|
|
|
|
|
if name !~ /[a-zA-Z0-9_.:\/]+/
|
|
|
|
raise ArgumentError, "Wrong XML-RPC method-name"
|
|
|
|
end
|
|
|
|
|
|
|
|
parameter = params.collect do |param|
|
|
|
|
@writer.ele("param", conv2value(param))
|
|
|
|
end
|
|
|
|
|
|
|
|
tree = @writer.document(
|
|
|
|
@writer.pi("xml", 'version="1.0"'),
|
|
|
|
@writer.ele("methodCall",
|
|
|
|
@writer.tag("methodName", name),
|
|
|
|
@writer.ele("params", *parameter)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|
|
|
|
@writer.document_to_str(tree) + "\n"
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#
|
|
|
|
# generates a XML-RPC methodResponse document
|
|
|
|
#
|
|
|
|
# if is_ret == false then the params array must
|
|
|
|
# contain only one element, which is a structure
|
|
|
|
# of a fault return-value.
|
|
|
|
#
|
|
|
|
# if is_ret == true then a normal
|
|
|
|
# return-value of all the given params is created.
|
|
|
|
#
|
|
|
|
def methodResponse(is_ret, *params)
|
|
|
|
|
|
|
|
if is_ret
|
|
|
|
resp = params.collect do |param|
|
|
|
|
@writer.ele("param", conv2value(param))
|
|
|
|
end
|
|
|
|
|
|
|
|
resp = [@writer.ele("params", *resp)]
|
|
|
|
else
|
|
|
|
if params.size != 1 or params[0] === XMLRPC::FaultException
|
|
|
|
raise ArgumentError, "no valid fault-structure given"
|
|
|
|
end
|
|
|
|
resp = @writer.ele("fault", conv2value(params[0].to_h))
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
tree = @writer.document(
|
|
|
|
@writer.pi("xml", 'version="1.0"'),
|
|
|
|
@writer.ele("methodResponse", resp)
|
|
|
|
)
|
|
|
|
|
|
|
|
@writer.document_to_str(tree) + "\n"
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#####################################
|
|
|
|
private
|
|
|
|
#####################################
|
|
|
|
|
|
|
|
#
|
|
|
|
# converts a Ruby object into
|
|
|
|
# a XML-RPC <value> tag
|
|
|
|
#
|
|
|
|
def conv2value(param)
|
|
|
|
|
|
|
|
val = case param
|
|
|
|
when Fixnum
|
|
|
|
@writer.tag("i4", param.to_s)
|
|
|
|
|
|
|
|
when Bignum
|
|
|
|
if Config::ENABLE_BIGINT
|
|
|
|
@writer.tag("i4", param.to_s)
|
|
|
|
else
|
|
|
|
if param >= -(2**31) and param <= (2**31-1)
|
|
|
|
@writer.tag("i4", param.to_s)
|
|
|
|
else
|
|
|
|
raise "Bignum is too big! Must be signed 32-bit integer!"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
when TrueClass, FalseClass
|
|
|
|
@writer.tag("boolean", param ? "1" : "0")
|
|
|
|
|
|
|
|
when Symbol
|
|
|
|
@writer.tag("string", param.to_s)
|
|
|
|
|
2006-11-02 03:21:28 +03:00
|
|
|
when String
|
|
|
|
@writer.tag("string", param)
|
|
|
|
|
2003-07-19 14:05:54 +04:00
|
|
|
when NilClass
|
|
|
|
if Config::ENABLE_NIL_CREATE
|
|
|
|
@writer.ele("nil")
|
|
|
|
else
|
|
|
|
raise "Wrong type NilClass. Not allowed!"
|
|
|
|
end
|
|
|
|
|
|
|
|
when Float
|
|
|
|
@writer.tag("double", param.to_s)
|
|
|
|
|
|
|
|
when Struct
|
|
|
|
h = param.members.collect do |key|
|
|
|
|
value = param[key]
|
|
|
|
@writer.ele("member",
|
|
|
|
@writer.tag("name", key.to_s),
|
|
|
|
conv2value(value)
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
|
|
|
@writer.ele("struct", *h)
|
|
|
|
|
|
|
|
when Hash
|
|
|
|
# TODO: can a Hash be empty?
|
|
|
|
|
|
|
|
h = param.collect do |key, value|
|
|
|
|
@writer.ele("member",
|
|
|
|
@writer.tag("name", key.to_s),
|
|
|
|
conv2value(value)
|
|
|
|
)
|
|
|
|
end
|
|
|
|
|
|
|
|
@writer.ele("struct", *h)
|
|
|
|
|
|
|
|
when Array
|
|
|
|
# TODO: can an Array be empty?
|
|
|
|
a = param.collect {|v| conv2value(v) }
|
|
|
|
|
|
|
|
@writer.ele("array",
|
|
|
|
@writer.ele("data", *a)
|
|
|
|
)
|
|
|
|
|
2007-02-23 06:42:01 +03:00
|
|
|
when Time, Date, ::DateTime
|
2003-07-19 14:05:54 +04:00
|
|
|
@writer.tag("dateTime.iso8601", param.strftime("%Y%m%dT%H:%M:%S"))
|
|
|
|
|
|
|
|
when XMLRPC::DateTime
|
|
|
|
@writer.tag("dateTime.iso8601",
|
|
|
|
format("%.4d%02d%02dT%02d:%02d:%02d", *param.to_a))
|
|
|
|
|
|
|
|
when XMLRPC::Base64
|
|
|
|
@writer.tag("base64", param.encoded)
|
|
|
|
|
|
|
|
else
|
|
|
|
if Config::ENABLE_MARSHALLING and param.class.included_modules.include? XMLRPC::Marshallable
|
|
|
|
# convert Ruby object into Hash
|
|
|
|
ret = {"___class___" => param.class.name}
|
2004-11-16 02:26:20 +03:00
|
|
|
param.instance_variables.each {|v|
|
|
|
|
name = v[1..-1]
|
|
|
|
val = param.instance_variable_get(v)
|
|
|
|
|
2003-07-19 14:05:54 +04:00
|
|
|
if val.nil?
|
|
|
|
ret[name] = val if Config::ENABLE_NIL_CREATE
|
|
|
|
else
|
|
|
|
ret[name] = val
|
|
|
|
end
|
|
|
|
}
|
|
|
|
return conv2value(ret)
|
|
|
|
else
|
|
|
|
ok, pa = wrong_type(param)
|
|
|
|
if ok
|
|
|
|
return conv2value(pa)
|
|
|
|
else
|
|
|
|
raise "Wrong type!"
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
@writer.ele("value", val)
|
|
|
|
end
|
|
|
|
|
|
|
|
def wrong_type(value)
|
|
|
|
false
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
end # class Create
|
|
|
|
|
|
|
|
end # module XMLRPC
|
|
|
|
|