1#--
2# Methods for generating HTML, parsing CGI-related parameters, and
3# generating HTTP responses.
4#++
5class CGI
6
7  $CGI_ENV = ENV    # for FCGI support
8
9  # String for carriage return
10  CR  = "\015"
11
12  # String for linefeed
13  LF  = "\012"
14
15  # Standard internet newline sequence
16  EOL = CR + LF
17
18  REVISION = '$Id: core.rb 44390 2013-12-24 15:37:51Z nagachika $' #:nodoc:
19
20  # Whether processing will be required in binary vs text
21  NEEDS_BINMODE = File::BINARY != 0
22
23  # Path separators in different environments.
24  PATH_SEPARATOR = {'UNIX'=>'/', 'WINDOWS'=>'\\', 'MACINTOSH'=>':'}
25
26  # HTTP status codes.
27  HTTP_STATUS = {
28    "OK"                  => "200 OK",
29    "PARTIAL_CONTENT"     => "206 Partial Content",
30    "MULTIPLE_CHOICES"    => "300 Multiple Choices",
31    "MOVED"               => "301 Moved Permanently",
32    "REDIRECT"            => "302 Found",
33    "NOT_MODIFIED"        => "304 Not Modified",
34    "BAD_REQUEST"         => "400 Bad Request",
35    "AUTH_REQUIRED"       => "401 Authorization Required",
36    "FORBIDDEN"           => "403 Forbidden",
37    "NOT_FOUND"           => "404 Not Found",
38    "METHOD_NOT_ALLOWED"  => "405 Method Not Allowed",
39    "NOT_ACCEPTABLE"      => "406 Not Acceptable",
40    "LENGTH_REQUIRED"     => "411 Length Required",
41    "PRECONDITION_FAILED" => "412 Precondition Failed",
42    "SERVER_ERROR"        => "500 Internal Server Error",
43    "NOT_IMPLEMENTED"     => "501 Method Not Implemented",
44    "BAD_GATEWAY"         => "502 Bad Gateway",
45    "VARIANT_ALSO_VARIES" => "506 Variant Also Negotiates"
46  }
47
48  # :startdoc:
49
50  # Synonym for ENV.
51  def env_table
52    ENV
53  end
54
55  # Synonym for $stdin.
56  def stdinput
57    $stdin
58  end
59
60  # Synonym for $stdout.
61  def stdoutput
62    $stdout
63  end
64
65  private :env_table, :stdinput, :stdoutput
66
67  # Create an HTTP header block as a string.
68  #
69  # :call-seq:
70  #   http_header(content_type_string="text/html")
71  #   http_header(headers_hash)
72  #
73  # Includes the empty line that ends the header block.
74  #
75  # +content_type_string+::
76  #   If this form is used, this string is the <tt>Content-Type</tt>
77  # +headers_hash+::
78  #   A Hash of header values. The following header keys are recognized:
79  #
80  #   type:: The Content-Type header.  Defaults to "text/html"
81  #   charset:: The charset of the body, appended to the Content-Type header.
82  #   nph:: A boolean value.  If true, prepend protocol string and status
83  #         code, and date; and sets default values for "server" and
84  #         "connection" if not explicitly set.
85  #   status::
86  #     The HTTP status code as a String, returned as the Status header.  The
87  #     values are:
88  #
89  #     OK:: 200 OK
90  #     PARTIAL_CONTENT:: 206 Partial Content
91  #     MULTIPLE_CHOICES:: 300 Multiple Choices
92  #     MOVED:: 301 Moved Permanently
93  #     REDIRECT:: 302 Found
94  #     NOT_MODIFIED:: 304 Not Modified
95  #     BAD_REQUEST:: 400 Bad Request
96  #     AUTH_REQUIRED:: 401 Authorization Required
97  #     FORBIDDEN:: 403 Forbidden
98  #     NOT_FOUND:: 404 Not Found
99  #     METHOD_NOT_ALLOWED:: 405 Method Not Allowed
100  #     NOT_ACCEPTABLE:: 406 Not Acceptable
101  #     LENGTH_REQUIRED:: 411 Length Required
102  #     PRECONDITION_FAILED:: 412 Precondition Failed
103  #     SERVER_ERROR:: 500 Internal Server Error
104  #     NOT_IMPLEMENTED:: 501 Method Not Implemented
105  #     BAD_GATEWAY:: 502 Bad Gateway
106  #     VARIANT_ALSO_VARIES:: 506 Variant Also Negotiates
107  #
108  #   server:: The server software, returned as the Server header.
109  #   connection:: The connection type, returned as the Connection header (for
110  #                instance, "close".
111  #   length:: The length of the content that will be sent, returned as the
112  #            Content-Length header.
113  #   language:: The language of the content, returned as the Content-Language
114  #              header.
115  #   expires:: The time on which the current content expires, as a +Time+
116  #             object, returned as the Expires header.
117  #   cookie::
118  #     A cookie or cookies, returned as one or more Set-Cookie headers.  The
119  #     value can be the literal string of the cookie; a CGI::Cookie object;
120  #     an Array of literal cookie strings or Cookie objects; or a hash all of
121  #     whose values are literal cookie strings or Cookie objects.
122  #
123  #     These cookies are in addition to the cookies held in the
124  #     @output_cookies field.
125  #
126  #   Other headers can also be set; they are appended as key: value.
127  #
128  # Examples:
129  #
130  #   http_header
131  #     # Content-Type: text/html
132  #
133  #   http_header("text/plain")
134  #     # Content-Type: text/plain
135  #
136  #   http_header("nph"        => true,
137  #               "status"     => "OK",  # == "200 OK"
138  #                 # "status"     => "200 GOOD",
139  #               "server"     => ENV['SERVER_SOFTWARE'],
140  #               "connection" => "close",
141  #               "type"       => "text/html",
142  #               "charset"    => "iso-2022-jp",
143  #                 # Content-Type: text/html; charset=iso-2022-jp
144  #               "length"     => 103,
145  #               "language"   => "ja",
146  #               "expires"    => Time.now + 30,
147  #               "cookie"     => [cookie1, cookie2],
148  #               "my_header1" => "my_value"
149  #               "my_header2" => "my_value")
150  #
151  # This method does not perform charset conversion.
152  def http_header(options='text/html')
153    if options.is_a?(String)
154      content_type = options
155      buf = _header_for_string(content_type)
156    elsif options.is_a?(Hash)
157      if options.size == 1 && options.has_key?('type')
158        content_type = options['type']
159        buf = _header_for_string(content_type)
160      else
161        buf = _header_for_hash(options.dup)
162      end
163    else
164      raise ArgumentError.new("expected String or Hash but got #{options.class}")
165    end
166    if defined?(MOD_RUBY)
167      _header_for_modruby(buf)
168      return ''
169    else
170      buf << EOL    # empty line of separator
171      return buf
172    end
173  end # http_header()
174
175  # This method is an alias for #http_header, when HTML5 tag maker is inactive.
176  #
177  # NOTE: use #http_header to create HTTP header blocks, this alias is only
178  # provided for backwards compatibility.
179  #
180  # Using #header with the HTML5 tag maker will create a <header> element.
181  alias :header :http_header
182
183  def _header_for_string(content_type) #:nodoc:
184    buf = ''
185    if nph?()
186      buf << "#{$CGI_ENV['SERVER_PROTOCOL'] || 'HTTP/1.0'} 200 OK#{EOL}"
187      buf << "Date: #{CGI.rfc1123_date(Time.now)}#{EOL}"
188      buf << "Server: #{$CGI_ENV['SERVER_SOFTWARE']}#{EOL}"
189      buf << "Connection: close#{EOL}"
190    end
191    buf << "Content-Type: #{content_type}#{EOL}"
192    if @output_cookies
193      @output_cookies.each {|cookie| buf << "Set-Cookie: #{cookie}#{EOL}" }
194    end
195    return buf
196  end # _header_for_string
197  private :_header_for_string
198
199  def _header_for_hash(options)  #:nodoc:
200    buf = ''
201    ## add charset to option['type']
202    options['type'] ||= 'text/html'
203    charset = options.delete('charset')
204    options['type'] += "; charset=#{charset}" if charset
205    ## NPH
206    options.delete('nph') if defined?(MOD_RUBY)
207    if options.delete('nph') || nph?()
208      protocol = $CGI_ENV['SERVER_PROTOCOL'] || 'HTTP/1.0'
209      status = options.delete('status')
210      status = HTTP_STATUS[status] || status || '200 OK'
211      buf << "#{protocol} #{status}#{EOL}"
212      buf << "Date: #{CGI.rfc1123_date(Time.now)}#{EOL}"
213      options['server'] ||= $CGI_ENV['SERVER_SOFTWARE'] || ''
214      options['connection'] ||= 'close'
215    end
216    ## common headers
217    status = options.delete('status')
218    buf << "Status: #{HTTP_STATUS[status] || status}#{EOL}" if status
219    server = options.delete('server')
220    buf << "Server: #{server}#{EOL}" if server
221    connection = options.delete('connection')
222    buf << "Connection: #{connection}#{EOL}" if connection
223    type = options.delete('type')
224    buf << "Content-Type: #{type}#{EOL}" #if type
225    length = options.delete('length')
226    buf << "Content-Length: #{length}#{EOL}" if length
227    language = options.delete('language')
228    buf << "Content-Language: #{language}#{EOL}" if language
229    expires = options.delete('expires')
230    buf << "Expires: #{CGI.rfc1123_date(expires)}#{EOL}" if expires
231    ## cookie
232    if cookie = options.delete('cookie')
233      case cookie
234      when String, Cookie
235        buf << "Set-Cookie: #{cookie}#{EOL}"
236      when Array
237        arr = cookie
238        arr.each {|c| buf << "Set-Cookie: #{c}#{EOL}" }
239      when Hash
240        hash = cookie
241        hash.each {|name, c| buf << "Set-Cookie: #{c}#{EOL}" }
242      end
243    end
244    if @output_cookies
245      @output_cookies.each {|c| buf << "Set-Cookie: #{c}#{EOL}" }
246    end
247    ## other headers
248    options.each do |key, value|
249      buf << "#{key}: #{value}#{EOL}"
250    end
251    return buf
252  end # _header_for_hash
253  private :_header_for_hash
254
255  def nph?  #:nodoc:
256    return /IIS\/(\d+)/.match($CGI_ENV['SERVER_SOFTWARE']) && $1.to_i < 5
257  end
258
259  def _header_for_modruby(buf)  #:nodoc:
260    request = Apache::request
261    buf.scan(/([^:]+): (.+)#{EOL}/o) do |name, value|
262      warn sprintf("name:%s value:%s\n", name, value) if $DEBUG
263      case name
264      when 'Set-Cookie'
265        request.headers_out.add(name, value)
266      when /^status$/i
267        request.status_line = value
268        request.status = value.to_i
269      when /^content-type$/i
270        request.content_type = value
271      when /^content-encoding$/i
272        request.content_encoding = value
273      when /^location$/i
274        request.status = 302 if request.status == 200
275        request.headers_out[name] = value
276      else
277        request.headers_out[name] = value
278      end
279    end
280    request.send_http_header
281    return ''
282  end
283  private :_header_for_modruby
284
285  # Print an HTTP header and body to $DEFAULT_OUTPUT ($>)
286  #
287  # :call-seq:
288  #   cgi.out(content_type_string='text/html')
289  #   cgi.out(headers_hash)
290  #
291  # +content_type_string+::
292  #   If a string is passed, it is assumed to be the content type.
293  # +headers_hash+::
294  #   This is a Hash of headers, similar to that used by #http_header.
295  # +block+::
296  #   A block is required and should evaluate to the body of the response.
297  #
298  # <tt>Content-Length</tt> is automatically calculated from the size of
299  # the String returned by the content block.
300  #
301  # If <tt>ENV['REQUEST_METHOD'] == "HEAD"</tt>, then only the header
302  # is output (the content block is still required, but it is ignored).
303  #
304  # If the charset is "iso-2022-jp" or "euc-jp" or "shift_jis" then the
305  # content is converted to this charset, and the language is set to "ja".
306  #
307  # Example:
308  #
309  #   cgi = CGI.new
310  #   cgi.out{ "string" }
311  #     # Content-Type: text/html
312  #     # Content-Length: 6
313  #     #
314  #     # string
315  #
316  #   cgi.out("text/plain") { "string" }
317  #     # Content-Type: text/plain
318  #     # Content-Length: 6
319  #     #
320  #     # string
321  #
322  #   cgi.out("nph"        => true,
323  #           "status"     => "OK",  # == "200 OK"
324  #           "server"     => ENV['SERVER_SOFTWARE'],
325  #           "connection" => "close",
326  #           "type"       => "text/html",
327  #           "charset"    => "iso-2022-jp",
328  #             # Content-Type: text/html; charset=iso-2022-jp
329  #           "language"   => "ja",
330  #           "expires"    => Time.now + (3600 * 24 * 30),
331  #           "cookie"     => [cookie1, cookie2],
332  #           "my_header1" => "my_value",
333  #           "my_header2" => "my_value") { "string" }
334  #      # HTTP/1.1 200 OK
335  #      # Date: Sun, 15 May 2011 17:35:54 GMT
336  #      # Server: Apache 2.2.0
337  #      # Connection: close
338  #      # Content-Type: text/html; charset=iso-2022-jp
339  #      # Content-Length: 6
340  #      # Content-Language: ja
341  #      # Expires: Tue, 14 Jun 2011 17:35:54 GMT
342  #      # Set-Cookie: foo
343  #      # Set-Cookie: bar
344  #      # my_header1: my_value
345  #      # my_header2: my_value
346  #      #
347  #      # string
348  def out(options = "text/html") # :yield:
349
350    options = { "type" => options } if options.kind_of?(String)
351    content = yield
352    options["length"] = content.bytesize.to_s
353    output = stdoutput
354    output.binmode if defined? output.binmode
355    output.print http_header(options)
356    output.print content unless "HEAD" == env_table['REQUEST_METHOD']
357  end
358
359
360  # Print an argument or list of arguments to the default output stream
361  #
362  #   cgi = CGI.new
363  #   cgi.print    # default:  cgi.print == $DEFAULT_OUTPUT.print
364  def print(*options)
365    stdoutput.print(*options)
366  end
367
368  # Parse an HTTP query string into a hash of key=>value pairs.
369  #
370  #   params = CGI::parse("query_string")
371  #     # {"name1" => ["value1", "value2", ...],
372  #     #  "name2" => ["value1", "value2", ...], ... }
373  #
374  def CGI::parse(query)
375    params = {}
376    query.split(/[&;]/).each do |pairs|
377      key, value = pairs.split('=',2).collect{|v| CGI::unescape(v) }
378
379      next unless key
380
381      params[key] ||= []
382      params[key].push(value) if value
383    end
384
385    params.default=[].freeze
386    params
387  end
388
389  # Maximum content length of post data
390  ##MAX_CONTENT_LENGTH  = 2 * 1024 * 1024
391
392  # Maximum content length of multipart data
393  MAX_MULTIPART_LENGTH  = 128 * 1024 * 1024
394
395  # Maximum number of request parameters when multipart
396  MAX_MULTIPART_COUNT = 128
397
398  # Mixin module that provides the following:
399  #
400  # 1. Access to the CGI environment variables as methods.  See
401  #    documentation to the CGI class for a list of these variables.  The
402  #    methods are exposed by removing the leading +HTTP_+ (if it exists) and
403  #    downcasing the name.  For example, +auth_type+ will return the
404  #    environment variable +AUTH_TYPE+, and +accept+ will return the value
405  #    for +HTTP_ACCEPT+.
406  #
407  # 2. Access to cookies, including the cookies attribute.
408  #
409  # 3. Access to parameters, including the params attribute, and overloading
410  #    #[] to perform parameter value lookup by key.
411  #
412  # 4. The initialize_query method, for initializing the above
413  #    mechanisms, handling multipart forms, and allowing the
414  #    class to be used in "offline" mode.
415  #
416  module QueryExtension
417
418    %w[ CONTENT_LENGTH SERVER_PORT ].each do |env|
419      define_method(env.sub(/^HTTP_/, '').downcase) do
420        (val = env_table[env]) && Integer(val)
421      end
422    end
423
424    %w[ AUTH_TYPE CONTENT_TYPE GATEWAY_INTERFACE PATH_INFO
425        PATH_TRANSLATED QUERY_STRING REMOTE_ADDR REMOTE_HOST
426        REMOTE_IDENT REMOTE_USER REQUEST_METHOD SCRIPT_NAME
427        SERVER_NAME SERVER_PROTOCOL SERVER_SOFTWARE
428
429        HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_ACCEPT_ENCODING
430        HTTP_ACCEPT_LANGUAGE HTTP_CACHE_CONTROL HTTP_FROM HTTP_HOST
431        HTTP_NEGOTIATE HTTP_PRAGMA HTTP_REFERER HTTP_USER_AGENT ].each do |env|
432      define_method(env.sub(/^HTTP_/, '').downcase) do
433        env_table[env]
434      end
435    end
436
437    # Get the raw cookies as a string.
438    def raw_cookie
439      env_table["HTTP_COOKIE"]
440    end
441
442    # Get the raw RFC2965 cookies as a string.
443    def raw_cookie2
444      env_table["HTTP_COOKIE2"]
445    end
446
447    # Get the cookies as a hash of cookie-name=>Cookie pairs.
448    attr_accessor :cookies
449
450    # Get the parameters as a hash of name=>values pairs, where
451    # values is an Array.
452    attr_reader :params
453
454    # Get the uploaded files as a hash of name=>values pairs
455    attr_reader :files
456
457    # Set all the parameters.
458    def params=(hash)
459      @params.clear
460      @params.update(hash)
461    end
462
463    ##
464    # Parses multipart form elements according to
465    #   http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.2
466    #
467    # Returns a hash of multipart form parameters with bodies of type StringIO or
468    # Tempfile depending on whether the multipart form element exceeds 10 KB
469    #
470    #   params[name => body]
471    #
472    def read_multipart(boundary, content_length)
473      ## read first boundary
474      stdin = stdinput
475      first_line = "--#{boundary}#{EOL}"
476      content_length -= first_line.bytesize
477      status = stdin.read(first_line.bytesize)
478      raise EOFError.new("no content body")  unless status
479      raise EOFError.new("bad content body") unless first_line == status
480      ## parse and set params
481      params = {}
482      @files = {}
483      boundary_rexp = /--#{Regexp.quote(boundary)}(#{EOL}|--)/
484      boundary_size = "#{EOL}--#{boundary}#{EOL}".bytesize
485      boundary_end  = nil
486      buf = ''
487      bufsize = 10 * 1024
488      max_count = MAX_MULTIPART_COUNT
489      n = 0
490      tempfiles = []
491      while true
492        (n += 1) < max_count or raise StandardError.new("too many parameters.")
493        ## create body (StringIO or Tempfile)
494        body = create_body(bufsize < content_length)
495        tempfiles << body if defined?(Tempfile) && body.kind_of?(Tempfile)
496        class << body
497          if method_defined?(:path)
498            alias local_path path
499          else
500            def local_path
501              nil
502            end
503          end
504          attr_reader :original_filename, :content_type
505        end
506        ## find head and boundary
507        head = nil
508        separator = EOL * 2
509        until head && matched = boundary_rexp.match(buf)
510          if !head && pos = buf.index(separator)
511            len  = pos + EOL.bytesize
512            head = buf[0, len]
513            buf  = buf[(pos+separator.bytesize)..-1]
514          else
515            if head && buf.size > boundary_size
516              len = buf.size - boundary_size
517              body.print(buf[0, len])
518              buf[0, len] = ''
519            end
520            c = stdin.read(bufsize < content_length ? bufsize : content_length)
521            raise EOFError.new("bad content body") if c.nil? || c.empty?
522            buf << c
523            content_length -= c.bytesize
524          end
525        end
526        ## read to end of boundary
527        m = matched
528        len = m.begin(0)
529        s = buf[0, len]
530        if s =~ /(\r?\n)\z/
531          s = buf[0, len - $1.bytesize]
532        end
533        body.print(s)
534        buf = buf[m.end(0)..-1]
535        boundary_end = m[1]
536        content_length = -1 if boundary_end == '--'
537        ## reset file cursor position
538        body.rewind
539        ## original filename
540        /Content-Disposition:.* filename=(?:"(.*?)"|([^;\r\n]*))/i.match(head)
541        filename = $1 || $2 || ''
542        filename = CGI.unescape(filename) if unescape_filename?()
543        body.instance_variable_set(:@original_filename, filename.taint)
544        ## content type
545        /Content-Type: (.*)/i.match(head)
546        (content_type = $1 || '').chomp!
547        body.instance_variable_set(:@content_type, content_type.taint)
548        ## query parameter name
549        /Content-Disposition:.* name=(?:"(.*?)"|([^;\r\n]*))/i.match(head)
550        name = $1 || $2 || ''
551        if body.original_filename.empty?
552          value=body.read.dup.force_encoding(@accept_charset)
553          body.unlink if defined?(Tempfile) && body.kind_of?(Tempfile)
554          (params[name] ||= []) << value
555          unless value.valid_encoding?
556            if @accept_charset_error_block
557              @accept_charset_error_block.call(name,value)
558            else
559              raise InvalidEncoding,"Accept-Charset encoding error"
560            end
561          end
562          class << params[name].last;self;end.class_eval do
563            define_method(:read){self}
564            define_method(:original_filename){""}
565            define_method(:content_type){""}
566          end
567        else
568          (params[name] ||= []) << body
569          @files[name]=body
570        end
571        ## break loop
572        break if content_length == -1
573      end
574      raise EOFError, "bad boundary end of body part" unless boundary_end =~ /--/
575      params.default = []
576      params
577    rescue Exception
578      if tempfiles
579        tempfiles.each {|t|
580          if t.path
581            t.unlink
582          end
583        }
584      end
585      raise
586    end # read_multipart
587    private :read_multipart
588    def create_body(is_large)  #:nodoc:
589      if is_large
590        require 'tempfile'
591        body = Tempfile.new('CGI', encoding: "ascii-8bit")
592      else
593        begin
594          require 'stringio'
595          body = StringIO.new("".force_encoding("ascii-8bit"))
596        rescue LoadError
597          require 'tempfile'
598          body = Tempfile.new('CGI', encoding: "ascii-8bit")
599        end
600      end
601      body.binmode if defined? body.binmode
602      return body
603    end
604    def unescape_filename?  #:nodoc:
605      user_agent = $CGI_ENV['HTTP_USER_AGENT']
606      return /Mac/i.match(user_agent) && /Mozilla/i.match(user_agent) && !/MSIE/i.match(user_agent)
607    end
608
609    # offline mode. read name=value pairs on standard input.
610    def read_from_cmdline
611      require "shellwords"
612
613      string = unless ARGV.empty?
614        ARGV.join(' ')
615      else
616        if STDIN.tty?
617          STDERR.print(
618            %|(offline mode: enter name=value pairs on standard input)\n|
619          )
620        end
621        array = readlines rescue nil
622        if not array.nil?
623            array.join(' ').gsub(/\n/n, '')
624        else
625            ""
626        end
627      end.gsub(/\\=/n, '%3D').gsub(/\\&/n, '%26')
628
629      words = Shellwords.shellwords(string)
630
631      if words.find{|x| /=/n.match(x) }
632        words.join('&')
633      else
634        words.join('+')
635      end
636    end
637    private :read_from_cmdline
638
639    # A wrapper class to use a StringIO object as the body and switch
640    # to a TempFile when the passed threshold is passed.
641    # Initialize the data from the query.
642    #
643    # Handles multipart forms (in particular, forms that involve file uploads).
644    # Reads query parameters in the @params field, and cookies into @cookies.
645    def initialize_query()
646      if ("POST" == env_table['REQUEST_METHOD']) and
647         %r|\Amultipart/form-data.*boundary=\"?([^\";,]+)\"?|.match(env_table['CONTENT_TYPE'])
648        raise StandardError.new("too large multipart data.") if env_table['CONTENT_LENGTH'].to_i > MAX_MULTIPART_LENGTH
649        boundary = $1.dup
650        @multipart = true
651        @params = read_multipart(boundary, Integer(env_table['CONTENT_LENGTH']))
652      else
653        @multipart = false
654        @params = CGI::parse(
655                    case env_table['REQUEST_METHOD']
656                    when "GET", "HEAD"
657                      if defined?(MOD_RUBY)
658                        Apache::request.args or ""
659                      else
660                        env_table['QUERY_STRING'] or ""
661                      end
662                    when "POST"
663                      stdinput.binmode if defined? stdinput.binmode
664                      stdinput.read(Integer(env_table['CONTENT_LENGTH'])) or ''
665                    else
666                      read_from_cmdline
667                    end.dup.force_encoding(@accept_charset)
668                  )
669        unless Encoding.find(@accept_charset) == Encoding::ASCII_8BIT
670          @params.each do |key,values|
671            values.each do |value|
672              unless value.valid_encoding?
673                if @accept_charset_error_block
674                  @accept_charset_error_block.call(key,value)
675                else
676                  raise InvalidEncoding,"Accept-Charset encoding error"
677                end
678              end
679            end
680          end
681        end
682      end
683
684      @cookies = CGI::Cookie::parse((env_table['HTTP_COOKIE'] or env_table['COOKIE']))
685    end
686    private :initialize_query
687
688    # Returns whether the form contained multipart/form-data
689    def multipart?
690      @multipart
691    end
692
693    # Get the value for the parameter with a given key.
694    #
695    # If the parameter has multiple values, only the first will be
696    # retrieved; use #params to get the array of values.
697    def [](key)
698      params = @params[key]
699      return '' unless params
700      value = params[0]
701      if @multipart
702        if value
703          return value
704        elsif defined? StringIO
705          StringIO.new("".force_encoding("ascii-8bit"))
706        else
707          Tempfile.new("CGI",encoding:"ascii-8bit")
708        end
709      else
710        str = if value then value.dup else "" end
711        str
712      end
713    end
714
715    # Return all query parameter names as an array of String.
716    def keys(*args)
717      @params.keys(*args)
718    end
719
720    # Returns true if a given query string parameter exists.
721    def has_key?(*args)
722      @params.has_key?(*args)
723    end
724    alias key? has_key?
725    alias include? has_key?
726
727  end # QueryExtension
728
729  # Exception raised when there is an invalid encoding detected
730  class InvalidEncoding < Exception; end
731
732  # @@accept_charset is default accept character set.
733  # This default value default is "UTF-8"
734  # If you want to change the default accept character set
735  # when create a new CGI instance, set this:
736  #
737  #   CGI.accept_charset = "EUC-JP"
738  #
739  @@accept_charset="UTF-8"
740
741  # Return the accept character set for all new CGI instances.
742  def self.accept_charset
743    @@accept_charset
744  end
745
746  # Set the accept character set for all new CGI instances.
747  def self.accept_charset=(accept_charset)
748    @@accept_charset=accept_charset
749  end
750
751  # Return the accept character set for this CGI instance.
752  attr_reader :accept_charset
753
754  # Create a new CGI instance.
755  #
756  # :call-seq:
757  #   CGI.new(tag_maker) { block }
758  #   CGI.new(options_hash = {}) { block }
759  #
760  #
761  # <tt>tag_maker</tt>::
762  #   This is the same as using the +options_hash+ form with the value <tt>{
763  #   :tag_maker => tag_maker }</tt> Note that it is recommended to use the
764  #   +options_hash+ form, since it also allows you specify the charset you
765  #   will accept.
766  # <tt>options_hash</tt>::
767  #   A Hash that recognizes two options:
768  #
769  #   <tt>:accept_charset</tt>::
770  #     specifies encoding of received query string.  If omitted,
771  #     <tt>@@accept_charset</tt> is used.  If the encoding is not valid, a
772  #     CGI::InvalidEncoding will be raised.
773  #
774  #     Example. Suppose <tt>@@accept_charset</tt> is "UTF-8"
775  #
776  #     when not specified:
777  #
778  #         cgi=CGI.new      # @accept_charset # => "UTF-8"
779  #
780  #     when specified as "EUC-JP":
781  #
782  #         cgi=CGI.new(:accept_charset => "EUC-JP") # => "EUC-JP"
783  #
784  #   <tt>:tag_maker</tt>::
785  #     String that specifies which version of the HTML generation methods to
786  #     use.  If not specified, no HTML generation methods will be loaded.
787  #
788  #     The following values are supported:
789  #
790  #     "html3":: HTML 3.x
791  #     "html4":: HTML 4.0
792  #     "html4Tr":: HTML 4.0 Transitional
793  #     "html4Fr":: HTML 4.0 with Framesets
794  #     "html5":: HTML 5
795  #
796  # <tt>block</tt>::
797  #   If provided, the block is called when an invalid encoding is
798  #   encountered. For example:
799  #
800  #     encoding_errors={}
801  #     cgi=CGI.new(:accept_charset=>"EUC-JP") do |name,value|
802  #       encoding_errors[name] = value
803  #     end
804  #
805  # Finally, if the CGI object is not created in a standard CGI call
806  # environment (that is, it can't locate REQUEST_METHOD in its environment),
807  # then it will run in "offline" mode.  In this mode, it reads its parameters
808  # from the command line or (failing that) from standard input.  Otherwise,
809  # cookies and other parameters are parsed automatically from the standard
810  # CGI locations, which varies according to the REQUEST_METHOD.
811  def initialize(options = {}, &block) # :yields: name, value
812    @accept_charset_error_block = block_given? ? block : nil
813    @options={:accept_charset=>@@accept_charset}
814    case options
815    when Hash
816      @options.merge!(options)
817    when String
818      @options[:tag_maker]=options
819    end
820    @accept_charset=@options[:accept_charset]
821    if defined?(MOD_RUBY) && !ENV.key?("GATEWAY_INTERFACE")
822      Apache.request.setup_cgi_env
823    end
824
825    extend QueryExtension
826    @multipart = false
827
828    initialize_query()  # set @params, @cookies
829    @output_cookies = nil
830    @output_hidden = nil
831
832    case @options[:tag_maker]
833    when "html3"
834      require 'cgi/html'
835      extend Html3
836      element_init()
837      extend HtmlExtension
838    when "html4"
839      require 'cgi/html'
840      extend Html4
841      element_init()
842      extend HtmlExtension
843    when "html4Tr"
844      require 'cgi/html'
845      extend Html4Tr
846      element_init()
847      extend HtmlExtension
848    when "html4Fr"
849      require 'cgi/html'
850      extend Html4Tr
851      element_init()
852      extend Html4Fr
853      element_init()
854      extend HtmlExtension
855    when "html5"
856      require 'cgi/html'
857      extend Html5
858      element_init()
859      extend HtmlExtension
860    end
861  end
862
863end   # class CGI
864
865
866