* io.c (rb_open_file): encoding in mode string was ignored if perm is
[ruby-svn.git] / lib / cgi.rb
blobb0c2ef92fe5784666ee528f4b7f82b814ea94f95
1
2 # cgi.rb - cgi support library
3
4 # Copyright (C) 2000  Network Applied Communication Laboratory, Inc.
5
6 # Copyright (C) 2000  Information-technology Promotion Agency, Japan
8 # Author: Wakou Aoyama <wakou@ruby-lang.org>
10 # Documentation: Wakou Aoyama (RDoc'd and embellished by William Webber) 
11
12 # == Overview
14 # The Common Gateway Interface (CGI) is a simple protocol
15 # for passing an HTTP request from a web server to a
16 # standalone program, and returning the output to the web
17 # browser.  Basically, a CGI program is called with the
18 # parameters of the request passed in either in the
19 # environment (GET) or via $stdin (POST), and everything
20 # it prints to $stdout is returned to the client.
21
22 # This file holds the +CGI+ class.  This class provides
23 # functionality for retrieving HTTP request parameters,
24 # managing cookies, and generating HTML output.  See the
25 # class documentation for more details and examples of use.
27 # The file cgi/session.rb provides session management
28 # functionality; see that file for more details.
30 # See http://www.w3.org/CGI/ for more information on the CGI
31 # protocol.
33 raise "Please, use ruby 1.5.4 or later." if RUBY_VERSION < "1.5.4"
35 require 'English'
37 # CGI class.  See documentation for the file cgi.rb for an overview
38 # of the CGI protocol.
40 # == Introduction
42 # CGI is a large class, providing several categories of methods, many of which
43 # are mixed in from other modules.  Some of the documentation is in this class,
44 # some in the modules CGI::QueryExtension and CGI::HtmlExtension.  See
45 # CGI::Cookie for specific information on handling cookies, and cgi/session.rb
46 # (CGI::Session) for information on sessions.
48 # For queries, CGI provides methods to get at environmental variables,
49 # parameters, cookies, and multipart request data.  For responses, CGI provides
50 # methods for writing output and generating HTML.
52 # Read on for more details.  Examples are provided at the bottom.
54 # == Queries
56 # The CGI class dynamically mixes in parameter and cookie-parsing
57 # functionality,  environmental variable access, and support for
58 # parsing multipart requests (including uploaded files) from the
59 # CGI::QueryExtension module.
61 # === Environmental Variables
63 # The standard CGI environmental variables are available as read-only
64 # attributes of a CGI object.  The following is a list of these variables:
67 #   AUTH_TYPE               HTTP_HOST          REMOTE_IDENT
68 #   CONTENT_LENGTH          HTTP_NEGOTIATE     REMOTE_USER
69 #   CONTENT_TYPE            HTTP_PRAGMA        REQUEST_METHOD
70 #   GATEWAY_INTERFACE       HTTP_REFERER       SCRIPT_NAME
71 #   HTTP_ACCEPT             HTTP_USER_AGENT    SERVER_NAME
72 #   HTTP_ACCEPT_CHARSET     PATH_INFO          SERVER_PORT
73 #   HTTP_ACCEPT_ENCODING    PATH_TRANSLATED    SERVER_PROTOCOL
74 #   HTTP_ACCEPT_LANGUAGE    QUERY_STRING       SERVER_SOFTWARE
75 #   HTTP_CACHE_CONTROL      REMOTE_ADDR
76 #   HTTP_FROM               REMOTE_HOST
79 # For each of these variables, there is a corresponding attribute with the
80 # same name, except all lower case and without a preceding HTTP_.  
81 # +content_length+ and +server_port+ are integers; the rest are strings.
83 # === Parameters
85 # The method #params() returns a hash of all parameters in the request as
86 # name/value-list pairs, where the value-list is an Array of one or more
87 # values.  The CGI object itself also behaves as a hash of parameter names 
88 # to values, but only returns a single value (as a String) for each 
89 # parameter name.
91 # For instance, suppose the request contains the parameter 
92 # "favourite_colours" with the multiple values "blue" and "green".  The
93 # following behaviour would occur:
95 #   cgi.params["favourite_colours"]  # => ["blue", "green"]
96 #   cgi["favourite_colours"]         # => "blue"
98 # If a parameter does not exist, the former method will return an empty
99 # array, the latter an empty string.  The simplest way to test for existence
100 # of a parameter is by the #has_key? method.
102 # === Cookies
104 # HTTP Cookies are automatically parsed from the request.  They are available
105 # from the #cookies() accessor, which returns a hash from cookie name to
106 # CGI::Cookie object.
108 # === Multipart requests
110 # If a request's method is POST and its content type is multipart/form-data, 
111 # then it may contain uploaded files.  These are stored by the QueryExtension
112 # module in the parameters of the request.  The parameter name is the name
113 # attribute of the file input field, as usual.  However, the value is not
114 # a string, but an IO object, either an IOString for small files, or a
115 # Tempfile for larger ones.  This object also has the additional singleton
116 # methods:
118 # #local_path():: the path of the uploaded file on the local filesystem
119 # #original_filename():: the name of the file on the client computer
120 # #content_type():: the content type of the file
122 # == Responses
124 # The CGI class provides methods for sending header and content output to
125 # the HTTP client, and mixes in methods for programmatic HTML generation
126 # from CGI::HtmlExtension and CGI::TagMaker modules.  The precise version of HTML
127 # to use for HTML generation is specified at object creation time.
129 # === Writing output
131 # The simplest way to send output to the HTTP client is using the #out() method.
132 # This takes the HTTP headers as a hash parameter, and the body content
133 # via a block.  The headers can be generated as a string using the #header()
134 # method.  The output stream can be written directly to using the #print()
135 # method.
137 # === Generating HTML
139 # Each HTML element has a corresponding method for generating that
140 # element as a String.  The name of this method is the same as that
141 # of the element, all lowercase.  The attributes of the element are 
142 # passed in as a hash, and the body as a no-argument block that evaluates
143 # to a String.  The HTML generation module knows which elements are
144 # always empty, and silently drops any passed-in body.  It also knows
145 # which elements require matching closing tags and which don't.  However,
146 # it does not know what attributes are legal for which elements.
148 # There are also some additional HTML generation methods mixed in from
149 # the CGI::HtmlExtension module.  These include individual methods for the
150 # different types of form inputs, and methods for elements that commonly
151 # take particular attributes where the attributes can be directly specified
152 # as arguments, rather than via a hash.
154 # == Examples of use
156 # === Get form values
158 #   require "cgi"
159 #   cgi = CGI.new
160 #   value = cgi['field_name']   # <== value string for 'field_name'
161 #     # if not 'field_name' included, then return "".
162 #   fields = cgi.keys            # <== array of field names
164 #   # returns true if form has 'field_name'
165 #   cgi.has_key?('field_name')
166 #   cgi.has_key?('field_name')
167 #   cgi.include?('field_name')
169 # CAUTION! cgi['field_name'] returned an Array with the old 
170 # cgi.rb(included in ruby 1.6)
172 # === Get form values as hash
174 #   require "cgi"
175 #   cgi = CGI.new
176 #   params = cgi.params
178 # cgi.params is a hash.
180 #   cgi.params['new_field_name'] = ["value"]  # add new param
181 #   cgi.params['field_name'] = ["new_value"]  # change value
182 #   cgi.params.delete('field_name')           # delete param
183 #   cgi.params.clear                          # delete all params
186 # === Save form values to file
188 #   require "pstore"
189 #   db = PStore.new("query.db")
190 #   db.transaction do
191 #     db["params"] = cgi.params
192 #   end
195 # === Restore form values from file
197 #   require "pstore"
198 #   db = PStore.new("query.db")
199 #   db.transaction do
200 #     cgi.params = db["params"]
201 #   end
204 # === Get multipart form values
206 #   require "cgi"
207 #   cgi = CGI.new
208 #   value = cgi['field_name']   # <== value string for 'field_name'
209 #   value.read                  # <== body of value
210 #   value.local_path            # <== path to local file of value
211 #   value.original_filename     # <== original filename of value
212 #   value.content_type          # <== content_type of value
214 # and value has StringIO or Tempfile class methods.
216 # === Get cookie values
218 #   require "cgi"
219 #   cgi = CGI.new
220 #   values = cgi.cookies['name']  # <== array of 'name'
221 #     # if not 'name' included, then return [].
222 #   names = cgi.cookies.keys      # <== array of cookie names
224 # and cgi.cookies is a hash.
226 # === Get cookie objects
228 #   require "cgi"
229 #   cgi = CGI.new
230 #   for name, cookie in cgi.cookies
231 #     cookie.expires = Time.now + 30
232 #   end
233 #   cgi.out("cookie" => cgi.cookies) {"string"}
235 #   cgi.cookies # { "name1" => cookie1, "name2" => cookie2, ... }
237 #   require "cgi"
238 #   cgi = CGI.new
239 #   cgi.cookies['name'].expires = Time.now + 30
240 #   cgi.out("cookie" => cgi.cookies['name']) {"string"}
242 # === Print http header and html string to $DEFAULT_OUTPUT ($>)
244 #   require "cgi"
245 #   cgi = CGI.new("html3")  # add HTML generation methods
246 #   cgi.out() do
247 #     cgi.html() do
248 #       cgi.head{ cgi.title{"TITLE"} } +
249 #       cgi.body() do
250 #         cgi.form() do
251 #           cgi.textarea("get_text") +
252 #           cgi.br +
253 #           cgi.submit
254 #         end +
255 #         cgi.pre() do
256 #           CGI::escapeHTML(
257 #             "params: " + cgi.params.inspect + "\n" +
258 #             "cookies: " + cgi.cookies.inspect + "\n" +
259 #             ENV.collect() do |key, value|
260 #               key + " --> " + value + "\n"
261 #             end.join("")
262 #           )
263 #         end
264 #       end
265 #     end
266 #   end
268 #   # add HTML generation methods
269 #   CGI.new("html3")    # html3.2
270 #   CGI.new("html4")    # html4.01 (Strict)
271 #   CGI.new("html4Tr")  # html4.01 Transitional
272 #   CGI.new("html4Fr")  # html4.01 Frameset
274 class CGI
276   # :stopdoc:
278   # String for carriage return
279   CR  = "\015"
281   # String for linefeed
282   LF  = "\012"
284   # Standard internet newline sequence
285   EOL = CR + LF
287   REVISION = '$Id$' #:nodoc:
289   NEEDS_BINMODE = true if /WIN/ni.match(RUBY_PLATFORM) 
291   # Path separators in different environments.
292   PATH_SEPARATOR = {'UNIX'=>'/', 'WINDOWS'=>'\\', 'MACINTOSH'=>':'}
294   # HTTP status codes.
295   HTTP_STATUS = {
296     "OK"                  => "200 OK",
297     "PARTIAL_CONTENT"     => "206 Partial Content",
298     "MULTIPLE_CHOICES"    => "300 Multiple Choices",
299     "MOVED"               => "301 Moved Permanently",
300     "REDIRECT"            => "302 Found",
301     "NOT_MODIFIED"        => "304 Not Modified",
302     "BAD_REQUEST"         => "400 Bad Request",
303     "AUTH_REQUIRED"       => "401 Authorization Required",
304     "FORBIDDEN"           => "403 Forbidden",
305     "NOT_FOUND"           => "404 Not Found",
306     "METHOD_NOT_ALLOWED"  => "405 Method Not Allowed",
307     "NOT_ACCEPTABLE"      => "406 Not Acceptable",
308     "LENGTH_REQUIRED"     => "411 Length Required",
309     "PRECONDITION_FAILED" => "412 Rrecondition Failed",
310     "SERVER_ERROR"        => "500 Internal Server Error",
311     "NOT_IMPLEMENTED"     => "501 Method Not Implemented",
312     "BAD_GATEWAY"         => "502 Bad Gateway",
313     "VARIANT_ALSO_VARIES" => "506 Variant Also Negotiates"
314   }
316   # Abbreviated day-of-week names specified by RFC 822
317   RFC822_DAYS = %w[ Sun Mon Tue Wed Thu Fri Sat ]
319   # Abbreviated month names specified by RFC 822
320   RFC822_MONTHS = %w[ Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec ]
322   # :startdoc:
324   def env_table 
325     ENV
326   end
328   def stdinput
329     $stdin
330   end
332   def stdoutput
333     $DEFAULT_OUTPUT
334   end
336   private :env_table, :stdinput, :stdoutput
338   # URL-encode a string.
339   #   url_encoded_string = CGI::escape("'Stop!' said Fred")
340   #      # => "%27Stop%21%27+said+Fred"
341   def CGI::escape(string)
342     string.gsub(/([^ a-zA-Z0-9_.-]+)/) do
343       '%' + $1.unpack('H2' * $1.bytesize).join('%').upcase
344     end.tr(' ', '+')
345   end
348   # URL-decode a string.
349   #   string = CGI::unescape("%27Stop%21%27+said+Fred")
350   #      # => "'Stop!' said Fred"
351   def CGI::unescape(string)
352     enc = string.encoding
353     string.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/) do
354       [$1.delete('%')].pack('H*').force_encoding(enc)
355     end
356   end
358   TABLE_FOR_ESCAPE_HTML__ = {
359     '&' => '&amp;',
360     '"' => '&quot;',
361     '<' => '&lt;',
362     '>' => '&gt;',
363   }
365   # Escape special characters in HTML, namely &\"<>
366   #   CGI::escapeHTML('Usage: foo "bar" <baz>')
367   #      # => "Usage: foo &quot;bar&quot; &lt;baz&gt;"
368   def CGI::escapeHTML(string)
369     string.gsub(/[&\"<>]/, TABLE_FOR_ESCAPE_HTML__)
370   end
373   # Unescape a string that has been HTML-escaped
374   #   CGI::unescapeHTML("Usage: foo &quot;bar&quot; &lt;baz&gt;")
375   #      # => "Usage: foo \"bar\" <baz>"
376   def CGI::unescapeHTML(string)
377     enc = string.encoding
378     string.gsub(/&(amp|quot|gt|lt|\#[0-9]+|\#x[0-9A-Fa-f]+);/) do
379       match = $1.dup
380       case match
381       when 'amp'                 then '&'
382       when 'quot'                then '"'
383       when 'gt'                  then '>'
384       when 'lt'                  then '<'
385       when /\A#0*(\d+)\z/        then
386         if Integer($1) < 256
387           Integer($1).chr.force_encoding(enc)
388         else
389           "&##{$1};"
390         end
391       when /\A#x([0-9a-f]+)\z/i then
392         if $1.hex < 256
393           $1.hex.chr.force_encoding(enc)
394         else
395           "&#x#{$1};"
396         end
397       else
398         "&#{match};"
399       end
400     end
401   end
402   def CGI::escape_html(str)
403     escapeHTML(str)
404   end
405   def CGI::unescape_html(str)
406     unescapeHTML(str)
407   end
409   # Escape only the tags of certain HTML elements in +string+.
410   #
411   # Takes an element or elements or array of elements.  Each element
412   # is specified by the name of the element, without angle brackets.
413   # This matches both the start and the end tag of that element.
414   # The attribute list of the open tag will also be escaped (for
415   # instance, the double-quotes surrounding attribute values).
416   #
417   #   print CGI::escapeElement('<BR><A HREF="url"></A>', "A", "IMG")
418   #     # "<BR>&lt;A HREF=&quot;url&quot;&gt;&lt;/A&gt"
419   #
420   #   print CGI::escapeElement('<BR><A HREF="url"></A>', ["A", "IMG"])
421   #     # "<BR>&lt;A HREF=&quot;url&quot;&gt;&lt;/A&gt"
422   def CGI::escapeElement(string, *elements)
423     elements = elements[0] if elements[0].kind_of?(Array)
424     unless elements.empty?
425       string.gsub(/<\/?(?:#{elements.join("|")})(?!\w)(?:.|\n)*?>/ni) do
426         CGI::escapeHTML($&)
427       end
428     else
429       string
430     end
431   end
434   # Undo escaping such as that done by CGI::escapeElement()
435   #
436   #   print CGI::unescapeElement(
437   #           CGI::escapeHTML('<BR><A HREF="url"></A>'), "A", "IMG")
438   #     # "&lt;BR&gt;<A HREF="url"></A>"
439   # 
440   #   print CGI::unescapeElement(
441   #           CGI::escapeHTML('<BR><A HREF="url"></A>'), ["A", "IMG"])
442   #     # "&lt;BR&gt;<A HREF="url"></A>"
443   def CGI::unescapeElement(string, *elements)
444     elements = elements[0] if elements[0].kind_of?(Array)
445     unless elements.empty?
446       string.gsub(/&lt;\/?(?:#{elements.join("|")})(?!\w)(?:.|\n)*?&gt;/ni) do
447         CGI::unescapeHTML($&)
448       end
449     else
450       string
451     end
452   end
453   def CGI::escape_element(str)
454     escapeElement(str)
455   end
456   def CGI::unescape_element(str)
457     unescapeElement(str)
458   end
460   # Format a +Time+ object as a String using the format specified by RFC 1123.
461   #
462   #   CGI::rfc1123_date(Time.now)
463   #     # Sat, 01 Jan 2000 00:00:00 GMT
464   def CGI::rfc1123_date(time)
465     t = time.clone.gmtime
466     return format("%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT",
467                 RFC822_DAYS[t.wday], t.day, RFC822_MONTHS[t.month-1], t.year,
468                 t.hour, t.min, t.sec)
469   end
472   # Create an HTTP header block as a string.
473   #
474   # Includes the empty line that ends the header block.
475   #
476   # +options+ can be a string specifying the Content-Type (defaults
477   # to text/html), or a hash of header key/value pairs.  The following
478   # header keys are recognized:
479   #
480   # type:: the Content-Type header.  Defaults to "text/html"
481   # charset:: the charset of the body, appended to the Content-Type header.
482   # nph:: a boolean value.  If true, prepend protocol string and status code, and
483   #       date; and sets default values for "server" and "connection" if not
484   #       explicitly set.
485   # status:: the HTTP status code, returned as the Status header.  See the
486   #          list of available status codes below.
487   # server:: the server software, returned as the Server header.
488   # connection:: the connection type, returned as the Connection header (for 
489   #              instance, "close".
490   # length:: the length of the content that will be sent, returned as the
491   #          Content-Length header.
492   # language:: the language of the content, returned as the Content-Language
493   #            header.
494   # expires:: the time on which the current content expires, as a +Time+
495   #           object, returned as the Expires header.
496   # cookie:: a cookie or cookies, returned as one or more Set-Cookie headers.
497   #          The value can be the literal string of the cookie; a CGI::Cookie
498   #          object; an Array of literal cookie strings or Cookie objects; or a 
499   #          hash all of whose values are literal cookie strings or Cookie objects.
500   #          These cookies are in addition to the cookies held in the
501   #          @output_cookies field.
502   #
503   # Other header lines can also be set; they are appended as key: value.
504   # 
505   #   header
506   #     # Content-Type: text/html
507   # 
508   #   header("text/plain")
509   #     # Content-Type: text/plain
510   # 
511   #   header("nph"        => true,
512   #          "status"     => "OK",  # == "200 OK"
513   #            # "status"     => "200 GOOD",
514   #          "server"     => ENV['SERVER_SOFTWARE'],
515   #          "connection" => "close",
516   #          "type"       => "text/html",
517   #          "charset"    => "iso-2022-jp",
518   #            # Content-Type: text/html; charset=iso-2022-jp
519   #          "length"     => 103,
520   #          "language"   => "ja",
521   #          "expires"    => Time.now + 30,
522   #          "cookie"     => [cookie1, cookie2],
523   #          "my_header1" => "my_value"
524   #          "my_header2" => "my_value")
525   # 
526   # The status codes are:
527   # 
528   #   "OK"                  --> "200 OK"
529   #   "PARTIAL_CONTENT"     --> "206 Partial Content"
530   #   "MULTIPLE_CHOICES"    --> "300 Multiple Choices"
531   #   "MOVED"               --> "301 Moved Permanently"
532   #   "REDIRECT"            --> "302 Found"
533   #   "NOT_MODIFIED"        --> "304 Not Modified"
534   #   "BAD_REQUEST"         --> "400 Bad Request"
535   #   "AUTH_REQUIRED"       --> "401 Authorization Required"
536   #   "FORBIDDEN"           --> "403 Forbidden"
537   #   "NOT_FOUND"           --> "404 Not Found"
538   #   "METHOD_NOT_ALLOWED"  --> "405 Method Not Allowed"
539   #   "NOT_ACCEPTABLE"      --> "406 Not Acceptable"
540   #   "LENGTH_REQUIRED"     --> "411 Length Required"
541   #   "PRECONDITION_FAILED" --> "412 Precondition Failed"
542   #   "SERVER_ERROR"        --> "500 Internal Server Error"
543   #   "NOT_IMPLEMENTED"     --> "501 Method Not Implemented"
544   #   "BAD_GATEWAY"         --> "502 Bad Gateway"
545   #   "VARIANT_ALSO_VARIES" --> "506 Variant Also Negotiates"
546   # 
547   # This method does not perform charset conversion. 
548   #
549   def header(options = "text/html")
551     buf = ""
553     case options
554     when String
555       options = { "type" => options }
556     when Hash
557       options = options.dup
558     end
560     unless options.has_key?("type")
561       options["type"] = "text/html"
562     end
564     if options.has_key?("charset")
565       options["type"] += "; charset=" + options.delete("charset")
566     end
568     options.delete("nph") if defined?(MOD_RUBY)
569     if options.delete("nph") or
570         (/IIS\/(\d+)/n.match(env_table['SERVER_SOFTWARE']) and $1.to_i < 5)
571       buf += (env_table["SERVER_PROTOCOL"] or "HTTP/1.0")  + " " +
572              (HTTP_STATUS[options["status"]] or options["status"] or "200 OK") +
573              EOL +
574              "Date: " + CGI::rfc1123_date(Time.now) + EOL
576       unless options.has_key?("server")
577         options["server"] = (env_table['SERVER_SOFTWARE'] or "")
578       end
580       unless options.has_key?("connection")
581         options["connection"] = "close"
582       end
584       options.delete("status")
585     end
587     if options.has_key?("status")
588       buf += "Status: " +
589              (HTTP_STATUS[options["status"]] or options["status"]) + EOL
590       options.delete("status")
591     end
593     if options.has_key?("server")
594       buf += "Server: " + options.delete("server") + EOL
595     end
597     if options.has_key?("connection")
598       buf += "Connection: " + options.delete("connection") + EOL
599     end
601     buf += "Content-Type: " + options.delete("type") + EOL
603     if options.has_key?("length")
604       buf += "Content-Length: " + options.delete("length").to_s + EOL
605     end
607     if options.has_key?("language")
608       buf += "Content-Language: " + options.delete("language") + EOL
609     end
611     if options.has_key?("expires")
612       buf += "Expires: " + CGI::rfc1123_date( options.delete("expires") ) + EOL
613     end
615     if options.has_key?("cookie")
616       if options["cookie"].kind_of?(String) or
617            options["cookie"].kind_of?(Cookie)
618         buf += "Set-Cookie: " + options.delete("cookie").to_s + EOL
619       elsif options["cookie"].kind_of?(Array)
620         options.delete("cookie").each{|cookie|
621           buf += "Set-Cookie: " + cookie.to_s + EOL
622         }
623       elsif options["cookie"].kind_of?(Hash)
624         options.delete("cookie").each_value{|cookie|
625           buf += "Set-Cookie: " + cookie.to_s + EOL
626         }
627       end
628     end
629     if @output_cookies
630       for cookie in @output_cookies
631         buf += "Set-Cookie: " + cookie.to_s + EOL
632       end
633     end
635     options.each{|key, value|
636       buf += key + ": " + value.to_s + EOL
637     }
639     if defined?(MOD_RUBY)
640       table = Apache::request.headers_out
641       buf.scan(/([^:]+): (.+)#{EOL}/n){ |name, value|
642         warn sprintf("name:%s value:%s\n", name, value) if $DEBUG
643         case name
644         when 'Set-Cookie'
645           table.add(name, value)
646         when /^status$/ni
647           Apache::request.status_line = value
648           Apache::request.status = value.to_i
649         when /^content-type$/ni
650           Apache::request.content_type = value
651         when /^content-encoding$/ni
652           Apache::request.content_encoding = value
653         when /^location$/ni
654           if Apache::request.status == 200
655             Apache::request.status = 302
656           end
657           Apache::request.headers_out[name] = value
658         else
659           Apache::request.headers_out[name] = value
660         end
661       }
662       Apache::request.send_http_header
663       ''
664     else
665       buf + EOL
666     end
668   end # header()
671   # Print an HTTP header and body to $DEFAULT_OUTPUT ($>)
672   #
673   # The header is provided by +options+, as for #header().
674   # The body of the document is that returned by the passed-
675   # in block.  This block takes no arguments.  It is required.
676   #
677   #   cgi = CGI.new
678   #   cgi.out{ "string" }
679   #     # Content-Type: text/html
680   #     # Content-Length: 6
681   #     #
682   #     # string
683   # 
684   #   cgi.out("text/plain") { "string" }
685   #     # Content-Type: text/plain
686   #     # Content-Length: 6
687   #     #
688   #     # string
689   # 
690   #   cgi.out("nph"        => true,
691   #           "status"     => "OK",  # == "200 OK"
692   #           "server"     => ENV['SERVER_SOFTWARE'],
693   #           "connection" => "close",
694   #           "type"       => "text/html",
695   #           "charset"    => "iso-2022-jp",
696   #             # Content-Type: text/html; charset=iso-2022-jp
697   #           "language"   => "ja",
698   #           "expires"    => Time.now + (3600 * 24 * 30),
699   #           "cookie"     => [cookie1, cookie2],
700   #           "my_header1" => "my_value",
701   #           "my_header2" => "my_value") { "string" }
702   # 
703   # Content-Length is automatically calculated from the size of
704   # the String returned by the content block.
705   #
706   # If ENV['REQUEST_METHOD'] == "HEAD", then only the header
707   # is outputted (the content block is still required, but it
708   # is ignored).
709   # 
710   # If the charset is "iso-2022-jp" or "euc-jp" or "shift_jis" then
711   # the content is converted to this charset, and the language is set 
712   # to "ja".
713   def out(options = "text/html") # :yield:
715     options = { "type" => options } if options.kind_of?(String)
716     content = yield
718     if options.has_key?("charset")
719       require "nkf"
720       case options["charset"]
721       when /iso-2022-jp/ni
722         content = NKF::nkf('-j -m0 -x', content)
723         options["language"] = "ja" unless options.has_key?("language")
724       when /euc-jp/ni
725         content = NKF::nkf('-e -m0 -x', content)
726         options["language"] = "ja" unless options.has_key?("language")
727       when /shift_jis/ni
728         content = NKF::nkf('-s -m0 -x', content)
729         options["language"] = "ja" unless options.has_key?("language")
730       end
731     end
733     options["length"] = content.length.to_s
734     output = stdoutput
735     output.binmode if defined? output.binmode
736     output.print header(options)
737     output.print content unless "HEAD" == env_table['REQUEST_METHOD']
738   end
741   # Print an argument or list of arguments to the default output stream
742   #
743   #   cgi = CGI.new
744   #   cgi.print    # default:  cgi.print == $DEFAULT_OUTPUT.print
745   def print(*options)
746     stdoutput.print(*options)
747   end
749   require "delegate"
751   # Class representing an HTTP cookie.
752   #
753   # In addition to its specific fields and methods, a Cookie instance
754   # is a delegator to the array of its values.
755   #
756   # See RFC 2965.
757   #
758   # == Examples of use
759   #   cookie1 = CGI::Cookie::new("name", "value1", "value2", ...)
760   #   cookie1 = CGI::Cookie::new("name" => "name", "value" => "value")
761   #   cookie1 = CGI::Cookie::new('name'    => 'name',
762   #                              'value'   => ['value1', 'value2', ...],
763   #                              'path'    => 'path',   # optional
764   #                              'domain'  => 'domain', # optional
765   #                              'expires' => Time.now, # optional
766   #                              'secure'  => true      # optional
767   #                             )
768   # 
769   #   cgi.out("cookie" => [cookie1, cookie2]) { "string" }
770   # 
771   #   name    = cookie1.name
772   #   values  = cookie1.value
773   #   path    = cookie1.path
774   #   domain  = cookie1.domain
775   #   expires = cookie1.expires
776   #   secure  = cookie1.secure
777   # 
778   #   cookie1.name    = 'name'
779   #   cookie1.value   = ['value1', 'value2', ...]
780   #   cookie1.path    = 'path'
781   #   cookie1.domain  = 'domain'
782   #   cookie1.expires = Time.now + 30
783   #   cookie1.secure  = true
784   class Cookie < DelegateClass(Array)
786     # Create a new CGI::Cookie object.
787     #
788     # The contents of the cookie can be specified as a +name+ and one
789     # or more +value+ arguments.  Alternatively, the contents can
790     # be specified as a single hash argument.  The possible keywords of
791     # this hash are as follows:
792     #
793     # name:: the name of the cookie.  Required.
794     # value:: the cookie's value or list of values.
795     # path:: the path for which this cookie applies.  Defaults to the
796     #        base directory of the CGI script.
797     # domain:: the domain for which this cookie applies.
798     # expires:: the time at which this cookie expires, as a +Time+ object.
799     # secure:: whether this cookie is a secure cookie or not (default to
800     #          false).  Secure cookies are only transmitted to HTTPS 
801     #          servers.
802     #
803     # These keywords correspond to attributes of the cookie object.
804     def initialize(name = "", *value)
805       if name.kind_of?(String)
806         @name = name
807         @value = value
808         %r|^(.*/)|.match(ENV["SCRIPT_NAME"])
809         @path = ($1 or "")
810         @secure = false
811         return super(@value)
812       end
814       options = name
815       unless options.has_key?("name")
816         raise ArgumentError, "`name' required"
817       end
819       @name = options["name"]
820       @value = Array(options["value"])
821       # simple support for IE
822       if options["path"]
823         @path = options["path"]
824       else
825         %r|^(.*/)|.match(ENV["SCRIPT_NAME"])
826         @path = ($1 or "")
827       end
828       @domain = options["domain"]
829       @expires = options["expires"]
830       @secure = options["secure"] == true ? true : false
832       super(@value)
833     end
835     attr_accessor("name", "value", "path", "domain", "expires")
836     attr_reader("secure")
838     # Set whether the Cookie is a secure cookie or not.
839     #
840     # +val+ must be a boolean.
841     def secure=(val)
842       @secure = val if val == true or val == false
843       @secure
844     end
846     # Convert the Cookie to its string representation.
847     def to_s
848       buf = ""
849       buf += @name + '='
851       if @value.kind_of?(String)
852         buf += CGI::escape(@value)
853       else
854         buf += @value.collect{|v| CGI::escape(v) }.join("&")
855       end
857       if @domain
858         buf += '; domain=' + @domain
859       end
861       if @path
862         buf += '; path=' + @path
863       end
865       if @expires
866         buf += '; expires=' + CGI::rfc1123_date(@expires)
867       end
869       if @secure == true
870         buf += '; secure'
871       end
873       buf
874     end
876   end # class Cookie
879   # Parse a raw cookie string into a hash of cookie-name=>Cookie
880   # pairs.
881   #
882   #   cookies = CGI::Cookie::parse("raw_cookie_string")
883   #     # { "name1" => cookie1, "name2" => cookie2, ... }
884   #
885   def Cookie::parse(raw_cookie)
886     cookies = Hash.new([])
887     return cookies unless raw_cookie
889     raw_cookie.split(/[;,]\s?/).each do |pairs|
890       name, values = pairs.split('=',2)
891       next unless name and values
892       name = CGI::unescape(name)
893       values ||= ""
894       values = values.split('&').collect{|v| CGI::unescape(v) }
895       if cookies.has_key?(name)
896         values = cookies[name].value + values
897       end
898       cookies[name] = Cookie::new(name, *values)
899     end
901     cookies
902   end
904   # Parse an HTTP query string into a hash of key=>value pairs.
905   #
906   #   params = CGI::parse("query_string")
907   #     # {"name1" => ["value1", "value2", ...],
908   #     #  "name2" => ["value1", "value2", ...], ... }
909   #
910   def CGI::parse(query)
911     params = Hash.new([].freeze)
913     query.split(/[&;]/n).each do |pairs|
914       key, value = pairs.split('=',2).collect{|v| CGI::unescape(v) }
915       if params.has_key?(key)
916         params[key].push(value)
917       else
918         params[key] = [value]
919       end
920     end
922     params
923   end
925   # Mixin module. It provides the follow functionality groups:
926   #
927   # 1. Access to CGI environment variables as methods.  See 
928   #    documentation to the CGI class for a list of these variables.
929   #
930   # 2. Access to cookies, including the cookies attribute.
931   #
932   # 3. Access to parameters, including the params attribute, and overloading
933   #    [] to perform parameter value lookup by key.
934   #
935   # 4. The initialize_query method, for initialising the above
936   #    mechanisms, handling multipart forms, and allowing the
937   #    class to be used in "offline" mode.
938   #
939   module QueryExtension
941     %w[ CONTENT_LENGTH SERVER_PORT ].each do |env|
942       define_method(env.sub(/^HTTP_/n, '').downcase) do
943         (val = env_table[env]) && Integer(val)
944       end
945     end
947     %w[ AUTH_TYPE CONTENT_TYPE GATEWAY_INTERFACE PATH_INFO
948         PATH_TRANSLATED QUERY_STRING REMOTE_ADDR REMOTE_HOST
949         REMOTE_IDENT REMOTE_USER REQUEST_METHOD SCRIPT_NAME
950         SERVER_NAME SERVER_PROTOCOL SERVER_SOFTWARE
952         HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_ACCEPT_ENCODING
953         HTTP_ACCEPT_LANGUAGE HTTP_CACHE_CONTROL HTTP_FROM HTTP_HOST
954         HTTP_NEGOTIATE HTTP_PRAGMA HTTP_REFERER HTTP_USER_AGENT ].each do |env|
955       define_method(env.sub(/^HTTP_/n, '').downcase) do
956         env_table[env]
957       end
958     end
960     # Get the raw cookies as a string.
961     def raw_cookie
962       env_table["HTTP_COOKIE"]
963     end
965     # Get the raw RFC2965 cookies as a string.
966     def raw_cookie2
967       env_table["HTTP_COOKIE2"]
968     end
970     # Get the cookies as a hash of cookie-name=>Cookie pairs.
971     attr_accessor :cookies
973     # Get the parameters as a hash of name=>values pairs, where
974     # values is an Array.
975     attr_reader :params
977     # Set all the parameters.
978     def params=(hash)
979       @params.clear
980       @params.update(hash)
981     end
983     def read_multipart(boundary, content_length)
984       params = Hash.new([])
985       boundary = "--" + boundary
986       quoted_boundary = Regexp.quote(boundary)
987       buf = ""
988       bufsize = 10 * 1024
989       boundary_end=""
991       # start multipart/form-data
992       stdinput.binmode if defined? stdinput.binmode
993       boundary_size = boundary.size + EOL.size
994       content_length -= boundary_size
995       status = stdinput.read(boundary_size)
996       if nil == status
997         raise EOFError, "no content body"
998       elsif boundary + EOL != status
999         raise EOFError, "bad content body"
1000       end
1002       loop do
1003         head = nil
1004         body = MorphingBody.new
1006         until head and /#{quoted_boundary}(?:#{EOL}|--)/n.match(buf)
1007           if (not head) and /#{EOL}#{EOL}/n.match(buf)
1008             buf = buf.sub(/\A((?:.|\n)*?#{EOL})#{EOL}/n) do
1009               head = $1.dup
1010               ""
1011             end
1012             next
1013           end
1015           if head and ( (EOL + boundary + EOL).size < buf.size )
1016             body.print buf[0 ... (buf.size - (EOL + boundary + EOL).size)]
1017             buf[0 ... (buf.size - (EOL + boundary + EOL).size)] = ""
1018           end
1020           c = if bufsize < content_length
1021                 stdinput.read(bufsize)
1022               else
1023                 stdinput.read(content_length)
1024               end
1025           if c.nil? || c.empty?
1026             raise EOFError, "bad content body"
1027           end
1028           buf.concat(c)
1029           content_length -= c.size
1030         end
1032         buf = buf.sub(/\A((?:.|\n)*?)(?:[\r\n]{1,2})?#{quoted_boundary}([\r\n]{1,2}|--)/n) do
1033           body.print $1
1034           if "--" == $2
1035             content_length = -1
1036           end
1037           boundary_end = $2.dup
1038           ""
1039         end
1041         body.rewind
1043         /Content-Disposition:.* filename=(?:"((?:\\.|[^\"])*)"|([^;\s]*))/ni.match(head)
1044         filename = ($1 or $2 or "")
1045         if /Mac/ni.match(env_table['HTTP_USER_AGENT']) and
1046             /Mozilla/ni.match(env_table['HTTP_USER_AGENT']) and
1047             (not /MSIE/ni.match(env_table['HTTP_USER_AGENT']))
1048           filename = CGI::unescape(filename)
1049         end
1050         
1051         /Content-Type: ([^\s]*)/ni.match(head)
1052         content_type = ($1 or "")
1054         (class << body; self; end).class_eval do
1055           alias local_path path
1056           define_method(:original_filename) {filename.dup.taint}
1057           define_method(:content_type) {content_type.dup.taint}
1058         end
1060         /Content-Disposition:.* name="?([^\";\s]*)"?/ni.match(head)
1061         name = ($1 || "").dup
1063         if params.has_key?(name)
1064           params[name].push(body)
1065         else
1066           params[name] = [body]
1067         end
1068         break if buf.size == 0
1069         break if content_length == -1
1070       end
1071       raise EOFError, "bad boundary end of body part" unless boundary_end=~/--/
1073       params
1074     end # read_multipart
1075     private :read_multipart
1077     # offline mode. read name=value pairs on standard input.
1078     def read_from_cmdline
1079       require "shellwords"
1081       string = unless ARGV.empty?
1082         ARGV.join(' ')
1083       else
1084         if STDIN.tty?
1085           STDERR.print(
1086             %|(offline mode: enter name=value pairs on standard input)\n|
1087           )
1088         end
1089         readlines.join(' ').gsub(/\n/n, '')
1090       end.gsub(/\\=/n, '%3D').gsub(/\\&/n, '%26')
1092       words = Shellwords.shellwords(string)
1094       if words.find{|x| /=/n.match(x) }
1095         words.join('&')
1096       else
1097         words.join('+')
1098       end
1099     end
1100     private :read_from_cmdline
1102     # A wrapper class to use a StringIO object as the body and switch
1103     # to a TempFile when the passed threshold is passed.
1104     class MorphingBody
1105       begin
1106         require "stringio"
1107         @@small_buffer = lambda{StringIO.new}
1108       rescue LoadError
1109         require "tempfile"
1110         @@small_buffer = lambda{
1111           n = Tempfile.new("CGI")
1112           n.binmode
1113           n
1114         }
1115       end
1117       def initialize(morph_threshold = 10240)
1118         @threshold = morph_threshold
1119         @body = @@small_buffer.call
1120         @cur_size = 0
1121         @morph_check = true
1122       end
1124       def print(data)
1125         if @morph_check && (@cur_size + data.size > @threshold)
1126           convert_body
1127         end
1128         @body.print data
1129       end
1130       def rewind
1131         @body.rewind
1132       end
1133       def path
1134         @body.path
1135       end
1137       # returns the true body object.
1138       def extract
1139         @body
1140       end
1142       private
1143       def convert_body
1144         new_body = TempFile.new("CGI")
1145         new_body.binmode if defined? @body.binmode
1146         new_body.binmode if defined? new_body.binmode
1148         @body.rewind
1149         new_body.print @body.read
1150         @body = new_body
1151         @morph_check = false
1152       end
1153     end
1155     # Initialize the data from the query.
1156     #
1157     # Handles multipart forms (in particular, forms that involve file uploads).
1158     # Reads query parameters in the @params field, and cookies into @cookies.
1159     def initialize_query()
1160       if ("POST" == env_table['REQUEST_METHOD']) and
1161          %r|\Amultipart/form-data.*boundary=\"?([^\";,]+)\"?|n.match(env_table['CONTENT_TYPE'])
1162         boundary = $1.dup
1163         @multipart = true
1164         @params = read_multipart(boundary, Integer(env_table['CONTENT_LENGTH']))
1165       else
1166         @multipart = false
1167         @params = CGI::parse(
1168                     case env_table['REQUEST_METHOD']
1169                     when "GET", "HEAD"
1170                       if defined?(MOD_RUBY)
1171                         Apache::request.args or ""
1172                       else
1173                         env_table['QUERY_STRING'] or ""
1174                       end
1175                     when "POST"
1176                       stdinput.binmode if defined? stdinput.binmode
1177                       stdinput.read(Integer(env_table['CONTENT_LENGTH'])) or ''
1178                     else
1179                       read_from_cmdline
1180                     end
1181                   )
1182       end
1184       @cookies = CGI::Cookie::parse((env_table['HTTP_COOKIE'] or env_table['COOKIE']))
1185     end
1186     private :initialize_query
1188     def multipart?
1189       @multipart
1190     end
1192     # Get the value for the parameter with a given key.
1193     #
1194     # If the parameter has multiple values, only the first will be 
1195     # retrieved; use #params() to get the array of values.
1196     def [](key)
1197       params = @params[key]
1198       return '' unless params
1199       value = params[0]
1200       if @multipart
1201         if value
1202           return value
1203         elsif defined? StringIO
1204           StringIO.new("")
1205         else
1206           Tempfile.new("CGI")
1207         end
1208       else
1209         str = if value then value.dup else "" end
1210         str
1211       end
1212     end
1214     # Return all parameter keys as an array.
1215     def keys(*args)
1216       @params.keys(*args)
1217     end
1219     # Returns true if a given parameter key exists in the query.
1220     def has_key?(*args)
1221       @params.has_key?(*args)
1222     end
1223     alias key? has_key?
1224     alias include? has_key?
1226   end # QueryExtension
1229   # Prettify (indent) an HTML string.
1230   #
1231   # +string+ is the HTML string to indent.  +shift+ is the indentation
1232   # unit to use; it defaults to two spaces.
1233   #
1234   #   print CGI::pretty("<HTML><BODY></BODY></HTML>")
1235   #     # <HTML>
1236   #     #   <BODY>
1237   #     #   </BODY>
1238   #     # </HTML>
1239   # 
1240   #   print CGI::pretty("<HTML><BODY></BODY></HTML>", "\t")
1241   #     # <HTML>
1242   #     #         <BODY>
1243   #     #         </BODY>
1244   #     # </HTML>
1245   #
1246   def CGI::pretty(string, shift = "  ")
1247     lines = string.gsub(/(?!\A)<(?:.|\n)*?>/n, "\n\\0").gsub(/<(?:.|\n)*?>(?!\n)/n, "\\0\n")
1248     end_pos = 0
1249     while end_pos = lines.index(/^<\/(\w+)/n, end_pos)
1250       element = $1.dup
1251       start_pos = lines.rindex(/^\s*<#{element}/ni, end_pos)
1252       lines[start_pos ... end_pos] = "__" + lines[start_pos ... end_pos].gsub(/\n(?!\z)/n, "\n" + shift) + "__"
1253     end
1254     lines.gsub(/^((?:#{Regexp::quote(shift)})*)__(?=<\/?\w)/n, '\1')
1255   end
1258   # Base module for HTML-generation mixins.
1259   #
1260   # Provides methods for code generation for tags following
1261   # the various DTD element types.
1262   module TagMaker # :nodoc:
1264     # Generate code for an element with required start and end tags.
1265     #
1266     #   - -
1267     def nn_element_def(element)
1268       nOE_element_def(element, <<-END)
1269           if block_given?
1270             yield.to_s
1271           else
1272             ""
1273           end +
1274           "</#{element.upcase}>"
1275       END
1276     end
1278     # Generate code for an empty element.
1279     #
1280     #   - O EMPTY
1281     def nOE_element_def(element, append = nil)
1282       s = <<-END
1283           "<#{element.upcase}" + attributes.collect{|name, value|
1284             next unless value
1285             " " + CGI::escapeHTML(name) +
1286             if true == value
1287               ""
1288             else
1289               '="' + CGI::escapeHTML(value) + '"'
1290             end
1291           }.join + ">"
1292       END
1293       s.sub!(/\Z/, " +") << append if append
1294       s
1295     end
1297     # Generate code for an element for which the end (and possibly the
1298     # start) tag is optional.
1299     #
1300     #   O O or - O
1301     def nO_element_def(element)
1302       nOE_element_def(element, <<-END)
1303           if block_given?
1304             yield.to_s + "</#{element.upcase}>"
1305           else
1306             ""
1307           end
1308       END
1309     end
1311   end # TagMaker
1314   #
1315   # Mixin module providing HTML generation methods.
1316   #
1317   # For example,
1318   #   cgi.a("http://www.example.com") { "Example" }
1319   #     # => "<A HREF=\"http://www.example.com\">Example</A>"
1320   #
1321   # Modules Http3, Http4, etc., contain more basic HTML-generation methods
1322   # (:title, :center, etc.).
1323   #
1324   # See class CGI for a detailed example. 
1325   #
1326   module HtmlExtension
1329     # Generate an Anchor element as a string.
1330     #
1331     # +href+ can either be a string, giving the URL
1332     # for the HREF attribute, or it can be a hash of
1333     # the element's attributes.
1334     #
1335     # The body of the element is the string returned by the no-argument
1336     # block passed in.
1337     #
1338     #   a("http://www.example.com") { "Example" }
1339     #     # => "<A HREF=\"http://www.example.com\">Example</A>"
1340     #
1341     #   a("HREF" => "http://www.example.com", "TARGET" => "_top") { "Example" }
1342     #     # => "<A HREF=\"http://www.example.com\" TARGET=\"_top\">Example</A>"
1343     #
1344     def a(href = "") # :yield:
1345       attributes = if href.kind_of?(String)
1346                      { "HREF" => href }
1347                    else
1348                      href
1349                    end
1350       if block_given?
1351         super(attributes){ yield }
1352       else
1353         super(attributes)
1354       end
1355     end
1357     # Generate a Document Base URI element as a String. 
1358     #
1359     # +href+ can either by a string, giving the base URL for the HREF
1360     # attribute, or it can be a has of the element's attributes.
1361     #
1362     # The passed-in no-argument block is ignored.
1363     #
1364     #   base("http://www.example.com/cgi")
1365     #     # => "<BASE HREF=\"http://www.example.com/cgi\">"
1366     def base(href = "") # :yield:
1367       attributes = if href.kind_of?(String)
1368                      { "HREF" => href }
1369                    else
1370                      href
1371                    end
1372       if block_given?
1373         super(attributes){ yield }
1374       else
1375         super(attributes)
1376       end
1377     end
1379     # Generate a BlockQuote element as a string.
1380     #
1381     # +cite+ can either be a string, give the URI for the source of
1382     # the quoted text, or a hash, giving all attributes of the element,
1383     # or it can be omitted, in which case the element has no attributes.
1384     #
1385     # The body is provided by the passed-in no-argument block
1386     #
1387     #   blockquote("http://www.example.com/quotes/foo.html") { "Foo!" }
1388     #     #=> "<BLOCKQUOTE CITE=\"http://www.example.com/quotes/foo.html\">Foo!</BLOCKQUOTE>
1389     def blockquote(cite = nil)  # :yield:
1390       attributes = if cite.kind_of?(String)
1391                      { "CITE" => cite }
1392                    else
1393                      cite or ""
1394                    end
1395       if block_given?
1396         super(attributes){ yield }
1397       else
1398         super(attributes)
1399       end
1400     end
1403     # Generate a Table Caption element as a string.
1404     #
1405     # +align+ can be a string, giving the alignment of the caption
1406     # (one of top, bottom, left, or right).  It can be a hash of
1407     # all the attributes of the element.  Or it can be omitted.
1408     #
1409     # The body of the element is provided by the passed-in no-argument block.
1410     #
1411     #   caption("left") { "Capital Cities" }
1412     #     # => <CAPTION ALIGN=\"left\">Capital Cities</CAPTION>
1413     def caption(align = nil) # :yield:
1414       attributes = if align.kind_of?(String)
1415                      { "ALIGN" => align }
1416                    else
1417                      align or ""
1418                    end
1419       if block_given?
1420         super(attributes){ yield }
1421       else
1422         super(attributes)
1423       end
1424     end
1427     # Generate a Checkbox Input element as a string.
1428     #
1429     # The attributes of the element can be specified as three arguments,
1430     # +name+, +value+, and +checked+.  +checked+ is a boolean value;
1431     # if true, the CHECKED attribute will be included in the element.
1432     #
1433     # Alternatively, the attributes can be specified as a hash.
1434     #
1435     #   checkbox("name")
1436     #     # = checkbox("NAME" => "name")
1437     # 
1438     #   checkbox("name", "value")
1439     #     # = checkbox("NAME" => "name", "VALUE" => "value")
1440     # 
1441     #   checkbox("name", "value", true)
1442     #     # = checkbox("NAME" => "name", "VALUE" => "value", "CHECKED" => true)
1443     def checkbox(name = "", value = nil, checked = nil)
1444       attributes = if name.kind_of?(String)
1445                      { "TYPE" => "checkbox", "NAME" => name,
1446                        "VALUE" => value, "CHECKED" => checked }
1447                    else
1448                      name["TYPE"] = "checkbox"
1449                      name
1450                    end
1451       input(attributes)
1452     end
1454     # Generate a sequence of checkbox elements, as a String.
1455     #
1456     # The checkboxes will all have the same +name+ attribute.
1457     # Each checkbox is followed by a label.
1458     # There will be one checkbox for each value.  Each value
1459     # can be specified as a String, which will be used both
1460     # as the value of the VALUE attribute and as the label
1461     # for that checkbox.  A single-element array has the
1462     # same effect.
1463     #
1464     # Each value can also be specified as a three-element array.
1465     # The first element is the VALUE attribute; the second is the
1466     # label; and the third is a boolean specifying whether this
1467     # checkbox is CHECKED.
1468     #
1469     # Each value can also be specified as a two-element
1470     # array, by omitting either the value element (defaults
1471     # to the same as the label), or the boolean checked element
1472     # (defaults to false).
1473     #
1474     #   checkbox_group("name", "foo", "bar", "baz")
1475     #     # <INPUT TYPE="checkbox" NAME="name" VALUE="foo">foo
1476     #     # <INPUT TYPE="checkbox" NAME="name" VALUE="bar">bar
1477     #     # <INPUT TYPE="checkbox" NAME="name" VALUE="baz">baz
1478     # 
1479     #   checkbox_group("name", ["foo"], ["bar", true], "baz")
1480     #     # <INPUT TYPE="checkbox" NAME="name" VALUE="foo">foo
1481     #     # <INPUT TYPE="checkbox" CHECKED NAME="name" VALUE="bar">bar
1482     #     # <INPUT TYPE="checkbox" NAME="name" VALUE="baz">baz
1483     # 
1484     #   checkbox_group("name", ["1", "Foo"], ["2", "Bar", true], "Baz")
1485     #     # <INPUT TYPE="checkbox" NAME="name" VALUE="1">Foo
1486     #     # <INPUT TYPE="checkbox" CHECKED NAME="name" VALUE="2">Bar
1487     #     # <INPUT TYPE="checkbox" NAME="name" VALUE="Baz">Baz
1488     # 
1489     #   checkbox_group("NAME" => "name",
1490     #                    "VALUES" => ["foo", "bar", "baz"])
1491     # 
1492     #   checkbox_group("NAME" => "name",
1493     #                    "VALUES" => [["foo"], ["bar", true], "baz"])
1494     # 
1495     #   checkbox_group("NAME" => "name",
1496     #                    "VALUES" => [["1", "Foo"], ["2", "Bar", true], "Baz"])
1497     def checkbox_group(name = "", *values)
1498       if name.kind_of?(Hash)
1499         values = name["VALUES"]
1500         name = name["NAME"]
1501       end
1502       values.collect{|value|
1503         if value.kind_of?(String)
1504           checkbox(name, value) + value
1505         else
1506           if value[value.size - 1] == true
1507             checkbox(name, value[0], true) +
1508             value[value.size - 2]
1509           else
1510             checkbox(name, value[0]) +
1511             value[value.size - 1]
1512           end
1513         end
1514       }.join
1515     end
1518     # Generate an File Upload Input element as a string.
1519     #
1520     # The attributes of the element can be specified as three arguments,
1521     # +name+, +size+, and +maxlength+.  +maxlength+ is the maximum length
1522     # of the file's _name_, not of the file's _contents_.
1523     #
1524     # Alternatively, the attributes can be specified as a hash.
1525     #
1526     # See #multipart_form() for forms that include file uploads.
1527     #
1528     #   file_field("name")
1529     #     # <INPUT TYPE="file" NAME="name" SIZE="20">
1530     # 
1531     #   file_field("name", 40)
1532     #     # <INPUT TYPE="file" NAME="name" SIZE="40">
1533     # 
1534     #   file_field("name", 40, 100)
1535     #     # <INPUT TYPE="file" NAME="name" SIZE="40" MAXLENGTH="100">
1536     # 
1537     #   file_field("NAME" => "name", "SIZE" => 40)
1538     #     # <INPUT TYPE="file" NAME="name" SIZE="40">
1539     def file_field(name = "", size = 20, maxlength = nil)
1540       attributes = if name.kind_of?(String)
1541                      { "TYPE" => "file", "NAME" => name,
1542                        "SIZE" => size.to_s }
1543                    else
1544                      name["TYPE"] = "file"
1545                      name
1546                    end
1547       attributes["MAXLENGTH"] = maxlength.to_s if maxlength
1548       input(attributes)
1549     end
1552     # Generate a Form element as a string.
1553     #
1554     # +method+ should be either "get" or "post", and defaults to the latter.
1555     # +action+ defaults to the current CGI script name.  +enctype+
1556     # defaults to "application/x-www-form-urlencoded".  
1557     #
1558     # Alternatively, the attributes can be specified as a hash.
1559     #
1560     # See also #multipart_form() for forms that include file uploads.
1561     #
1562     #   form{ "string" }
1563     #     # <FORM METHOD="post" ENCTYPE="application/x-www-form-urlencoded">string</FORM>
1564     # 
1565     #   form("get") { "string" }
1566     #     # <FORM METHOD="get" ENCTYPE="application/x-www-form-urlencoded">string</FORM>
1567     # 
1568     #   form("get", "url") { "string" }
1569     #     # <FORM METHOD="get" ACTION="url" ENCTYPE="application/x-www-form-urlencoded">string</FORM>
1570     # 
1571     #   form("METHOD" => "post", "ENCTYPE" => "enctype") { "string" }
1572     #     # <FORM METHOD="post" ENCTYPE="enctype">string</FORM>
1573     def form(method = "post", action = script_name, enctype = "application/x-www-form-urlencoded")
1574       attributes = if method.kind_of?(String)
1575                      { "METHOD" => method, "ACTION" => action,
1576                        "ENCTYPE" => enctype } 
1577                    else
1578                      unless method.has_key?("METHOD")
1579                        method["METHOD"] = "post"
1580                      end
1581                      unless method.has_key?("ENCTYPE")
1582                        method["ENCTYPE"] = enctype
1583                      end
1584                      method
1585                    end
1586       if block_given?
1587         body = yield
1588       else
1589         body = ""
1590       end
1591       if @output_hidden
1592         body += @output_hidden.collect{|k,v|
1593           "<INPUT TYPE=\"HIDDEN\" NAME=\"#{k}\" VALUE=\"#{v}\">"
1594         }.join
1595       end
1596       super(attributes){body}
1597     end
1599     # Generate a Hidden Input element as a string.
1600     #
1601     # The attributes of the element can be specified as two arguments,
1602     # +name+ and +value+.
1603     #
1604     # Alternatively, the attributes can be specified as a hash.
1605     #
1606     #   hidden("name")
1607     #     # <INPUT TYPE="hidden" NAME="name">
1608     # 
1609     #   hidden("name", "value")
1610     #     # <INPUT TYPE="hidden" NAME="name" VALUE="value">
1611     # 
1612     #   hidden("NAME" => "name", "VALUE" => "reset", "ID" => "foo")
1613     #     # <INPUT TYPE="hidden" NAME="name" VALUE="value" ID="foo">
1614     def hidden(name = "", value = nil)
1615       attributes = if name.kind_of?(String)
1616                      { "TYPE" => "hidden", "NAME" => name, "VALUE" => value }
1617                    else
1618                      name["TYPE"] = "hidden"
1619                      name
1620                    end
1621       input(attributes)
1622     end
1624     # Generate a top-level HTML element as a string.
1625     #
1626     # The attributes of the element are specified as a hash.  The
1627     # pseudo-attribute "PRETTY" can be used to specify that the generated
1628     # HTML string should be indented.  "PRETTY" can also be specified as
1629     # a string as the sole argument to this method.  The pseudo-attribute
1630     # "DOCTYPE", if given, is used as the leading DOCTYPE SGML tag; it
1631     # should include the entire text of this tag, including angle brackets.
1632     #
1633     # The body of the html element is supplied as a block.
1634     # 
1635     #   html{ "string" }
1636     #     # <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><HTML>string</HTML>
1637     # 
1638     #   html("LANG" => "ja") { "string" }
1639     #     # <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><HTML LANG="ja">string</HTML>
1640     # 
1641     #   html("DOCTYPE" => false) { "string" }
1642     #     # <HTML>string</HTML>
1643     # 
1644     #   html("DOCTYPE" => '<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">') { "string" }
1645     #     # <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"><HTML>string</HTML>
1646     # 
1647     #   html("PRETTY" => "  ") { "<BODY></BODY>" }
1648     #     # <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
1649     #     # <HTML>
1650     #     #   <BODY>
1651     #     #   </BODY>
1652     #     # </HTML>
1653     # 
1654     #   html("PRETTY" => "\t") { "<BODY></BODY>" }
1655     #     # <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
1656     #     # <HTML>
1657     #     #         <BODY>
1658     #     #         </BODY>
1659     #     # </HTML>
1660     # 
1661     #   html("PRETTY") { "<BODY></BODY>" }
1662     #     # = html("PRETTY" => "  ") { "<BODY></BODY>" }
1663     # 
1664     #   html(if $VERBOSE then "PRETTY" end) { "HTML string" }
1665     #
1666     def html(attributes = {}) # :yield:
1667       if nil == attributes
1668         attributes = {}
1669       elsif "PRETTY" == attributes
1670         attributes = { "PRETTY" => true }
1671       end
1672       pretty = attributes.delete("PRETTY")
1673       pretty = "  " if true == pretty
1674       buf = ""
1676       if attributes.has_key?("DOCTYPE")
1677         if attributes["DOCTYPE"]
1678           buf += attributes.delete("DOCTYPE")
1679         else
1680           attributes.delete("DOCTYPE")
1681         end
1682       else
1683         buf += doctype
1684       end
1686       if block_given?
1687         buf += super(attributes){ yield }
1688       else
1689         buf += super(attributes)
1690       end
1692       if pretty
1693         CGI::pretty(buf, pretty)
1694       else
1695         buf
1696       end
1698     end
1700     # Generate an Image Button Input element as a string.
1701     #
1702     # +src+ is the URL of the image to use for the button.  +name+ 
1703     # is the input name.  +alt+ is the alternative text for the image.
1704     #
1705     # Alternatively, the attributes can be specified as a hash.
1706     # 
1707     #   image_button("url")
1708     #     # <INPUT TYPE="image" SRC="url">
1709     # 
1710     #   image_button("url", "name", "string")
1711     #     # <INPUT TYPE="image" SRC="url" NAME="name" ALT="string">
1712     # 
1713     #   image_button("SRC" => "url", "ATL" => "strng")
1714     #     # <INPUT TYPE="image" SRC="url" ALT="string">
1715     def image_button(src = "", name = nil, alt = nil)
1716       attributes = if src.kind_of?(String)
1717                      { "TYPE" => "image", "SRC" => src, "NAME" => name,
1718                        "ALT" => alt }
1719                    else
1720                      src["TYPE"] = "image"
1721                      src["SRC"] ||= ""
1722                      src
1723                    end
1724       input(attributes)
1725     end
1728     # Generate an Image element as a string.
1729     #
1730     # +src+ is the URL of the image.  +alt+ is the alternative text for
1731     # the image.  +width+ is the width of the image, and +height+ is
1732     # its height.
1733     #
1734     # Alternatively, the attributes can be specified as a hash.
1735     #
1736     #   img("src", "alt", 100, 50)
1737     #     # <IMG SRC="src" ALT="alt" WIDTH="100" HEIGHT="50">
1738     # 
1739     #   img("SRC" => "src", "ALT" => "alt", "WIDTH" => 100, "HEIGHT" => 50)
1740     #     # <IMG SRC="src" ALT="alt" WIDTH="100" HEIGHT="50">
1741     def img(src = "", alt = "", width = nil, height = nil)
1742       attributes = if src.kind_of?(String)
1743                      { "SRC" => src, "ALT" => alt }
1744                    else
1745                      src
1746                    end
1747       attributes["WIDTH"] = width.to_s if width
1748       attributes["HEIGHT"] = height.to_s if height
1749       super(attributes)
1750     end
1753     # Generate a Form element with multipart encoding as a String.
1754     #
1755     # Multipart encoding is used for forms that include file uploads.
1756     #
1757     # +action+ is the action to perform.  +enctype+ is the encoding
1758     # type, which defaults to "multipart/form-data".
1759     #
1760     # Alternatively, the attributes can be specified as a hash.
1761     #
1762     #   multipart_form{ "string" }
1763     #     # <FORM METHOD="post" ENCTYPE="multipart/form-data">string</FORM>
1764     # 
1765     #   multipart_form("url") { "string" }
1766     #     # <FORM METHOD="post" ACTION="url" ENCTYPE="multipart/form-data">string</FORM>
1767     def multipart_form(action = nil, enctype = "multipart/form-data")
1768       attributes = if action == nil
1769                      { "METHOD" => "post", "ENCTYPE" => enctype } 
1770                    elsif action.kind_of?(String)
1771                      { "METHOD" => "post", "ACTION" => action,
1772                        "ENCTYPE" => enctype } 
1773                    else
1774                      unless action.has_key?("METHOD")
1775                        action["METHOD"] = "post"
1776                      end
1777                      unless action.has_key?("ENCTYPE")
1778                        action["ENCTYPE"] = enctype
1779                      end
1780                      action
1781                    end
1782       if block_given?
1783         form(attributes){ yield }
1784       else
1785         form(attributes)
1786       end
1787     end
1790     # Generate a Password Input element as a string.
1791     #
1792     # +name+ is the name of the input field.  +value+ is its default
1793     # value.  +size+ is the size of the input field display.  +maxlength+
1794     # is the maximum length of the inputted password.
1795     #
1796     # Alternatively, attributes can be specified as a hash.
1797     #
1798     #   password_field("name")
1799     #     # <INPUT TYPE="password" NAME="name" SIZE="40">
1800     # 
1801     #   password_field("name", "value")
1802     #     # <INPUT TYPE="password" NAME="name" VALUE="value" SIZE="40">
1803     # 
1804     #   password_field("password", "value", 80, 200)
1805     #     # <INPUT TYPE="password" NAME="name" VALUE="value" SIZE="80" MAXLENGTH="200">
1806     # 
1807     #   password_field("NAME" => "name", "VALUE" => "value")
1808     #     # <INPUT TYPE="password" NAME="name" VALUE="value">
1809     def password_field(name = "", value = nil, size = 40, maxlength = nil)
1810       attributes = if name.kind_of?(String)
1811                      { "TYPE" => "password", "NAME" => name,
1812                        "VALUE" => value, "SIZE" => size.to_s }
1813                    else
1814                      name["TYPE"] = "password"
1815                      name
1816                    end
1817       attributes["MAXLENGTH"] = maxlength.to_s if maxlength
1818       input(attributes)
1819     end
1821     # Generate a Select element as a string.
1822     #
1823     # +name+ is the name of the element.  The +values+ are the options that
1824     # can be selected from the Select menu.  Each value can be a String or
1825     # a one, two, or three-element Array.  If a String or a one-element
1826     # Array, this is both the value of that option and the text displayed for
1827     # it.  If a three-element Array, the elements are the option value, displayed
1828     # text, and a boolean value specifying whether this option starts as selected.
1829     # The two-element version omits either the option value (defaults to the same
1830     # as the display text) or the boolean selected specifier (defaults to false).
1831     #
1832     # The attributes and options can also be specified as a hash.  In this
1833     # case, options are specified as an array of values as described above,
1834     # with the hash key of "VALUES".
1835     #
1836     #   popup_menu("name", "foo", "bar", "baz")
1837     #     # <SELECT NAME="name">
1838     #     #   <OPTION VALUE="foo">foo</OPTION>
1839     #     #   <OPTION VALUE="bar">bar</OPTION>
1840     #     #   <OPTION VALUE="baz">baz</OPTION>
1841     #     # </SELECT>
1842     # 
1843     #   popup_menu("name", ["foo"], ["bar", true], "baz")
1844     #     # <SELECT NAME="name">
1845     #     #   <OPTION VALUE="foo">foo</OPTION>
1846     #     #   <OPTION VALUE="bar" SELECTED>bar</OPTION>
1847     #     #   <OPTION VALUE="baz">baz</OPTION>
1848     #     # </SELECT>
1849     # 
1850     #   popup_menu("name", ["1", "Foo"], ["2", "Bar", true], "Baz")
1851     #     # <SELECT NAME="name">
1852     #     #   <OPTION VALUE="1">Foo</OPTION>
1853     #     #   <OPTION SELECTED VALUE="2">Bar</OPTION>
1854     #     #   <OPTION VALUE="Baz">Baz</OPTION>
1855     #     # </SELECT>
1856     # 
1857     #   popup_menu("NAME" => "name", "SIZE" => 2, "MULTIPLE" => true,
1858     #               "VALUES" => [["1", "Foo"], ["2", "Bar", true], "Baz"])
1859     #     # <SELECT NAME="name" MULTIPLE SIZE="2">
1860     #     #   <OPTION VALUE="1">Foo</OPTION>
1861     #     #   <OPTION SELECTED VALUE="2">Bar</OPTION>
1862     #     #   <OPTION VALUE="Baz">Baz</OPTION>
1863     #     # </SELECT>
1864     def popup_menu(name = "", *values)
1866       if name.kind_of?(Hash)
1867         values   = name["VALUES"]
1868         size     = name["SIZE"].to_s if name["SIZE"]
1869         multiple = name["MULTIPLE"]
1870         name     = name["NAME"]
1871       else
1872         size = nil
1873         multiple = nil
1874       end
1876       select({ "NAME" => name, "SIZE" => size,
1877                "MULTIPLE" => multiple }){
1878         values.collect{|value|
1879           if value.kind_of?(String)
1880             option({ "VALUE" => value }){ value }
1881           else
1882             if value[value.size - 1] == true
1883               option({ "VALUE" => value[0], "SELECTED" => true }){
1884                 value[value.size - 2]
1885               }
1886             else
1887               option({ "VALUE" => value[0] }){
1888                 value[value.size - 1]
1889               }
1890             end
1891           end
1892         }.join
1893       }
1895     end
1897     # Generates a radio-button Input element.
1898     #
1899     # +name+ is the name of the input field.  +value+ is the value of
1900     # the field if checked.  +checked+ specifies whether the field
1901     # starts off checked.
1902     #
1903     # Alternatively, the attributes can be specified as a hash.
1904     #
1905     #   radio_button("name", "value")
1906     #     # <INPUT TYPE="radio" NAME="name" VALUE="value">
1907     # 
1908     #   radio_button("name", "value", true)
1909     #     # <INPUT TYPE="radio" NAME="name" VALUE="value" CHECKED>
1910     # 
1911     #   radio_button("NAME" => "name", "VALUE" => "value", "ID" => "foo")
1912     #     # <INPUT TYPE="radio" NAME="name" VALUE="value" ID="foo">
1913     def radio_button(name = "", value = nil, checked = nil)
1914       attributes = if name.kind_of?(String)
1915                      { "TYPE" => "radio", "NAME" => name,
1916                        "VALUE" => value, "CHECKED" => checked }
1917                    else
1918                      name["TYPE"] = "radio"
1919                      name
1920                    end
1921       input(attributes)
1922     end
1924     # Generate a sequence of radio button Input elements, as a String.
1925     #
1926     # This works the same as #checkbox_group().  However, it is not valid
1927     # to have more than one radiobutton in a group checked.
1928     # 
1929     #   radio_group("name", "foo", "bar", "baz")
1930     #     # <INPUT TYPE="radio" NAME="name" VALUE="foo">foo
1931     #     # <INPUT TYPE="radio" NAME="name" VALUE="bar">bar
1932     #     # <INPUT TYPE="radio" NAME="name" VALUE="baz">baz
1933     # 
1934     #   radio_group("name", ["foo"], ["bar", true], "baz")
1935     #     # <INPUT TYPE="radio" NAME="name" VALUE="foo">foo
1936     #     # <INPUT TYPE="radio" CHECKED NAME="name" VALUE="bar">bar
1937     #     # <INPUT TYPE="radio" NAME="name" VALUE="baz">baz
1938     # 
1939     #   radio_group("name", ["1", "Foo"], ["2", "Bar", true], "Baz")
1940     #     # <INPUT TYPE="radio" NAME="name" VALUE="1">Foo
1941     #     # <INPUT TYPE="radio" CHECKED NAME="name" VALUE="2">Bar
1942     #     # <INPUT TYPE="radio" NAME="name" VALUE="Baz">Baz
1943     # 
1944     #   radio_group("NAME" => "name",
1945     #                 "VALUES" => ["foo", "bar", "baz"])
1946     # 
1947     #   radio_group("NAME" => "name",
1948     #                 "VALUES" => [["foo"], ["bar", true], "baz"])
1949     # 
1950     #   radio_group("NAME" => "name",
1951     #                 "VALUES" => [["1", "Foo"], ["2", "Bar", true], "Baz"])
1952     def radio_group(name = "", *values)
1953       if name.kind_of?(Hash)
1954         values = name["VALUES"]
1955         name = name["NAME"]
1956       end
1957       values.collect{|value|
1958         if value.kind_of?(String)
1959           radio_button(name, value) + value
1960         else
1961           if value[value.size - 1] == true
1962             radio_button(name, value[0], true) +
1963             value[value.size - 2]
1964           else
1965             radio_button(name, value[0]) +
1966             value[value.size - 1]
1967           end
1968         end
1969       }.join
1970     end
1972     # Generate a reset button Input element, as a String.
1973     #
1974     # This resets the values on a form to their initial values.  +value+
1975     # is the text displayed on the button. +name+ is the name of this button.
1976     #
1977     # Alternatively, the attributes can be specified as a hash.
1978     #
1979     #   reset
1980     #     # <INPUT TYPE="reset">
1981     # 
1982     #   reset("reset")
1983     #     # <INPUT TYPE="reset" VALUE="reset">
1984     # 
1985     #   reset("VALUE" => "reset", "ID" => "foo")
1986     #     # <INPUT TYPE="reset" VALUE="reset" ID="foo">
1987     def reset(value = nil, name = nil)
1988       attributes = if (not value) or value.kind_of?(String)
1989                      { "TYPE" => "reset", "VALUE" => value, "NAME" => name }
1990                    else
1991                      value["TYPE"] = "reset"
1992                      value
1993                    end
1994       input(attributes)
1995     end
1997     alias scrolling_list popup_menu
1999     # Generate a submit button Input element, as a String.
2000     #
2001     # +value+ is the text to display on the button.  +name+ is the name
2002     # of the input.
2003     #
2004     # Alternatively, the attributes can be specified as a hash.
2005     #
2006     #   submit
2007     #     # <INPUT TYPE="submit">
2008     # 
2009     #   submit("ok")
2010     #     # <INPUT TYPE="submit" VALUE="ok">
2011     # 
2012     #   submit("ok", "button1")
2013     #     # <INPUT TYPE="submit" VALUE="ok" NAME="button1">
2014     # 
2015     #   submit("VALUE" => "ok", "NAME" => "button1", "ID" => "foo")
2016     #     # <INPUT TYPE="submit" VALUE="ok" NAME="button1" ID="foo">
2017     def submit(value = nil, name = nil)
2018       attributes = if (not value) or value.kind_of?(String)
2019                      { "TYPE" => "submit", "VALUE" => value, "NAME" => name }
2020                    else
2021                      value["TYPE"] = "submit"
2022                      value
2023                    end
2024       input(attributes)
2025     end
2027     # Generate a text field Input element, as a String.
2028     #
2029     # +name+ is the name of the input field.  +value+ is its initial
2030     # value.  +size+ is the size of the input area.  +maxlength+
2031     # is the maximum length of input accepted.
2032     #
2033     # Alternatively, the attributes can be specified as a hash.
2034     #
2035     #   text_field("name")
2036     #     # <INPUT TYPE="text" NAME="name" SIZE="40">
2037     # 
2038     #   text_field("name", "value")
2039     #     # <INPUT TYPE="text" NAME="name" VALUE="value" SIZE="40">
2040     # 
2041     #   text_field("name", "value", 80)
2042     #     # <INPUT TYPE="text" NAME="name" VALUE="value" SIZE="80">
2043     # 
2044     #   text_field("name", "value", 80, 200)
2045     #     # <INPUT TYPE="text" NAME="name" VALUE="value" SIZE="80" MAXLENGTH="200">
2046     # 
2047     #   text_field("NAME" => "name", "VALUE" => "value")
2048     #     # <INPUT TYPE="text" NAME="name" VALUE="value">
2049     def text_field(name = "", value = nil, size = 40, maxlength = nil)
2050       attributes = if name.kind_of?(String)
2051                      { "TYPE" => "text", "NAME" => name, "VALUE" => value,
2052                        "SIZE" => size.to_s }
2053                    else
2054                      name["TYPE"] = "text"
2055                      name
2056                    end
2057       attributes["MAXLENGTH"] = maxlength.to_s if maxlength
2058       input(attributes)
2059     end
2061     # Generate a TextArea element, as a String.
2062     #
2063     # +name+ is the name of the textarea.  +cols+ is the number of
2064     # columns and +rows+ is the number of rows in the display.
2065     #
2066     # Alternatively, the attributes can be specified as a hash.
2067     #
2068     # The body is provided by the passed-in no-argument block
2069     #
2070     #   textarea("name")
2071     #      # = textarea("NAME" => "name", "COLS" => 70, "ROWS" => 10)
2072     #
2073     #   textarea("name", 40, 5)
2074     #      # = textarea("NAME" => "name", "COLS" => 40, "ROWS" => 5)
2075     def textarea(name = "", cols = 70, rows = 10)  # :yield:
2076       attributes = if name.kind_of?(String)
2077                      { "NAME" => name, "COLS" => cols.to_s,
2078                        "ROWS" => rows.to_s }
2079                    else
2080                      name
2081                    end
2082       if block_given?
2083         super(attributes){ yield }
2084       else
2085         super(attributes)
2086       end
2087     end
2089   end # HtmlExtension
2092   # Mixin module for HTML version 3 generation methods.
2093   module Html3 # :nodoc:
2095     # The DOCTYPE declaration for this version of HTML
2096     def doctype
2097       %|<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">|
2098     end
2100     # Initialise the HTML generation methods for this version.
2101     def element_init
2102       extend TagMaker
2103       methods = ""
2104       # - -
2105       for element in %w[ A TT I B U STRIKE BIG SMALL SUB SUP EM STRONG
2106           DFN CODE SAMP KBD VAR CITE FONT ADDRESS DIV center MAP
2107           APPLET PRE XMP LISTING DL OL UL DIR MENU SELECT table TITLE
2108           STYLE SCRIPT H1 H2 H3 H4 H5 H6 TEXTAREA FORM BLOCKQUOTE
2109           CAPTION ]
2110         methods += <<-BEGIN + nn_element_def(element) + <<-END
2111           def #{element.downcase}(attributes = {})
2112         BEGIN
2113           end
2114         END
2115       end
2117       # - O EMPTY
2118       for element in %w[ IMG BASE BASEFONT BR AREA LINK PARAM HR INPUT
2119           ISINDEX META ]
2120         methods += <<-BEGIN + nOE_element_def(element) + <<-END
2121           def #{element.downcase}(attributes = {})
2122         BEGIN
2123           end
2124         END
2125       end
2127       # O O or - O
2128       for element in %w[ HTML HEAD BODY P PLAINTEXT DT DD LI OPTION tr
2129           th td ]
2130         methods += <<-BEGIN + nO_element_def(element) + <<-END
2131           def #{element.downcase}(attributes = {})
2132         BEGIN
2133           end
2134         END
2135       end
2136       eval(methods)
2137     end
2139   end # Html3
2142   # Mixin module for HTML version 4 generation methods.
2143   module Html4 # :nodoc:
2145     # The DOCTYPE declaration for this version of HTML
2146     def doctype
2147       %|<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">|
2148     end
2150     # Initialise the HTML generation methods for this version.
2151     def element_init
2152       extend TagMaker
2153       methods = ""
2154       # - -
2155       for element in %w[ TT I B BIG SMALL EM STRONG DFN CODE SAMP KBD
2156         VAR CITE ABBR ACRONYM SUB SUP SPAN BDO ADDRESS DIV MAP OBJECT
2157         H1 H2 H3 H4 H5 H6 PRE Q INS DEL DL OL UL LABEL SELECT OPTGROUP
2158         FIELDSET LEGEND BUTTON TABLE TITLE STYLE SCRIPT NOSCRIPT
2159         TEXTAREA FORM A BLOCKQUOTE CAPTION ]
2160         methods += <<-BEGIN + nn_element_def(element) + <<-END
2161           def #{element.downcase}(attributes = {})
2162         BEGIN
2163           end
2164         END
2165       end
2167       # - O EMPTY
2168       for element in %w[ IMG BASE BR AREA LINK PARAM HR INPUT COL META ]
2169         methods += <<-BEGIN + nOE_element_def(element) + <<-END
2170           def #{element.downcase}(attributes = {})
2171         BEGIN
2172           end
2173         END
2174       end
2176       # O O or - O
2177       for element in %w[ HTML BODY P DT DD LI OPTION THEAD TFOOT TBODY
2178           COLGROUP TR TH TD HEAD]
2179         methods += <<-BEGIN + nO_element_def(element) + <<-END
2180           def #{element.downcase}(attributes = {})
2181         BEGIN
2182           end
2183         END
2184       end
2185       eval(methods)
2186     end
2188   end # Html4
2191   # Mixin module for HTML version 4 transitional generation methods.
2192   module Html4Tr # :nodoc:
2194     # The DOCTYPE declaration for this version of HTML
2195     def doctype
2196       %|<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">|
2197     end
2199     # Initialise the HTML generation methods for this version.
2200     def element_init
2201       extend TagMaker
2202       methods = ""
2203       # - -
2204       for element in %w[ TT I B U S STRIKE BIG SMALL EM STRONG DFN
2205           CODE SAMP KBD VAR CITE ABBR ACRONYM FONT SUB SUP SPAN BDO
2206           ADDRESS DIV CENTER MAP OBJECT APPLET H1 H2 H3 H4 H5 H6 PRE Q
2207           INS DEL DL OL UL DIR MENU LABEL SELECT OPTGROUP FIELDSET
2208           LEGEND BUTTON TABLE IFRAME NOFRAMES TITLE STYLE SCRIPT
2209           NOSCRIPT TEXTAREA FORM A BLOCKQUOTE CAPTION ]
2210         methods += <<-BEGIN + nn_element_def(element) + <<-END
2211           def #{element.downcase}(attributes = {})
2212         BEGIN
2213           end
2214         END
2215       end
2217       # - O EMPTY
2218       for element in %w[ IMG BASE BASEFONT BR AREA LINK PARAM HR INPUT
2219           COL ISINDEX META ]
2220         methods += <<-BEGIN + nOE_element_def(element) + <<-END
2221           def #{element.downcase}(attributes = {})
2222         BEGIN
2223           end
2224         END
2225       end
2227       # O O or - O
2228       for element in %w[ HTML BODY P DT DD LI OPTION THEAD TFOOT TBODY
2229           COLGROUP TR TH TD HEAD ]
2230         methods += <<-BEGIN + nO_element_def(element) + <<-END
2231           def #{element.downcase}(attributes = {})
2232         BEGIN
2233           end
2234         END
2235       end
2236       eval(methods)
2237     end
2239   end # Html4Tr
2242   # Mixin module for generating HTML version 4 with framesets.
2243   module Html4Fr # :nodoc:
2245     # The DOCTYPE declaration for this version of HTML
2246     def doctype
2247       %|<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">|
2248     end
2250     # Initialise the HTML generation methods for this version.
2251     def element_init
2252       methods = ""
2253       # - -
2254       for element in %w[ FRAMESET ]
2255         methods += <<-BEGIN + nn_element_def(element) + <<-END
2256           def #{element.downcase}(attributes = {})
2257         BEGIN
2258           end
2259         END
2260       end
2262       # - O EMPTY
2263       for element in %w[ FRAME ]
2264         methods += <<-BEGIN + nOE_element_def(element) + <<-END
2265           def #{element.downcase}(attributes = {})
2266         BEGIN
2267           end
2268         END
2269       end
2270       eval(methods)
2271     end
2273   end # Html4Fr
2276   # Creates a new CGI instance.
2277   #
2278   # +type+ specifies which version of HTML to load the HTML generation
2279   # methods for.  The following versions of HTML are supported:
2280   #
2281   # html3:: HTML 3.x
2282   # html4:: HTML 4.0
2283   # html4Tr:: HTML 4.0 Transitional
2284   # html4Fr:: HTML 4.0 with Framesets
2285   #
2286   # If not specified, no HTML generation methods will be loaded.
2287   #
2288   # If the CGI object is not created in a standard CGI call environment
2289   # (that is, it can't locate REQUEST_METHOD in its environment), then
2290   # it will run in "offline" mode.  In this mode, it reads its parameters
2291   # from the command line or (failing that) from standard input.  Otherwise,
2292   # cookies and other parameters are parsed automatically from the standard
2293   # CGI locations, which varies according to the REQUEST_METHOD.
2294   def initialize(type = "query")
2295     if defined?(MOD_RUBY) && !ENV.key?("GATEWAY_INTERFACE")
2296       Apache.request.setup_cgi_env
2297     end
2299       (class << self; self; end).class_eval do
2300         const_set(:CGI_PARAMS,  [1])
2301         const_set(:CGI_COOKIES, [2])
2302       end
2304     extend QueryExtension
2305     @multipart = false
2307     initialize_query()  # set @params, @cookies
2308     @output_cookies = nil
2309     @output_hidden = nil
2311     case type
2312     when "html3"
2313       extend Html3
2314       element_init()
2315       extend HtmlExtension
2316     when "html4"
2317       extend Html4
2318       element_init()
2319       extend HtmlExtension
2320     when "html4Tr"
2321       extend Html4Tr
2322       element_init()
2323       extend HtmlExtension
2324     when "html4Fr"
2325       extend Html4Tr
2326       element_init()
2327       extend Html4Fr
2328       element_init()
2329       extend HtmlExtension
2330     end
2331   end
2333 end   # class CGI