pax_global_header00006660000000000000000000000064140070636560014521gustar00rootroot0000000000000052 comment=e57c3b7d0f3522c36806b30d45b6d787346e8fa8 xmlrpc-0.3.2/000077500000000000000000000000001400706365600130305ustar00rootroot00000000000000xmlrpc-0.3.2/.github/000077500000000000000000000000001400706365600143705ustar00rootroot00000000000000xmlrpc-0.3.2/.github/workflows/000077500000000000000000000000001400706365600164255ustar00rootroot00000000000000xmlrpc-0.3.2/.github/workflows/test.yml000066400000000000000000000013101400706365600201220ustar00rootroot00000000000000name: Test on: - push - pull_request jobs: test: name: >- ${{ matrix.os }} ${{ matrix.ruby }} runs-on: ${{ matrix.os }}-latest strategy: fail-fast: false matrix: os: - ubuntu - macos - windows ruby: - "2.5" - "2.6" - "2.7" - "3.0" - head include: - { os: windows , ruby: mingw } - { os: windows , ruby: mswin } exclude: - { os: windows , ruby: head } steps: - uses: actions/checkout@v2 - uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby }} - run: bundle install - run: rake xmlrpc-0.3.2/.gitignore000066400000000000000000000001271400706365600150200ustar00rootroot00000000000000/.bundle/ /.yardoc /Gemfile.lock /_yardoc/ /coverage/ /doc/ /pkg/ /spec/reports/ /tmp/ xmlrpc-0.3.2/Gemfile000066400000000000000000000000471400706365600143240ustar00rootroot00000000000000source 'https://rubygems.org' gemspec xmlrpc-0.3.2/LICENSE.txt000066400000000000000000000047101400706365600146550ustar00rootroot00000000000000Ruby is copyrighted free software by Yukihiro Matsumoto . You can redistribute it and/or modify it under either the terms of the 2-clause BSDL (see the file BSDL), or the conditions below: 1. You may make and give away verbatim copies of the source form of the software without restriction, provided that you duplicate all of the original copyright notices and associated disclaimers. 2. You may modify your copy of the software in any way, provided that you do at least ONE of the following: a) place your modifications in the Public Domain or otherwise make them Freely Available, such as by posting said modifications to Usenet or an equivalent medium, or by allowing the author to include your modifications in the software. b) use the modified software only within your corporation or organization. c) give non-standard binaries non-standard names, with instructions on where to get the original software distribution. d) make other distribution arrangements with the author. 3. You may distribute the software in object code or binary form, provided that you do at least ONE of the following: a) distribute the binaries and library files of the software, together with instructions (in the manual page or equivalent) on where to get the original distribution. b) accompany the distribution with the machine-readable source of the software. c) give non-standard binaries non-standard names, with instructions on where to get the original software distribution. d) make other distribution arrangements with the author. 4. You may modify and include the part of the software into any other software (possibly commercial). But some files in the distribution are not written by the author, so that they are not under these terms. For the list of those files and their copying conditions, see the file LEGAL. 5. The scripts and library files supplied as input to or produced as output from the software do not automatically fall under the copyright of the software, but belong to whomever generated them, and may be sold commercially, and may be aggregated with this software. 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. xmlrpc-0.3.2/NEWS.md000066400000000000000000000012541400706365600141300ustar00rootroot00000000000000# News ## 0.3.2 ### Improvements * Added support for Ruby 3.0. [GitHub#27][Reported by Herwin Weststrate] ### Thanks * Herwin Weststrate ## 0.3.1 ### Improvements * Added support for comparing `XMLRPC::Base64`. [GitHub#14][Patch by Herwin Weststrate] * Added support for `application/xml` as `Content-Type`. [GitHub#24][Reported by Reiermeyer] * Added `XMLRPC::Server#port`. [GitHub#17][Reported by Harald Sitter] [GitHub#18][Patch by Herwin Weststrate] ### Fixes * Fixed a bug that unexpected exception is raised on no data. [GitHub#25][Patch by Herwin Weststrate] ### Thanks * Herwin Weststrate * Reiermeyer * Harald Sitter xmlrpc-0.3.2/README.md000066400000000000000000000034241400706365600143120ustar00rootroot00000000000000# XMLRPC [![Build Status](https://travis-ci.org/ruby/xmlrpc.svg?branch=master)](https://travis-ci.org/ruby/xmlrpc) ## Overview XMLRPC is a lightweight protocol that enables remote procedure calls over HTTP. It is defined at http://www.xmlrpc.com. XMLRPC allows you to create simple distributed computing solutions that span computer languages. Its distinctive feature is its simplicity compared to other approaches like SOAP and CORBA. The Ruby standard library package 'xmlrpc' enables you to create a server that implements remote procedures and a client that calls them. Very little code is required to achieve either of these. ## Installation Add this line to your application's Gemfile: ```ruby gem 'xmlrpc' ``` And then execute: $ bundle Or install it yourself as: $ gem install xmlrpc ## Example Try the following code. It calls a standard demonstration remote procedure. ```ruby require 'xmlrpc/client' require 'pp' server = XMLRPC::Client.new2("http://xmlrpc-c.sourceforge.net/api/sample.php") result = server.call("sample.sumAndDifference", 5, 3) pp result ``` ## Development After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org). ## Contributing Bug reports and pull requests are welcome on GitHub at https://github.com/ruby/xmlrpc. ## License Released under the same term of license as Ruby. xmlrpc-0.3.2/Rakefile000066400000000000000000000003061400706365600144740ustar00rootroot00000000000000require "bundler/gem_tasks" require "rake/testtask" Rake::TestTask.new(:test) do |t| t.libs << "test" t.libs << "lib" t.test_files = FileList['test/**/test_*.rb'] end task :default => :test xmlrpc-0.3.2/bin/000077500000000000000000000000001400706365600136005ustar00rootroot00000000000000xmlrpc-0.3.2/bin/console000077500000000000000000000005131400706365600151670ustar00rootroot00000000000000#!/usr/bin/env ruby require "bundler/setup" require "xmlrpc" # You can add fixtures and/or initialization code here to make experimenting # with your gem easier. You can also use a different console, if you like. # (If you use this, don't forget to add pry to your Gemfile!) # require "pry" # Pry.start require "irb" IRB.start xmlrpc-0.3.2/bin/setup000077500000000000000000000002031400706365600146610ustar00rootroot00000000000000#!/usr/bin/env bash set -euo pipefail IFS=$'\n\t' set -vx bundle install # Do any other automated setup that you need to do here xmlrpc-0.3.2/lib/000077500000000000000000000000001400706365600135765ustar00rootroot00000000000000xmlrpc-0.3.2/lib/xmlrpc.rb000066400000000000000000000177671400706365600154520ustar00rootroot00000000000000# frozen_string_literal: false # == Author and Copyright # # Copyright (C) 2001-2004 by Michael Neumann (mailto:mneumann@ntecs.de) # # Released under the same term of license as Ruby. # # == Overview # # XMLRPC is a lightweight protocol that enables remote procedure calls over # HTTP. It is defined at http://www.xmlrpc.com. # # XMLRPC allows you to create simple distributed computing solutions that span # computer languages. Its distinctive feature is its simplicity compared to # other approaches like SOAP and CORBA. # # The Ruby standard library package 'xmlrpc' enables you to create a server that # implements remote procedures and a client that calls them. Very little code # is required to achieve either of these. # # == Example # # Try the following code. It calls a standard demonstration remote procedure. # # require 'xmlrpc/client' # require 'pp' # # server = XMLRPC::Client.new2("http://xmlrpc-c.sourceforge.net/api/sample.php") # result = server.call("sample.sumAndDifference", 5, 3) # pp result # # == Documentation # # See http://www.ntecs.de/ruby/xmlrpc4r/. There is plenty of detail there to # use the client and implement a server. # # == Features of XMLRPC for Ruby # # * Extensions # * Introspection # * multiCall # * optionally nil values and integers larger than 32 Bit # # * Server # * Standalone XML-RPC server # * CGI-based (works with FastCGI) # * Apache mod_ruby server # * WEBrick servlet # # * Client # * synchronous/asynchronous calls # * Basic HTTP-401 Authentication # * HTTPS protocol (SSL) # # * Parsers # * REXML (XMLParser::REXMLStreamParser) # * Not compiled (pure ruby) # * See ruby standard library # * libxml (LibXMLStreamParser) # * Compiled # * See https://rubygems.org/gems/libxml-ruby/ # # * General # * possible to choose between XMLParser module (Expat wrapper) and REXML (pure Ruby) parsers # * Marshalling Ruby objects to Hashes and reconstruct them later from a Hash # * SandStorm component architecture XMLRPC::Client interface # # == Howto # # === Client # # require "xmlrpc/client" # # # Make an object to represent the XML-RPC server. # server = XMLRPC::Client.new( "xmlrpc-c.sourceforge.net", "/api/sample.php") # # # Call the remote server and get our result # result = server.call("sample.sumAndDifference", 5, 3) # # sum = result["sum"] # difference = result["difference"] # # puts "Sum: #{sum}, Difference: #{difference}" # # === XMLRPC::Client with XML-RPC fault-structure handling # # There are two possible ways, of handling a fault-structure: # # ==== by catching a XMLRPC::FaultException exception # # require "xmlrpc/client" # # # Make an object to represent the XML-RPC server. # server = XMLRPC::Client.new( "xmlrpc-c.sourceforge.net", "/api/sample.php") # # begin # # Call the remote server and get our result # result = server.call("sample.sumAndDifference", 5, 3) # # sum = result["sum"] # difference = result["difference"] # # puts "Sum: #{sum}, Difference: #{difference}" # # rescue XMLRPC::FaultException => e # puts "Error: " # puts e.faultCode # puts e.faultString # end # # ==== by calling "call2" which returns a boolean # # require "xmlrpc/client" # # # Make an object to represent the XML-RPC server. # server = XMLRPC::Client.new( "xmlrpc-c.sourceforge.net", "/api/sample.php") # # # Call the remote server and get our result # ok, result = server.call2("sample.sumAndDifference", 5, 3) # # if ok # sum = result["sum"] # difference = result["difference"] # # puts "Sum: #{sum}, Difference: #{difference}" # else # puts "Error: " # puts result.faultCode # puts result.faultString # end # # === Using XMLRPC::Client::Proxy # # You can create a Proxy object onto which you can call methods. This way it # looks nicer. Both forms, _call_ and _call2_ are supported through _proxy_ and # _proxy2_. You can additionally give arguments to the Proxy, which will be # given to each XML-RPC call using that Proxy. # # require "xmlrpc/client" # # # Make an object to represent the XML-RPC server. # server = XMLRPC::Client.new( "xmlrpc-c.sourceforge.net", "/api/sample.php") # # # Create a Proxy object # sample = server.proxy("sample") # # # Call the remote server and get our result # result = sample.sumAndDifference(5,3) # # sum = result["sum"] # difference = result["difference"] # # puts "Sum: #{sum}, Difference: #{difference}" # # === CGI-based server using XMLRPC::CGIServer # # There are also two ways to define handler, the first is # like C/PHP, the second like Java, of course both ways # can be mixed: # # ==== C/PHP-like (handler functions) # # require "xmlrpc/server" # # s = XMLRPC::CGIServer.new # # s.add_handler("sample.sumAndDifference") do |a,b| # { "sum" => a + b, "difference" => a - b } # end # # s.serve # # ==== Java-like (handler classes) # # require "xmlrpc/server" # # s = XMLRPC::CGIServer.new # # class MyHandler # def sumAndDifference(a, b) # { "sum" => a + b, "difference" => a - b } # end # end # # # NOTE: Security Hole (read below)!!! # s.add_handler("sample", MyHandler.new) # s.serve # # # To return a fault-structure you have to raise an XMLRPC::FaultException e.g.: # # raise XMLRPC::FaultException.new(3, "division by Zero") # # ===== Security Note # # From Brian Candler: # # Above code sample has an extremely nasty security hole, in that you can now call # any method of 'MyHandler' remotely, including methods inherited from Object # and Kernel! For example, in the client code, you can use # # puts server.call("sample.send","`","ls") # # (backtick being the method name for running system processes). Needless to # say, 'ls' can be replaced with something else. # # The version which binds proc objects (or the version presented below in the next section) # doesn't have this problem, but people may be tempted to use the second version because it's # so nice and 'Rubyesque'. I think it needs a big red disclaimer. # # # From Michael: # # A solution is to undef insecure methods or to use # XMLRPC::Service::PublicInstanceMethodsInterface as shown below: # # class MyHandler # def sumAndDifference(a, b) # { "sum" => a + b, "difference" => a - b } # end # end # # # ... server initialization ... # # s.add_handler(XMLRPC::iPIMethods("sample"), MyHandler.new) # # # ... # # This adds only public instance methods explicitly declared in class MyHandler # (and not those inherited from any other class). # # ==== With interface declarations # # Code sample from the book Ruby Developer's Guide: # # require "xmlrpc/server" # # class Num # INTERFACE = XMLRPC::interface("num") { # meth 'int add(int, int)', 'Add two numbers', 'add' # meth 'int div(int, int)', 'Divide two numbers' # } # # def add(a, b) a + b end # def div(a, b) a / b end # end # # # s = XMLRPC::CGIServer.new # s.add_handler(Num::INTERFACE, Num.new) # s.serve # # === Standalone XMLRPC::Server # # Same as CGI-based server, the only difference being # # server = XMLRPC::CGIServer.new # # must be changed to # # server = XMLRPC::Server.new(8080) # # if you want a server listening on port 8080. # The rest is the same. # # === Choosing a different XMLParser or XMLWriter # # The examples above all use the default parser (which is now since 1.8 # XMLParser::REXMLStreamParser) and a default XMLRPC::XMLWriter. # If you want to use a different XMLParser, then you have to call the # ParserWriterChooseMixin#set_parser method of XMLRPC::Client instances # or instances of subclasses of XMLRPC::BasicServer or by editing # xmlrpc/config.rb. # # XMLRPC::Client Example: # # # ... # server = XMLRPC::Client.new( "xmlrpc-c.sourceforge.net", "/api/sample.php") # server.set_parser(XMLRPC::XMLParser::XMLParser.new) # # ... # # XMLRPC::Server Example: # # # ... # s = XMLRPC::CGIServer.new # s.set_parser(XMLRPC::XMLParser::XMLParser.new) # # ... # # # You can change the XML-writer by calling method ParserWriterChooseMixin#set_writer. module XMLRPC VERSION = "0.3.2" end xmlrpc-0.3.2/lib/xmlrpc/000077500000000000000000000000001400706365600151035ustar00rootroot00000000000000xmlrpc-0.3.2/lib/xmlrpc/base64.rb000066400000000000000000000031021400706365600165100ustar00rootroot00000000000000# frozen_string_literal: false # # xmlrpc/base64.rb # Copyright (C) 2001, 2002, 2003 by Michael Neumann (mneumann@ntecs.de) # # Released under the same term of license as Ruby. module XMLRPC # :nodoc: # This class is necessary for 'xmlrpc4r' to determine that a string should # be transmitted base64-encoded and not as a raw-string. # # You can use XMLRPC::Base64 on the client and server-side as a # parameter and/or return-value. class Base64 include Comparable # Creates a new XMLRPC::Base64 instance with string +str+ as the # internal string. When +state+ is +:dec+ it assumes that the # string +str+ is not in base64 format (perhaps already decoded), # otherwise if +state+ is +:enc+ it decodes +str+ # and stores it as the internal string. def initialize(str, state = :dec) case state when :enc @str = Base64.decode(str) when :dec @str = str else raise ArgumentError, "wrong argument; either :enc or :dec" end end # Returns the decoded internal string. def decoded @str end # Returns the base64 encoded internal string. def encoded Base64.encode(@str) end # Compare two base64 values, based on decoded string def <=>(other) return nil unless other.is_a?(self.class) decoded <=> other.decoded end # Decodes string +str+ with base64 and returns that value. def Base64.decode(str) str.gsub(/\s+/, "").unpack("m")[0] end # Encodes string +str+ with base64 and returns that value. def Base64.encode(str) [str].pack("m") end end end # module XMLRPC =begin = History $Id$ =end xmlrpc-0.3.2/lib/xmlrpc/client.rb000066400000000000000000000456751400706365600167270ustar00rootroot00000000000000# frozen_string_literal: false # xmlrpc/client.rb # Copyright (C) 2001, 2002, 2003 by Michael Neumann (mneumann@ntecs.de) # # Released under the same term of license as Ruby. # # History # $Id$ # require "xmlrpc/parser" require "xmlrpc/create" require "xmlrpc/config" require "xmlrpc/utils" # ParserWriterChooseMixin require "net/http" require "uri" module XMLRPC # :nodoc: # Provides remote procedure calls to a XML-RPC server. # # After setting the connection-parameters with XMLRPC::Client.new which # creates a new XMLRPC::Client instance, you can execute a remote procedure # by sending the XMLRPC::Client#call or XMLRPC::Client#call2 # message to this new instance. # # The given parameters indicate which method to call on the remote-side and # of course the parameters for the remote procedure. # # require "xmlrpc/client" # # server = XMLRPC::Client.new("www.ruby-lang.org", "/RPC2", 80) # begin # param = server.call("michael.add", 4, 5) # puts "4 + 5 = #{param}" # rescue XMLRPC::FaultException => e # puts "Error:" # puts e.faultCode # puts e.faultString # end # # or # # require "xmlrpc/client" # # server = XMLRPC::Client.new("www.ruby-lang.org", "/RPC2", 80) # ok, param = server.call2("michael.add", 4, 5) # if ok then # puts "4 + 5 = #{param}" # else # puts "Error:" # puts param.faultCode # puts param.faultString # end class Client USER_AGENT = "XMLRPC::Client (Ruby #{RUBY_VERSION})" include ParserWriterChooseMixin include ParseContentType # Creates an object which represents the remote XML-RPC server on the # given +host+. If the server is CGI-based, +path+ is the # path to the CGI-script, which will be called, otherwise (in the # case of a standalone server) +path+ should be "/RPC2". # +port+ is the port on which the XML-RPC server listens. # # If +proxy_host+ is given, then a proxy server listening at # +proxy_host+ is used. +proxy_port+ is the port of the # proxy server. # # Default values for +host+, +path+ and +port+ are 'localhost', '/RPC2' and # '80' respectively using SSL '443'. # # If +user+ and +password+ are given, each time a request is sent, # an Authorization header is sent. Currently only Basic Authentication is # implemented, no Digest. # # If +use_ssl+ is set to +true+, communication over SSL is enabled. # # Parameter +timeout+ is the time to wait for a XML-RPC response, defaults to 30. def initialize(host=nil, path=nil, port=nil, proxy_host=nil, proxy_port=nil, user=nil, password=nil, use_ssl=nil, timeout=nil) @http_header_extra = nil @http_last_response = nil @cookie = nil @host = host || "localhost" @path = path || "/RPC2" @proxy_host = proxy_host @proxy_port = proxy_port @proxy_host ||= 'localhost' if @proxy_port != nil @proxy_port ||= 8080 if @proxy_host != nil @use_ssl = use_ssl || false @timeout = timeout || 30 if use_ssl require "net/https" @port = port || 443 else @port = port || 80 end @user, @password = user, password set_auth # convert ports to integers @port = @port.to_i if @port != nil @proxy_port = @proxy_port.to_i if @proxy_port != nil # HTTP object for synchronous calls @http = net_http(@host, @port, @proxy_host, @proxy_port) @http.use_ssl = @use_ssl if @use_ssl @http.read_timeout = @timeout @http.open_timeout = @timeout @parser = nil @create = nil end class << self # Creates an object which represents the remote XML-RPC server at the # given +uri+. The URI should have a host, port, path, user and password. # Example: https://user:password@host:port/path # # Raises an ArgumentError if the +uri+ is invalid, # or if the protocol isn't http or https. # # If a +proxy+ is given it should be in the form of "host:port". # # The optional +timeout+ defaults to 30 seconds. def new2(uri, proxy=nil, timeout=nil) begin url = URI(uri) rescue URI::InvalidURIError => e raise ArgumentError, e.message, e.backtrace end unless URI::HTTP === url raise ArgumentError, "Wrong protocol specified. Only http or https allowed!" end proto = url.scheme user = url.user passwd = url.password host = url.host port = url.port path = url.path.empty? ? nil : url.request_uri proxy_host, proxy_port = (proxy || "").split(":") proxy_port = proxy_port.to_i if proxy_port self.new(host, path, port, proxy_host, proxy_port, user, passwd, (proto == "https"), timeout) end alias new_from_uri new2 # Receives a Hash and calls XMLRPC::Client.new # with the corresponding values. # # The +hash+ parameter has following case-insensitive keys: # * host # * path # * port # * proxy_host # * proxy_port # * user # * password # * use_ssl # * timeout def new3(hash={}) # convert all keys into lowercase strings h = {} hash.each { |k,v| h[k.to_s.downcase] = v } self.new(h['host'], h['path'], h['port'], h['proxy_host'], h['proxy_port'], h['user'], h['password'], h['use_ssl'], h['timeout']) end alias new_from_hash new3 end # Returns the Net::HTTP object for the client. If you want to # change HTTP client options except header, cookie, timeout, # user and password, use Net::HTTP directly. # # Since 2.1.0. attr_reader :http # Add additional HTTP headers to the request attr_accessor :http_header_extra # Returns the Net::HTTPResponse object of the last RPC. attr_reader :http_last_response # Get and set the HTTP Cookie header. attr_accessor :cookie # Return the corresponding attributes. attr_reader :timeout, :user, :password # Sets the Net::HTTP#read_timeout and Net::HTTP#open_timeout to # +new_timeout+ def timeout=(new_timeout) @timeout = new_timeout @http.read_timeout = @timeout @http.open_timeout = @timeout end # Changes the user for the Basic Authentication header to +new_user+ def user=(new_user) @user = new_user set_auth end # Changes the password for the Basic Authentication header to # +new_password+ def password=(new_password) @password = new_password set_auth end # Invokes the method named +method+ with the parameters given by # +args+ on the XML-RPC server. # # The +method+ parameter is converted into a String and should # be a valid XML-RPC method-name. # # Each parameter of +args+ must be of one of the following types, # where Hash, Struct and Array can contain any of these listed _types_: # # * Integer # * TrueClass, FalseClass, +true+, +false+ # * String, Symbol # * Float # * Hash, Struct # * Array # * Date, Time, XMLRPC::DateTime # * XMLRPC::Base64 # * A Ruby object which class includes XMLRPC::Marshallable # (only if Config::ENABLE_MARSHALLING is +true+). # That object is converted into a hash, with one additional key/value # pair ___class___ which contains the class name # for restoring that object later. # # The method returns the return-value from the Remote Procedure Call. # # The type of the return-value is one of the types shown above. # # An Integer is only allowed when it fits in 32-bit. A XML-RPC # +dateTime.iso8601+ type is always returned as a XMLRPC::DateTime object. # Struct is never returned, only a Hash, the same for a Symbol, where as a # String is always returned. XMLRPC::Base64 is returned as a String from # xmlrpc4r version 1.6.1 on. # # If the remote procedure returned a fault-structure, then a # XMLRPC::FaultException exception is raised, which has two accessor-methods # +faultCode+ an Integer, and +faultString+ a String. def call(method, *args) ok, param = call2(method, *args) if ok param else raise param end end # The difference between this method and XMLRPC::Client#call is, that # this method will NOT raise a XMLRPC::FaultException exception. # # The method returns an array of two values. The first value indicates if # the second value is +true+ or an XMLRPC::FaultException. # # Both are explained in XMLRPC::Client#call. # # Simple to remember: The "2" in "call2" denotes the number of values it returns. def call2(method, *args) request = create().methodCall(method, *args) data = do_rpc(request, false) parser().parseMethodResponse(data) end # Similar to XMLRPC::Client#call, however can be called concurrently and # use a new connection for each request. In contrast to the corresponding # method without the +_async+ suffix, which use connect-alive (one # connection for all requests). # # Note, that you have to use Thread to call these methods concurrently. # The following example calls two methods concurrently: # # Thread.new { # p client.call_async("michael.add", 4, 5) # } # # Thread.new { # p client.call_async("michael.div", 7, 9) # } # def call_async(method, *args) ok, param = call2_async(method, *args) if ok param else raise param end end # Same as XMLRPC::Client#call2, but can be called concurrently. # # See also XMLRPC::Client#call_async def call2_async(method, *args) request = create().methodCall(method, *args) data = do_rpc(request, true) parser().parseMethodResponse(data) end # You can use this method to execute several methods on a XMLRPC server # which support the multi-call extension. # # s.multicall( # ['michael.add', 3, 4], # ['michael.sub', 4, 5] # ) # # => [7, -1] def multicall(*methods) ok, params = multicall2(*methods) if ok params else raise params end end # Same as XMLRPC::Client#multicall, but returns two parameters instead of # raising an XMLRPC::FaultException. # # See XMLRPC::Client#call2 def multicall2(*methods) gen_multicall(methods, false) end # Similar to XMLRPC::Client#multicall, however can be called concurrently and # use a new connection for each request. In contrast to the corresponding # method without the +_async+ suffix, which use connect-alive (one # connection for all requests). # # Note, that you have to use Thread to call these methods concurrently. # The following example calls two methods concurrently: # # Thread.new { # p client.multicall_async("michael.add", 4, 5) # } # # Thread.new { # p client.multicall_async("michael.div", 7, 9) # } # def multicall_async(*methods) ok, params = multicall2_async(*methods) if ok params else raise params end end # Same as XMLRPC::Client#multicall2, but can be called concurrently. # # See also XMLRPC::Client#multicall_async def multicall2_async(*methods) gen_multicall(methods, true) end # Returns an object of class XMLRPC::Client::Proxy, initialized with # +prefix+ and +args+. # # A proxy object returned by this method behaves like XMLRPC::Client#call, # i.e. a call on that object will raise a XMLRPC::FaultException when a # fault-structure is returned by that call. def proxy(prefix=nil, *args) Proxy.new(self, prefix, args, :call) end # Almost the same like XMLRPC::Client#proxy only that a call on the returned # XMLRPC::Client::Proxy object will return two parameters. # # See XMLRPC::Client#call2 def proxy2(prefix=nil, *args) Proxy.new(self, prefix, args, :call2) end # Similar to XMLRPC::Client#proxy, however can be called concurrently and # use a new connection for each request. In contrast to the corresponding # method without the +_async+ suffix, which use connect-alive (one # connection for all requests). # # Note, that you have to use Thread to call these methods concurrently. # The following example calls two methods concurrently: # # Thread.new { # p client.proxy_async("michael.add", 4, 5) # } # # Thread.new { # p client.proxy_async("michael.div", 7, 9) # } # def proxy_async(prefix=nil, *args) Proxy.new(self, prefix, args, :call_async) end # Same as XMLRPC::Client#proxy2, but can be called concurrently. # # See also XMLRPC::Client#proxy_async def proxy2_async(prefix=nil, *args) Proxy.new(self, prefix, args, :call2_async) end private def net_http(host, port, proxy_host, proxy_port) Net::HTTP.new host, port, proxy_host, proxy_port end def dup_net_http http = net_http(@http.address, @http.port, @http.proxy_address, @http.proxy_port) http.proxy_user = @http.proxy_user http.proxy_pass = @http.proxy_pass if @http.use_ssl? http.use_ssl = true Net::HTTP::SSL_ATTRIBUTES.each do |attribute| http.__send__("#{attribute}=", @http.__send__(attribute)) end end http.read_timeout = @http.read_timeout http.open_timeout = @http.open_timeout http end def set_auth if @user.nil? @auth = nil else a = "#@user" a << ":#@password" if @password != nil @auth = "Basic " + [a].pack("m0") end end def do_rpc(request, async=false) header = { "User-Agent" => USER_AGENT, "Content-Type" => "text/xml; charset=utf-8", "Content-Length" => request.bytesize.to_s, "Connection" => (async ? "close" : "keep-alive") } header["Cookie"] = @cookie if @cookie header.update(@http_header_extra) if @http_header_extra if @auth != nil # add authorization header header["Authorization"] = @auth end resp = nil @http_last_response = nil if async # use a new HTTP object for each call http = dup_net_http # post request http.start { resp = http.request_post(@path, request, header) } else # reuse the HTTP object for each call => connection alive is possible # we must start connection explicitly first time so that http.request # does not assume that we don't want keepalive @http.start if not @http.started? # post request resp = @http.request_post(@path, request, header) end @http_last_response = resp data = resp.body if resp.code == "401" # Authorization Required raise "Authorization failed.\nHTTP-Error: #{resp.code} #{resp.message}" elsif resp.code[0,1] != "2" raise "HTTP-Error: #{resp.code} #{resp.message}" end # assume text/xml on instances where Content-Type header is not set ct_expected = resp["Content-Type"] || 'text/xml' ct = parse_content_type(ct_expected).first case ct when "text/xml", "application/xml" # OK else message = "Wrong content-type " + "(received '#{ct}' but expected 'text/xml' or 'application/xml')" if ct == "text/html" raise "#{message}:\n#{data}" else raise message end end if data.nil? raise "No data" end expected = resp["Content-Length"] || "" if data.bytesize == 0 raise "Wrong size. Was #{data.bytesize}, should be #{expected}" end parse_set_cookies(resp.get_fields("Set-Cookie")) return data end def parse_set_cookies(set_cookies) return if set_cookies.nil? return if set_cookies.empty? require 'webrick/cookie' pairs = {} set_cookies.each do |set_cookie| cookie = WEBrick::Cookie.parse_set_cookie(set_cookie) pairs.delete(cookie.name) pairs[cookie.name] = cookie.value end cookies = pairs.collect do |name, value| WEBrick::Cookie.new(name, value).to_s end @cookie = cookies.join("; ") end def gen_multicall(methods=[], async=false) meth = :call2 meth = :call2_async if async ok, params = self.send(meth, "system.multicall", methods.collect {|m| {'methodName' => m[0], 'params' => m[1..-1]} } ) if ok params = params.collect do |param| if param.is_a? Array param[0] elsif param.is_a? Hash XMLRPC::FaultException.new(param["faultCode"], param["faultString"]) else raise "Wrong multicall return value" end end end return ok, params end # XML-RPC calls look nicer! # # You can call any method onto objects of that class - the object handles # XMLRPC::Client::Proxy#method_missing and will forward the method call to # a XML-RPC server. # # Don't use this class directly, instead use the public instance method # XMLRPC::Client#proxy or XMLRPC::Client#proxy2. # # require "xmlrpc/client" # # server = XMLRPC::Client.new("www.ruby-lang.org", "/RPC2", 80) # # michael = server.proxy("michael") # michael2 = server.proxy("michael", 4) # # # both calls should return the same value '9'. # p michael.add(4,5) # p michael2.add(5) class Proxy # Creates an object which provides XMLRPC::Client::Proxy#method_missing. # # The given +server+ must be an instance of XMLRPC::Client, which is the # XML-RPC server to be used for a XML-RPC call. # # +prefix+ and +delim+ will be prepended to the method name called onto this object. # # An optional parameter +meth+ is the method to use for a RPC. # It can be either, call, call2, call_async, call2_async # # +args+ are arguments which are automatically given to every XML-RPC # call before being provided through +method_missing+. def initialize(server, prefix, args=[], meth=:call, delim=".") @server = server @prefix = prefix ? prefix + delim : "" @args = args @meth = meth end # Every method call is forwarded to the XML-RPC server defined in # XMLRPC::Client::Proxy#new. # # Note: Inherited methods from class Object cannot be used as XML-RPC # names, because they get around +method_missing+. def method_missing(mid, *args) pre = @prefix + mid.to_s arg = @args + args @server.send(@meth, pre, *arg) end end # class Proxy end # class Client end # module XMLRPC xmlrpc-0.3.2/lib/xmlrpc/config.rb000066400000000000000000000014711400706365600167000ustar00rootroot00000000000000# frozen_string_literal: false # # $Id$ # Configuration file for XML-RPC for Ruby # module XMLRPC # :nodoc: module Config # or XMLWriter::XMLParser DEFAULT_WRITER = XMLWriter::Simple # === Available parsers # # * XMLParser::REXMLStreamParser # * XMLParser::LibXMLStreamParser DEFAULT_PARSER = XMLParser::REXMLStreamParser # enable tag ENABLE_NIL_CREATE = false ENABLE_NIL_PARSER = false # allows integers greater than 32-bit if +true+ ENABLE_BIGINT = false # enable marshalling Ruby objects which include XMLRPC::Marshallable ENABLE_MARSHALLING = true # enable multiCall extension by default ENABLE_MULTICALL = false # enable Introspection extension by default ENABLE_INTROSPECTION = false end end xmlrpc-0.3.2/lib/xmlrpc/create.rb000066400000000000000000000146601400706365600167020ustar00rootroot00000000000000# frozen_string_literal: false # # Copyright (C) 2001, 2002, 2003 by Michael Neumann (mneumann@ntecs.de) # # $Id$ # require "date" require "xmlrpc/base64" module XMLRPC # :nodoc: module XMLWriter module Element def ele(name, *children) element(name, nil, *children) end def tag(name, txt) element(name, nil, text(txt)) end end class Simple include Element def document_to_str(doc) doc end def document(*params) params.join("") end def pi(name, *params) "" end def element(name, attrs, *children) raise "attributes not yet implemented" unless attrs.nil? if children.empty? "<#{name}/>" else "<#{name}>" + children.join("") + "" end end def text(txt) cleaned = txt.dup cleaned.gsub!(/&/, '&') cleaned.gsub!(//, '>') cleaned end end # class Simple class XMLParser include Element 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 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 end # module XMLWriter # Creates XML-RPC call/response documents # 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 # # When +is_ret+ is +false+ then the +params+ array must # contain only one element, which is a structure # of a fault return-value. # # When +is_ret+ is +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 tag # def conv2value(param) # :doc: val = case param when Integer # XML-RPC's int is 32bit int 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 "Integer 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) when String @writer.tag("string", param) when NilClass if Config::ENABLE_NIL_CREATE @writer.ele("nil") else raise "Wrong type NilClass. Not allowed!" end when Float raise "Wrong value #{param}. Not allowed!" unless param.finite? @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 h = param.collect do |key, value| @writer.ele("member", @writer.tag("name", key.to_s), conv2value(value) ) end @writer.ele("struct", *h) when Array a = param.collect {|v| conv2value(v) } @writer.ele("array", @writer.ele("data", *a) ) when Time, Date, ::DateTime @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} param.instance_variables.each {|v| name = v[1..-1] val = param.instance_variable_get(v) 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 xmlrpc-0.3.2/lib/xmlrpc/datetime.rb000066400000000000000000000065411400706365600172320ustar00rootroot00000000000000# frozen_string_literal: false # # xmlrpc/datetime.rb # Copyright (C) 2001, 2002, 2003 by Michael Neumann (mneumann@ntecs.de) # # Released under the same term of license as Ruby. # require "date" module XMLRPC # :nodoc: # This class is important to handle XMLRPC +dateTime.iso8601+ values, # correctly, because normal UNIX-dates, ie: Date, only handle dates # from year 1970 on, and ruby's native Time class handles dates without the # time component. # # XMLRPC::DateTime is able to store a XMLRPC +dateTime.iso8601+ value correctly. class DateTime # Return the value of the specified date/time component. attr_reader :year, :month, :day, :hour, :min, :sec # Set +value+ as the new date/time component. # # Raises ArgumentError if the given +value+ is out of range, or in the case # of XMLRPC::DateTime#year= if +value+ is not of type Integer. def year=(value) raise ArgumentError, "date/time out of range" unless value.is_a? Integer @year = value end # Set +value+ as the new date/time component. # # Raises an ArgumentError if the given +value+ isn't between 1 and 12. def month=(value) raise ArgumentError, "date/time out of range" unless (1..12).include? value @month = value end # Set +value+ as the new date/time component. # # Raises an ArgumentError if the given +value+ isn't between 1 and 31. def day=(value) raise ArgumentError, "date/time out of range" unless (1..31).include? value @day = value end # Set +value+ as the new date/time component. # # Raises an ArgumentError if the given +value+ isn't between 0 and 24. def hour=(value) raise ArgumentError, "date/time out of range" unless (0..24).include? value @hour = value end # Set +value+ as the new date/time component. # # Raises an ArgumentError if the given +value+ isn't between 0 and 59. def min=(value) raise ArgumentError, "date/time out of range" unless (0..59).include? value @min = value end # Set +value+ as the new date/time component. # # Raises an ArgumentError if the given +value+ isn't between 0 and 59. def sec=(value) raise ArgumentError, "date/time out of range" unless (0..59).include? value @sec = value end # Alias for XMLRPC::DateTime#month. alias mon month # Alias for XMLRPC::DateTime#month=. alias mon= month= # Creates a new XMLRPC::DateTime instance with the # parameters +year+, +month+, +day+ as date and # +hour+, +min+, +sec+ as time. # # Raises an ArgumentError if a parameter is out of range, # or if +year+ is not of the Integer type. def initialize(year, month, day, hour, min, sec) self.year, self.month, self.day = year, month, day self.hour, self.min, self.sec = hour, min, sec end # Return a Time object of the date/time which represents +self+. # # The timezone used is GMT. def to_time Time.gm(*to_a) end # Return a Date object of the date which represents +self+. # # The Date object do _not_ contain the time component (only date). def to_date Date.new(*to_a[0,3]) end # Returns all date/time components in an array. # # Returns +[year, month, day, hour, min, sec]+. def to_a [@year, @month, @day, @hour, @min, @sec] end # Returns whether or not all date/time components are an array. def ==(o) self.to_a == Array(o) rescue false end end end # module XMLRPC =begin = History $Id$ =end xmlrpc-0.3.2/lib/xmlrpc/marshal.rb000066400000000000000000000027161400706365600170650ustar00rootroot00000000000000# frozen_string_literal: false # # Copyright (C) 2001, 2002, 2003 by Michael Neumann (mneumann@ntecs.de) # # $Id$ # require "xmlrpc/parser" require "xmlrpc/create" require "xmlrpc/config" require "xmlrpc/utils" module XMLRPC # :nodoc: # Marshalling of XMLRPC::Create#methodCall and XMLRPC::Create#methodResponse class Marshal include ParserWriterChooseMixin class << self def dump_call( methodName, *params ) new.dump_call( methodName, *params ) end def dump_response( param ) new.dump_response( param ) end def load_call( stringOrReadable ) new.load_call( stringOrReadable ) end def load_response( stringOrReadable ) new.load_response( stringOrReadable ) end alias dump dump_response alias load load_response end # class self def initialize( parser = nil, writer = nil ) set_parser( parser ) set_writer( writer ) end def dump_call( methodName, *params ) create.methodCall( methodName, *params ) end def dump_response( param ) create.methodResponse( ! param.kind_of?( XMLRPC::FaultException ) , param ) end # Returns [ methodname, params ] def load_call( stringOrReadable ) parser.parseMethodCall( stringOrReadable ) end # Returns +paramOrFault+ def load_response( stringOrReadable ) parser.parseMethodResponse( stringOrReadable )[1] end end # class Marshal end xmlrpc-0.3.2/lib/xmlrpc/parser.rb000066400000000000000000000367151400706365600167400ustar00rootroot00000000000000# frozen_string_literal: false # Copyright (C) 2001, 2002, 2003 by Michael Neumann (mneumann@ntecs.de) # # $Id$ # require "date" require "xmlrpc/base64" require "xmlrpc/datetime" module XMLRPC # :nodoc: # Raised when the remote procedure returns a fault-structure, which has two # accessor-methods +faultCode+ an Integer, and +faultString+ a String. class FaultException < StandardError attr_reader :faultCode, :faultString # Creates a new XMLRPC::FaultException instance. # # +faultString+ is passed to StandardError as the +msg+ of the Exception. def initialize(faultCode, faultString) @faultCode = faultCode @faultString = faultString super(@faultString) end # The +faultCode+ and +faultString+ of the exception in a Hash. def to_h {"faultCode" => @faultCode, "faultString" => @faultString} end end # Helper class used to convert types. module Convert # Converts a String to an Integer # # See also String.to_i def self.int(str) str.to_i end # Converts a String to +true+ or +false+ # # Raises an exception if +str+ is not +0+ or +1+ def self.boolean(str) case str when "0" then false when "1" then true else raise "RPC-value of type boolean is wrong" end end # Converts a String to a Float # # See also String.to_f def self.double(str) str.to_f end # Converts a the given +str+ to a +dateTime.iso8601+ formatted date. # # Raises an exception if the String isn't in +dateTime.iso8601+ format. # # See also, XMLRPC::DateTime def self.dateTime(str) case str when /^(-?\d\d\d\d)-?(\d\d)-?(\d\d)T(\d\d):(\d\d):(\d\d)(?:Z|([+-])(\d\d):?(\d\d))?$/ a = [$1, $2, $3, $4, $5, $6].collect{|i| i.to_i} if $7 ofs = $8.to_i*3600 + $9.to_i*60 ofs = -ofs if $7=='+' utc = Time.utc(*a) + ofs a = [ utc.year, utc.month, utc.day, utc.hour, utc.min, utc.sec ] end XMLRPC::DateTime.new(*a) when /^(-?\d\d)-?(\d\d)-?(\d\d)T(\d\d):(\d\d):(\d\d)(Z|([+-]\d\d):(\d\d))?$/ a = [$1, $2, $3, $4, $5, $6].collect{|i| i.to_i} if a[0] < 70 a[0] += 2000 else a[0] += 1900 end if $7 ofs = $8.to_i*3600 + $9.to_i*60 ofs = -ofs if $7=='+' utc = Time.utc(*a) + ofs a = [ utc.year, utc.month, utc.day, utc.hour, utc.min, utc.sec ] end XMLRPC::DateTime.new(*a) else raise "wrong dateTime.iso8601 format " + str end end # Decodes the given +str+ using XMLRPC::Base64.decode def self.base64(str) XMLRPC::Base64.decode(str) end # Converts the given +hash+ to a marshalled object. # # Returns the given +hash+ if an exception occurs. def self.struct(hash) # convert to marshalled object klass = hash["___class___"] if klass.nil? or Config::ENABLE_MARSHALLING == false hash else begin mod = Module klass.split("::").each {|const| mod = mod.const_get(const.strip)} obj = mod.allocate hash.delete "___class___" hash.each {|key, value| obj.instance_variable_set("@#{ key }", value) if key =~ /^([a-zA-Z_]\w*)$/ } obj rescue hash end end end # Converts the given +hash+ to an XMLRPC::FaultException object by passing # the +faultCode+ and +faultString+ attributes of the Hash to # XMLRPC::FaultException.new # # Raises an Exception if the given +hash+ doesn't meet the requirements. # Those requirements being: # * 2 keys # * 'faultCode' key is an Integer # * 'faultString' key is a String def self.fault(hash) if hash.kind_of? Hash and hash.size == 2 and hash.has_key? "faultCode" and hash.has_key? "faultString" and hash["faultCode"].kind_of? Integer and hash["faultString"].kind_of? String XMLRPC::FaultException.new(hash["faultCode"], hash["faultString"]) else raise "wrong fault-structure: #{hash.inspect}" end end end # module Convert # Parser for XML-RPC call and response module XMLParser class AbstractTreeParser def parseMethodResponse(str) methodResponse_document(createCleanedTree(str)) end def parseMethodCall(str) methodCall_document(createCleanedTree(str)) end private # Removes all whitespaces but in the tags i4, i8, int, boolean.... # and all comments def removeWhitespacesAndComments(node) remove = [] childs = node.childNodes.to_a childs.each do |nd| case _nodeType(nd) when :TEXT # TODO: add nil? unless %w(i4 i8 int boolean string double dateTime.iso8601 base64).include? node.nodeName if node.nodeName == "value" if not node.childNodes.to_a.detect {|n| _nodeType(n) == :ELEMENT}.nil? remove << nd if nd.nodeValue.strip == "" end else remove << nd if nd.nodeValue.strip == "" end end when :COMMENT remove << nd else removeWhitespacesAndComments(nd) end end remove.each { |i| node.removeChild(i) } end def nodeMustBe(node, name) cmp = case name when Array name.include?(node.nodeName) when String name == node.nodeName else raise "error" end if not cmp then raise "wrong xml-rpc (name)" end node end # Returns, when successfully the only child-node def hasOnlyOneChild(node, name=nil) if node.childNodes.to_a.size != 1 raise "wrong xml-rpc (size)" end if name != nil then nodeMustBe(node.firstChild, name) end end def assert(b) if not b then raise "assert-fail" end end # The node `node` has empty string or string def text_zero_one(node) nodes = node.childNodes.to_a.size if nodes == 1 text(node.firstChild) elsif nodes == 0 "" else raise "wrong xml-rpc (size)" end end def integer(node) #TODO: check string for float because to_i returnsa # 0 when wrong string nodeMustBe(node, %w(i4 i8 int)) hasOnlyOneChild(node) Convert.int(text(node.firstChild)) end def boolean(node) nodeMustBe(node, "boolean") hasOnlyOneChild(node) Convert.boolean(text(node.firstChild)) end def v_nil(node) nodeMustBe(node, "nil") assert( node.childNodes.to_a.size == 0 ) nil end def string(node) nodeMustBe(node, "string") text_zero_one(node) end def double(node) #TODO: check string for float because to_f returnsa # 0.0 when wrong string nodeMustBe(node, "double") hasOnlyOneChild(node) Convert.double(text(node.firstChild)) end def dateTime(node) nodeMustBe(node, "dateTime.iso8601") hasOnlyOneChild(node) Convert.dateTime( text(node.firstChild) ) end def base64(node) nodeMustBe(node, "base64") #hasOnlyOneChild(node) Convert.base64(text_zero_one(node)) end def member(node) nodeMustBe(node, "member") assert( node.childNodes.to_a.size == 2 ) [ name(node[0]), value(node[1]) ] end def name(node) nodeMustBe(node, "name") #hasOnlyOneChild(node) text_zero_one(node) end def array(node) nodeMustBe(node, "array") hasOnlyOneChild(node, "data") data(node.firstChild) end def data(node) nodeMustBe(node, "data") node.childNodes.to_a.collect do |val| value(val) end end def param(node) nodeMustBe(node, "param") hasOnlyOneChild(node, "value") value(node.firstChild) end def methodResponse(node) nodeMustBe(node, "methodResponse") hasOnlyOneChild(node, %w(params fault)) child = node.firstChild case child.nodeName when "params" [ true, params(child,false) ] when "fault" [ false, fault(child) ] else raise "unexpected error" end end def methodName(node) nodeMustBe(node, "methodName") hasOnlyOneChild(node) text(node.firstChild) end def params(node, call=true) nodeMustBe(node, "params") if call node.childNodes.to_a.collect do |n| param(n) end else # response (only one param) hasOnlyOneChild(node) param(node.firstChild) end end def fault(node) nodeMustBe(node, "fault") hasOnlyOneChild(node, "value") f = value(node.firstChild) Convert.fault(f) end # _nodeType is defined in the subclass def text(node) assert( _nodeType(node) == :TEXT ) assert( node.hasChildNodes == false ) assert( node.nodeValue != nil ) node.nodeValue.to_s end def struct(node) nodeMustBe(node, "struct") hash = {} node.childNodes.to_a.each do |me| n, v = member(me) hash[n] = v end Convert.struct(hash) end def value(node) nodeMustBe(node, "value") nodes = node.childNodes.to_a.size if nodes == 0 return "" elsif nodes > 1 raise "wrong xml-rpc (size)" end child = node.firstChild case _nodeType(child) when :TEXT text_zero_one(node) when :ELEMENT case child.nodeName when "i4", "i8", "int" then integer(child) when "boolean" then boolean(child) when "string" then string(child) when "double" then double(child) when "dateTime.iso8601" then dateTime(child) when "base64" then base64(child) when "struct" then struct(child) when "array" then array(child) when "nil" if Config::ENABLE_NIL_PARSER v_nil(child) else raise "wrong/unknown XML-RPC type 'nil'" end else raise "wrong/unknown XML-RPC type" end else raise "wrong type of node" end end def methodCall(node) nodeMustBe(node, "methodCall") assert( (1..2).include?( node.childNodes.to_a.size ) ) name = methodName(node[0]) if node.childNodes.to_a.size == 2 then pa = params(node[1]) else # no parameters given pa = [] end [name, pa] end end # module TreeParserMixin class AbstractStreamParser def parseMethodResponse(str) parser = @parser_class.new parser.parse(str) raise "No valid method response!" if parser.method_name != nil if parser.fault != nil # is a fault structure [false, parser.fault] else # is a normal return value raise "Missing return value!" if parser.params.size == 0 raise "Too many return values. Only one allowed!" if parser.params.size > 1 [true, parser.params[0]] end end def parseMethodCall(str) parser = @parser_class.new parser.parse(str) raise "No valid method call - missing method name!" if parser.method_name.nil? [parser.method_name, parser.params] end end module StreamParserMixin attr_reader :params attr_reader :method_name attr_reader :fault def initialize(*a) super(*a) @params = [] @values = [] @val_stack = [] @names = [] @name = [] @structs = [] @struct = {} @method_name = nil @fault = nil @data = nil end def startElement(name, attrs=[]) @data = nil case name when "value" @value = nil when "nil" raise "wrong/unknown XML-RPC type 'nil'" unless Config::ENABLE_NIL_PARSER @value = :nil when "array" @val_stack << @values @values = [] when "struct" @names << @name @name = [] @structs << @struct @struct = {} end end def endElement(name) @data ||= "" case name when "string" @value = @data when "i4", "i8", "int" @value = Convert.int(@data) when "boolean" @value = Convert.boolean(@data) when "double" @value = Convert.double(@data) when "dateTime.iso8601" @value = Convert.dateTime(@data) when "base64" @value = Convert.base64(@data) when "value" @value = @data if @value.nil? @values << (@value == :nil ? nil : @value) when "array" @value = @values @values = @val_stack.pop when "struct" @value = Convert.struct(@struct) @name = @names.pop @struct = @structs.pop when "name" @name[0] = @data when "member" @struct[@name[0]] = @values.pop when "param" @params << @values[0] @values = [] when "fault" @fault = Convert.fault(@values[0]) when "methodName" @method_name = @data end @data = nil end def character(data) if @data @data << data else @data = data end end end # module StreamParserMixin class REXMLStreamParser < AbstractStreamParser def initialize require "rexml/document" @parser_class = StreamListener end class StreamListener include StreamParserMixin alias :tag_start :startElement alias :tag_end :endElement alias :text :character alias :cdata :character def method_missing(*a) # ignore end def parse(str) REXML::Document.parse_stream(str, self) end end end class LibXMLStreamParser < AbstractStreamParser def initialize require 'libxml' @parser_class = LibXMLStreamListener end class LibXMLStreamListener include StreamParserMixin def on_start_element_ns(name, attributes, prefix, uri, namespaces) startElement(name) end def on_end_element_ns(name, prefix, uri) endElement(name) end alias :on_characters :character alias :on_cdata_block :character def method_missing(*a) end def parse(str) parser = LibXML::XML::SaxParser.string(str) parser.callbacks = self parser.parse() end end end Classes = [REXMLStreamParser, LibXMLStreamParser] # yields an instance of each installed parser def self.each_installed_parser XMLRPC::XMLParser::Classes.each do |klass| begin yield klass.new rescue LoadError end end end end # module XMLParser end # module XMLRPC xmlrpc-0.3.2/lib/xmlrpc/server.rb000066400000000000000000000476241400706365600167530ustar00rootroot00000000000000# frozen_string_literal: false # xmlrpc/server.rb # Copyright (C) 2001, 2002, 2003, 2005 by Michael Neumann (mneumann@ntecs.de) # # Released under the same term of license as Ruby. require "xmlrpc/parser" require "xmlrpc/create" require "xmlrpc/config" require "xmlrpc/utils" # ParserWriterChooseMixin module XMLRPC # :nodoc: # This is the base class for all XML-RPC server-types (CGI, standalone). # You can add handler and set a default handler. # Do not use this server, as this is/should be an abstract class. # # === How the method to call is found # The arity (number of accepted arguments) of a handler (method or Proc # object) is compared to the given arguments submitted by the client for a # RPC, or Remote Procedure Call. # # A handler is only called if it accepts the number of arguments, otherwise # the search for another handler will go on. When at the end no handler was # found, the default_handler, XMLRPC::BasicServer#set_default_handler will be # called. # # With this technique it is possible to do overloading by number of parameters, but # only for Proc handler, because you cannot define two methods of the same name in # the same class. class BasicServer include ParserWriterChooseMixin include ParseContentType ERR_METHOD_MISSING = 1 ERR_UNCAUGHT_EXCEPTION = 2 ERR_MC_WRONG_PARAM = 3 ERR_MC_MISSING_PARAMS = 4 ERR_MC_MISSING_METHNAME = 5 ERR_MC_RECURSIVE_CALL = 6 ERR_MC_WRONG_PARAM_PARAMS = 7 ERR_MC_EXPECTED_STRUCT = 8 # Creates a new XMLRPC::BasicServer instance, which should not be # done, because XMLRPC::BasicServer is an abstract class. This # method should be called from a subclass indirectly by a +super+ call # in the initialize method. # # The parameter +class_delim+ is used by add_handler, see # XMLRPC::BasicServer#add_handler, when an object is added as a handler, to # delimit the object-prefix and the method-name. def initialize(class_delim=".") @handler = [] @default_handler = nil @service_hook = nil @class_delim = class_delim @create = nil @parser = nil add_multicall if Config::ENABLE_MULTICALL add_introspection if Config::ENABLE_INTROSPECTION end # Adds +aBlock+ to the list of handlers, with +name+ as the name of # the method. # # Parameters +signature+ and +help+ are used by the Introspection method if # specified, where +signature+ is either an Array containing strings each # representing a type of it's signature (the first is the return value) or # an Array of Arrays if the method has multiple signatures. # # Value type-names are "int, boolean, double, string, dateTime.iso8601, # base64, array, struct". # # Parameter +help+ is a String with information about how to call this method etc. # # When a method fails, it can tell the client by throwing an # XMLRPC::FaultException like in this example: # # s.add_handler("michael.div") do |a,b| # if b == 0 # raise XMLRPC::FaultException.new(1, "division by zero") # else # a / b # end # end # # In the case of b==0 the client gets an object back of type # XMLRPC::FaultException that has a +faultCode+ and +faultString+ field. # # This is the second form of (()). # To add an object write: # # server.add_handler("michael", MyHandlerClass.new) # # All public methods of MyHandlerClass are accessible to # the XML-RPC clients by michael."name of method". This is # where the +class_delim+ in XMLRPC::BasicServer.new plays it's role, a # XML-RPC method-name is defined by +prefix+ + +class_delim+ + "name # of method". # # The third form of +add_handler is to use XMLRPC::Service::Interface to # generate an object, which represents an interface (with signature and # help text) for a handler class. # # The +interface+ parameter must be an instance of XMLRPC::Service::Interface. # Adds all methods of +obj+ which are defined in the +interface+ to the server. # # This is the recommended way of adding services to a server! def add_handler(prefix, obj_or_signature=nil, help=nil, &block) if block_given? # proc-handler @handler << [prefix, block, obj_or_signature, help] else if prefix.kind_of? String # class-handler raise ArgumentError, "Expected non-nil value" if obj_or_signature.nil? @handler << [prefix + @class_delim, obj_or_signature] elsif prefix.kind_of? XMLRPC::Service::BasicInterface # class-handler with interface # add all methods @handler += prefix.get_methods(obj_or_signature, @class_delim) else raise ArgumentError, "Wrong type for parameter 'prefix'" end end self end # Returns the service-hook, which is called on each service request (RPC) # unless it's +nil+. def get_service_hook @service_hook end # A service-hook is called for each service request (RPC). # # You can use a service-hook for example to wrap existing methods and catch # exceptions of them or convert values to values recognized by XMLRPC. # # You can disable it by passing +nil+ as the +handler+ parameter. # # The service-hook is called with a Proc object along with any parameters. # # An example: # # server.set_service_hook {|obj, *args| # begin # ret = obj.call(*args) # call the original service-method # # could convert the return value # rescue # # rescue exceptions # end # } # def set_service_hook(&handler) @service_hook = handler self end # Returns the default-handler, which is called when no handler for # a method-name is found. # # It is either a Proc object or +nil+. def get_default_handler @default_handler end # Sets +handler+ as the default-handler, which is called when # no handler for a method-name is found. # # +handler+ is a code-block. # # The default-handler is called with the (XML-RPC) method-name as first # argument, and the other arguments are the parameters given by the # client-call. # # If no block is specified the default of XMLRPC::BasicServer is # used, which raises a XMLRPC::FaultException saying "method missing". def set_default_handler(&handler) @default_handler = handler self end # Adds the multi-call handler "system.multicall". def add_multicall add_handler("system.multicall", %w(array array), "Multicall Extension") do |arrStructs| unless arrStructs.is_a? Array raise XMLRPC::FaultException.new(ERR_MC_WRONG_PARAM, "system.multicall expects an array") end arrStructs.collect {|call| if call.is_a? Hash methodName = call["methodName"] params = call["params"] if params.nil? multicall_fault(ERR_MC_MISSING_PARAMS, "Missing params") elsif methodName.nil? multicall_fault(ERR_MC_MISSING_METHNAME, "Missing methodName") else if methodName == "system.multicall" multicall_fault(ERR_MC_RECURSIVE_CALL, "Recursive system.multicall forbidden") else unless params.is_a? Array multicall_fault(ERR_MC_WRONG_PARAM_PARAMS, "Parameter params have to be an Array") else ok, val = call_method(methodName, *params) if ok # correct return value [val] else # exception multicall_fault(val.faultCode, val.faultString) end end end end else multicall_fault(ERR_MC_EXPECTED_STRUCT, "system.multicall expected struct") end } end # end add_handler self end # Adds the introspection handlers "system.listMethods", # "system.methodSignature" and # "system.methodHelp", where only the first one works. def add_introspection add_handler("system.listMethods",%w(array), "List methods available on this XML-RPC server") do methods = [] @handler.each do |name, obj| if obj.kind_of? Proc methods << name else obj.class.public_instance_methods(false).each do |meth| methods << "#{name}#{meth}" end end end methods end add_handler("system.methodSignature", %w(array string), "Returns method signature") do |meth| sigs = [] @handler.each do |name, obj, sig| if obj.kind_of? Proc and sig != nil and name == meth if sig[0].kind_of? Array # sig contains multiple signatures, e.g. [["array"], ["array", "string"]] sig.each {|s| sigs << s} else # sig is a single signature, e.g. ["array"] sigs << sig end end end sigs.uniq! || sigs # remove eventually duplicated signatures end add_handler("system.methodHelp", %w(string string), "Returns help on using this method") do |meth| help = nil @handler.each do |name, obj, sig, hlp| if obj.kind_of? Proc and name == meth help = hlp break end end help || "" end self end def process(data) method, params = parser().parseMethodCall(data) handle(method, *params) end private def multicall_fault(nr, str) {"faultCode" => nr, "faultString" => str} end def dispatch(methodname, *args) for name, obj in @handler if obj.kind_of? Proc next unless methodname == name else next unless methodname =~ /^#{name}(.+)$/ next unless obj.respond_to? $1 obj = obj.method($1) end if check_arity(obj, args.size) if @service_hook.nil? return obj.call(*args) else return @service_hook.call(obj, *args) end end end if @default_handler.nil? raise XMLRPC::FaultException.new(ERR_METHOD_MISSING, "Method #{methodname} missing or wrong number of parameters!") else @default_handler.call(methodname, *args) end end # Returns +true+, if the arity of +obj+ matches +n_args+ def check_arity(obj, n_args) ary = obj.arity if ary >= 0 n_args == ary else n_args >= (ary+1).abs end end def call_method(methodname, *args) begin [true, dispatch(methodname, *args)] rescue XMLRPC::FaultException => e [false, e] rescue Exception => e [false, XMLRPC::FaultException.new(ERR_UNCAUGHT_EXCEPTION, "Uncaught exception #{e.message} in method #{methodname}")] end end def handle(methodname, *args) create().methodResponse(*call_method(methodname, *args)) end end # Implements a CGI-based XML-RPC server. # # require "xmlrpc/server" # # s = XMLRPC::CGIServer.new # # s.add_handler("michael.add") do |a,b| # a + b # end # # s.add_handler("michael.div") do |a,b| # if b == 0 # raise XMLRPC::FaultException.new(1, "division by zero") # else # a / b # end # end # # s.set_default_handler do |name, *args| # raise XMLRPC::FaultException.new(-99, "Method #{name} missing" + # " or wrong number of parameters!") # end # # s.serve # # # Note: Make sure that you don't write to standard-output in a # handler, or in any other part of your program, this would cause a CGI-based # server to fail! class CGIServer < BasicServer @@obj = nil # Creates a new XMLRPC::CGIServer instance. # # All parameters given are by-passed to XMLRPC::BasicServer.new. # # You can only create one XMLRPC::CGIServer instance, because more # than one makes no sense. def CGIServer.new(*a) @@obj = super(*a) if @@obj.nil? @@obj end def initialize(*a) super(*a) end # Call this after you have added all you handlers to the server. # # This method processes a XML-RPC method call and sends the answer # back to the client. def serve catch(:exit_serve) { length = ENV['CONTENT_LENGTH'].to_i http_error(405, "Method Not Allowed") unless ENV['REQUEST_METHOD'] == "POST" http_error(400, "Bad Request") unless parse_content_type(ENV['CONTENT_TYPE']).first == "text/xml" http_error(411, "Length Required") unless length > 0 # TODO: do we need a call to binmode? $stdin.binmode if $stdin.respond_to? :binmode data = $stdin.read(length) http_error(400, "Bad Request") if data.nil? or data.bytesize != length http_write(process(data), "Content-type" => "text/xml; charset=utf-8") } end private def http_error(status, message) err = "#{status} #{message}" msg = <<-"MSGEND" #{err}

#{err}

Unexpected error occurred while processing XML-RPC request!

MSGEND http_write(msg, "Status" => err, "Content-type" => "text/html") throw :exit_serve # exit from the #serve method end def http_write(body, header) h = {} header.each {|key, value| h[key.to_s.capitalize] = value} h['Status'] ||= "200 OK" h['Content-length'] ||= body.bytesize.to_s str = "" h.each {|key, value| str << "#{key}: #{value}\r\n"} str << "\r\n#{body}" print str end end # Implements a XML-RPC server, which works with Apache mod_ruby. # # Use it in the same way as XMLRPC::CGIServer! class ModRubyServer < BasicServer # Creates a new XMLRPC::ModRubyServer instance. # # All parameters given are by-passed to XMLRPC::BasicServer.new. def initialize(*a) @ap = Apache::request super(*a) end # Call this after you have added all you handlers to the server. # # This method processes a XML-RPC method call and sends the answer # back to the client. def serve catch(:exit_serve) { header = {} @ap.headers_in.each {|key, value| header[key.capitalize] = value} length = header['Content-length'].to_i http_error(405, "Method Not Allowed") unless @ap.request_method == "POST" http_error(400, "Bad Request") unless parse_content_type(header['Content-type']).first == "text/xml" http_error(411, "Length Required") unless length > 0 # TODO: do we need a call to binmode? @ap.binmode data = @ap.read(length) http_error(400, "Bad Request") if data.nil? or data.bytesize != length http_write(process(data), 200, "Content-type" => "text/xml; charset=utf-8") } end private def http_error(status, message) err = "#{status} #{message}" msg = <<-"MSGEND" #{err}

#{err}

Unexpected error occurred while processing XML-RPC request!

MSGEND http_write(msg, status, "Status" => err, "Content-type" => "text/html") throw :exit_serve # exit from the #serve method end def http_write(body, status, header) h = {} header.each {|key, value| h[key.to_s.capitalize] = value} h['Status'] ||= "200 OK" h['Content-length'] ||= body.bytesize.to_s h.each {|key, value| @ap.headers_out[key] = value } @ap.content_type = h["Content-type"] @ap.status = status.to_i @ap.send_http_header @ap.print body end end class WEBrickServlet < BasicServer; end # forward declaration # Implements a standalone XML-RPC server. The method XMLRPC::Server#serve is # left if a SIGHUP is sent to the program. # # require "xmlrpc/server" # # s = XMLRPC::Server.new(8080) # # s.add_handler("michael.add") do |a,b| # a + b # end # # s.add_handler("michael.div") do |a,b| # if b == 0 # raise XMLRPC::FaultException.new(1, "division by zero") # else # a / b # end # end # # s.set_default_handler do |name, *args| # raise XMLRPC::FaultException.new(-99, "Method #{name} missing" + # " or wrong number of parameters!") # end # # s.serve class Server < WEBrickServlet # Creates a new XMLRPC::Server instance, which is a XML-RPC server # listening on the given +port+ and accepts requests for the given +host+, # which is +localhost+ by default. # # The server is not started, to start it you have to call # XMLRPC::Server#serve. # # The optional +audit+ and +debug+ parameters are obsolete! # # All additionally provided parameters in *a are by-passed to # XMLRPC::BasicServer.new. def initialize(port=8080, host="127.0.0.1", maxConnections=4, stdlog=$stdout, audit=true, debug=true, *a) super(*a) require 'webrick' @server = WEBrick::HTTPServer.new(:Port => port, :BindAddress => host, :MaxClients => maxConnections, :Logger => WEBrick::Log.new(stdlog)) @server.mount("/", self) end # Call this after you have added all you handlers to the server. # This method starts the server to listen for XML-RPC requests and answer them. def serve signals = %w[INT TERM HUP] & Signal.list.keys signals.each { |signal| trap(signal) { @server.shutdown } } @server.start end # Stops and shuts the server down. def shutdown @server.shutdown end # Get the port of the server, useful when started with port=0 def port @server.config[:Port] end end # Implements a servlet for use with WEBrick, a pure Ruby (HTTP) server # framework. # # require "webrick" # require "xmlrpc/server" # # s = XMLRPC::WEBrickServlet.new # s.add_handler("michael.add") do |a,b| # a + b # end # # s.add_handler("michael.div") do |a,b| # if b == 0 # raise XMLRPC::FaultException.new(1, "division by zero") # else # a / b # end # end # # s.set_default_handler do |name, *args| # raise XMLRPC::FaultException.new(-99, "Method #{name} missing" + # " or wrong number of parameters!") # end # # httpserver = WEBrick::HTTPServer.new(:Port => 8080) # httpserver.mount("/RPC2", s) # trap("HUP") { httpserver.shutdown } # use 1 instead of "HUP" on Windows # httpserver.start class WEBrickServlet < BasicServer def initialize(*a) super require "webrick/httpstatus" @valid_ip = nil end # Deprecated from WEBrick/1.2.2, but does not break anything. def require_path_info? false end def get_instance(config, *options) # TODO: set config & options self end # Specifies the valid IP addresses that are allowed to connect to the server. # # Each IP is either a String or a Regexp. def set_valid_ip(*ip_addr) if ip_addr.size == 1 and ip_addr[0].nil? @valid_ip = nil else @valid_ip = ip_addr end end # Return the valid IP addresses that are allowed to connect to the server. # # See also, XMLRPC::Server#set_valid_ip def get_valid_ip @valid_ip end def service(request, response) if @valid_ip raise WEBrick::HTTPStatus::Forbidden unless @valid_ip.any? { |ip| request.peeraddr[3] =~ ip } end if request.request_method != "POST" raise WEBrick::HTTPStatus::MethodNotAllowed, "unsupported method `#{request.request_method}'." end if parse_content_type(request['Content-type']).first != "text/xml" raise WEBrick::HTTPStatus::BadRequest end length = (request['Content-length'] || 0).to_i raise WEBrick::HTTPStatus::LengthRequired unless length > 0 data = request.body if data.nil? or data.bytesize != length raise WEBrick::HTTPStatus::BadRequest end resp = process(data) if resp.nil? or resp.bytesize <= 0 raise WEBrick::HTTPStatus::InternalServerError end response.status = 200 response['Content-Length'] = resp.bytesize response['Content-Type'] = "text/xml; charset=utf-8" response.body = resp end end end # module XMLRPC =begin = History $Id$ =end xmlrpc-0.3.2/lib/xmlrpc/utils.rb000066400000000000000000000076341400706365600166020ustar00rootroot00000000000000# frozen_string_literal: false # # Copyright (C) 2001, 2002, 2003 by Michael Neumann (mneumann@ntecs.de) # # $Id$ # module XMLRPC # :nodoc: # This module enables a user-class to be marshalled # by XML-RPC for Ruby into a Hash, with one additional # key/value pair ___class___ => ClassName # module Marshallable end # Defines ParserWriterChooseMixin, which makes it possible to choose a # different XMLWriter and/or XMLParser then the default one. # # The Mixin is used in client.rb (class XMLRPC::Client) # and server.rb (class XMLRPC::BasicServer) module ParserWriterChooseMixin # Sets the XMLWriter to use for generating XML output. # # Should be an instance of a class from module XMLRPC::XMLWriter. # # If this method is not called, then XMLRPC::Config::DEFAULT_WRITER is used. def set_writer(writer) @create = Create.new(writer) self end # Sets the XMLParser to use for parsing XML documents. # # Should be an instance of a class from module XMLRPC::XMLParser. # # If this method is not called, then XMLRPC::Config::DEFAULT_PARSER is used. def set_parser(parser) @parser = parser self end private def create # if set_writer was not already called then call it now if @create.nil? then set_writer(Config::DEFAULT_WRITER.new) end @create end def parser # if set_parser was not already called then call it now if @parser.nil? then set_parser(Config::DEFAULT_PARSER.new) end @parser end end # module ParserWriterChooseMixin module Service # Base class for XMLRPC::Service::Interface definitions, used # by XMLRPC::BasicServer#add_handler class BasicInterface attr_reader :prefix, :methods def initialize(prefix) @prefix = prefix @methods = [] end def add_method(sig, help=nil, meth_name=nil) mname = nil sig = [sig] if sig.kind_of? String sig = sig.collect do |s| name, si = parse_sig(s) raise "Wrong signatures!" if mname != nil and name != mname mname = name si end @methods << [mname, meth_name || mname, sig, help] end private def parse_sig(sig) # sig is a String if sig =~ /^\s*(\w+)\s+([^(]+)(\(([^)]*)\))?\s*$/ params = [$1] name = $2.strip $4.split(",").each {|i| params << i.strip} if $4 != nil return name, params else raise "Syntax error in signature" end end end # class BasicInterface # # Class which wraps a XMLRPC::Service::Interface definition, used # by XMLRPC::BasicServer#add_handler # class Interface < BasicInterface def initialize(prefix, &p) raise "No interface specified" if p.nil? super(prefix) instance_eval(&p) end def get_methods(obj, delim=".") prefix = @prefix + delim @methods.collect { |name, meth, sig, help| [prefix + name.to_s, obj.method(meth).to_proc, sig, help] } end private def meth(*a) add_method(*a) end end # class Interface class PublicInstanceMethodsInterface < BasicInterface def initialize(prefix) super(prefix) end def get_methods(obj, delim=".") prefix = @prefix + delim obj.class.public_instance_methods(false).collect { |name| [prefix + name.to_s, obj.method(name).to_proc, nil, nil] } end end end # module Service # # Short-form to create a XMLRPC::Service::Interface # def self.interface(prefix, &p) Service::Interface.new(prefix, &p) end # Short-cut for creating a XMLRPC::Service::PublicInstanceMethodsInterface def self.iPIMethods(prefix) Service::PublicInstanceMethodsInterface.new(prefix) end module ParseContentType def parse_content_type(str) a, *b = str.split(";") return a.strip.downcase, *b end end end # module XMLRPC xmlrpc-0.3.2/test/000077500000000000000000000000001400706365600140075ustar00rootroot00000000000000xmlrpc-0.3.2/test/data/000077500000000000000000000000001400706365600147205ustar00rootroot00000000000000xmlrpc-0.3.2/test/data/blog.xml000066400000000000000000000012101400706365600163570ustar00rootroot00000000000000 isAdmin1 urlhttp://tenderlovemaking.com/ blogid1 blogNameTender Lovemaking xmlrpchttp://tenderlovemaking.com/xmlrpc.php xmlrpc-0.3.2/test/data/bug_bool.expected000066400000000000000000000000231400706365600202260ustar00rootroot00000000000000--- - true - falsexmlrpc-0.3.2/test/data/bug_bool.xml000066400000000000000000000002231400706365600172270ustar00rootroot00000000000000 0 xmlrpc-0.3.2/test/data/bug_cdata.expected000066400000000000000000000000221400706365600203460ustar00rootroot00000000000000--- - true - testxmlrpc-0.3.2/test/data/bug_cdata.xml000066400000000000000000000002401400706365600173470ustar00rootroot00000000000000 xmlrpc-0.3.2/test/data/bug_covert.expected000066400000000000000000000002611400706365600206010ustar00rootroot00000000000000--- - true - > Site,SANs,Array Configured Capacity,Array Reserved Capacity,Array Ava ilable Capacity,Array % Reserved,Host Allocated,Host Used,Host Free,Host % Used xmlrpc-0.3.2/test/data/bug_covert.xml000066400000000000000000000004221400706365600175770ustar00rootroot00000000000000Site,SANs,Array Configured Capacity,Array Reserved Capacity,Array Ava ilable Capacity,Array % Reserved,Host Allocated,Host Used,Host Free,Host % Used xmlrpc-0.3.2/test/data/datetime_iso8601.xml000066400000000000000000000002661400706365600204330ustar00rootroot00000000000000 20041105T01:15:23Z xmlrpc-0.3.2/test/data/fault.xml000066400000000000000000000005311400706365600165540ustar00rootroot00000000000000 faultCode 4 faultString an error message xmlrpc-0.3.2/test/data/value.expected000066400000000000000000000001011400706365600175470ustar00rootroot00000000000000--- - Test - - Hallo Leute - " Hallo " - '' - " "xmlrpc-0.3.2/test/data/value.xml000066400000000000000000000005271400706365600165620ustar00rootroot00000000000000 Test Hallo Leute Hallo xmlrpc-0.3.2/test/data/xml1.expected000066400000000000000000000125221400706365600173260ustar00rootroot00000000000000--- - true - - subscriber: MegaCorp lastName: Baker telephone1: 1-508-791-1267 telephone2: 1-800-445-2588 password: p1111 OID: "1" email: hbaker@yahoo.com adminId: hbaker objectName: AdministratorDO - subscriber: CornerStore lastName: Dragon telephone1: 1-781-789-9089 telephone2: 1-800-445-2588 password: p3333 OID: "3" email: adragon@yahoo.com adminId: adragon objectName: AdministratorDO - subscriber: Cyberdyne lastName: Rodman telephone1: 1-617-789-1890 telephone2: 1-800-445-2588 password: p4444 OID: "4" email: mrodman@yahoo.com adminId: mrodman objectName: AdministratorDO - subscriber: StarSports lastName: Jordan telephone1: 1-617-890-7897 telephone2: 1-800-445-2588 password: p5555 OID: "5" email: mjordan@yahoo.com adminId: mjordan objectName: AdministratorDO - subscriber: GreatBooks lastName: Pippen telephone1: 1-781-789-9876 telephone2: 1-800-445-2588 password: p6666 OID: "6" email: gpippen@yahoo.com adminId: gpippen objectName: AdministratorDO - subscriber: AxisChemicals lastName: Andhrew telephone1: 1-781-678-8970 telephone2: 1-800-445-2588 password: p7777 OID: "7" email: aandrew@yahoo.com adminId: aandrew objectName: AdministratorDO - subscriber: MediaShop lastName: Vincent telephone1: 1-786-897-8908 telephone2: 1-800-445-2588 password: p8888 OID: "8" email: tvincent@yahoo.com adminId: tvincent objectName: AdministratorDO - subscriber: SmartShop lastName: Richard telephone1: 1-508-789-6789 telephone2: 1-800-445-2588 password: p9999 OID: "9" email: krichard@yahoo.com adminId: krichard objectName: AdministratorDO - subscriber: HomeNeeds lastName: Cornell telephone1: 1-617-789-8979 telephone2: 1-800-445-2588 password: paaaa OID: "10" email: gconell@yahoo.com adminId: gcornell objectName: AdministratorDO - subscriber: MegaCorp lastName: HorstMann telephone1: 1-508-791-1267 telephone2: 1-800-445-2588 password: p1111 OID: "11" email: shorstmann@yahoo.com adminId: shorstmann objectName: AdministratorDO - subscriber: CornerStore lastName: Bob telephone1: 1-781-789-9089 telephone2: 1-800-445-2588 password: p3333 OID: "13" email: rbob@yahoo.com adminId: rbob objectName: AdministratorDO - subscriber: Cyberdyne lastName: Peter telephone1: 1-617-789-1890 telephone2: 1-800-445-2588 password: p4444 OID: "14" email: speter@yahoo.com adminId: speter objectName: AdministratorDO - subscriber: StarSports lastName: Novak telephone1: 1-617-890-7897 telephone2: 1-800-445-2588 password: p5555 OID: "15" email: pnovak@yahoo.com adminId: pnovak objectName: AdministratorDO - subscriber: GreatBooks lastName: Nancy telephone1: 1-781-789-9876 telephone2: 1-800-445-2588 password: p6666 OID: "16" email: pnancy@yahoo.com adminId: pnancy objectName: AdministratorDO - subscriber: AxisChemicals lastName: Michel telephone1: 1-781-678-8970 telephone2: 1-800-445-2588 password: p7777 OID: "17" email: hmichel@yahoo.com adminId: hmichel objectName: AdministratorDO - subscriber: MediaShop lastName: David telephone1: 1-786-897-8908 telephone2: 1-800-445-2588 password: p8888 OID: "18" email: kdavid@yahoo.com adminId: kdavid objectName: AdministratorDO - subscriber: SmartShop lastName: Valnoor telephone1: 1-508-789-6789 telephone2: 1-800-445-2588 password: p9999 OID: "19" email: pvalnoor@yahoo.com adminId: pvalnoor objectName: AdministratorDO - subscriber: HomeNeeds lastName: Smith telephone1: 1-617-789-8979 telephone2: 1-800-445-2588 password: paaaa OID: "20" email: wsmith@yahoo.com adminId: wsmith objectName: AdministratorDO - subscriber: MegaCorp lastName: Caral telephone1: 1-781-789-9876 telephone2: 1-800-445-2588 password: p6666 OID: "21" email: gcaral@yahoo.com adminId: gcaral objectName: AdministratorDO - subscriber: CornerStore lastName: Hillary telephone1: 1-786-897-8908 telephone2: 1-800-445-2588 password: p8888 OID: "23" email: phillary@yahoo.com adminId: phillary objectName: AdministratorDO - subscriber: Cyberdyne lastName: Philip telephone1: 1-508-789-6789 telephone2: 1-800-445-2588 password: p9999 OID: "24" email: bphilip@yahoo.com adminId: bphilip objectName: AdministratorDO - subscriber: StarSports lastName: Andrea telephone1: 1-617-789-8979 telephone2: 1-800-445-2588 password: paaaa OID: "25" email: sandrea@yahoo.com adminId: sandrea objectName: AdministratorDO - subscriber: s4 lastName: "null" telephone1: "null" telephone2: "null" password: s4 OID: "26" email: "null" adminId: s4 objectName: AdministratorDO - subscriber: BigBank lastName: administrator telephone1: '' telephone2: '' password: admin OID: "82" email: '' adminId: admin objectName: AdministratorDOxmlrpc-0.3.2/test/data/xml1.xml000066400000000000000000000340661400706365600163340ustar00rootroot00000000000000objectNameAdministratorDOadminIdhbakeremailhbaker@yahoo.comtelephone21-800-445-2588telephone11-508-791-1267OID1passwordp1111lastNameBakersubscriberMegaCorpobjectNameAdministratorDOadminIdadragonemailadragon@yahoo.comtelephone21-800-445-2588telephone11-781-789-9089OID3passwordp3333lastNameDragonsubscriberCornerStoreobjectNameAdministratorDOadminIdmrodmanemailmrodman@yahoo.comtelephone21-800-445-2588telephone11-617-789-1890OID4passwordp4444lastNameRodmansubscriberCyberdyneobjectNameAdministratorDOadminIdmjordanemailmjordan@yahoo.comtelephone21-800-445-2588telephone11-617-890-7897OID5passwordp5555lastNameJordansubscriberStarSportsobjectNameAdministratorDOadminIdgpippenemailgpippen@yahoo.comtelephone21-800-445-2588telephone11-781-789-9876OID6passwordp6666lastNamePippensubscriberGreatBooksobjectNameAdministratorDOadminIdaandrewemailaandrew@yahoo.comtelephone21-800-445-2588telephone11-781-678-8970OID7passwordp7777lastNameAndhrewsubscriberAxisChemicalsobjectNameAdministratorDOadminIdtvincentemailtvincent@yahoo.comtelephone21-800-445-2588telephone11-786-897-8908OID8passwordp8888lastNameVincentsubscriberMediaShopobjectNameAdministratorDOadminIdkrichardemailkrichard@yahoo.comtelephone21-800-445-2588telephone11-508-789-6789OID9passwordp9999lastNameRichardsubscriberSmartShopobjectNameAdministratorDOadminIdgcornellemailgconell@yahoo.comtelephone21-800-445-2588telephone11-617-789-8979OID10passwordpaaaalastNameCornellsubscriberHomeNeedsobjectNameAdministratorDOadminIdshorstmannemailshorstmann@yahoo.comtelephone21-800-445-2588telephone11-508-791-1267OID11passwordp1111lastNameHorstMannsubscriberMegaCorpobjectNameAdministratorDOadminIdrbobemailrbob@yahoo.comtelephone21-800-445-2588telephone11-781-789-9089OID13passwordp3333lastNameBobsubscriberCornerStoreobjectNameAdministratorDOadminIdspeteremailspeter@yahoo.comtelephone21-800-445-2588telephone11-617-789-1890OID14passwordp4444lastNamePetersubscriberCyberdyneobjectNameAdministratorDOadminIdpnovakemailpnovak@yahoo.comtelephone21-800-445-2588telephone11-617-890-7897OID15passwordp5555lastNameNovaksubscriberStarSportsobjectNameAdministratorDOadminIdpnancyemailpnancy@yahoo.comtelephone21-800-445-2588telephone11-781-789-9876OID16passwordp6666lastNameNancysubscriberGreatBooksobjectNameAdministratorDOadminIdhmichelemailhmichel@yahoo.comtelephone21-800-445-2588telephone11-781-678-8970OID17passwordp7777lastNameMichelsubscriberAxisChemicalsobjectNameAdministratorDOadminIdkdavidemailkdavid@yahoo.comtelephone21-800-445-2588telephone11-786-897-8908OID18passwordp8888lastNameDavidsubscriberMediaShopobjectNameAdministratorDOadminIdpvalnooremailpvalnoor@yahoo.comtelephone21-800-445-2588telephone11-508-789-6789OID19passwordp9999lastNameValnoorsubscriberSmartShopobjectNameAdministratorDOadminIdwsmithemailwsmith@yahoo.comtelephone21-800-445-2588telephone11-617-789-8979OID20passwordpaaaalastNameSmithsubscriberHomeNeedsobjectNameAdministratorDOadminIdgcaralemailgcaral@yahoo.comtelephone21-800-445-2588telephone11-781-789-9876OID21passwordp6666lastNameCaralsubscriberMegaCorpobjectNameAdministratorDOadminIdphillaryemailphillary@yahoo.comtelephone21-800-445-2588telephone11-786-897-8908OID23passwordp8888lastNameHillarysubscriberCornerStoreobjectNameAdministratorDOadminIdbphilipemailbphilip@yahoo.comtelephone21-800-445-2588telephone11-508-789-6789OID24passwordp9999lastNamePhilipsubscriberCyberdyneobjectNameAdministratorDOadminIdsandreaemailsandrea@yahoo.comtelephone21-800-445-2588telephone11-617-789-8979OID25passwordpaaaalastNameAndreasubscriberStarSportsobjectNameAdministratorDOadminIds4emailnulltelephone2nulltelephone1nullOID26passwords4lastNamenullsubscribers4objectNameAdministratorDOadminIdadminemailtelephone2telephone1OID82passwordadminlastNameadministratorsubscriberBigBank xmlrpc-0.3.2/test/htpasswd000066400000000000000000000001631400706365600155670ustar00rootroot00000000000000admin:Qg266hq/YYKe2 01234567890123456789012345678901234567890123456789012345678901234567890123456789:Yl.SJmoFETpS2 xmlrpc-0.3.2/test/test_base64.rb000066400000000000000000000006011400706365600164540ustar00rootroot00000000000000# frozen_string_literal: true require 'test/unit' require 'xmlrpc/base64' module TestXMLRPC class Test_Base64 < Test::Unit::TestCase def test_equals refute_equal(XMLRPC::Base64.new('foobar'), 'foobar') refute_equal(XMLRPC::Base64.new('foo'), XMLRPC::Base64.new('bar')) assert_equal(XMLRPC::Base64.new('foobar'), XMLRPC::Base64.new('foobar')) end end end xmlrpc-0.3.2/test/test_client.rb000066400000000000000000000220261400706365600166530ustar00rootroot00000000000000# frozen_string_literal: false require 'test/unit' require 'xmlrpc/client' require 'net/http' begin require 'openssl' rescue LoadError end module XMLRPC class ClientTest < Test::Unit::TestCase module Fake class HTTP < Net::HTTP class << self def new(*args, &block) Class.method(:new).unbind.bind(self).call(*args, &block) end end def initialize responses = {} super("127.0.0.1") @started = false @responses = responses end def started? @started end def start @started = true if block_given? begin return yield(self) ensure @started = false end end self end def request_post path, request, headers @responses[path].shift end end class Client < XMLRPC::Client attr_reader :args, :http def initialize(*args) @args = args super end private def net_http host, port, proxy_host, proxy_port HTTP.new end end class Response def self.new body, fields = [], status = '200' klass = Class.new(Net::HTTPResponse::CODE_TO_OBJ[status]) { def initialize(*args) super @read = true end } resp = klass.new '1.1', status, 'OK' resp.body = body fields.each do |k,v| resp.add_field k, v end resp end end end def test_new2_host_path_port client = Fake::Client.new2 'http://example.org/foo' host, path, port, *rest = client.args assert_equal 'example.org', host assert_equal '/foo', path assert_equal 80, port rest.each { |x| refute x } end def test_new2_custom_port client = Fake::Client.new2 'http://example.org:1234/foo' host, path, port, *rest = client.args assert_equal 'example.org', host assert_equal '/foo', path assert_equal 1234, port rest.each { |x| refute x } end def test_new2_ssl client = Fake::Client.new2 'https://example.org/foo' host, path, port, proxy_host, proxy_port, user, password, use_ssl, timeout = client.args assert_equal 'example.org', host assert_equal '/foo', path assert_equal 443, port assert use_ssl refute proxy_host refute proxy_port refute user refute password refute timeout end if defined?(OpenSSL) def test_new2_ssl_custom_port client = Fake::Client.new2 'https://example.org:1234/foo' host, path, port, proxy_host, proxy_port, user, password, use_ssl, timeout = client.args assert_equal 'example.org', host assert_equal '/foo', path assert_equal 1234, port assert use_ssl refute proxy_host refute proxy_port refute user refute password refute timeout end if defined?(OpenSSL) def test_new2_user_password client = Fake::Client.new2 'http://aaron:tenderlove@example.org/foo' host, path, port, proxy_host, proxy_port, user, password, use_ssl, timeout = client.args [ host, path, port ].each { |x| assert x } assert_equal 'aaron', user assert_equal 'tenderlove', password [ proxy_host, proxy_port, use_ssl, timeout ].each { |x| refute x } end def test_new2_proxy_host client = Fake::Client.new2 'http://example.org/foo', 'example.com' host, path, port, proxy_host, proxy_port, user, password, use_ssl, timeout = client.args [ host, path, port ].each { |x| assert x } assert_equal 'example.com', proxy_host [ user, password, proxy_port, use_ssl, timeout ].each { |x| refute x } end def test_new2_proxy_port client = Fake::Client.new2 'http://example.org/foo', 'example.com:1234' host, path, port, proxy_host, proxy_port, user, password, use_ssl, timeout = client.args [ host, path, port ].each { |x| assert x } assert_equal 'example.com', proxy_host assert_equal 1234, proxy_port [ user, password, use_ssl, timeout ].each { |x| refute x } end def test_new2_no_path client = Fake::Client.new2 'http://example.org' host, path, port, *rest = client.args assert_equal 'example.org', host assert_nil path assert port rest.each { |x| refute x } end def test_new2_slash_path client = Fake::Client.new2 'http://example.org/' host, path, port, *rest = client.args assert_equal 'example.org', host assert_equal '/', path assert port rest.each { |x| refute x } end def test_new2_bad_protocol assert_raise(ArgumentError) do XMLRPC::Client.new2 'ftp://example.org' end end def test_new2_bad_uri assert_raise(ArgumentError) do XMLRPC::Client.new2 ':::::' end end def test_new2_path_with_query client = Fake::Client.new2 'http://example.org/foo?bar=baz' host, path, port, *rest = client.args assert_equal 'example.org', host assert_equal '/foo?bar=baz', path assert port rest.each { |x| refute x } end def test_request fh = read 'blog.xml' responses = { '/foo' => [ Fake::Response.new(fh, [['Content-Type', 'text/xml']]) ] } client = fake_client(responses).new2 'http://example.org/foo' resp = client.call('wp.getUsersBlogs', 'tlo', 'omg') expected = [{ "isAdmin" => true, "url" => "http://tenderlovemaking.com/", "blogid" => "1", "blogName" => "Tender Lovemaking", "xmlrpc" => "http://tenderlovemaking.com/xmlrpc.php" }] assert_equal expected, resp end def test_async_request fh = read 'blog.xml' responses = { '/foo' => [ Fake::Response.new(fh, [['Content-Type', 'text/xml']]) ] } client = fake_client(responses).new2 'http://example.org/foo' resp = client.call_async('wp.getUsersBlogs', 'tlo', 'omg') expected = [{ "isAdmin" => true, "url" => "http://tenderlovemaking.com/", "blogid" => "1", "blogName" => "Tender Lovemaking", "xmlrpc" => "http://tenderlovemaking.com/xmlrpc.php" }] assert_equal expected, resp end def test_application_xml_content_type fh = read 'blog.xml' responses = { '/foo' => [ Fake::Response.new(fh, [['Content-Type', 'application/xml']]) ] } client = fake_client(responses).new2 'http://example.org/foo' resp = client.call('wp.getUsersBlogs', 'tlo', 'omg') expected = [{ "isAdmin" => true, "url" => "http://tenderlovemaking.com/", "blogid" => "1", "blogName" => "Tender Lovemaking", "xmlrpc" => "http://tenderlovemaking.com/xmlrpc.php" }] assert_equal expected, resp end # make a request without content-type header def test_bad_content_type fh = read 'blog.xml' responses = { '/foo' => [ Fake::Response.new(fh) ] } client = fake_client(responses).new2 'http://example.org/foo' resp = client.call('wp.getUsersBlogs', 'tlo', 'omg') expected = [{ "isAdmin" => true, "url" => "http://tenderlovemaking.com/", "blogid" => "1", "blogName" => "Tender Lovemaking", "xmlrpc" => "http://tenderlovemaking.com/xmlrpc.php" }] assert_equal expected, resp end def test_no_data responses = { '/foo' => [ Fake::Response.new(nil, [['Content-Type', 'text/xml']]) ] } client = fake_client(responses).new2 'http://example.org/foo' assert_raise(RuntimeError.new("No data")) do client.call('wp.getUsersBlogs', 'tlo', 'omg') end end def test_i8_tag fh = read('blog.xml').gsub(/string/, 'i8') responses = { '/foo' => [ Fake::Response.new(fh) ] } client = fake_client(responses).new2 'http://example.org/foo' resp = client.call('wp.getUsersBlogs', 'tlo', 'omg') assert_equal 1, resp.first['blogid'] end def test_cookie_simple client = Fake::Client.new2('http://example.org/cookie') assert_nil(client.cookie) client.send(:parse_set_cookies, ["param1=value1", "param2=value2"]) assert_equal("param1=value1; param2=value2", client.cookie) end def test_cookie_override client = Fake::Client.new2('http://example.org/cookie') client.send(:parse_set_cookies, [ "param1=value1", "param2=value2", "param1=value3", ]) assert_equal("param2=value2; param1=value3", client.cookie) end private def read filename File.read File.expand_path(File.join(__FILE__, '..', 'data', filename)) end def fake_client responses Class.new(Fake::Client) { define_method(:net_http) { |*_| Fake::HTTP.new(responses) } } end end end xmlrpc-0.3.2/test/test_cookie.rb000066400000000000000000000044761400706365600166570ustar00rootroot00000000000000# frozen_string_literal: false require 'test/unit' require 'time' require 'webrick' require_relative 'webrick_testing' require "xmlrpc/server" require 'xmlrpc/client' module TestXMLRPC class TestCookie < Test::Unit::TestCase include WEBrick_Testing def create_servlet s = XMLRPC::WEBrickServlet.new def s.logged_in_users @logged_in_users ||= {} end def s.request @request end def s.response @response end def s.service(request, response) @request = request @response = response super ensure @request = nil @response = nil end key = Time.now.to_i.to_s valid_user = "valid-user" s.add_handler("test.login") do |user, password| ok = (user == valid_user and password == "secret") if ok s.logged_in_users[key] = user expires = (Time.now + 60 * 60).httpdate cookies = s.response.cookies cookies << "key=\"#{key}\"; path=\"/RPC2\"; expires=#{expires}" cookies << "user=\"#{user}\"; path=\"/RPC2\"" end ok end s.add_handler("test.require_authenticate_echo") do |string| cookies = {} s.request.cookies.each do |cookie| cookies[cookie.name] = cookie.value end if cookies == {"key" => key, "user" => valid_user} string else raise XMLRPC::FaultException.new(29, "Authentication required") end end s.set_default_handler do |name, *args| raise XMLRPC::FaultException.new(-99, "Method #{name} missing" + " or wrong number of parameters!") end s.add_introspection s end def setup_http_server_option {:Port => 0} end def test_cookie option = setup_http_server_option with_server(option, create_servlet) {|addr| begin @s = XMLRPC::Client.new3(:host => addr.ip_address, :port => addr.ip_port) do_test ensure @s.http.finish end } end def do_test assert(!@s.call("test.login", "invalid-user", "invalid-password")) exception = assert_raise(XMLRPC::FaultException) do @s.call("test.require_authenticate_echo", "Hello") end assert_equal(29, exception.faultCode) assert(@s.call("test.login", "valid-user", "secret")) assert_equal("Hello", @s.call("test.require_authenticate_echo", "Hello")) end end end xmlrpc-0.3.2/test/test_datetime.rb000066400000000000000000000111611400706365600171670ustar00rootroot00000000000000# frozen_string_literal: false require 'test/unit' require "xmlrpc/datetime" module TestXMLRPC class Test_DateTime < Test::Unit::TestCase def test_new dt = createDateTime() assert_instance_of(XMLRPC::DateTime, dt) end def test_new_exception assert_raise(ArgumentError) { XMLRPC::DateTime.new(4.5, 13, 32, 25, 60, 60) } assert_raise(ArgumentError) { XMLRPC::DateTime.new(2001, 12, 32, 25, 60, 60) } assert_raise(ArgumentError) { XMLRPC::DateTime.new(2001, 12, 31, 25, 60, 60) } assert_raise(ArgumentError) { XMLRPC::DateTime.new(2001, 12, 31, 24, 60, 60) } assert_raise(ArgumentError) { XMLRPC::DateTime.new(2001, 12, 31, 24, 59, 60) } assert_nothing_raised(ArgumentError) { XMLRPC::DateTime.new(2001, 12, 31, 24, 59, 59) } assert_raise(ArgumentError) { XMLRPC::DateTime.new(2001, 0, 0, -1, -1, -1) } assert_raise(ArgumentError) { XMLRPC::DateTime.new(2001, 1, 0, -1, -1, -1) } assert_raise(ArgumentError) { XMLRPC::DateTime.new(2001, 1, 1, -1, -1, -1) } assert_raise(ArgumentError) { XMLRPC::DateTime.new(2001, 1, 1, 0, -1, -1) } assert_raise(ArgumentError) { XMLRPC::DateTime.new(2001, 1, 1, 0, 0, -1) } assert_nothing_raised(ArgumentError) { XMLRPC::DateTime.new(2001, 1, 1, 0, 0, 0) } end def test_get_values y, m, d, h, mi, s = 1970, 3, 24, 12, 0, 5 dt = XMLRPC::DateTime.new(y, m, d, h, mi, s) assert_equal(y, dt.year) assert_equal(m, dt.month) assert_equal(m, dt.mon) assert_equal(d, dt.day) assert_equal(h, dt.hour) assert_equal(mi,dt.min) assert_equal(s, dt.sec) end def test_set_values dt = createDateTime() y, m, d, h, mi, s = 1950, 12, 9, 8, 52, 30 dt.year = y dt.month = m dt.day = d dt.hour = h dt.min = mi dt.sec = s assert_equal(y, dt.year) assert_equal(m, dt.month) assert_equal(m, dt.mon) assert_equal(d, dt.day) assert_equal(h, dt.hour) assert_equal(mi,dt.min) assert_equal(s, dt.sec) dt.mon = 5 assert_equal(5, dt.month) assert_equal(5, dt.mon) end def test_set_exception dt = createDateTime() assert_raise(ArgumentError) { dt.year = 4.5 } assert_nothing_raised(ArgumentError) { dt.year = -2000 } assert_raise(ArgumentError) { dt.month = 0 } assert_raise(ArgumentError) { dt.month = 13 } assert_nothing_raised(ArgumentError) { dt.month = 7 } assert_raise(ArgumentError) { dt.mon = 0 } assert_raise(ArgumentError) { dt.mon = 13 } assert_nothing_raised(ArgumentError) { dt.mon = 7 } assert_raise(ArgumentError) { dt.day = 0 } assert_raise(ArgumentError) { dt.day = 32 } assert_nothing_raised(ArgumentError) { dt.day = 16 } assert_raise(ArgumentError) { dt.hour = -1 } assert_raise(ArgumentError) { dt.hour = 25 } assert_nothing_raised(ArgumentError) { dt.hour = 12 } assert_raise(ArgumentError) { dt.min = -1 } assert_raise(ArgumentError) { dt.min = 60 } assert_nothing_raised(ArgumentError) { dt.min = 30 } assert_raise(ArgumentError) { dt.sec = -1 } assert_raise(ArgumentError) { dt.sec = 60 } assert_nothing_raised(ArgumentError) { dt.sec = 30 } end def test_to_a y, m, d, h, mi, s = 1970, 3, 24, 12, 0, 5 dt = XMLRPC::DateTime.new(y, m, d, h, mi, s) a = dt.to_a assert_instance_of(Array, a) assert_equal(6, a.size, "Returned array has wrong size") assert_equal(y, a[0]) assert_equal(m, a[1]) assert_equal(d, a[2]) assert_equal(h, a[3]) assert_equal(mi, a[4]) assert_equal(s, a[5]) end def test_to_time1 y, m, d, h, mi, s = 1970, 3, 24, 12, 0, 5 dt = XMLRPC::DateTime.new(y, m, d, h, mi, s) time = dt.to_time assert_not_nil(time) assert_equal(y, time.year) assert_equal(m, time.month) assert_equal(d, time.day) assert_equal(h, time.hour) assert_equal(mi, time.min) assert_equal(s, time.sec) end def test_to_time2 y, m, d, h, mi, s = 1969, 3, 24, 12, 0, 5 dt = XMLRPC::DateTime.new(y, m, d, h, mi, s) time = dt.to_time assert_not_nil(time) assert_equal(y, time.year) assert_equal(m, time.month) assert_equal(d, time.day) assert_equal(h, time.hour) assert_equal(mi, time.min) assert_equal(s, time.sec) end def test_to_date1 y, m, d, h, mi, s = 1970, 3, 24, 12, 0, 5 dt = XMLRPC::DateTime.new(y, m, d, h, mi, s) date = dt.to_date assert_equal(y, date.year) assert_equal(m, date.month) assert_equal(d, date.day) end def test_to_date2 dt = createDateTime() dt.year = 666 assert_equal(666, dt.to_date.year) end def createDateTime XMLRPC::DateTime.new(1970, 3, 24, 12, 0, 5) end end end xmlrpc-0.3.2/test/test_features.rb000066400000000000000000000031031400706365600172060ustar00rootroot00000000000000# frozen_string_literal: false require 'test/unit' require "xmlrpc/create" require "xmlrpc/parser" require "xmlrpc/config" module TestXMLRPC class Test_Features < Test::Unit::TestCase def setup @params = [nil, {"test" => nil}, [nil, 1, nil]] end def test_nil_create XMLRPC::XMLWriter.each_installed_writer do |writer| c = XMLRPC::Create.new(writer) XMLRPC::Config.module_eval {remove_const(:ENABLE_NIL_CREATE)} XMLRPC::Config.const_set(:ENABLE_NIL_CREATE, false) assert_raise(RuntimeError) { c.methodCall("test", *@params) } XMLRPC::Config.module_eval {remove_const(:ENABLE_NIL_CREATE)} XMLRPC::Config.const_set(:ENABLE_NIL_CREATE, true) assert_nothing_raised { c.methodCall("test", *@params) } end end def test_nil_parse XMLRPC::Config.module_eval {remove_const(:ENABLE_NIL_CREATE)} XMLRPC::Config.const_set(:ENABLE_NIL_CREATE, true) XMLRPC::XMLWriter.each_installed_writer do |writer| c = XMLRPC::Create.new(writer) str = c.methodCall("test", *@params) XMLRPC::XMLParser.each_installed_parser do |parser| para = nil XMLRPC::Config.module_eval {remove_const(:ENABLE_NIL_PARSER)} XMLRPC::Config.const_set(:ENABLE_NIL_PARSER, false) assert_raise(RuntimeError) { para = parser.parseMethodCall(str) } XMLRPC::Config.module_eval {remove_const(:ENABLE_NIL_PARSER)} XMLRPC::Config.const_set(:ENABLE_NIL_PARSER, true) assert_nothing_raised { para = parser.parseMethodCall(str) } assert_equal(para[1], @params) end end end end end xmlrpc-0.3.2/test/test_marshal.rb000066400000000000000000000051661400706365600170320ustar00rootroot00000000000000# frozen_string_literal: false require 'test/unit' require "xmlrpc/marshal" module TestXMLRPC class Test_Marshal < Test::Unit::TestCase # for test_parser_values class Person include XMLRPC::Marshallable attr_reader :name def initialize(name) @name = name end end def test1_dump_response assert_nothing_raised(NameError) { XMLRPC::Marshal.dump_response('arg') } end def test1_dump_call assert_nothing_raised(NameError) { XMLRPC::Marshal.dump_call('methodName', 'arg') } end def test2_dump_load_response value = [1, 2, 3, {"test" => true}, 3.4] res = XMLRPC::Marshal.dump_response(value) assert_equal(value, XMLRPC::Marshal.load_response(res)) end def test2_dump_load_call methodName = "testMethod" value = [1, 2, 3, {"test" => true}, 3.4] exp = [methodName, [value, value]] res = XMLRPC::Marshal.dump_call(methodName, value, value) assert_equal(exp, XMLRPC::Marshal.load_call(res)) end def test_parser_values v1 = [ 1, -7778, -(2**31), 2**31-1, # integers 1.0, 0.0, -333.0, 2343434343.0, # floats false, true, true, false, # booleans "Hallo", "with < and >", "" # strings ] v2 = [ [v1, v1, v1], {"a" => v1} ] v3 = [ XMLRPC::Base64.new("\001"*1000), # base64 :aSymbol, :anotherSym # symbols (-> string) ] v3_exp = [ "\001"*1000, "aSymbol", "anotherSym" ] person = Person.new("Michael") XMLRPC::XMLParser.each_installed_parser do |parser| m = XMLRPC::Marshal.new(parser) assert_equal( v1, m.load_response(m.dump_response(v1)) ) assert_equal( v2, m.load_response(m.dump_response(v2)) ) assert_equal( v3_exp, m.load_response(m.dump_response(v3)) ) pers = m.load_response(m.dump_response(person)) assert_kind_of( Person, pers ) assert_equal( person.name, pers.name ) end # missing, Date, Time, DateTime # Struct end def test_parser_invalid_values values = [ -1-(2**31), 2**31, Float::INFINITY, -Float::INFINITY, Float::NAN ] XMLRPC::XMLParser.each_installed_parser do |parser| m = XMLRPC::Marshal.new(parser) values.each do |v| assert_raise(RuntimeError, "#{v} shouldn't be dumped, but dumped") \ { m.dump_response(v) } end end end def test_no_params_tag # bug found by Idan Sofer expect = %{myMethod\n} str = XMLRPC::Marshal.dump_call("myMethod") assert_equal(expect, str) end end end xmlrpc-0.3.2/test/test_parser.rb000066400000000000000000000047061400706365600166760ustar00rootroot00000000000000# frozen_string_literal: false require 'test/unit' require 'xmlrpc/datetime' require "xmlrpc/parser" require 'yaml' module TestXMLRPC module GenericParserTest def datafile(base) File.join(File.dirname(__FILE__), "data", base) end def load_data(name) [File.read(datafile(name) + ".xml"), YAML.load(File.read(datafile(name) + ".expected"))] end def setup @xml1, @expected1 = load_data('xml1') @xml2, @expected2 = load_data('bug_covert') @xml3, @expected3 = load_data('bug_bool') @xml4, @expected4 = load_data('value') @cdata_xml, @cdata_expected = load_data('bug_cdata') @datetime_xml = File.read(datafile('datetime_iso8601.xml')) @datetime_expected = XMLRPC::DateTime.new(2004, 11, 5, 1, 15, 23) @fault_doc = File.read(datafile('fault.xml')) end # test parseMethodResponse -------------------------------------------------- def test_parseMethodResponse1 assert_equal(@expected1, @p.parseMethodResponse(@xml1)) end def test_parseMethodResponse2 assert_equal(@expected2, @p.parseMethodResponse(@xml2)) end def test_parseMethodResponse3 assert_equal(@expected3, @p.parseMethodResponse(@xml3)) end def test_cdata assert_equal(@cdata_expected, @p.parseMethodResponse(@cdata_xml)) end def test_dateTime assert_equal(@datetime_expected, @p.parseMethodResponse(@datetime_xml)[1]) end # test parseMethodCall ------------------------------------------------------ def test_parseMethodCall assert_equal(@expected4, @p.parseMethodCall(@xml4)) end # test fault ---------------------------------------------------------------- def test_fault flag, fault = @p.parseMethodResponse(@fault_doc) assert_equal(flag, false) assert_kind_of(XMLRPC::FaultException, fault, "must be an instance of class XMLRPC::FaultException") assert_equal(fault.faultCode, 4) assert_equal(fault.faultString, "an error message") end def test_fault_message fault = XMLRPC::FaultException.new(1234, 'an error message') assert_equal('an error message', fault.to_s) assert_equal('#', fault.inspect) end end # create test class for each installed parser XMLRPC::XMLParser.each_installed_parser do |parser| klass = parser.class name = klass.to_s.split("::").last eval %{ class Test_#{name} < Test::Unit::TestCase include GenericParserTest def setup super @p = #{klass}.new end end } end end xmlrpc-0.3.2/test/test_server.rb000066400000000000000000000014001400706365600166740ustar00rootroot00000000000000# coding: utf-8 # frozen_string_literal: true require 'test/unit' require 'stringio' require 'xmlrpc/client' require 'xmlrpc/server' module TestXMLRPC class TestXMLRPCServer < Test::Unit::TestCase def test_port s = nil begin stdout, $stdout = $stdout, StringIO.new stderr, $stderr = $stderr, StringIO.new s = XMLRPC::Server.new(0, '127.0.0.1', 1) ensure $stdout = stdout $stderr = stderr end refute_equal(0, s.port, 'Selected random port') s.add_handler('test.add') { |a, b| a + b } srv_thread = Thread.new { s.serve } begin c = XMLRPC::Client.new('127.0.0.1', '/', s.port) assert_equal(7, c.call('test.add', 3, 4)) ensure s.shutdown srv_thread.kill end end end end xmlrpc-0.3.2/test/test_webrick_server.rb000066400000000000000000000067211400706365600204150ustar00rootroot00000000000000# coding: utf-8 # frozen_string_literal: false require 'test/unit' require 'webrick' require_relative 'webrick_testing' require "xmlrpc/server" require 'xmlrpc/client' require 'logger' module TestXMLRPC class Test_Webrick < Test::Unit::TestCase include WEBrick_Testing def create_servlet(server) s = XMLRPC::WEBrickServlet.new basic_auth = WEBrick::HTTPAuth::BasicAuth.new( :Realm => 'auth', :UserDB => WEBrick::HTTPAuth::Htpasswd.new(File.expand_path('./htpasswd', File.dirname(__FILE__))), :Logger => server.logger, ) class << s; self end.send(:define_method, :service) {|req, res| basic_auth.authenticate(req, res) super(req, res) } s.add_handler("test.add") do |a,b| a + b end s.add_handler("test.div") do |a,b| if b == 0 raise XMLRPC::FaultException.new(1, "division by zero") else a / b end end s.set_default_handler do |name, *args| raise XMLRPC::FaultException.new(-99, "Method #{name} missing" + " or wrong number of parameters!") end s.add_introspection return s end def setup_http_server_option(use_ssl) option = { :BindAddress => "localhost", :Port => 0, :SSLEnable => use_ssl, } if use_ssl require 'webrick/https' option.update( :SSLVerifyClient => ::OpenSSL::SSL::VERIFY_NONE, :SSLCertName => [] ) end option end def test_client_server # NOTE: I don't enable SSL testing as this hangs [false].each do |use_ssl| option = setup_http_server_option(use_ssl) with_server(option, method(:create_servlet)) {|addr| @s = XMLRPC::Client.new3(:host => addr.ip_address, :port => addr.ip_port, :use_ssl => use_ssl) @s.user = 'admin' @s.password = 'admin' silent do do_test end @s.http.finish @s = XMLRPC::Client.new3(:host => addr.ip_address, :port => addr.ip_port, :use_ssl => use_ssl) @s.user = '01234567890123456789012345678901234567890123456789012345678901234567890123456789' @s.password = 'guest' silent do do_test end @s.http.finish } end end def silent begin back, $VERBOSE = $VERBOSE, nil yield ensure $VERBOSE = back end end def do_test # simple call assert_equal 9, @s.call('test.add', 4, 5) # fault exception assert_raise(XMLRPC::FaultException) { @s.call('test.div', 1, 0) } # fault exception via call2 ok, param = @s.call2('test.div', 1, 0) assert_equal false, ok assert_instance_of XMLRPC::FaultException, param assert_equal 1, param.faultCode assert_equal 'division by zero', param.faultString # call2 without fault exception ok, param = @s.call2('test.div', 10, 5) assert_equal true, ok assert_equal param, 2 # introspection assert_equal ["test.add", "test.div", "system.listMethods", "system.methodSignature", "system.methodHelp"], @s.call("system.listMethods") # default handler (missing handler) ok, param = @s.call2('test.nonexisting') assert_equal false, ok assert_equal(-99, param.faultCode) # default handler (wrong number of arguments) ok, param = @s.call2('test.add', 1, 2, 3) assert_equal false, ok assert_equal(-99, param.faultCode) # multibyte characters assert_equal "あいうえおかきくけこ", @s.call('test.add', "あいうえお", "かきくけこ") end end end xmlrpc-0.3.2/test/test_xmlrpc.rb000066400000000000000000000002351400706365600167000ustar00rootroot00000000000000require 'test/unit' require 'xmlrpc' class XmlrpcTest < Test::Unit::TestCase def test_that_it_has_a_version_number assert ::XMLRPC::VERSION end end xmlrpc-0.3.2/test/webrick_testing.rb000066400000000000000000000030031400706365600175130ustar00rootroot00000000000000# frozen_string_literal: false require 'timeout' # Backport of WEBrick::Utils::TimeoutHandler.terminate from Ruby 2.4. unless WEBrick::Utils::TimeoutHandler.respond_to? :terminate class WEBrick::Utils::TimeoutHandler def self.terminate instance.terminate end def terminate TimeoutMutex.synchronize{ @timeout_info.clear @watcher&.kill&.join } end end end module TestXMLRPC module WEBrick_Testing def teardown WEBrick::Utils::TimeoutHandler.terminate super end def start_server(logger, config={}) raise "already started" if defined?(@__server) && @__server @__started = false @__server = WEBrick::HTTPServer.new( { :BindAddress => "localhost", :Logger => logger, :AccessLog => [], }.update(config)) yield @__server @__started = true addr = @__server.listeners.first.connect_address @__server_thread = Thread.new { begin @__server.start rescue IOError => e assert_match(/closed/, e.message) ensure @__started = false end } addr end def with_server(config, servlet) log = [] logger = WEBrick::Log.new(log, WEBrick::BasicLog::WARN) addr = start_server(logger, config) {|w| servlet = servlet.call(w) if servlet.respond_to? :call w.mount('/RPC2', servlet) } begin yield addr ensure @__server.shutdown end @__server_thread.join @__server = nil assert_equal([], log) end end end xmlrpc-0.3.2/xmlrpc.gemspec000066400000000000000000000021531400706365600157030ustar00rootroot00000000000000# coding: utf-8 lib = File.expand_path('../lib', __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'xmlrpc' Gem::Specification.new do |spec| spec.name = "xmlrpc" spec.version = XMLRPC::VERSION spec.authors = ["SHIBATA Hiroshi", "Sutou Kouhei"] spec.email = ["hsbt@ruby-lang.org", "kou@cozmixng.org"] spec.summary = %q{XMLRPC is a lightweight protocol that enables remote procedure calls over HTTP.} spec.description = %q{XMLRPC is a lightweight protocol that enables remote procedure calls over HTTP.} spec.homepage = "https://github.com/ruby/xmlrpc" spec.licenses = ["Ruby", "BSD-2-Clause"] spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } spec.bindir = "exe" spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } spec.require_paths = ["lib"] spec.required_ruby_version = ">= 2.3" spec.add_dependency "webrick" spec.add_development_dependency "bundler" spec.add_development_dependency "rake" spec.add_development_dependency "test-unit" end