# module CGI::QueryExtension

Mixin module that provides the following:

1. 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 variable `AUTH_TYPE`, and `accept` will return the value for `HTTP_ACCEPT`.

2. Access to cookies, including the cookies attribute.

3. Access to parameters, including the params attribute, and overloading `[]` to perform parameter value lookup by key.

4. The `initialize_query` method, for initializing the above mechanisms, handling multipart forms, and allowing the class to be used in “offline” mode.

### Attributes

cookies[RW]

Get the cookies as a hash of cookie-name=>Cookie pairs.

files[R]

Get the uploaded files as a hash of name=>values pairs

params[R]

Get the parameters as a hash of name=>values pairs, where values is an `Array`.

### Public Instance Methods

[](key) click to toggle source

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 703
    def [](key)
      params = @params[key]
      return '' unless params
      value = params[0]
      if @multipart
        if value
          return value
        elsif defined? StringIO
          StringIO.new("".b)
        else
          Tempfile.new("CGI",encoding: Encoding::ASCII_8BIT)
        end
      else
        str = if value then value.dup else "" end
        str
      end
    end

has_key?(*args) click to toggle source

Returns true if a given query string parameter exists.

    # File lib/cgi/core.rb, line 727
    def has_key?(*args)
      @params.has_key?(*args)
    end

Also aliased as: key?, include?
include?(*args)
Alias for: has_key?
key?(*args)
Alias for: has_key?
keys(*args) click to toggle source

Return all query parameter names as an array of `String`.

    # File lib/cgi/core.rb, line 722
    def keys(*args)
      @params.keys(*args)
    end

multipart?() click to toggle source

Returns whether the form contained multipart/form-data

    # File lib/cgi/core.rb, line 695
    def multipart?
      @multipart
    end

params=(hash) click to toggle source

`Set` all the parameters.

    # File lib/cgi/core.rb, line 463
    def params=(hash)
      @params.clear
      @params.update(hash)
    end

raw_cookie() click to toggle source

Get the raw cookies as a string.

    # File lib/cgi/core.rb, line 443
    def raw_cookie
      env_table["HTTP_COOKIE"]
    end

raw_cookie2() click to toggle source

Get the raw RFC2965 cookies as a string.

    # File lib/cgi/core.rb, line 448
    def raw_cookie2
      env_table["HTTP_COOKIE2"]
    end

### Private Instance Methods

initialize_query() click to toggle source

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 650
    def initialize_query()
      if ("POST" == env_table['REQUEST_METHOD']) and
        %r|\Amultipart/form-data.*boundary=\"?([^\";,]+)\"?| =~ env_table['CONTENT_TYPE']
        current_max_multipart_length = @max_multipart_length.respond_to?(:call) ? @max_multipart_length.call : @max_multipart_length
        raise StandardError.new("too large multipart data.") if env_table['CONTENT_LENGTH'].to_i > current_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

read_from_cmdline() click to toggle source

offline mode. read name=value pairs on standard input.

    # File lib/cgi/core.rb, line 615
    def read_from_cmdline
      require "shellwords"

      string = unless ARGV.empty?
        ARGV.join(' ')
      else
        if STDIN.tty?
          STDERR.print(
            %|(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

read_multipart(boundary, content_length) click to toggle source

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 477
    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
      buf = ''.dup
      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 || ''.dup
        filename = CGI.unescape(filename) if unescape_filename?()
        body.instance_variable_set(:@original_filename, filename)
        ## content type
        /Content-Type: (.*)/i.match(head)
        (content_type = $1 || ''.dup).chomp!
        body.instance_variable_set(:@content_type, content_type)
        ## 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.close! 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.close!
          end
        }
      end
      raise
    end