2 # cgi.rb - cgi support library
4 # Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
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)
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.
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
33 raise "Please, use ruby 1.5.4 or later." if RUBY_VERSION < "1.5.4"
37 # CGI class. See documentation for the file cgi.rb for an overview
38 # of the CGI protocol.
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.
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.
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
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.
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
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
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.
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()
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.
156 # === Get form values
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
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
189 # db = PStore.new("query.db")
191 # db["params"] = cgi.params
195 # === Restore form values from file
198 # db = PStore.new("query.db")
200 # cgi.params = db["params"]
204 # === Get multipart form values
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
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
230 # for name, cookie in cgi.cookies
231 # cookie.expires = Time.now + 30
233 # cgi.out("cookie" => cgi.cookies) {"string"}
235 # cgi.cookies # { "name1" => cookie1, "name2" => cookie2, ... }
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 ($>)
245 # cgi = CGI.new("html3") # add HTML generation methods
248 # cgi.head{ cgi.title{"TITLE"} } +
251 # cgi.textarea("get_text") +
257 # "params: " + cgi.params.inspect + "\n" +
258 # "cookies: " + cgi.cookies.inspect + "\n" +
259 # ENV.collect() do |key, value|
260 # key + " --> " + value + "\n"
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
278 # String for carriage return
281 # String for linefeed
284 # Standard internet newline sequence
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'=>':'}
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"
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 ]
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
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)
358 TABLE_FOR_ESCAPE_HTML__ = {
365 # Escape special characters in HTML, namely &\"<>
366 # CGI::escapeHTML('Usage: foo "bar" <baz>')
367 # # => "Usage: foo "bar" <baz>"
368 def CGI::escapeHTML(string)
369 string.gsub(/[&\"<>]/, TABLE_FOR_ESCAPE_HTML__)
373 # Unescape a string that has been HTML-escaped
374 # CGI::unescapeHTML("Usage: foo "bar" <baz>")
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
385 when /\A#0*(\d+)\z/ then
387 Integer($1).chr.force_encoding(enc)
391 when /\A#x([0-9a-f]+)\z/i then
393 $1.hex.chr.force_encoding(enc)
402 def CGI::escape_html(str)
405 def CGI::unescape_html(str)
409 # Escape only the tags of certain HTML elements in +string+.
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).
417 # print CGI::escapeElement('<BR><A HREF="url"></A>', "A", "IMG")
418 # # "<BR><A HREF="url"></A>"
420 # print CGI::escapeElement('<BR><A HREF="url"></A>', ["A", "IMG"])
421 # # "<BR><A HREF="url"></A>"
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
434 # Undo escaping such as that done by CGI::escapeElement()
436 # print CGI::unescapeElement(
437 # CGI::escapeHTML('<BR><A HREF="url"></A>'), "A", "IMG")
438 # # "<BR><A HREF="url"></A>"
440 # print CGI::unescapeElement(
441 # CGI::escapeHTML('<BR><A HREF="url"></A>'), ["A", "IMG"])
442 # # "<BR><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(/<\/?(?:#{elements.join("|")})(?!\w)(?:.|\n)*?>/ni) do
447 CGI::unescapeHTML($&)
453 def CGI::escape_element(str)
456 def CGI::unescape_element(str)
460 # Format a +Time+ object as a String using the format specified by RFC 1123.
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)
472 # Create an HTTP header block as a string.
474 # Includes the empty line that ends the header block.
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:
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
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
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
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.
503 # Other header lines can also be set; they are appended as key: value.
506 # # Content-Type: text/html
508 # header("text/plain")
509 # # Content-Type: text/plain
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
520 # "language" => "ja",
521 # "expires" => Time.now + 30,
522 # "cookie" => [cookie1, cookie2],
523 # "my_header1" => "my_value"
524 # "my_header2" => "my_value")
526 # The status codes are:
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"
547 # This method does not perform charset conversion.
549 def header(options = "text/html")
555 options = { "type" => options }
557 options = options.dup
560 unless options.has_key?("type")
561 options["type"] = "text/html"
564 if options.has_key?("charset")
565 options["type"] += "; charset=" + options.delete("charset")
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") +
574 "Date: " + CGI::rfc1123_date(Time.now) + EOL
576 unless options.has_key?("server")
577 options["server"] = (env_table['SERVER_SOFTWARE'] or "")
580 unless options.has_key?("connection")
581 options["connection"] = "close"
584 options.delete("status")
587 if options.has_key?("status")
589 (HTTP_STATUS[options["status"]] or options["status"]) + EOL
590 options.delete("status")
593 if options.has_key?("server")
594 buf += "Server: " + options.delete("server") + EOL
597 if options.has_key?("connection")
598 buf += "Connection: " + options.delete("connection") + EOL
601 buf += "Content-Type: " + options.delete("type") + EOL
603 if options.has_key?("length")
604 buf += "Content-Length: " + options.delete("length").to_s + EOL
607 if options.has_key?("language")
608 buf += "Content-Language: " + options.delete("language") + EOL
611 if options.has_key?("expires")
612 buf += "Expires: " + CGI::rfc1123_date( options.delete("expires") ) + EOL
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
623 elsif options["cookie"].kind_of?(Hash)
624 options.delete("cookie").each_value{|cookie|
625 buf += "Set-Cookie: " + cookie.to_s + EOL
630 for cookie in @output_cookies
631 buf += "Set-Cookie: " + cookie.to_s + EOL
635 options.each{|key, value|
636 buf += key + ": " + value.to_s + EOL
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
645 table.add(name, value)
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
654 if Apache::request.status == 200
655 Apache::request.status = 302
657 Apache::request.headers_out[name] = value
659 Apache::request.headers_out[name] = value
662 Apache::request.send_http_header
671 # Print an HTTP header and body to $DEFAULT_OUTPUT ($>)
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.
678 # cgi.out{ "string" }
679 # # Content-Type: text/html
680 # # Content-Length: 6
684 # cgi.out("text/plain") { "string" }
685 # # Content-Type: text/plain
686 # # Content-Length: 6
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" }
703 # Content-Length is automatically calculated from the size of
704 # the String returned by the content block.
706 # If ENV['REQUEST_METHOD'] == "HEAD", then only the header
707 # is outputted (the content block is still required, but it
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
713 def out(options = "text/html") # :yield:
715 options = { "type" => options } if options.kind_of?(String)
718 if options.has_key?("charset")
720 case options["charset"]
722 content = NKF::nkf('-j -m0 -x', content)
723 options["language"] = "ja" unless options.has_key?("language")
725 content = NKF::nkf('-e -m0 -x', content)
726 options["language"] = "ja" unless options.has_key?("language")
728 content = NKF::nkf('-s -m0 -x', content)
729 options["language"] = "ja" unless options.has_key?("language")
733 options["length"] = content.length.to_s
735 output.binmode if defined? output.binmode
736 output.print header(options)
737 output.print content unless "HEAD" == env_table['REQUEST_METHOD']
741 # Print an argument or list of arguments to the default output stream
744 # cgi.print # default: cgi.print == $DEFAULT_OUTPUT.print
746 stdoutput.print(*options)
751 # Class representing an HTTP cookie.
753 # In addition to its specific fields and methods, a Cookie instance
754 # is a delegator to the array of its values.
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
769 # cgi.out("cookie" => [cookie1, cookie2]) { "string" }
771 # name = cookie1.name
772 # values = cookie1.value
773 # path = cookie1.path
774 # domain = cookie1.domain
775 # expires = cookie1.expires
776 # secure = cookie1.secure
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.
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:
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
803 # These keywords correspond to attributes of the cookie object.
804 def initialize(name = "", *value)
805 if name.kind_of?(String)
808 %r|^(.*/)|.match(ENV["SCRIPT_NAME"])
815 unless options.has_key?("name")
816 raise ArgumentError, "`name' required"
819 @name = options["name"]
820 @value = Array(options["value"])
821 # simple support for IE
823 @path = options["path"]
825 %r|^(.*/)|.match(ENV["SCRIPT_NAME"])
828 @domain = options["domain"]
829 @expires = options["expires"]
830 @secure = options["secure"] == true ? true : false
835 attr_accessor("name", "value", "path", "domain", "expires")
836 attr_reader("secure")
838 # Set whether the Cookie is a secure cookie or not.
840 # +val+ must be a boolean.
842 @secure = val if val == true or val == false
846 # Convert the Cookie to its string representation.
851 if @value.kind_of?(String)
852 buf += CGI::escape(@value)
854 buf += @value.collect{|v| CGI::escape(v) }.join("&")
858 buf += '; domain=' + @domain
862 buf += '; path=' + @path
866 buf += '; expires=' + CGI::rfc1123_date(@expires)
879 # Parse a raw cookie string into a hash of cookie-name=>Cookie
882 # cookies = CGI::Cookie::parse("raw_cookie_string")
883 # # { "name1" => cookie1, "name2" => cookie2, ... }
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)
894 values = values.split('&').collect{|v| CGI::unescape(v) }
895 if cookies.has_key?(name)
896 values = cookies[name].value + values
898 cookies[name] = Cookie::new(name, *values)
904 # Parse an HTTP query string into a hash of key=>value pairs.
906 # params = CGI::parse("query_string")
907 # # {"name1" => ["value1", "value2", ...],
908 # # "name2" => ["value1", "value2", ...], ... }
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)
918 params[key] = [value]
925 # Mixin module. It provides the follow functionality groups:
927 # 1. Access to CGI environment variables as methods. See
928 # documentation to the CGI class for a list of these variables.
930 # 2. Access to cookies, including the cookies attribute.
932 # 3. Access to parameters, including the params attribute, and overloading
933 # [] to perform parameter value lookup by key.
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.
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)
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
960 # Get the raw cookies as a string.
962 env_table["HTTP_COOKIE"]
965 # Get the raw RFC2965 cookies as a string.
967 env_table["HTTP_COOKIE2"]
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.
977 # Set all the parameters.
983 def read_multipart(boundary, content_length)
984 params = Hash.new([])
985 boundary = "--" + boundary
986 quoted_boundary = Regexp.quote(boundary)
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)
997 raise EOFError, "no content body"
998 elsif boundary + EOL != status
999 raise EOFError, "bad content body"
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
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)] = ""
1020 c = if bufsize < content_length
1021 stdinput.read(bufsize)
1023 stdinput.read(content_length)
1025 if c.nil? || c.empty?
1026 raise EOFError, "bad content body"
1029 content_length -= c.size
1032 buf = buf.sub(/\A((?:.|\n)*?)(?:[\r\n]{1,2})?#{quoted_boundary}([\r\n]{1,2}|--)/n) do
1037 boundary_end = $2.dup
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)
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}
1060 /Content-Disposition:.* name="?([^\";\s]*)"?/ni.match(head)
1061 name = ($1 || "").dup
1063 if params.has_key?(name)
1064 params[name].push(body)
1066 params[name] = [body]
1068 break if buf.size == 0
1069 break if content_length == -1
1071 raise EOFError, "bad boundary end of body part" unless boundary_end=~/--/
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?
1086 %|(offline mode: enter name=value pairs on standard input)\n|
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) }
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.
1107 @@small_buffer = lambda{StringIO.new}
1110 @@small_buffer = lambda{
1111 n = Tempfile.new("CGI")
1117 def initialize(morph_threshold = 10240)
1118 @threshold = morph_threshold
1119 @body = @@small_buffer.call
1125 if @morph_check && (@cur_size + data.size > @threshold)
1137 # returns the true body object.
1144 new_body = TempFile.new("CGI")
1145 new_body.binmode if defined? @body.binmode
1146 new_body.binmode if defined? new_body.binmode
1149 new_body.print @body.read
1151 @morph_check = false
1155 # Initialize the data from the query.
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'])
1164 @params = read_multipart(boundary, Integer(env_table['CONTENT_LENGTH']))
1167 @params = CGI::parse(
1168 case env_table['REQUEST_METHOD']
1170 if defined?(MOD_RUBY)
1171 Apache::request.args or ""
1173 env_table['QUERY_STRING'] or ""
1176 stdinput.binmode if defined? stdinput.binmode
1177 stdinput.read(Integer(env_table['CONTENT_LENGTH'])) or ''
1184 @cookies = CGI::Cookie::parse((env_table['HTTP_COOKIE'] or env_table['COOKIE']))
1186 private :initialize_query
1192 # Get the value for the parameter with a given key.
1194 # If the parameter has multiple values, only the first will be
1195 # retrieved; use #params() to get the array of values.
1197 params = @params[key]
1198 return '' unless params
1203 elsif defined? StringIO
1209 str = if value then value.dup else "" end
1214 # Return all parameter keys as an array.
1219 # Returns true if a given parameter key exists in the query.
1221 @params.has_key?(*args)
1224 alias include? has_key?
1226 end # QueryExtension
1229 # Prettify (indent) an HTML string.
1231 # +string+ is the HTML string to indent. +shift+ is the indentation
1232 # unit to use; it defaults to two spaces.
1234 # print CGI::pretty("<HTML><BODY></BODY></HTML>")
1240 # print CGI::pretty("<HTML><BODY></BODY></HTML>", "\t")
1246 def CGI::pretty(string, shift = " ")
1247 lines = string.gsub(/(?!\A)<(?:.|\n)*?>/n, "\n\\0").gsub(/<(?:.|\n)*?>(?!\n)/n, "\\0\n")
1249 while end_pos = lines.index(/^<\/(\w+)/n, end_pos)
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) + "__"
1254 lines.gsub(/^((?:#{Regexp::quote(shift)})*)__(?=<\/?\w)/n, '\1')
1258 # Base module for HTML-generation mixins.
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.
1267 def nn_element_def(element)
1268 nOE_element_def(element, <<-END)
1274 "</#{element.upcase}>"
1278 # Generate code for an empty element.
1281 def nOE_element_def(element, append = nil)
1283 "<#{element.upcase}" + attributes.collect{|name, value|
1285 " " + CGI::escapeHTML(name) +
1289 '="' + CGI::escapeHTML(value) + '"'
1293 s.sub!(/\Z/, " +") << append if append
1297 # Generate code for an element for which the end (and possibly the
1298 # start) tag is optional.
1301 def nO_element_def(element)
1302 nOE_element_def(element, <<-END)
1304 yield.to_s + "</#{element.upcase}>"
1315 # Mixin module providing HTML generation methods.
1318 # cgi.a("http://www.example.com") { "Example" }
1319 # # => "<A HREF=\"http://www.example.com\">Example</A>"
1321 # Modules Http3, Http4, etc., contain more basic HTML-generation methods
1322 # (:title, :center, etc.).
1324 # See class CGI for a detailed example.
1326 module HtmlExtension
1329 # Generate an Anchor element as a string.
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.
1335 # The body of the element is the string returned by the no-argument
1338 # a("http://www.example.com") { "Example" }
1339 # # => "<A HREF=\"http://www.example.com\">Example</A>"
1341 # a("HREF" => "http://www.example.com", "TARGET" => "_top") { "Example" }
1342 # # => "<A HREF=\"http://www.example.com\" TARGET=\"_top\">Example</A>"
1344 def a(href = "") # :yield:
1345 attributes = if href.kind_of?(String)
1351 super(attributes){ yield }
1357 # Generate a Document Base URI element as a String.
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.
1362 # The passed-in no-argument block is ignored.
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)
1373 super(attributes){ yield }
1379 # Generate a BlockQuote element as a string.
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.
1385 # The body is provided by the passed-in no-argument block
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)
1396 super(attributes){ yield }
1403 # Generate a Table Caption element as a string.
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.
1409 # The body of the element is provided by the passed-in no-argument block.
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 }
1420 super(attributes){ yield }
1427 # Generate a Checkbox Input element as a string.
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.
1433 # Alternatively, the attributes can be specified as a hash.
1436 # # = checkbox("NAME" => "name")
1438 # checkbox("name", "value")
1439 # # = checkbox("NAME" => "name", "VALUE" => "value")
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 }
1448 name["TYPE"] = "checkbox"
1454 # Generate a sequence of checkbox elements, as a String.
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
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.
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).
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
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
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
1489 # checkbox_group("NAME" => "name",
1490 # "VALUES" => ["foo", "bar", "baz"])
1492 # checkbox_group("NAME" => "name",
1493 # "VALUES" => [["foo"], ["bar", true], "baz"])
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"]
1502 values.collect{|value|
1503 if value.kind_of?(String)
1504 checkbox(name, value) + value
1506 if value[value.size - 1] == true
1507 checkbox(name, value[0], true) +
1508 value[value.size - 2]
1510 checkbox(name, value[0]) +
1511 value[value.size - 1]
1518 # Generate an File Upload Input element as a string.
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_.
1524 # Alternatively, the attributes can be specified as a hash.
1526 # See #multipart_form() for forms that include file uploads.
1528 # file_field("name")
1529 # # <INPUT TYPE="file" NAME="name" SIZE="20">
1531 # file_field("name", 40)
1532 # # <INPUT TYPE="file" NAME="name" SIZE="40">
1534 # file_field("name", 40, 100)
1535 # # <INPUT TYPE="file" NAME="name" SIZE="40" MAXLENGTH="100">
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 }
1544 name["TYPE"] = "file"
1547 attributes["MAXLENGTH"] = maxlength.to_s if maxlength
1552 # Generate a Form element as a string.
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".
1558 # Alternatively, the attributes can be specified as a hash.
1560 # See also #multipart_form() for forms that include file uploads.
1563 # # <FORM METHOD="post" ENCTYPE="application/x-www-form-urlencoded">string</FORM>
1565 # form("get") { "string" }
1566 # # <FORM METHOD="get" ENCTYPE="application/x-www-form-urlencoded">string</FORM>
1568 # form("get", "url") { "string" }
1569 # # <FORM METHOD="get" ACTION="url" ENCTYPE="application/x-www-form-urlencoded">string</FORM>
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 }
1578 unless method.has_key?("METHOD")
1579 method["METHOD"] = "post"
1581 unless method.has_key?("ENCTYPE")
1582 method["ENCTYPE"] = enctype
1592 body += @output_hidden.collect{|k,v|
1593 "<INPUT TYPE=\"HIDDEN\" NAME=\"#{k}\" VALUE=\"#{v}\">"
1596 super(attributes){body}
1599 # Generate a Hidden Input element as a string.
1601 # The attributes of the element can be specified as two arguments,
1602 # +name+ and +value+.
1604 # Alternatively, the attributes can be specified as a hash.
1607 # # <INPUT TYPE="hidden" NAME="name">
1609 # hidden("name", "value")
1610 # # <INPUT TYPE="hidden" NAME="name" VALUE="value">
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 }
1618 name["TYPE"] = "hidden"
1624 # Generate a top-level HTML element as a string.
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.
1633 # The body of the html element is supplied as a block.
1636 # # <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><HTML>string</HTML>
1638 # html("LANG" => "ja") { "string" }
1639 # # <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><HTML LANG="ja">string</HTML>
1641 # html("DOCTYPE" => false) { "string" }
1642 # # <HTML>string</HTML>
1644 # html("DOCTYPE" => '<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">') { "string" }
1645 # # <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"><HTML>string</HTML>
1647 # html("PRETTY" => " ") { "<BODY></BODY>" }
1648 # # <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
1654 # html("PRETTY" => "\t") { "<BODY></BODY>" }
1655 # # <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
1661 # html("PRETTY") { "<BODY></BODY>" }
1662 # # = html("PRETTY" => " ") { "<BODY></BODY>" }
1664 # html(if $VERBOSE then "PRETTY" end) { "HTML string" }
1666 def html(attributes = {}) # :yield:
1667 if nil == attributes
1669 elsif "PRETTY" == attributes
1670 attributes = { "PRETTY" => true }
1672 pretty = attributes.delete("PRETTY")
1673 pretty = " " if true == pretty
1676 if attributes.has_key?("DOCTYPE")
1677 if attributes["DOCTYPE"]
1678 buf += attributes.delete("DOCTYPE")
1680 attributes.delete("DOCTYPE")
1687 buf += super(attributes){ yield }
1689 buf += super(attributes)
1693 CGI::pretty(buf, pretty)
1700 # Generate an Image Button Input element as a string.
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.
1705 # Alternatively, the attributes can be specified as a hash.
1707 # image_button("url")
1708 # # <INPUT TYPE="image" SRC="url">
1710 # image_button("url", "name", "string")
1711 # # <INPUT TYPE="image" SRC="url" NAME="name" ALT="string">
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,
1720 src["TYPE"] = "image"
1728 # Generate an Image element as a string.
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
1734 # Alternatively, the attributes can be specified as a hash.
1736 # img("src", "alt", 100, 50)
1737 # # <IMG SRC="src" ALT="alt" WIDTH="100" HEIGHT="50">
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 }
1747 attributes["WIDTH"] = width.to_s if width
1748 attributes["HEIGHT"] = height.to_s if height
1753 # Generate a Form element with multipart encoding as a String.
1755 # Multipart encoding is used for forms that include file uploads.
1757 # +action+ is the action to perform. +enctype+ is the encoding
1758 # type, which defaults to "multipart/form-data".
1760 # Alternatively, the attributes can be specified as a hash.
1762 # multipart_form{ "string" }
1763 # # <FORM METHOD="post" ENCTYPE="multipart/form-data">string</FORM>
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 }
1774 unless action.has_key?("METHOD")
1775 action["METHOD"] = "post"
1777 unless action.has_key?("ENCTYPE")
1778 action["ENCTYPE"] = enctype
1783 form(attributes){ yield }
1790 # Generate a Password Input element as a string.
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.
1796 # Alternatively, attributes can be specified as a hash.
1798 # password_field("name")
1799 # # <INPUT TYPE="password" NAME="name" SIZE="40">
1801 # password_field("name", "value")
1802 # # <INPUT TYPE="password" NAME="name" VALUE="value" SIZE="40">
1804 # password_field("password", "value", 80, 200)
1805 # # <INPUT TYPE="password" NAME="name" VALUE="value" SIZE="80" MAXLENGTH="200">
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 }
1814 name["TYPE"] = "password"
1817 attributes["MAXLENGTH"] = maxlength.to_s if maxlength
1821 # Generate a Select element as a string.
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).
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".
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>
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>
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>
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>
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"]
1876 select({ "NAME" => name, "SIZE" => size,
1877 "MULTIPLE" => multiple }){
1878 values.collect{|value|
1879 if value.kind_of?(String)
1880 option({ "VALUE" => value }){ value }
1882 if value[value.size - 1] == true
1883 option({ "VALUE" => value[0], "SELECTED" => true }){
1884 value[value.size - 2]
1887 option({ "VALUE" => value[0] }){
1888 value[value.size - 1]
1897 # Generates a radio-button Input element.
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.
1903 # Alternatively, the attributes can be specified as a hash.
1905 # radio_button("name", "value")
1906 # # <INPUT TYPE="radio" NAME="name" VALUE="value">
1908 # radio_button("name", "value", true)
1909 # # <INPUT TYPE="radio" NAME="name" VALUE="value" CHECKED>
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 }
1918 name["TYPE"] = "radio"
1924 # Generate a sequence of radio button Input elements, as a String.
1926 # This works the same as #checkbox_group(). However, it is not valid
1927 # to have more than one radiobutton in a group checked.
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
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
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
1944 # radio_group("NAME" => "name",
1945 # "VALUES" => ["foo", "bar", "baz"])
1947 # radio_group("NAME" => "name",
1948 # "VALUES" => [["foo"], ["bar", true], "baz"])
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"]
1957 values.collect{|value|
1958 if value.kind_of?(String)
1959 radio_button(name, value) + value
1961 if value[value.size - 1] == true
1962 radio_button(name, value[0], true) +
1963 value[value.size - 2]
1965 radio_button(name, value[0]) +
1966 value[value.size - 1]
1972 # Generate a reset button Input element, as a String.
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.
1977 # Alternatively, the attributes can be specified as a hash.
1980 # # <INPUT TYPE="reset">
1983 # # <INPUT TYPE="reset" VALUE="reset">
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 }
1991 value["TYPE"] = "reset"
1997 alias scrolling_list popup_menu
1999 # Generate a submit button Input element, as a String.
2001 # +value+ is the text to display on the button. +name+ is the name
2004 # Alternatively, the attributes can be specified as a hash.
2007 # # <INPUT TYPE="submit">
2010 # # <INPUT TYPE="submit" VALUE="ok">
2012 # submit("ok", "button1")
2013 # # <INPUT TYPE="submit" VALUE="ok" NAME="button1">
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 }
2021 value["TYPE"] = "submit"
2027 # Generate a text field Input element, as a String.
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.
2033 # Alternatively, the attributes can be specified as a hash.
2035 # text_field("name")
2036 # # <INPUT TYPE="text" NAME="name" SIZE="40">
2038 # text_field("name", "value")
2039 # # <INPUT TYPE="text" NAME="name" VALUE="value" SIZE="40">
2041 # text_field("name", "value", 80)
2042 # # <INPUT TYPE="text" NAME="name" VALUE="value" SIZE="80">
2044 # text_field("name", "value", 80, 200)
2045 # # <INPUT TYPE="text" NAME="name" VALUE="value" SIZE="80" MAXLENGTH="200">
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 }
2054 name["TYPE"] = "text"
2057 attributes["MAXLENGTH"] = maxlength.to_s if maxlength
2061 # Generate a TextArea element, as a String.
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.
2066 # Alternatively, the attributes can be specified as a hash.
2068 # The body is provided by the passed-in no-argument block
2071 # # = textarea("NAME" => "name", "COLS" => 70, "ROWS" => 10)
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 }
2083 super(attributes){ yield }
2092 # Mixin module for HTML version 3 generation methods.
2093 module Html3 # :nodoc:
2095 # The DOCTYPE declaration for this version of HTML
2097 %|<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">|
2100 # Initialise the HTML generation methods for this version.
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
2110 methods += <<-BEGIN + nn_element_def(element) + <<-END
2111 def #{element.downcase}(attributes = {})
2118 for element in %w[ IMG BASE BASEFONT BR AREA LINK PARAM HR INPUT
2120 methods += <<-BEGIN + nOE_element_def(element) + <<-END
2121 def #{element.downcase}(attributes = {})
2128 for element in %w[ HTML HEAD BODY P PLAINTEXT DT DD LI OPTION tr
2130 methods += <<-BEGIN + nO_element_def(element) + <<-END
2131 def #{element.downcase}(attributes = {})
2142 # Mixin module for HTML version 4 generation methods.
2143 module Html4 # :nodoc:
2145 # The DOCTYPE declaration for this version of HTML
2147 %|<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">|
2150 # Initialise the HTML generation methods for this version.
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 = {})
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 = {})
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 = {})
2191 # Mixin module for HTML version 4 transitional generation methods.
2192 module Html4Tr # :nodoc:
2194 # The DOCTYPE declaration for this version of HTML
2196 %|<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">|
2199 # Initialise the HTML generation methods for this version.
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 = {})
2218 for element in %w[ IMG BASE BASEFONT BR AREA LINK PARAM HR INPUT
2220 methods += <<-BEGIN + nOE_element_def(element) + <<-END
2221 def #{element.downcase}(attributes = {})
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 = {})
2242 # Mixin module for generating HTML version 4 with framesets.
2243 module Html4Fr # :nodoc:
2245 # The DOCTYPE declaration for this version of HTML
2247 %|<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">|
2250 # Initialise the HTML generation methods for this version.
2254 for element in %w[ FRAMESET ]
2255 methods += <<-BEGIN + nn_element_def(element) + <<-END
2256 def #{element.downcase}(attributes = {})
2263 for element in %w[ FRAME ]
2264 methods += <<-BEGIN + nOE_element_def(element) + <<-END
2265 def #{element.downcase}(attributes = {})
2276 # Creates a new CGI instance.
2278 # +type+ specifies which version of HTML to load the HTML generation
2279 # methods for. The following versions of HTML are supported:
2283 # html4Tr:: HTML 4.0 Transitional
2284 # html4Fr:: HTML 4.0 with Framesets
2286 # If not specified, no HTML generation methods will be loaded.
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
2299 (class << self; self; end).class_eval do
2300 const_set(:CGI_PARAMS, [1])
2301 const_set(:CGI_COOKIES, [2])
2304 extend QueryExtension
2307 initialize_query() # set @params, @cookies
2308 @output_cookies = nil
2309 @output_hidden = nil
2315 extend HtmlExtension
2319 extend HtmlExtension
2323 extend HtmlExtension
2329 extend HtmlExtension