module CGI::QueryExtension
Mixin module that provides the following:
-
Access to the CGI environment variables as methods. See documentation to the CGI class for a list of these variables. The methods are exposed by removing the leading
HTTP_
(if it exists) and downcasing the name. For example,auth_type
will return the environment variableAUTH_TYPE
, andaccept
will return the value forHTTP_ACCEPT
. -
Access to cookies, including the cookies attribute.
-
Access to parameters, including the params attribute, and overloading [] to perform parameter value lookup by key.
-
The #initialize_query method, for initializing the above mechanisms, handling multipart forms, and allowing the class to be used in “offline” mode.
Attributes
Get the uploaded files as a hash of name=>values pairs
Get the parameters as a hash of name=>values pairs, where values is an Array.
Public Instance Methods
Get the value for the parameter with a given key.
If the parameter has multiple values, only the first will be retrieved; use params to get the array of values.
# File lib/cgi/core.rb, line 697 def [](key) params = @params[key] return '' unless params value = params[0] if @multipart if value return value elsif defined? StringIO StringIO.new("".force_encoding(Encoding::ASCII_8BIT)) else Tempfile.new("CGI",encoding: Encoding::ASCII_8BIT) end else str = if value then value.dup else "" end str end end
Returns true if a given query string parameter exists.
# File lib/cgi/core.rb, line 721 def has_key?(*args) @params.has_key?(*args) end
Return all query parameter names as an array of String.
# File lib/cgi/core.rb, line 716 def keys(*args) @params.keys(*args) end
Returns whether the form contained multipart/form-data
# File lib/cgi/core.rb, line 689 def multipart? @multipart end
Set all the parameters.
# File lib/cgi/core.rb, line 458 def params=(hash) @params.clear @params.update(hash) end
Private Instance Methods
A wrapper class to use a StringIO object as the body and switch to a TempFile when the passed threshold is passed. Initialize the data from the query.
Handles multipart forms (in particular, forms that involve file uploads). Reads query parameters in the @params field, and cookies into @cookies.
# File lib/cgi/core.rb, line 645 def initialize_query() if ("POST" == env_table['REQUEST_METHOD']) and %r|\Amultipart/form-data.*boundary=\"?([^\";,]+)\"?|.match(env_table['CONTENT_TYPE']) raise StandardError.new("too large multipart data.") if env_table['CONTENT_LENGTH'].to_i > MAX_MULTIPART_LENGTH boundary = $1.dup @multipart = true @params = read_multipart(boundary, Integer(env_table['CONTENT_LENGTH'])) else @multipart = false @params = CGI::parse( case env_table['REQUEST_METHOD'] when "GET", "HEAD" if defined?(MOD_RUBY) Apache::request.args or "" else env_table['QUERY_STRING'] or "" end when "POST" stdinput.binmode if defined? stdinput.binmode stdinput.read(Integer(env_table['CONTENT_LENGTH'])) or '' else read_from_cmdline end.dup.force_encoding(@accept_charset) ) unless Encoding.find(@accept_charset) == Encoding::ASCII_8BIT @params.each do |key,values| values.each do |value| unless value.valid_encoding? if @accept_charset_error_block @accept_charset_error_block.call(key,value) else raise InvalidEncoding,"Accept-Charset encoding error" end end end end end end @cookies = CGI::Cookie::parse((env_table['HTTP_COOKIE'] or env_table['COOKIE'])) end
offline mode. read name=value pairs on standard input.
# File lib/cgi/core.rb, line 610 def read_from_cmdline require "shellwords" string = unless ARGV.empty? ARGV.join(' ') else if STDIN.tty? STDERR.print( %Q|(offline mode: enter name=value pairs on standard input)\n| ) end array = readlines rescue nil if not array.nil? array.join(' ').gsub(/\n/n, '') else "" end end.gsub(/\=/n, '%3D').gsub(/\&/n, '%26') words = Shellwords.shellwords(string) if words.find{|x| /=/n.match(x) } words.join('&') else words.join('+') end end
Parses multipart form elements according to
http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.2
Returns a hash of multipart form parameters with bodies of type StringIO or Tempfile depending on whether the multipart form element exceeds 10 KB
params[name => body]
# File lib/cgi/core.rb, line 472 def read_multipart(boundary, content_length) ## read first boundary stdin = stdinput first_line = "--#{boundary}#{EOL}" content_length -= first_line.bytesize status = stdin.read(first_line.bytesize) raise EOFError.new("no content body") unless status raise EOFError.new("bad content body") unless first_line == status ## parse and set params params = {} @files = {} boundary_rexp = /--#{Regexp.quote(boundary)}(#{EOL}|--)/ boundary_size = "#{EOL}--#{boundary}#{EOL}".bytesize boundary_end = nil buf = '' bufsize = 10 * 1024 max_count = MAX_MULTIPART_COUNT n = 0 tempfiles = [] while true (n += 1) < max_count or raise StandardError.new("too many parameters.") ## create body (StringIO or Tempfile) body = create_body(bufsize < content_length) tempfiles << body if defined?(Tempfile) && body.kind_of?(Tempfile) class << body if method_defined?(:path) alias local_path path else def local_path nil end end attr_reader :original_filename, :content_type end ## find head and boundary head = nil separator = EOL * 2 until head && matched = boundary_rexp.match(buf) if !head && pos = buf.index(separator) len = pos + EOL.bytesize head = buf[0, len] buf = buf[(pos+separator.bytesize)..-1] else if head && buf.size > boundary_size len = buf.size - boundary_size body.print(buf[0, len]) buf[0, len] = '' end c = stdin.read(bufsize < content_length ? bufsize : content_length) raise EOFError.new("bad content body") if c.nil? || c.empty? buf << c content_length -= c.bytesize end end ## read to end of boundary m = matched len = m.begin(0) s = buf[0, len] if s =~ /(\r?\n)\z/ s = buf[0, len - $1.bytesize] end body.print(s) buf = buf[m.end(0)..-1] boundary_end = m[1] content_length = -1 if boundary_end == '--' ## reset file cursor position body.rewind ## original filename /Content-Disposition:.* filename=(?:"(.*?)"|([^;\r\n]*))/i.match(head) filename = $1 || $2 || '' filename = CGI.unescape(filename) if unescape_filename?() body.instance_variable_set(:@original_filename, filename.taint) ## content type /Content-Type: (.*)/i.match(head) (content_type = $1 || '').chomp! body.instance_variable_set(:@content_type, content_type.taint) ## query parameter name /Content-Disposition:.* name=(?:"(.*?)"|([^;\r\n]*))/i.match(head) name = $1 || $2 || '' if body.original_filename.empty? value=body.read.dup.force_encoding(@accept_charset) body.unlink if defined?(Tempfile) && body.kind_of?(Tempfile) (params[name] ||= []) << value unless value.valid_encoding? if @accept_charset_error_block @accept_charset_error_block.call(name,value) else raise InvalidEncoding,"Accept-Charset encoding error" end end class << params[name].last;self;end.class_eval do define_method(:read){self} define_method(:original_filename){""} define_method(:content_type){""} end else (params[name] ||= []) << body @files[name]=body end ## break loop break if content_length == -1 end raise EOFError, "bad boundary end of body part" unless boundary_end =~ /--/ params.default = [] params rescue Exception if tempfiles tempfiles.each {|t| if t.path t.unlink end } end raise end