xmlrpc 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,67 @@
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 "xmlrpc/parser"
9
+ require "xmlrpc/create"
10
+ require "xmlrpc/config"
11
+ require "xmlrpc/utils"
12
+
13
+ module XMLRPC # :nodoc:
14
+
15
+ # Marshalling of XMLRPC::Create#methodCall and XMLRPC::Create#methodResponse
16
+ class Marshal
17
+ include ParserWriterChooseMixin
18
+
19
+ class << self
20
+
21
+ def dump_call( methodName, *params )
22
+ new.dump_call( methodName, *params )
23
+ end
24
+
25
+ def dump_response( param )
26
+ new.dump_response( param )
27
+ end
28
+
29
+ def load_call( stringOrReadable )
30
+ new.load_call( stringOrReadable )
31
+ end
32
+
33
+ def load_response( stringOrReadable )
34
+ new.load_response( stringOrReadable )
35
+ end
36
+
37
+ alias dump dump_response
38
+ alias load load_response
39
+
40
+ end # class self
41
+
42
+ def initialize( parser = nil, writer = nil )
43
+ set_parser( parser )
44
+ set_writer( writer )
45
+ end
46
+
47
+ def dump_call( methodName, *params )
48
+ create.methodCall( methodName, *params )
49
+ end
50
+
51
+ def dump_response( param )
52
+ create.methodResponse( ! param.kind_of?( XMLRPC::FaultException ) , param )
53
+ end
54
+
55
+ # Returns <code>[ methodname, params ]</code>
56
+ def load_call( stringOrReadable )
57
+ parser.parseMethodCall( stringOrReadable )
58
+ end
59
+
60
+ # Returns +paramOrFault+
61
+ def load_response( stringOrReadable )
62
+ parser.parseMethodResponse( stringOrReadable )[1]
63
+ end
64
+
65
+ end # class Marshal
66
+
67
+ end
@@ -0,0 +1,642 @@
1
+ # frozen_string_literal: false
2
+ # Copyright (C) 2001, 2002, 2003 by Michael Neumann (mneumann@ntecs.de)
3
+ #
4
+ # $Id$
5
+ #
6
+
7
+
8
+ require "date"
9
+ require "xmlrpc/base64"
10
+ require "xmlrpc/datetime"
11
+
12
+
13
+ module XMLRPC # :nodoc:
14
+
15
+ # Raised when the remote procedure returns a fault-structure, which has two
16
+ # accessor-methods +faultCode+ an Integer, and +faultString+ a String.
17
+ class FaultException < StandardError
18
+ attr_reader :faultCode, :faultString
19
+
20
+ # Creates a new XMLRPC::FaultException instance.
21
+ #
22
+ # +faultString+ is passed to StandardError as the +msg+ of the Exception.
23
+ def initialize(faultCode, faultString)
24
+ @faultCode = faultCode
25
+ @faultString = faultString
26
+ super(@faultString)
27
+ end
28
+
29
+ # The +faultCode+ and +faultString+ of the exception in a Hash.
30
+ def to_h
31
+ {"faultCode" => @faultCode, "faultString" => @faultString}
32
+ end
33
+ end
34
+
35
+ # Helper class used to convert types.
36
+ module Convert
37
+
38
+ # Converts a String to an Integer
39
+ #
40
+ # See also String.to_i
41
+ def self.int(str)
42
+ str.to_i
43
+ end
44
+
45
+ # Converts a String to +true+ or +false+
46
+ #
47
+ # Raises an exception if +str+ is not +0+ or +1+
48
+ def self.boolean(str)
49
+ case str
50
+ when "0" then false
51
+ when "1" then true
52
+ else
53
+ raise "RPC-value of type boolean is wrong"
54
+ end
55
+ end
56
+
57
+ # Converts a String to a Float
58
+ #
59
+ # See also String.to_f
60
+ def self.double(str)
61
+ str.to_f
62
+ end
63
+
64
+ # Converts a the given +str+ to a +dateTime.iso8601+ formatted date.
65
+ #
66
+ # Raises an exception if the String isn't in +dateTime.iso8601+ format.
67
+ #
68
+ # See also, XMLRPC::DateTime
69
+ def self.dateTime(str)
70
+ case str
71
+ when /^(-?\d\d\d\d)-?(\d\d)-?(\d\d)T(\d\d):(\d\d):(\d\d)(?:Z|([+-])(\d\d):?(\d\d))?$/
72
+ a = [$1, $2, $3, $4, $5, $6].collect{|i| i.to_i}
73
+ if $7
74
+ ofs = $8.to_i*3600 + $9.to_i*60
75
+ ofs = -ofs if $7=='+'
76
+ utc = Time.utc(*a) + ofs
77
+ a = [ utc.year, utc.month, utc.day, utc.hour, utc.min, utc.sec ]
78
+ end
79
+ XMLRPC::DateTime.new(*a)
80
+ when /^(-?\d\d)-?(\d\d)-?(\d\d)T(\d\d):(\d\d):(\d\d)(Z|([+-]\d\d):(\d\d))?$/
81
+ a = [$1, $2, $3, $4, $5, $6].collect{|i| i.to_i}
82
+ if a[0] < 70
83
+ a[0] += 2000
84
+ else
85
+ a[0] += 1900
86
+ end
87
+ if $7
88
+ ofs = $8.to_i*3600 + $9.to_i*60
89
+ ofs = -ofs if $7=='+'
90
+ utc = Time.utc(*a) + ofs
91
+ a = [ utc.year, utc.month, utc.day, utc.hour, utc.min, utc.sec ]
92
+ end
93
+ XMLRPC::DateTime.new(*a)
94
+ else
95
+ raise "wrong dateTime.iso8601 format " + str
96
+ end
97
+ end
98
+
99
+ # Decodes the given +str+ using XMLRPC::Base64.decode
100
+ def self.base64(str)
101
+ XMLRPC::Base64.decode(str)
102
+ end
103
+
104
+ # Converts the given +hash+ to a marshalled object.
105
+ #
106
+ # Returns the given +hash+ if an exception occurs.
107
+ def self.struct(hash)
108
+ # convert to marshalled object
109
+ klass = hash["___class___"]
110
+ if klass.nil? or Config::ENABLE_MARSHALLING == false
111
+ hash
112
+ else
113
+ begin
114
+ mod = Module
115
+ klass.split("::").each {|const| mod = mod.const_get(const.strip)}
116
+
117
+ obj = mod.allocate
118
+
119
+ hash.delete "___class___"
120
+ hash.each {|key, value|
121
+ obj.instance_variable_set("@#{ key }", value) if key =~ /^([a-zA-Z_]\w*)$/
122
+ }
123
+ obj
124
+ rescue
125
+ hash
126
+ end
127
+ end
128
+ end
129
+
130
+ # Converts the given +hash+ to an XMLRPC::FaultException object by passing
131
+ # the +faultCode+ and +faultString+ attributes of the Hash to
132
+ # XMLRPC::FaultException.new
133
+ #
134
+ # Raises an Exception if the given +hash+ doesn't meet the requirements.
135
+ # Those requirements being:
136
+ # * 2 keys
137
+ # * <code>'faultCode'</code> key is an Integer
138
+ # * <code>'faultString'</code> key is a String
139
+ def self.fault(hash)
140
+ if hash.kind_of? Hash and hash.size == 2 and
141
+ hash.has_key? "faultCode" and hash.has_key? "faultString" and
142
+ hash["faultCode"].kind_of? Integer and hash["faultString"].kind_of? String
143
+
144
+ XMLRPC::FaultException.new(hash["faultCode"], hash["faultString"])
145
+ else
146
+ raise "wrong fault-structure: #{hash.inspect}"
147
+ end
148
+ end
149
+
150
+ end # module Convert
151
+
152
+ # Parser for XML-RPC call and response
153
+ module XMLParser
154
+
155
+ class AbstractTreeParser
156
+
157
+ def parseMethodResponse(str)
158
+ methodResponse_document(createCleanedTree(str))
159
+ end
160
+
161
+ def parseMethodCall(str)
162
+ methodCall_document(createCleanedTree(str))
163
+ end
164
+
165
+ private
166
+
167
+ # Removes all whitespaces but in the tags i4, i8, int, boolean....
168
+ # and all comments
169
+ def removeWhitespacesAndComments(node)
170
+ remove = []
171
+ childs = node.childNodes.to_a
172
+ childs.each do |nd|
173
+ case _nodeType(nd)
174
+ when :TEXT
175
+ # TODO: add nil?
176
+ unless %w(i4 i8 int boolean string double dateTime.iso8601 base64).include? node.nodeName
177
+
178
+ if node.nodeName == "value"
179
+ if not node.childNodes.to_a.detect {|n| _nodeType(n) == :ELEMENT}.nil?
180
+ remove << nd if nd.nodeValue.strip == ""
181
+ end
182
+ else
183
+ remove << nd if nd.nodeValue.strip == ""
184
+ end
185
+ end
186
+ when :COMMENT
187
+ remove << nd
188
+ else
189
+ removeWhitespacesAndComments(nd)
190
+ end
191
+ end
192
+
193
+ remove.each { |i| node.removeChild(i) }
194
+ end
195
+
196
+
197
+ def nodeMustBe(node, name)
198
+ cmp = case name
199
+ when Array
200
+ name.include?(node.nodeName)
201
+ when String
202
+ name == node.nodeName
203
+ else
204
+ raise "error"
205
+ end
206
+
207
+ if not cmp then
208
+ raise "wrong xml-rpc (name)"
209
+ end
210
+
211
+ node
212
+ end
213
+
214
+ # Returns, when successfully the only child-node
215
+ def hasOnlyOneChild(node, name=nil)
216
+ if node.childNodes.to_a.size != 1
217
+ raise "wrong xml-rpc (size)"
218
+ end
219
+ if name != nil then
220
+ nodeMustBe(node.firstChild, name)
221
+ end
222
+ end
223
+
224
+
225
+ def assert(b)
226
+ if not b then
227
+ raise "assert-fail"
228
+ end
229
+ end
230
+
231
+ # The node `node` has empty string or string
232
+ def text_zero_one(node)
233
+ nodes = node.childNodes.to_a.size
234
+
235
+ if nodes == 1
236
+ text(node.firstChild)
237
+ elsif nodes == 0
238
+ ""
239
+ else
240
+ raise "wrong xml-rpc (size)"
241
+ end
242
+ end
243
+
244
+
245
+ def integer(node)
246
+ #TODO: check string for float because to_i returnsa
247
+ # 0 when wrong string
248
+ nodeMustBe(node, %w(i4 i8 int))
249
+ hasOnlyOneChild(node)
250
+
251
+ Convert.int(text(node.firstChild))
252
+ end
253
+
254
+ def boolean(node)
255
+ nodeMustBe(node, "boolean")
256
+ hasOnlyOneChild(node)
257
+
258
+ Convert.boolean(text(node.firstChild))
259
+ end
260
+
261
+ def v_nil(node)
262
+ nodeMustBe(node, "nil")
263
+ assert( node.childNodes.to_a.size == 0 )
264
+ nil
265
+ end
266
+
267
+ def string(node)
268
+ nodeMustBe(node, "string")
269
+ text_zero_one(node)
270
+ end
271
+
272
+ def double(node)
273
+ #TODO: check string for float because to_f returnsa
274
+ # 0.0 when wrong string
275
+ nodeMustBe(node, "double")
276
+ hasOnlyOneChild(node)
277
+
278
+ Convert.double(text(node.firstChild))
279
+ end
280
+
281
+ def dateTime(node)
282
+ nodeMustBe(node, "dateTime.iso8601")
283
+ hasOnlyOneChild(node)
284
+
285
+ Convert.dateTime( text(node.firstChild) )
286
+ end
287
+
288
+ def base64(node)
289
+ nodeMustBe(node, "base64")
290
+ #hasOnlyOneChild(node)
291
+
292
+ Convert.base64(text_zero_one(node))
293
+ end
294
+
295
+ def member(node)
296
+ nodeMustBe(node, "member")
297
+ assert( node.childNodes.to_a.size == 2 )
298
+
299
+ [ name(node[0]), value(node[1]) ]
300
+ end
301
+
302
+ def name(node)
303
+ nodeMustBe(node, "name")
304
+ #hasOnlyOneChild(node)
305
+ text_zero_one(node)
306
+ end
307
+
308
+ def array(node)
309
+ nodeMustBe(node, "array")
310
+ hasOnlyOneChild(node, "data")
311
+ data(node.firstChild)
312
+ end
313
+
314
+ def data(node)
315
+ nodeMustBe(node, "data")
316
+
317
+ node.childNodes.to_a.collect do |val|
318
+ value(val)
319
+ end
320
+ end
321
+
322
+ def param(node)
323
+ nodeMustBe(node, "param")
324
+ hasOnlyOneChild(node, "value")
325
+ value(node.firstChild)
326
+ end
327
+
328
+ def methodResponse(node)
329
+ nodeMustBe(node, "methodResponse")
330
+ hasOnlyOneChild(node, %w(params fault))
331
+ child = node.firstChild
332
+
333
+ case child.nodeName
334
+ when "params"
335
+ [ true, params(child,false) ]
336
+ when "fault"
337
+ [ false, fault(child) ]
338
+ else
339
+ raise "unexpected error"
340
+ end
341
+
342
+ end
343
+
344
+ def methodName(node)
345
+ nodeMustBe(node, "methodName")
346
+ hasOnlyOneChild(node)
347
+ text(node.firstChild)
348
+ end
349
+
350
+ def params(node, call=true)
351
+ nodeMustBe(node, "params")
352
+
353
+ if call
354
+ node.childNodes.to_a.collect do |n|
355
+ param(n)
356
+ end
357
+ else # response (only one param)
358
+ hasOnlyOneChild(node)
359
+ param(node.firstChild)
360
+ end
361
+ end
362
+
363
+ def fault(node)
364
+ nodeMustBe(node, "fault")
365
+ hasOnlyOneChild(node, "value")
366
+ f = value(node.firstChild)
367
+ Convert.fault(f)
368
+ end
369
+
370
+
371
+
372
+ # _nodeType is defined in the subclass
373
+ def text(node)
374
+ assert( _nodeType(node) == :TEXT )
375
+ assert( node.hasChildNodes == false )
376
+ assert( node.nodeValue != nil )
377
+
378
+ node.nodeValue.to_s
379
+ end
380
+
381
+ def struct(node)
382
+ nodeMustBe(node, "struct")
383
+
384
+ hash = {}
385
+ node.childNodes.to_a.each do |me|
386
+ n, v = member(me)
387
+ hash[n] = v
388
+ end
389
+
390
+ Convert.struct(hash)
391
+ end
392
+
393
+
394
+ def value(node)
395
+ nodeMustBe(node, "value")
396
+ nodes = node.childNodes.to_a.size
397
+ if nodes == 0
398
+ return ""
399
+ elsif nodes > 1
400
+ raise "wrong xml-rpc (size)"
401
+ end
402
+
403
+ child = node.firstChild
404
+
405
+ case _nodeType(child)
406
+ when :TEXT
407
+ text_zero_one(node)
408
+ when :ELEMENT
409
+ case child.nodeName
410
+ when "i4", "i8", "int" then integer(child)
411
+ when "boolean" then boolean(child)
412
+ when "string" then string(child)
413
+ when "double" then double(child)
414
+ when "dateTime.iso8601" then dateTime(child)
415
+ when "base64" then base64(child)
416
+ when "struct" then struct(child)
417
+ when "array" then array(child)
418
+ when "nil"
419
+ if Config::ENABLE_NIL_PARSER
420
+ v_nil(child)
421
+ else
422
+ raise "wrong/unknown XML-RPC type 'nil'"
423
+ end
424
+ else
425
+ raise "wrong/unknown XML-RPC type"
426
+ end
427
+ else
428
+ raise "wrong type of node"
429
+ end
430
+
431
+ end
432
+
433
+ def methodCall(node)
434
+ nodeMustBe(node, "methodCall")
435
+ assert( (1..2).include?( node.childNodes.to_a.size ) )
436
+ name = methodName(node[0])
437
+
438
+ if node.childNodes.to_a.size == 2 then
439
+ pa = params(node[1])
440
+ else # no parameters given
441
+ pa = []
442
+ end
443
+ [name, pa]
444
+ end
445
+
446
+ end # module TreeParserMixin
447
+
448
+ class AbstractStreamParser
449
+ def parseMethodResponse(str)
450
+ parser = @parser_class.new
451
+ parser.parse(str)
452
+ raise "No valid method response!" if parser.method_name != nil
453
+ if parser.fault != nil
454
+ # is a fault structure
455
+ [false, parser.fault]
456
+ else
457
+ # is a normal return value
458
+ raise "Missing return value!" if parser.params.size == 0
459
+ raise "Too many return values. Only one allowed!" if parser.params.size > 1
460
+ [true, parser.params[0]]
461
+ end
462
+ end
463
+
464
+ def parseMethodCall(str)
465
+ parser = @parser_class.new
466
+ parser.parse(str)
467
+ raise "No valid method call - missing method name!" if parser.method_name.nil?
468
+ [parser.method_name, parser.params]
469
+ end
470
+ end
471
+
472
+ module StreamParserMixin
473
+ attr_reader :params
474
+ attr_reader :method_name
475
+ attr_reader :fault
476
+
477
+ def initialize(*a)
478
+ super(*a)
479
+ @params = []
480
+ @values = []
481
+ @val_stack = []
482
+
483
+ @names = []
484
+ @name = []
485
+
486
+ @structs = []
487
+ @struct = {}
488
+
489
+ @method_name = nil
490
+ @fault = nil
491
+
492
+ @data = nil
493
+ end
494
+
495
+ def startElement(name, attrs=[])
496
+ @data = nil
497
+ case name
498
+ when "value"
499
+ @value = nil
500
+ when "nil"
501
+ raise "wrong/unknown XML-RPC type 'nil'" unless Config::ENABLE_NIL_PARSER
502
+ @value = :nil
503
+ when "array"
504
+ @val_stack << @values
505
+ @values = []
506
+ when "struct"
507
+ @names << @name
508
+ @name = []
509
+
510
+ @structs << @struct
511
+ @struct = {}
512
+ end
513
+ end
514
+
515
+ def endElement(name)
516
+ @data ||= ""
517
+ case name
518
+ when "string"
519
+ @value = @data
520
+ when "i4", "i8", "int"
521
+ @value = Convert.int(@data)
522
+ when "boolean"
523
+ @value = Convert.boolean(@data)
524
+ when "double"
525
+ @value = Convert.double(@data)
526
+ when "dateTime.iso8601"
527
+ @value = Convert.dateTime(@data)
528
+ when "base64"
529
+ @value = Convert.base64(@data)
530
+ when "value"
531
+ @value = @data if @value.nil?
532
+ @values << (@value == :nil ? nil : @value)
533
+ when "array"
534
+ @value = @values
535
+ @values = @val_stack.pop
536
+ when "struct"
537
+ @value = Convert.struct(@struct)
538
+
539
+ @name = @names.pop
540
+ @struct = @structs.pop
541
+ when "name"
542
+ @name[0] = @data
543
+ when "member"
544
+ @struct[@name[0]] = @values.pop
545
+
546
+ when "param"
547
+ @params << @values[0]
548
+ @values = []
549
+
550
+ when "fault"
551
+ @fault = Convert.fault(@values[0])
552
+
553
+ when "methodName"
554
+ @method_name = @data
555
+ end
556
+
557
+ @data = nil
558
+ end
559
+
560
+ def character(data)
561
+ if @data
562
+ @data << data
563
+ else
564
+ @data = data
565
+ end
566
+ end
567
+
568
+ end # module StreamParserMixin
569
+
570
+ class REXMLStreamParser < AbstractStreamParser
571
+ def initialize
572
+ require "rexml/document"
573
+ @parser_class = StreamListener
574
+ end
575
+
576
+ class StreamListener
577
+ include StreamParserMixin
578
+
579
+ alias :tag_start :startElement
580
+ alias :tag_end :endElement
581
+ alias :text :character
582
+ alias :cdata :character
583
+
584
+ def method_missing(*a)
585
+ # ignore
586
+ end
587
+
588
+ def parse(str)
589
+ REXML::Document.parse_stream(str, self)
590
+ end
591
+ end
592
+
593
+ end
594
+
595
+ class LibXMLStreamParser < AbstractStreamParser
596
+ def initialize
597
+ require 'libxml'
598
+ @parser_class = LibXMLStreamListener
599
+ end
600
+
601
+ class LibXMLStreamListener
602
+ include StreamParserMixin
603
+
604
+ def on_start_element_ns(name, attributes, prefix, uri, namespaces)
605
+ startElement(name)
606
+ end
607
+
608
+ def on_end_element_ns(name, prefix, uri)
609
+ endElement(name)
610
+ end
611
+
612
+ alias :on_characters :character
613
+ alias :on_cdata_block :character
614
+
615
+ def method_missing(*a)
616
+ end
617
+
618
+ def parse(str)
619
+ parser = LibXML::XML::SaxParser.string(str)
620
+ parser.callbacks = self
621
+ parser.parse()
622
+ end
623
+ end
624
+ end
625
+
626
+ Classes = [REXMLStreamParser, LibXMLStreamParser]
627
+
628
+ # yields an instance of each installed parser
629
+ def self.each_installed_parser
630
+ XMLRPC::XMLParser::Classes.each do |klass|
631
+ begin
632
+ yield klass.new
633
+ rescue LoadError
634
+ end
635
+ end
636
+ end
637
+
638
+ end # module XMLParser
639
+
640
+
641
+ end # module XMLRPC
642
+