xmlrpc 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.travis.yml +4 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +56 -0
- data/README.md +58 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/xmlrpc.rb +293 -0
- data/lib/xmlrpc/base64.rb +63 -0
- data/lib/xmlrpc/client.rb +629 -0
- data/lib/xmlrpc/config.rb +39 -0
- data/lib/xmlrpc/create.rb +287 -0
- data/lib/xmlrpc/datetime.rb +130 -0
- data/lib/xmlrpc/marshal.rb +67 -0
- data/lib/xmlrpc/parser.rb +642 -0
- data/lib/xmlrpc/server.rb +708 -0
- data/lib/xmlrpc/utils.rb +172 -0
- data/xmlrpc.gemspec +26 -0
- metadata +107 -0
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: false
|
2
|
+
#
|
3
|
+
# $Id$
|
4
|
+
# Configuration file for XML-RPC for Ruby
|
5
|
+
#
|
6
|
+
|
7
|
+
module XMLRPC # :nodoc:
|
8
|
+
|
9
|
+
module Config
|
10
|
+
|
11
|
+
# or XMLWriter::XMLParser
|
12
|
+
DEFAULT_WRITER = XMLWriter::Simple
|
13
|
+
|
14
|
+
# === Available parsers
|
15
|
+
#
|
16
|
+
# * XMLParser::REXMLStreamParser
|
17
|
+
# * XMLParser::LibXMLStreamParser
|
18
|
+
DEFAULT_PARSER = XMLParser::REXMLStreamParser
|
19
|
+
|
20
|
+
# enable <code><nil/></code> tag
|
21
|
+
ENABLE_NIL_CREATE = false
|
22
|
+
ENABLE_NIL_PARSER = false
|
23
|
+
|
24
|
+
# allows integers greater than 32-bit if +true+
|
25
|
+
ENABLE_BIGINT = false
|
26
|
+
|
27
|
+
# enable marshalling Ruby objects which include XMLRPC::Marshallable
|
28
|
+
ENABLE_MARSHALLING = true
|
29
|
+
|
30
|
+
# enable multiCall extension by default
|
31
|
+
ENABLE_MULTICALL = false
|
32
|
+
|
33
|
+
# enable Introspection extension by default
|
34
|
+
ENABLE_INTROSPECTION = false
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
@@ -0,0 +1,287 @@
|
|
1
|
+
# frozen_string_literal: false
|
2
|
+
#
|
3
|
+
# Copyright (C) 2001, 2002, 2003 by Michael Neumann (mneumann@ntecs.de)
|
4
|
+
#
|
5
|
+
# $Id$
|
6
|
+
#
|
7
|
+
|
8
|
+
require "date"
|
9
|
+
require "xmlrpc/base64"
|
10
|
+
|
11
|
+
module XMLRPC # :nodoc:
|
12
|
+
|
13
|
+
module XMLWriter
|
14
|
+
|
15
|
+
class Abstract
|
16
|
+
def ele(name, *children)
|
17
|
+
element(name, nil, *children)
|
18
|
+
end
|
19
|
+
|
20
|
+
def tag(name, txt)
|
21
|
+
element(name, nil, text(txt))
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
class Simple < Abstract
|
27
|
+
|
28
|
+
def document_to_str(doc)
|
29
|
+
doc
|
30
|
+
end
|
31
|
+
|
32
|
+
def document(*params)
|
33
|
+
params.join("")
|
34
|
+
end
|
35
|
+
|
36
|
+
def pi(name, *params)
|
37
|
+
"<?#{name} " + params.join(" ") + " ?>"
|
38
|
+
end
|
39
|
+
|
40
|
+
def element(name, attrs, *children)
|
41
|
+
raise "attributes not yet implemented" unless attrs.nil?
|
42
|
+
if children.empty?
|
43
|
+
"<#{name}/>"
|
44
|
+
else
|
45
|
+
"<#{name}>" + children.join("") + "</#{name}>"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def text(txt)
|
50
|
+
cleaned = txt.dup
|
51
|
+
cleaned.gsub!(/&/, '&')
|
52
|
+
cleaned.gsub!(/</, '<')
|
53
|
+
cleaned.gsub!(/>/, '>')
|
54
|
+
cleaned
|
55
|
+
end
|
56
|
+
|
57
|
+
end # class Simple
|
58
|
+
|
59
|
+
|
60
|
+
class XMLParser < Abstract
|
61
|
+
|
62
|
+
def initialize
|
63
|
+
require "xmltreebuilder"
|
64
|
+
end
|
65
|
+
|
66
|
+
def document_to_str(doc)
|
67
|
+
doc.to_s
|
68
|
+
end
|
69
|
+
|
70
|
+
def document(*params)
|
71
|
+
XML::SimpleTree::Document.new(*params)
|
72
|
+
end
|
73
|
+
|
74
|
+
def pi(name, *params)
|
75
|
+
XML::SimpleTree::ProcessingInstruction.new(name, *params)
|
76
|
+
end
|
77
|
+
|
78
|
+
def element(name, attrs, *children)
|
79
|
+
XML::SimpleTree::Element.new(name, attrs, *children)
|
80
|
+
end
|
81
|
+
|
82
|
+
def text(txt)
|
83
|
+
XML::SimpleTree::Text.new(txt)
|
84
|
+
end
|
85
|
+
|
86
|
+
end # class XMLParser
|
87
|
+
|
88
|
+
Classes = [Simple, XMLParser]
|
89
|
+
|
90
|
+
# yields an instance of each installed XML writer
|
91
|
+
def self.each_installed_writer
|
92
|
+
XMLRPC::XMLWriter::Classes.each do |klass|
|
93
|
+
begin
|
94
|
+
yield klass.new
|
95
|
+
rescue LoadError
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
end # module XMLWriter
|
101
|
+
|
102
|
+
# Creates XML-RPC call/response documents
|
103
|
+
#
|
104
|
+
class Create
|
105
|
+
|
106
|
+
def initialize(xml_writer = nil)
|
107
|
+
@writer = xml_writer || Config::DEFAULT_WRITER.new
|
108
|
+
end
|
109
|
+
|
110
|
+
|
111
|
+
def methodCall(name, *params)
|
112
|
+
name = name.to_s
|
113
|
+
|
114
|
+
if name !~ /[a-zA-Z0-9_.:\/]+/
|
115
|
+
raise ArgumentError, "Wrong XML-RPC method-name"
|
116
|
+
end
|
117
|
+
|
118
|
+
parameter = params.collect do |param|
|
119
|
+
@writer.ele("param", conv2value(param))
|
120
|
+
end
|
121
|
+
|
122
|
+
tree = @writer.document(
|
123
|
+
@writer.pi("xml", 'version="1.0"'),
|
124
|
+
@writer.ele("methodCall",
|
125
|
+
@writer.tag("methodName", name),
|
126
|
+
@writer.ele("params", *parameter)
|
127
|
+
)
|
128
|
+
)
|
129
|
+
|
130
|
+
@writer.document_to_str(tree) + "\n"
|
131
|
+
end
|
132
|
+
|
133
|
+
|
134
|
+
|
135
|
+
#
|
136
|
+
# Generates a XML-RPC methodResponse document
|
137
|
+
#
|
138
|
+
# When +is_ret+ is +false+ then the +params+ array must
|
139
|
+
# contain only one element, which is a structure
|
140
|
+
# of a fault return-value.
|
141
|
+
#
|
142
|
+
# When +is_ret+ is +true+ then a normal
|
143
|
+
# return-value of all the given +params+ is created.
|
144
|
+
#
|
145
|
+
def methodResponse(is_ret, *params)
|
146
|
+
|
147
|
+
if is_ret
|
148
|
+
resp = params.collect do |param|
|
149
|
+
@writer.ele("param", conv2value(param))
|
150
|
+
end
|
151
|
+
|
152
|
+
resp = [@writer.ele("params", *resp)]
|
153
|
+
else
|
154
|
+
if params.size != 1 or params[0] === XMLRPC::FaultException
|
155
|
+
raise ArgumentError, "no valid fault-structure given"
|
156
|
+
end
|
157
|
+
resp = @writer.ele("fault", conv2value(params[0].to_h))
|
158
|
+
end
|
159
|
+
|
160
|
+
|
161
|
+
tree = @writer.document(
|
162
|
+
@writer.pi("xml", 'version="1.0"'),
|
163
|
+
@writer.ele("methodResponse", resp)
|
164
|
+
)
|
165
|
+
|
166
|
+
@writer.document_to_str(tree) + "\n"
|
167
|
+
end
|
168
|
+
|
169
|
+
|
170
|
+
|
171
|
+
private
|
172
|
+
|
173
|
+
#
|
174
|
+
# Converts a Ruby object into a XML-RPC <code><value></code> tag
|
175
|
+
#
|
176
|
+
def conv2value(param) # :doc:
|
177
|
+
|
178
|
+
val = case param
|
179
|
+
when Fixnum, Bignum
|
180
|
+
# XML-RPC's int is 32bit int, and Fixnum also may be beyond 32bit
|
181
|
+
if Config::ENABLE_BIGINT
|
182
|
+
@writer.tag("i4", param.to_s)
|
183
|
+
else
|
184
|
+
if param >= -(2**31) and param <= (2**31-1)
|
185
|
+
@writer.tag("i4", param.to_s)
|
186
|
+
else
|
187
|
+
raise "Bignum is too big! Must be signed 32-bit integer!"
|
188
|
+
end
|
189
|
+
end
|
190
|
+
when TrueClass, FalseClass
|
191
|
+
@writer.tag("boolean", param ? "1" : "0")
|
192
|
+
|
193
|
+
when Symbol
|
194
|
+
@writer.tag("string", param.to_s)
|
195
|
+
|
196
|
+
when String
|
197
|
+
@writer.tag("string", param)
|
198
|
+
|
199
|
+
when NilClass
|
200
|
+
if Config::ENABLE_NIL_CREATE
|
201
|
+
@writer.ele("nil")
|
202
|
+
else
|
203
|
+
raise "Wrong type NilClass. Not allowed!"
|
204
|
+
end
|
205
|
+
|
206
|
+
when Float
|
207
|
+
raise "Wrong value #{param}. Not allowed!" unless param.finite?
|
208
|
+
@writer.tag("double", param.to_s)
|
209
|
+
|
210
|
+
when Struct
|
211
|
+
h = param.members.collect do |key|
|
212
|
+
value = param[key]
|
213
|
+
@writer.ele("member",
|
214
|
+
@writer.tag("name", key.to_s),
|
215
|
+
conv2value(value)
|
216
|
+
)
|
217
|
+
end
|
218
|
+
|
219
|
+
@writer.ele("struct", *h)
|
220
|
+
|
221
|
+
when Hash
|
222
|
+
# TODO: can a Hash be empty?
|
223
|
+
|
224
|
+
h = param.collect do |key, value|
|
225
|
+
@writer.ele("member",
|
226
|
+
@writer.tag("name", key.to_s),
|
227
|
+
conv2value(value)
|
228
|
+
)
|
229
|
+
end
|
230
|
+
|
231
|
+
@writer.ele("struct", *h)
|
232
|
+
|
233
|
+
when Array
|
234
|
+
# TODO: can an Array be empty?
|
235
|
+
a = param.collect {|v| conv2value(v) }
|
236
|
+
|
237
|
+
@writer.ele("array",
|
238
|
+
@writer.ele("data", *a)
|
239
|
+
)
|
240
|
+
|
241
|
+
when Time, Date, ::DateTime
|
242
|
+
@writer.tag("dateTime.iso8601", param.strftime("%Y%m%dT%H:%M:%S"))
|
243
|
+
|
244
|
+
when XMLRPC::DateTime
|
245
|
+
@writer.tag("dateTime.iso8601",
|
246
|
+
format("%.4d%02d%02dT%02d:%02d:%02d", *param.to_a))
|
247
|
+
|
248
|
+
when XMLRPC::Base64
|
249
|
+
@writer.tag("base64", param.encoded)
|
250
|
+
|
251
|
+
else
|
252
|
+
if Config::ENABLE_MARSHALLING and param.class.included_modules.include? XMLRPC::Marshallable
|
253
|
+
# convert Ruby object into Hash
|
254
|
+
ret = {"___class___" => param.class.name}
|
255
|
+
param.instance_variables.each {|v|
|
256
|
+
name = v[1..-1]
|
257
|
+
val = param.instance_variable_get(v)
|
258
|
+
|
259
|
+
if val.nil?
|
260
|
+
ret[name] = val if Config::ENABLE_NIL_CREATE
|
261
|
+
else
|
262
|
+
ret[name] = val
|
263
|
+
end
|
264
|
+
}
|
265
|
+
return conv2value(ret)
|
266
|
+
else
|
267
|
+
ok, pa = wrong_type(param)
|
268
|
+
if ok
|
269
|
+
return conv2value(pa)
|
270
|
+
else
|
271
|
+
raise "Wrong type!"
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
@writer.ele("value", val)
|
277
|
+
end
|
278
|
+
|
279
|
+
def wrong_type(value)
|
280
|
+
false
|
281
|
+
end
|
282
|
+
|
283
|
+
|
284
|
+
end # class Create
|
285
|
+
|
286
|
+
end # module XMLRPC
|
287
|
+
|
@@ -0,0 +1,130 @@
|
|
1
|
+
# frozen_string_literal: false
|
2
|
+
#
|
3
|
+
# xmlrpc/datetime.rb
|
4
|
+
# Copyright (C) 2001, 2002, 2003 by Michael Neumann (mneumann@ntecs.de)
|
5
|
+
#
|
6
|
+
# Released under the same term of license as Ruby.
|
7
|
+
#
|
8
|
+
require "date"
|
9
|
+
|
10
|
+
module XMLRPC # :nodoc:
|
11
|
+
|
12
|
+
# This class is important to handle XMLRPC +dateTime.iso8601+ values,
|
13
|
+
# correctly, because normal UNIX-dates, ie: Date, only handle dates
|
14
|
+
# from year 1970 on, and ruby's native Time class handles dates without the
|
15
|
+
# time component.
|
16
|
+
#
|
17
|
+
# XMLRPC::DateTime is able to store a XMLRPC +dateTime.iso8601+ value correctly.
|
18
|
+
class DateTime
|
19
|
+
|
20
|
+
# Return the value of the specified date/time component.
|
21
|
+
attr_reader :year, :month, :day, :hour, :min, :sec
|
22
|
+
|
23
|
+
# Set +value+ as the new date/time component.
|
24
|
+
#
|
25
|
+
# Raises ArgumentError if the given +value+ is out of range, or in the case
|
26
|
+
# of XMLRPC::DateTime#year= if +value+ is not of type Integer.
|
27
|
+
def year= (value)
|
28
|
+
raise ArgumentError, "date/time out of range" unless value.is_a? Integer
|
29
|
+
@year = value
|
30
|
+
end
|
31
|
+
|
32
|
+
# Set +value+ as the new date/time component.
|
33
|
+
#
|
34
|
+
# Raises an ArgumentError if the given +value+ isn't between 1 and 12.
|
35
|
+
def month= (value)
|
36
|
+
raise ArgumentError, "date/time out of range" unless (1..12).include? value
|
37
|
+
@month = value
|
38
|
+
end
|
39
|
+
|
40
|
+
# Set +value+ as the new date/time component.
|
41
|
+
#
|
42
|
+
# Raises an ArgumentError if the given +value+ isn't between 1 and 31.
|
43
|
+
def day= (value)
|
44
|
+
raise ArgumentError, "date/time out of range" unless (1..31).include? value
|
45
|
+
@day = value
|
46
|
+
end
|
47
|
+
|
48
|
+
# Set +value+ as the new date/time component.
|
49
|
+
#
|
50
|
+
# Raises an ArgumentError if the given +value+ isn't between 0 and 24.
|
51
|
+
def hour= (value)
|
52
|
+
raise ArgumentError, "date/time out of range" unless (0..24).include? value
|
53
|
+
@hour = value
|
54
|
+
end
|
55
|
+
|
56
|
+
# Set +value+ as the new date/time component.
|
57
|
+
#
|
58
|
+
# Raises an ArgumentError if the given +value+ isn't between 0 and 59.
|
59
|
+
def min= (value)
|
60
|
+
raise ArgumentError, "date/time out of range" unless (0..59).include? value
|
61
|
+
@min = value
|
62
|
+
end
|
63
|
+
|
64
|
+
# Set +value+ as the new date/time component.
|
65
|
+
#
|
66
|
+
# Raises an ArgumentError if the given +value+ isn't between 0 and 59.
|
67
|
+
def sec= (value)
|
68
|
+
raise ArgumentError, "date/time out of range" unless (0..59).include? value
|
69
|
+
@sec = value
|
70
|
+
end
|
71
|
+
|
72
|
+
# Alias for XMLRPC::DateTime#month.
|
73
|
+
alias mon month
|
74
|
+
# Alias for XMLRPC::DateTime#month=.
|
75
|
+
alias mon= month=
|
76
|
+
|
77
|
+
|
78
|
+
# Creates a new XMLRPC::DateTime instance with the
|
79
|
+
# parameters +year+, +month+, +day+ as date and
|
80
|
+
# +hour+, +min+, +sec+ as time.
|
81
|
+
#
|
82
|
+
# Raises an ArgumentError if a parameter is out of range,
|
83
|
+
# or if +year+ is not of the Integer type.
|
84
|
+
def initialize(year, month, day, hour, min, sec)
|
85
|
+
self.year, self.month, self.day = year, month, day
|
86
|
+
self.hour, self.min, self.sec = hour, min, sec
|
87
|
+
end
|
88
|
+
|
89
|
+
# Return a Time object of the date/time which represents +self+.
|
90
|
+
# If the <code>@year</code> is below 1970, this method returns +nil+,
|
91
|
+
# because Time cannot handle years below 1970.
|
92
|
+
#
|
93
|
+
# The timezone used is GMT.
|
94
|
+
def to_time
|
95
|
+
if @year >= 1970
|
96
|
+
Time.gm(*to_a)
|
97
|
+
else
|
98
|
+
nil
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Return a Date object of the date which represents +self+.
|
103
|
+
#
|
104
|
+
# The Date object do _not_ contain the time component (only date).
|
105
|
+
def to_date
|
106
|
+
Date.new(*to_a[0,3])
|
107
|
+
end
|
108
|
+
|
109
|
+
# Returns all date/time components in an array.
|
110
|
+
#
|
111
|
+
# Returns +[year, month, day, hour, min, sec]+.
|
112
|
+
def to_a
|
113
|
+
[@year, @month, @day, @hour, @min, @sec]
|
114
|
+
end
|
115
|
+
|
116
|
+
# Returns whether or not all date/time components are an array.
|
117
|
+
def ==(o)
|
118
|
+
self.to_a == Array(o) rescue false
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
|
123
|
+
|
124
|
+
end # module XMLRPC
|
125
|
+
|
126
|
+
|
127
|
+
=begin
|
128
|
+
= History
|
129
|
+
$Id$
|
130
|
+
=end
|