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