lib/cgi.rb


DEFINITIONS

This source file includes following functions.


   1  =begin
   2  
   3  == NAME
   4  
   5  cgi.rb - cgi support library
   6  
   7  Copyright (C) 2000  Network Applied Communication Laboratory, Inc.
   8  
   9  Copyright (C) 2000  Information-technology Promotion Agency, Japan
  10  
  11  Wakou Aoyama <wakou@ruby-lang.org>
  12  
  13  
  14  
  15  == EXAMPLE
  16  
  17  === GET FORM VALUES
  18  
  19    require "cgi"
  20    cgi = CGI.new
  21    values = cgi['field_name']   # <== array of 'field_name'
  22      # if not 'field_name' included, then return [].
  23    fields = cgi.keys            # <== array of field names
  24  
  25    # returns true if form has 'field_name'
  26    cgi.has_key?('field_name')
  27    cgi.has_key?('field_name')
  28    cgi.include?('field_name')
  29  
  30  
  31  === GET FORM VALUES AS HASH
  32  
  33    require "cgi"
  34    cgi = CGI.new
  35    params = cgi.params
  36  
  37  cgi.params is a hash.
  38  
  39    cgi.params['new_field_name'] = ["value"]  # add new param
  40    cgi.params['field_name'] = ["new_value"]  # change value
  41    cgi.params.delete('field_name')           # delete param
  42    cgi.params.clear                          # delete all params
  43  
  44  
  45  === SAVE FORM VALUES TO FILE
  46  
  47    require "pstore"
  48    db = PStore.new("query.db")
  49    db.transaction do
  50      db["params"] = cgi.params
  51    end
  52  
  53  
  54  === RESTORE FORM VALUES FROM FILE
  55  
  56    require "pstore"
  57    db = PStore.new("query.db")
  58    db.transaction do
  59      cgi.params = db["params"]
  60    end
  61  
  62  
  63  === GET MULTIPART FORM VALUES
  64  
  65    require "cgi"
  66    cgi = CGI.new
  67    values = cgi['field_name']   # <== array of 'field_name'
  68    values[0].read               # <== body of values[0]
  69    values[0].local_path         # <== path to local file of values[0]
  70    values[0].original_filename  # <== original filename of values[0]
  71    values[0].content_type       # <== content_type of values[0]
  72  
  73  and values[0] has StringIO or Tempfile class methods.
  74  
  75  
  76  === GET COOKIE VALUES
  77  
  78    require "cgi"
  79    cgi = CGI.new
  80    values = cgi.cookies['name']  # <== array of 'name'
  81      # if not 'name' included, then return [].
  82    names = cgi.cookies.keys      # <== array of cookie names
  83  
  84  and cgi.cookies is a hash.
  85  
  86  
  87  === GET COOKIE OBJECTS
  88  
  89    require "cgi"
  90    cgi = CGI.new
  91    for name, cookie in cgi.cookies
  92      cookie.expires = Time.now + 30
  93    end
  94    cgi.out("cookie" => cgi.cookies){"string"}
  95  
  96    cgi.cookies # { "name1" => cookie1, "name2" => cookie2, ... }
  97  
  98    require "cgi"
  99    cgi = CGI.new
 100    cgi.cookies['name'].expires = Time.now + 30
 101    cgi.out("cookie" => cgi.cookies['name']){"string"}
 102  
 103  and see MAKE COOKIE OBJECT.
 104  
 105  
 106  === GET ENVIRONMENT VALUE
 107  
 108    require "cgi"
 109    cgi = CGI.new
 110    value = cgi.auth_type
 111      # ENV["AUTH_TYPE"]
 112  
 113  see http://www.w3.org/CGI/
 114  
 115  AUTH_TYPE CONTENT_LENGTH CONTENT_TYPE GATEWAY_INTERFACE PATH_INFO
 116  PATH_TRANSLATED QUERY_STRING REMOTE_ADDR REMOTE_HOST REMOTE_IDENT
 117  REMOTE_USER REQUEST_METHOD SCRIPT_NAME SERVER_NAME SERVER_PORT
 118  SERVER_PROTOCOL SERVER_SOFTWARE
 119  
 120  content_length and server_port return Integer. and the others return String.
 121  
 122  and HTTP_COOKIE, HTTP_COOKIE2
 123  
 124    value = cgi.raw_cookie
 125      # ENV["HTTP_COOKIE"]
 126    value = cgi.raw_cookie2
 127      # ENV["HTTP_COOKIE2"]
 128  
 129  and other HTTP_*
 130  
 131    value = cgi.accept
 132      # ENV["HTTP_ACCEPT"]
 133    value = cgi.accept_charset
 134      # ENV["HTTP_ACCEPT_CHARSET"]
 135  
 136  HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_ACCEPT_ENCODING HTTP_ACCEPT_LANGUAGE
 137  HTTP_CACHE_CONTROL HTTP_FROM HTTP_HOST HTTP_NEGOTIATE HTTP_PRAGMA
 138  HTTP_REFERER HTTP_USER_AGENT
 139  
 140  
 141  === PRINT HTTP HEADER AND HTML STRING TO $DEFAULT_OUTPUT ($>)
 142  
 143    require "cgi"
 144    cgi = CGI.new("html3")  # add HTML generation methods
 145    cgi.out() do
 146      cgi.html() do
 147        cgi.head{ cgi.title{"TITLE"} } +
 148        cgi.body() do
 149          cgi.form() do
 150            cgi.textarea("get_text") +
 151            cgi.br +
 152            cgi.submit
 153          end +
 154          cgi.pre() do
 155            CGI::escapeHTML(
 156              "params: " + cgi.params.inspect + "\n" +
 157              "cookies: " + cgi.cookies.inspect + "\n" +
 158              ENV.collect() do |key, value|
 159                key + " --> " + value + "\n"
 160              end.join("")
 161            )
 162          end
 163        end
 164      end
 165    end
 166  
 167    # add HTML generation methods
 168    CGI.new("html3")    # html3.2
 169    CGI.new("html4")    # html4.01 (Strict)
 170    CGI.new("html4Tr")  # html4.01 Transitional
 171    CGI.new("html4Fr")  # html4.01 Frameset
 172  
 173  
 174  =end
 175  
 176  raise "Please, use ruby1.5.4 or later." if RUBY_VERSION < "1.5.4"
 177  
 178  require 'English'
 179  
 180  class CGI
 181  
 182    CR  = "\015"
 183    LF  = "\012"
 184    EOL = CR + LF
 185    REVISION = '$Id: cgi.rb,v 1.42 2002/08/25 20:15:05 wakou Exp $'
 186  
 187    NEEDS_BINMODE = true if /WIN/ni.match(RUBY_PLATFORM)
 188    PATH_SEPARATOR = {'UNIX'=>'/', 'WINDOWS'=>'\\', 'MACINTOSH'=>':'}
 189  
 190    HTTP_STATUS = {
 191      "OK"                  => "200 OK",
 192      "PARTIAL_CONTENT"     => "206 Partial Content",
 193      "MULTIPLE_CHOICES"    => "300 Multiple Choices",
 194      "MOVED"               => "301 Moved Permanently",
 195      "REDIRECT"            => "302 Found",
 196      "NOT_MODIFIED"        => "304 Not Modified",
 197      "BAD_REQUEST"         => "400 Bad Request",
 198      "AUTH_REQUIRED"       => "401 Authorization Required",
 199      "FORBIDDEN"           => "403 Forbidden",
 200      "NOT_FOUND"           => "404 Not Found",
 201      "METHOD_NOT_ALLOWED"  => "405 Method Not Allowed",
 202      "NOT_ACCEPTABLE"      => "406 Not Acceptable",
 203      "LENGTH_REQUIRED"     => "411 Length Required",
 204      "PRECONDITION_FAILED" => "412 Rrecondition Failed",
 205      "SERVER_ERROR"        => "500 Internal Server Error",
 206      "NOT_IMPLEMENTED"     => "501 Method Not Implemented",
 207      "BAD_GATEWAY"         => "502 Bad Gateway",
 208      "VARIANT_ALSO_VARIES" => "506 Variant Also Negotiates"
 209    }
 210  
 211    RFC822_DAYS = %w[ Sun Mon Tue Wed Thu Fri Sat ]
 212    RFC822_MONTHS = %w[ Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec ]
 213  
 214    def env_table
 215      ENV
 216    end
 217  
 218    def stdinput
 219      $stdin
 220    end
 221  
 222    def stdoutput
 223      $DEFAULT_OUTPUT
 224    end
 225  
 226    private :env_table, :stdinput, :stdoutput
 227  
 228  =begin
 229  == METHODS
 230  =end
 231  
 232  =begin
 233  === ESCAPE URL ENCODE
 234    url_encoded_string = CGI::escape("string")
 235  =end
 236    def CGI::escape(string)
 237      string.gsub(/([^ a-zA-Z0-9_.-]+)/n) do
 238        '%' + $1.unpack('H2' * $1.size).join('%').upcase
 239      end.tr(' ', '+')
 240    end
 241  
 242  
 243  =begin
 244  === UNESCAPE URL ENCODED
 245    string = CGI::unescape("url encoded string")
 246  =end
 247    def CGI::unescape(string)
 248      string.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n) do
 249        [$1.delete('%')].pack('H*')
 250      end
 251    end
 252  
 253  
 254  =begin
 255  === ESCAPE HTML &\"<>
 256    CGI::escapeHTML("string")
 257  =end
 258    def CGI::escapeHTML(string)
 259      string.gsub(/&/n, '&amp;').gsub(/\"/n, '&quot;').gsub(/>/n, '&gt;').gsub(/</n, '&lt;')
 260    end
 261  
 262  
 263  =begin
 264  === UNESCAPE HTML
 265    CGI::unescapeHTML("HTML escaped string")
 266  =end
 267    def CGI::unescapeHTML(string)
 268      string.gsub(/&(.*?);/n) do
 269        match = $1.dup
 270        case match
 271        when /\Aamp\z/ni           then '&'
 272        when /\Aquot\z/ni          then '"'
 273        when /\Agt\z/ni            then '>'
 274        when /\Alt\z/ni            then '<'
 275        when /\A#0*(\d+)\z/n       then
 276          if Integer($1) < 256
 277            Integer($1).chr
 278          else
 279            if Integer($1) < 65536 and ($KCODE[0] == ?u or $KCODE[0] == ?U)
 280              [Integer($1)].pack("U")
 281            else
 282              "&##{$1};"
 283            end
 284          end
 285        when /\A#x([0-9a-f]+)\z/ni then
 286          if $1.hex < 256
 287            $1.hex.chr
 288          else
 289            if $1.hex < 65536 and ($KCODE[0] == ?u or $KCODE[0] == ?U)
 290              [$1.hex].pack("U")
 291            else
 292              "&#x#{$1};"
 293            end
 294          end
 295        else
 296          "&#{match};"
 297        end
 298      end
 299    end
 300  
 301  
 302  =begin
 303  === ESCAPE ELEMENT
 304    print CGI::escapeElement('<BR><A HREF="url"></A>', "A", "IMG")
 305      # "<BR>&lt;A HREF="url"&gt;&lt;/A&gt"
 306  
 307    print CGI::escapeElement('<BR><A HREF="url"></A>', ["A", "IMG"])
 308      # "<BR>&lt;A HREF="url"&gt;&lt;/A&gt"
 309  =end
 310    def CGI::escapeElement(string, *elements)
 311      elements = elements[0] if elements[0].kind_of?(Array)
 312      unless elements.empty?
 313        string.gsub(/<\/?(?:#{elements.join("|")})(?!\w)(?:.|\n)*?>/ni) do
 314          CGI::escapeHTML($&)
 315        end
 316      else
 317        string
 318      end
 319    end
 320  
 321  
 322  =begin
 323  === UNESCAPE ELEMENT
 324    print CGI::unescapeElement(
 325            CGI::escapeHTML('<BR><A HREF="url"></A>'), "A", "IMG")
 326      # "&lt;BR&gt;<A HREF="url"></A>"
 327  
 328    print CGI::unescapeElement(
 329            CGI::escapeHTML('<BR><A HREF="url"></A>'), ["A", "IMG"])
 330      # "&lt;BR&gt;<A HREF="url"></A>"
 331  =end
 332    def CGI::unescapeElement(string, *elements)
 333      elements = elements[0] if elements[0].kind_of?(Array)
 334      unless elements.empty?
 335        string.gsub(/&lt;\/?(?:#{elements.join("|")})(?!\w)(?:.|\n)*?&gt;/ni) do
 336          CGI::unescapeHTML($&)
 337        end
 338      else
 339        string
 340      end
 341    end
 342  
 343  
 344  =begin
 345  === MAKE RFC1123 DATE STRING
 346    CGI::rfc1123_date(Time.now)
 347      # Sat, 01 Jan 2000 00:00:00 GMT
 348  =end
 349    def CGI::rfc1123_date(time)
 350      t = time.clone.gmtime
 351      return format("%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT",
 352                  RFC822_DAYS[t.wday], t.day, RFC822_MONTHS[t.month-1], t.year,
 353                  t.hour, t.min, t.sec)
 354    end
 355  
 356  
 357  =begin
 358  === MAKE HTTP HEADER STRING
 359    header
 360      # Content-Type: text/html
 361  
 362    header("text/plain")
 363      # Content-Type: text/plain
 364  
 365    header({"nph"        => true,
 366            "status"     => "OK",  # == "200 OK"
 367              # "status"     => "200 GOOD",
 368            "server"     => ENV['SERVER_SOFTWARE'],
 369            "connection" => "close",
 370            "type"       => "text/html",
 371            "charset"    => "iso-2022-jp",
 372              # Content-Type: text/html; charset=iso-2022-jp
 373            "language"   => "ja",
 374            "expires"    => Time.now + 30,
 375            "cookie"     => [cookie1, cookie2],
 376            "my_header1" => "my_value"
 377            "my_header2" => "my_value"})
 378  
 379  header will not convert charset.
 380  
 381  status:
 382  
 383    "OK"                  --> "200 OK"
 384    "PARTIAL_CONTENT"     --> "206 Partial Content"
 385    "MULTIPLE_CHOICES"    --> "300 Multiple Choices"
 386    "MOVED"               --> "301 Moved Permanently"
 387    "REDIRECT"            --> "302 Found"
 388    "NOT_MODIFIED"        --> "304 Not Modified"
 389    "BAD_REQUEST"         --> "400 Bad Request"
 390    "AUTH_REQUIRED"       --> "401 Authorization Required"
 391    "FORBIDDEN"           --> "403 Forbidden"
 392    "NOT_FOUND"           --> "404 Not Found"
 393    "METHOD_NOT_ALLOWED"  --> "405 Method Not Allowed"
 394    "NOT_ACCEPTABLE"      --> "406 Not Acceptable"
 395    "LENGTH_REQUIRED"     --> "411 Length Required"
 396    "PRECONDITION_FAILED" --> "412 Rrecondition Failed"
 397    "SERVER_ERROR"        --> "500 Internal Server Error"
 398    "NOT_IMPLEMENTED"     --> "501 Method Not Implemented"
 399    "BAD_GATEWAY"         --> "502 Bad Gateway"
 400    "VARIANT_ALSO_VARIES" --> "506 Variant Also Negotiates"
 401  
 402  =end
 403    def header(options = "text/html")
 404  
 405      buf = ""
 406  
 407      case options
 408      when String
 409        options = { "type" => options }
 410      when Hash
 411        options = options.dup
 412      end
 413  
 414      unless options.has_key?("type")
 415        options["type"] = "text/html"
 416      end
 417  
 418      if options.has_key?("charset")
 419        options["type"] += "; charset=" + options.delete("charset")
 420      end
 421  
 422      options.delete("nph") if defined?(MOD_RUBY)
 423      if options.delete("nph") or /IIS/n.match(env_table['SERVER_SOFTWARE'])
 424        buf += (env_table["SERVER_PROTOCOL"] or "HTTP/1.0")  + " " +
 425               (HTTP_STATUS[options["status"]] or options["status"] or "200 OK") +
 426               EOL +
 427               "Date: " + CGI::rfc1123_date(Time.now) + EOL
 428  
 429        unless options.has_key?("server")
 430          options["server"] = (env_table['SERVER_SOFTWARE'] or "")
 431        end
 432  
 433        unless options.has_key?("connection")
 434          options["connection"] = "close"
 435        end
 436  
 437        options.delete("status")
 438      end
 439  
 440      if options.has_key?("status")
 441        buf += "Status: " +
 442               (HTTP_STATUS[options["status"]] or options["status"]) + EOL
 443        options.delete("status")
 444      end
 445  
 446      if options.has_key?("server")
 447        buf += "Server: " + options.delete("server") + EOL
 448      end
 449  
 450      if options.has_key?("connection")
 451        buf += "Connection: " + options.delete("connection") + EOL
 452      end
 453  
 454      buf += "Content-Type: " + options.delete("type") + EOL
 455  
 456      if options.has_key?("length")
 457        buf += "Content-Length: " + options.delete("length").to_s + EOL
 458      end
 459  
 460      if options.has_key?("language")
 461        buf += "Content-Language: " + options.delete("language") + EOL
 462      end
 463  
 464      if options.has_key?("expires")
 465        buf += "Expires: " + CGI::rfc1123_date( options.delete("expires") ) + EOL
 466      end
 467  
 468      if options.has_key?("cookie")
 469        if options["cookie"].kind_of?(String) or
 470             options["cookie"].kind_of?(Cookie)
 471          buf += "Set-Cookie: " + options.delete("cookie").to_s + EOL
 472        elsif options["cookie"].kind_of?(Array)
 473          options.delete("cookie").each{|cookie|
 474            buf += "Set-Cookie: " + cookie.to_s + EOL
 475          }
 476        elsif options["cookie"].kind_of?(Hash)
 477          options.delete("cookie").each_value{|cookie|
 478            buf += "Set-Cookie: " + cookie.to_s + EOL
 479          }
 480        end
 481      end
 482      if @output_cookies
 483        for cookie in @output_cookies
 484          buf += "Set-Cookie: " + cookie.to_s + EOL
 485        end
 486      end
 487  
 488      options.each{|key, value|
 489        buf += key + ": " + value.to_s + EOL
 490      }
 491  
 492      if defined?(MOD_RUBY)
 493        table = Apache::request.headers_out
 494        buf.scan(/([^:]+): (.+)#{EOL}/n){ |name, value|
 495          $stderr.printf("name:%s value:%s\n", name, value) if $DEBUG
 496          case name
 497          when 'Set-Cookie'
 498            table.add($1, $2)
 499          when /^status$/ni
 500            Apache::request.status_line = value
 501            Apache::request.status = value.to_i
 502          when /^content-type$/ni
 503            Apache::request.content_type = value
 504          when /^content-encoding$/ni
 505            Apache::request.content_encoding = value
 506          when /^location$/ni
 507            if Apache::request.status == 200
 508              Apache::request.status = 302
 509            end
 510            Apache::request.headers_out[name] = value
 511          else
 512            Apache::request.headers_out[name] = value
 513          end
 514        }
 515        Apache::request.send_http_header
 516        ''
 517      else
 518        buf + EOL
 519      end
 520  
 521    end # header()
 522  
 523  
 524  =begin
 525  === PRINT HTTP HEADER AND STRING TO $DEFAULT_OUTPUT ($>)
 526    cgi = CGI.new
 527    cgi.out{ "string" }
 528      # Content-Type: text/html
 529      # Content-Length: 6
 530      #
 531      # string
 532  
 533    cgi.out("text/plain"){ "string" }
 534      # Content-Type: text/plain
 535      # Content-Length: 6
 536      #
 537      # string
 538  
 539    cgi.out({"nph"        => true,
 540             "status"     => "OK",  # == "200 OK"
 541             "server"     => ENV['SERVER_SOFTWARE'],
 542             "connection" => "close",
 543             "type"       => "text/html",
 544             "charset"    => "iso-2022-jp",
 545               # Content-Type: text/html; charset=iso-2022-jp
 546             "language"   => "ja",
 547             "expires"    => Time.now + (3600 * 24 * 30),
 548             "cookie"     => [cookie1, cookie2],
 549             "my_header1" => "my_value",
 550             "my_header2" => "my_value"}){ "string" }
 551  
 552  if "HEAD" == REQUEST_METHOD then output only HTTP header.
 553  
 554  if charset is "iso-2022-jp" or "euc-jp" or "shift_jis" then
 555  convert string charset, and set language to "ja".
 556  
 557  =end
 558    def out(options = "text/html")
 559  
 560      options = { "type" => options } if options.kind_of?(String)
 561      content = yield
 562  
 563      if options.has_key?("charset")
 564        require "nkf"
 565        case options["charset"]
 566        when /iso-2022-jp/ni
 567          content = NKF::nkf('-j', content)
 568          options["language"] = "ja" unless options.has_key?("language")
 569        when /euc-jp/ni
 570          content = NKF::nkf('-e', content)
 571          options["language"] = "ja" unless options.has_key?("language")
 572        when /shift_jis/ni
 573          content = NKF::nkf('-s', content)
 574          options["language"] = "ja" unless options.has_key?("language")
 575        end
 576      end
 577  
 578      options["length"] = content.length.to_s
 579      output = stdoutput
 580      output.binmode if defined? output.binmode
 581      output.print header(options)
 582      output.print content unless "HEAD" == env_table['REQUEST_METHOD']
 583    end
 584  
 585  
 586  =begin
 587  === PRINT
 588    cgi = CGI.new
 589    cgi.print    # default:  cgi.print == $DEFAULT_OUTPUT.print
 590  =end
 591    def print(*options)
 592      stdoutput.print(*options)
 593    end
 594  
 595  
 596  =begin
 597  === MAKE COOKIE OBJECT
 598    cookie1 = CGI::Cookie::new("name", "value1", "value2", ...)
 599    cookie1 = CGI::Cookie::new({"name" => "name", "value" => "value"})
 600    cookie1 = CGI::Cookie::new({'name'    => 'name',
 601                                'value'   => ['value1', 'value2', ...],
 602                                'path'    => 'path',   # optional
 603                                'domain'  => 'domain', # optional
 604                                'expires' => Time.now, # optional
 605                                'secure'  => true      # optional
 606                               })
 607  
 608    cgi.out({"cookie" => [cookie1, cookie2]}){ "string" }
 609  
 610    name    = cookie1.name
 611    values  = cookie1.value
 612    path    = cookie1.path
 613    domain  = cookie1.domain
 614    expires = cookie1.expires
 615    secure  = cookie1.secure
 616  
 617    cookie1.name    = 'name'
 618    cookie1.value   = ['value1', 'value2', ...]
 619    cookie1.path    = 'path'
 620    cookie1.domain  = 'domain'
 621    cookie1.expires = Time.now + 30
 622    cookie1.secure  = true
 623  =end
 624    require "delegate"
 625    class Cookie < SimpleDelegator
 626  
 627      def initialize(name = "", *value)
 628        options = if name.kind_of?(String)
 629                    { "name" => name, "value" => value }
 630                  else
 631                    name
 632                  end
 633        unless options.has_key?("name")
 634          raise ArgumentError, "`name' required"
 635        end
 636  
 637        @name = options["name"]
 638        @value = Array(options["value"])
 639        # simple support for IE
 640        if options["path"]
 641          @path = options["path"]
 642        else
 643          %r|^(.*/)|.match(ENV["SCRIPT_NAME"])
 644          @path = ($1 or "")
 645        end
 646        @domain = options["domain"]
 647        @expires = options["expires"]
 648        @secure = options["secure"] == true ? true : false
 649  
 650        super(@value)
 651      end
 652  
 653      attr_accessor("name", "value", "path", "domain", "expires")
 654      attr_reader("secure")
 655      def secure=(val)
 656        @secure = val if val == true or val == false
 657        @secure
 658      end
 659  
 660      def to_s
 661        buf = ""
 662        buf += @name + '='
 663  
 664        if @value.kind_of?(String)
 665          buf += CGI::escape(@value)
 666        else
 667          buf += @value.collect{|v| CGI::escape(v) }.join("&")
 668        end
 669  
 670        if @domain
 671          buf += '; domain=' + @domain
 672        end
 673  
 674        if @path
 675          buf += '; path=' + @path
 676        end
 677  
 678        if @expires
 679          buf += '; expires=' + CGI::rfc1123_date(@expires)
 680        end
 681  
 682        if @secure == true
 683          buf += '; secure'
 684        end
 685  
 686        buf
 687      end
 688  
 689    end # class Cookie
 690  
 691  
 692  =begin
 693  === PARSE RAW COOKIE STRING
 694    cookies = CGI::Cookie::parse("raw_cookie_string")
 695      # { "name1" => cookie1, "name2" => cookie2, ... }
 696  =end
 697    def Cookie::parse(raw_cookie)
 698      cookies = Hash.new([])
 699      return cookies unless raw_cookie
 700  
 701      raw_cookie.split('; ').each do |pairs|
 702        name, values = pairs.split('=',2)
 703        name = CGI::unescape(name)
 704        values ||= ""
 705        values = values.split('&').collect{|v| CGI::unescape(v) }
 706        unless cookies.has_key?(name)
 707          cookies[name] = Cookie::new({ "name" => name, "value" => values })
 708        end
 709      end
 710  
 711      cookies
 712    end
 713  
 714  
 715  =begin
 716  === PARSE QUERY STRING
 717    params = CGI::parse("query_string")
 718      # {"name1" => ["value1", "value2", ...],
 719      #  "name2" => ["value1", "value2", ...], ... }
 720  =end
 721    def CGI::parse(query)
 722      params = Hash.new([])
 723  
 724      query.split(/[&;]/n).each do |pairs|
 725        key, value = pairs.split('=',2).collect{|v| CGI::unescape(v) }
 726        if params.has_key?(key)
 727          params[key].push(value)
 728        else
 729          params[key] = [value]
 730        end
 731      end
 732  
 733      params
 734    end
 735  
 736  
 737    module QueryExtension
 738  
 739      for env in %w[ CONTENT_LENGTH SERVER_PORT ]
 740        eval( <<-END )
 741          def #{env.sub(/^HTTP_/n, '').downcase}
 742            env_table["#{env}"] && Integer(env_table["#{env}"])
 743          end
 744        END
 745      end
 746  
 747      for env in %w[ AUTH_TYPE CONTENT_TYPE GATEWAY_INTERFACE PATH_INFO
 748          PATH_TRANSLATED QUERY_STRING REMOTE_ADDR REMOTE_HOST
 749          REMOTE_IDENT REMOTE_USER REQUEST_METHOD SCRIPT_NAME
 750          SERVER_NAME SERVER_PROTOCOL SERVER_SOFTWARE
 751  
 752          HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_ACCEPT_ENCODING
 753          HTTP_ACCEPT_LANGUAGE HTTP_CACHE_CONTROL HTTP_FROM HTTP_HOST
 754          HTTP_NEGOTIATE HTTP_PRAGMA HTTP_REFERER HTTP_USER_AGENT ]
 755        eval( <<-END )
 756          def #{env.sub(/^HTTP_/n, '').downcase}
 757            env_table["#{env}"]
 758          end
 759        END
 760      end
 761  
 762      def raw_cookie
 763        env_table["HTTP_COOKIE"]
 764      end
 765  
 766      def raw_cookie2
 767        env_table["HTTP_COOKIE2"]
 768      end
 769  
 770      attr_accessor("cookies")
 771      attr("params")
 772      def params=(hash)
 773        @params.clear
 774        @params.update(hash)
 775      end
 776  
 777      def read_multipart(boundary, content_length)
 778        params = Hash.new([])
 779        boundary = "--" + boundary
 780        buf = ""
 781        bufsize = 10 * 1024
 782  
 783        # start multipart/form-data
 784        stdinput.binmode
 785        boundary_size = boundary.size + EOL.size
 786        content_length -= boundary_size
 787        status = stdinput.read(boundary_size)
 788        if nil == status
 789          raise EOFError, "no content body"
 790        elsif boundary + EOL != status
 791          raise EOFError, "bad content body"
 792        end
 793  
 794        until -1 == content_length
 795          head = nil
 796          if 10240 < content_length
 797            require "tempfile"
 798            body = Tempfile.new("CGI")
 799          else
 800            begin
 801              require "stringio" if not defined? StringIO
 802              body = StringIO.new
 803            rescue LoadError
 804              require "tempfile"
 805              body = Tempfile.new("CGI")
 806            end
 807          end
 808          body.binmode
 809  
 810          until head and /#{boundary}(?:#{EOL}|--)/n.match(buf)
 811  
 812            if (not head) and /#{EOL}#{EOL}/n.match(buf)
 813              buf = buf.sub(/\A((?:.|\n)*?#{EOL})#{EOL}/n) do
 814                head = $1.dup
 815                ""
 816              end
 817              next
 818            end
 819  
 820            if head and ( (EOL + boundary + EOL).size < buf.size )
 821              body.print buf[0 ... (buf.size - (EOL + boundary + EOL).size)]
 822              buf[0 ... (buf.size - (EOL + boundary + EOL).size)] = ""
 823            end
 824  
 825            c = if bufsize < content_length
 826                  stdinput.read(bufsize) or ''
 827                else
 828                  stdinput.read(content_length) or ''
 829                end
 830            buf += c
 831            content_length -= c.size
 832  
 833          end
 834  
 835          buf = buf.sub(/\A((?:.|\n)*?)(?:#{EOL})?#{boundary}(#{EOL}|--)/n) do
 836            body.print $1
 837            if "--" == $2
 838              content_length = -1
 839            end
 840            ""
 841          end
 842  
 843          body.rewind
 844  
 845          eval <<-END
 846            def body.local_path
 847              #{body.path.dump}
 848            end
 849          END
 850  
 851          /Content-Disposition:.* filename="?([^\";]*)"?/ni.match(head)
 852          eval <<-END
 853            def body.original_filename
 854              #{
 855                filename = ($1 or "").dup
 856                if /Mac/ni.match(env_table['HTTP_USER_AGENT']) and
 857                   /Mozilla/ni.match(env_table['HTTP_USER_AGENT']) and
 858                   (not /MSIE/ni.match(env_table['HTTP_USER_AGENT']))
 859                  CGI::unescape(filename)
 860                else
 861                  filename
 862                end.dump.untaint
 863              }.taint
 864            end
 865          END
 866  
 867          /Content-Type: (.*)/ni.match(head)
 868          eval <<-END
 869            def body.content_type
 870              #{($1 or "").dump.untaint}.taint
 871            end
 872          END
 873  
 874          /Content-Disposition:.* name="?([^\";]*)"?/ni.match(head)
 875          name = $1.dup
 876  
 877          if params.has_key?(name)
 878            params[name].push(body)
 879          else
 880            params[name] = [body]
 881          end
 882  
 883        end
 884  
 885        params
 886      end # read_multipart
 887      private :read_multipart
 888  
 889      # offline mode. read name=value pairs on standard input.
 890      def read_from_cmdline
 891        require "shellwords"
 892  
 893        string = unless ARGV.empty?
 894          ARGV.join(' ')
 895        else
 896          if STDIN.tty?
 897            STDERR.print(
 898              %|(offline mode: enter name=value pairs on standard input)\n|
 899            )
 900          end
 901          readlines.join(' ').gsub(/\n/n, '')
 902        end.gsub(/\\=/n, '%3D').gsub(/\\&/n, '%26')
 903  
 904        words = Shellwords.shellwords(string)
 905  
 906        if words.find{|x| /=/n.match(x) }
 907          words.join('&')
 908        else
 909          words.join('+')
 910        end
 911      end
 912      private :read_from_cmdline
 913  
 914      def initialize_query()
 915        if ("POST" == env_table['REQUEST_METHOD']) and
 916           %r|\Amultipart/form-data.*boundary=\"?([^\";,]+)\"?|n.match(env_table['CONTENT_TYPE'])
 917          boundary = $1.dup
 918          @params = read_multipart(boundary, Integer(env_table['CONTENT_LENGTH']))
 919        else
 920          @params = CGI::parse(
 921                      case env_table['REQUEST_METHOD']
 922                      when "GET", "HEAD"
 923                        if defined?(MOD_RUBY)
 924                          Apache::request.args or ""
 925                        else
 926                          env_table['QUERY_STRING'] or ""
 927                        end
 928                      when "POST"
 929                        stdinput.binmode
 930                        stdinput.read(Integer(env_table['CONTENT_LENGTH'])) or ''
 931                      else
 932                        read_from_cmdline
 933                      end
 934                    )
 935        end
 936  
 937        @cookies = CGI::Cookie::parse((env_table['HTTP_COOKIE'] or env_table['COOKIE']))
 938  
 939      end
 940      private :initialize_query
 941  
 942      def [](*args)
 943        @params[*args]
 944      end
 945  
 946      def keys(*args)
 947        @params.keys(*args)
 948      end
 949  
 950      def has_key?(*args)
 951        @params.has_key?(*args)
 952      end
 953      alias key? has_key?
 954      alias include? has_key?
 955  
 956    end # QueryExtension
 957  
 958  
 959  =begin
 960  === HTML PRETTY FORMAT
 961    print CGI::pretty("<HTML><BODY></BODY></HTML>")
 962      # <HTML>
 963      #   <BODY>
 964      #   </BODY>
 965      # </HTML>
 966  
 967    print CGI::pretty("<HTML><BODY></BODY></HTML>", "\t")
 968      # <HTML>
 969      #         <BODY>
 970      #         </BODY>
 971      # </HTML>
 972  =end
 973    def CGI::pretty(string, shift = "  ")
 974      lines = string.gsub(/(?!\A)<(?:.|\n)*?>/n, "\n\\0").gsub(/<(?:.|\n)*?>(?!\n)/n, "\\0\n")
 975      end_pos = 0
 976      while end_pos = lines.index(/^<\/(\w+)/n, end_pos)
 977        element = $1.dup
 978        start_pos = lines.rindex(/^\s*<#{element}/ni, end_pos)
 979        lines[start_pos ... end_pos] = "__" + lines[start_pos ... end_pos].gsub(/\n(?!\z)/n, "\n" + shift) + "__"
 980      end
 981      lines.gsub(/^((?:#{Regexp::quote(shift)})*)__(?=<\/?\w)/n, '\1')
 982    end
 983  
 984  
 985  =begin
 986  == HTML ELEMENTS
 987  
 988    cgi = CGI.new("html3")  # add HTML generation methods
 989    cgi.element
 990    cgi.element{ "string" }
 991    cgi.element({ "ATTRIBUTE1" => "value1", "ATTRIBUTE2" => "value2" })
 992    cgi.element({ "ATTRIBUTE1" => "value1", "ATTRIBUTE2" => "value2" }){ "string" }
 993  
 994    # add HTML generation methods
 995    CGI.new("html3")    # html3.2
 996    CGI.new("html4")    # html4.0 (Strict)
 997    CGI.new("html4Tr")  # html4.0 Transitional
 998    CGI.new("html4Fr")  # html4.0 Frameset
 999  
1000  =end
1001  
1002  
1003    module TagMaker
1004  
1005      # - -
1006      def nn_element_def(element)
1007        <<-END.gsub(/element\.downcase/n, element.downcase).gsub(/element\.upcase/n, element.upcase)
1008            "<element.upcase" + attributes.collect{|name, value|
1009              next unless value
1010              " " + CGI::escapeHTML(name) +
1011              if true == value
1012                ""
1013              else
1014                '="' + CGI::escapeHTML(value) + '"'
1015              end
1016            }.to_s + ">" +
1017            if block_given?
1018              yield.to_s
1019            else
1020              ""
1021            end +
1022            "</element.upcase>"
1023        END
1024      end
1025  
1026      # - O EMPTY
1027      def nOE_element_def(element)
1028        <<-END.gsub(/element\.downcase/n, element.downcase).gsub(/element\.upcase/n, element.upcase)
1029            "<element.upcase" + attributes.collect{|name, value|
1030              next unless value
1031              " " + CGI::escapeHTML(name) +
1032              if true == value
1033                ""
1034              else
1035                '="' + CGI::escapeHTML(value) + '"'
1036              end
1037            }.to_s + ">"
1038        END
1039      end
1040  
1041      # O O or - O
1042      def nO_element_def(element)
1043        <<-END.gsub(/element\.downcase/n, element.downcase).gsub(/element\.upcase/n, element.upcase)
1044            "<element.upcase" + attributes.collect{|name, value|
1045              next unless value
1046              " " + CGI::escapeHTML(name) +
1047              if true == value
1048                ""
1049              else
1050                '="' + CGI::escapeHTML(value) + '"'
1051              end
1052            }.to_s + ">" +
1053            if block_given?
1054              yield.to_s + "</element.upcase>"
1055            else
1056              ""
1057            end
1058        END
1059      end
1060  
1061    end # TagMaker
1062  
1063  
1064    module HtmlExtension
1065  
1066  
1067  =begin
1068  === A ELEMENT
1069    a("url")
1070      # = a({ "HREF" => "url" })
1071  =end
1072      def a(href = "")
1073        attributes = if href.kind_of?(String)
1074                       { "HREF" => href }
1075                     else
1076                       href
1077                     end
1078        if block_given?
1079          super(attributes){ yield }
1080        else
1081          super(attributes)
1082        end
1083      end
1084  
1085  
1086  =begin
1087  === BASE ELEMENT
1088    base("url")
1089      # = base({ "HREF" => "url" })
1090  =end
1091      def base(href = "")
1092        attributes = if href.kind_of?(String)
1093                       { "HREF" => href }
1094                     else
1095                       href
1096                     end
1097        if block_given?
1098          super(attributes){ yield }
1099        else
1100          super(attributes)
1101        end
1102      end
1103  
1104  
1105  =begin
1106  === BLOCKQUOTE ELEMENT
1107    blockquote("url"){ "string" }
1108      # = blockquote({ "CITE" => "url" }){ "string" }
1109  =end
1110      def blockquote(cite = nil)
1111        attributes = if cite.kind_of?(String)
1112                       { "CITE" => cite }
1113                     else
1114                       cite or ""
1115                     end
1116        if block_given?
1117          super(attributes){ yield }
1118        else
1119          super(attributes)
1120        end
1121      end
1122  
1123  
1124  =begin
1125  === CAPTION ELEMENT
1126    caption("align"){ "string" }
1127      # = caption({ "ALIGN" => "align" }){ "string" }
1128  =end
1129      def caption(align = nil)
1130        attributes = if align.kind_of?(String)
1131                       { "ALIGN" => align }
1132                     else
1133                       align or ""
1134                     end
1135        if block_given?
1136          super(attributes){ yield }
1137        else
1138          super(attributes)
1139        end
1140      end
1141  
1142  
1143  =begin
1144  === CHECKBOX
1145    checkbox("name")
1146      # = checkbox({ "NAME" => "name" })
1147  
1148    checkbox("name", "value")
1149      # = checkbox({ "NAME" => "name", "VALUE" => "value" })
1150  
1151    checkbox("name", "value", true)
1152      # = checkbox({ "NAME" => "name", "VALUE" => "value", "CHECKED" => true })
1153  =end
1154      def checkbox(name = "", value = nil, checked = nil)
1155        attributes = if name.kind_of?(String)
1156                       { "TYPE" => "checkbox", "NAME" => name,
1157                         "VALUE" => value, "CHECKED" => checked }
1158                     else
1159                       name["TYPE"] = "checkbox"
1160                       name
1161                     end
1162        input(attributes)
1163      end
1164  
1165  
1166  =begin
1167  === CHECKBOX_GROUP
1168    checkbox_group("name", "foo", "bar", "baz")
1169      # <INPUT TYPE="checkbox" NAME="name" VALUE="foo">foo
1170      # <INPUT TYPE="checkbox" NAME="name" VALUE="bar">bar
1171      # <INPUT TYPE="checkbox" NAME="name" VALUE="baz">baz
1172  
1173    checkbox_group("name", ["foo"], ["bar", true], "baz")
1174      # <INPUT TYPE="checkbox" NAME="name" VALUE="foo">foo
1175      # <INPUT TYPE="checkbox" CHECKED NAME="name" VALUE="bar">bar
1176      # <INPUT TYPE="checkbox" NAME="name" VALUE="baz">baz
1177  
1178    checkbox_group("name", ["1", "Foo"], ["2", "Bar", true], "Baz")
1179      # <INPUT TYPE="checkbox" NAME="name" VALUE="1">Foo
1180      # <INPUT TYPE="checkbox" CHECKED NAME="name" VALUE="2">Bar
1181      # <INPUT TYPE="checkbox" NAME="name" VALUE="Baz">Baz
1182  
1183    checkbox_group({ "NAME" => "name",
1184                     "VALUES" => ["foo", "bar", "baz"] })
1185  
1186    checkbox_group({ "NAME" => "name",
1187                     "VALUES" => [["foo"], ["bar", true], "baz"] })
1188  
1189    checkbox_group({ "NAME" => "name",
1190                     "VALUES" => [["1", "Foo"], ["2", "Bar", true], "Baz"] })
1191  =end
1192      def checkbox_group(name = "", *values)
1193        if name.kind_of?(Hash)
1194          values = name["VALUES"]
1195          name = name["NAME"]
1196        end
1197        values.collect{|value|
1198          if value.kind_of?(String)
1199            checkbox(name, value) + value
1200          else
1201            if value[value.size - 1] == true
1202              checkbox(name, value[0], true) +
1203              value[value.size - 2]
1204            else
1205              checkbox(name, value[0]) +
1206              value[value.size - 1]
1207            end
1208          end
1209        }.to_s
1210      end
1211  
1212  
1213  =begin
1214  === FILE_FIELD
1215    file_field("name")
1216      # <INPUT TYPE="file" NAME="name" SIZE="20">
1217  
1218    file_field("name", 40)
1219      # <INPUT TYPE="file" NAME="name" SIZE="40">
1220  
1221    file_field("name", 40, 100)
1222      # <INPUT TYPE="file" NAME="name" SIZE="40" MAXLENGTH="100">
1223  
1224    file_field({ "NAME" => "name", "SIZE" => 40 })
1225      # <INPUT TYPE="file" NAME="name" SIZE="40">
1226  =end
1227      def file_field(name = "", size = 20, maxlength = nil)
1228        attributes = if name.kind_of?(String)
1229                       { "TYPE" => "file", "NAME" => name,
1230                         "SIZE" => size.to_s }
1231                     else
1232                       name["TYPE"] = "file"
1233                       name
1234                     end
1235        attributes["MAXLENGTH"] = maxlength.to_s if maxlength
1236        input(attributes)
1237      end
1238  
1239  
1240  =begin
1241  === FORM ELEMENT
1242    form{ "string" }
1243      # <FORM METHOD="post" ENCTYPE="application/x-www-form-urlencoded">string</FORM>
1244  
1245    form("get"){ "string" }
1246      # <FORM METHOD="get" ENCTYPE="application/x-www-form-urlencoded">string</FORM>
1247  
1248    form("get", "url"){ "string" }
1249      # <FORM METHOD="get" ACTION="url" ENCTYPE="application/x-www-form-urlencoded">string</FORM>
1250  
1251    form({"METHOD" => "post", "ENCTYPE" => "enctype"}){ "string" }
1252      # <FORM METHOD="post" ENCTYPE="enctype">string</FORM>
1253  
1254  The hash keys are case sensitive. Ask the samples.
1255  =end
1256      def form(method = "post", action = script_name, enctype = "application/x-www-form-urlencoded")
1257        attributes = if method.kind_of?(String)
1258                       { "METHOD" => method, "ACTION" => action,
1259                         "ENCTYPE" => enctype } 
1260                     else
1261                       unless method.has_key?("METHOD")
1262                         method["METHOD"] = "post"
1263                       end
1264                       unless method.has_key?("ENCTYPE")
1265                         method["ENCTYPE"] = enctype
1266                       end
1267                       method
1268                     end
1269        if block_given?
1270          body = yield
1271        else
1272          body = ""
1273        end
1274        if @output_hidden
1275          hidden = @output_hidden.collect{|k,v|
1276            "<INPUT TYPE=HIDDEN NAME=\"#{k}\" VALUE=\"#{v}\">"
1277          }.to_s
1278          if defined? fieldset
1279            body += fieldset{ hidden }
1280          else
1281            body += hidden
1282          end
1283        end
1284        super(attributes){body}
1285      end
1286  
1287  =begin
1288  === HIDDEN FIELD
1289    hidden("name")
1290      # <INPUT TYPE="hidden" NAME="name">
1291  
1292    hidden("name", "value")
1293      # <INPUT TYPE="hidden" NAME="name" VALUE="value">
1294  
1295    hidden({ "NAME" => "name", "VALUE" => "reset", "ID" => "foo" })
1296      # <INPUT TYPE="hidden" NAME="name" VALUE="value" ID="foo">
1297  =end
1298      def hidden(name = "", value = nil)
1299        attributes = if name.kind_of?(String)
1300                       { "TYPE" => "hidden", "NAME" => name, "VALUE" => value }
1301                     else
1302                       name["TYPE"] = "hidden"
1303                       name
1304                     end
1305        input(attributes)
1306      end
1307  
1308  
1309  =begin
1310  === HTML ELEMENT
1311  
1312    html{ "string" }
1313      # <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><HTML>string</HTML>
1314  
1315    html({ "LANG" => "ja" }){ "string" }
1316      # <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><HTML LANG="ja">string</HTML>
1317  
1318    html({ "DOCTYPE" => false }){ "string" }
1319      # <HTML>string</HTML>
1320  
1321    html({ "DOCTYPE" => '<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">' }){ "string" }
1322      # <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"><HTML>string</HTML>
1323  
1324    html({ "PRETTY" => "  " }){ "<BODY></BODY>" }
1325      # <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
1326      # <HTML>
1327      #   <BODY>
1328      #   </BODY>
1329      # </HTML>
1330  
1331    html({ "PRETTY" => "\t" }){ "<BODY></BODY>" }
1332      # <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
1333      # <HTML>
1334      #         <BODY>
1335      #         </BODY>
1336      # </HTML>
1337  
1338    html("PRETTY"){ "<BODY></BODY>" }
1339      # = html({ "PRETTY" => "  " }){ "<BODY></BODY>" }
1340  
1341    html(if $VERBOSE then "PRETTY" end){ "HTML string" }
1342  
1343  =end
1344      def html(attributes = {})
1345        if nil == attributes
1346          attributes = {}
1347        elsif "PRETTY" == attributes
1348          attributes = { "PRETTY" => true }
1349        end
1350        pretty = attributes.delete("PRETTY")
1351        pretty = "  " if true == pretty
1352        buf = ""
1353  
1354        if attributes.has_key?("DOCTYPE")
1355          if attributes["DOCTYPE"]
1356            buf += attributes.delete("DOCTYPE")
1357          else
1358            attributes.delete("DOCTYPE")
1359          end
1360        else
1361          buf += doctype
1362        end
1363  
1364        if block_given?
1365          buf += super(attributes){ yield }
1366        else
1367          buf += super(attributes)
1368        end
1369  
1370        if pretty
1371          CGI::pretty(buf, pretty)
1372        else
1373          buf
1374        end
1375  
1376      end
1377  
1378  
1379  =begin
1380  === IMAGE_BUTTON
1381    image_button("url")
1382      # <INPUT TYPE="image" SRC="url">
1383  
1384    image_button("url", "name", "string")
1385      # <INPUT TYPE="image" SRC="url" NAME="name" ALT="string">
1386  
1387    image_button({ "SRC" => "url", "ATL" => "strng" })
1388      # <INPUT TYPE="image" SRC="url" ALT="string">
1389  =end
1390      def image_button(src = "", name = nil, alt = nil)
1391        attributes = if src.kind_of?(String)
1392                       { "TYPE" => "image", "SRC" => src, "NAME" => name,
1393                         "ALT" => alt }
1394                     else
1395                       src["TYPE"] = "image"
1396                       src["SRC"] ||= ""
1397                       src
1398                     end
1399        input(attributes)
1400      end
1401  
1402  
1403  =begin
1404  === IMG ELEMENT
1405    img("src", "alt", 100, 50)
1406      # <IMG SRC="src" ALT="alt" WIDTH="100" HEIGHT="50">
1407  
1408    img({ "SRC" => "src", "ALT" => "alt", "WIDTH" => 100, "HEIGHT" => 50 })
1409      # <IMG SRC="src" ALT="alt" WIDTH="100" HEIGHT="50">
1410  =end
1411      def img(src = "", alt = "", width = nil, height = nil)
1412        attributes = if src.kind_of?(String)
1413                       { "SRC" => src, "ALT" => alt }
1414                     else
1415                       src
1416                     end
1417        attributes["WIDTH"] = width.to_s if width
1418        attributes["HEIGHT"] = height.to_s if height
1419        super(attributes)
1420      end
1421  
1422  
1423  =begin
1424  === MULTIPART FORM
1425    multipart_form{ "string" }
1426      # <FORM METHOD="post" ENCTYPE="multipart/form-data">string</FORM>
1427  
1428    multipart_form("url"){ "string" }
1429      # <FORM METHOD="post" ACTION="url" ENCTYPE="multipart/form-data">string</FORM>
1430  =end
1431      def multipart_form(action = nil, enctype = "multipart/form-data")
1432        attributes = if action == nil
1433                       { "METHOD" => "post", "ENCTYPE" => enctype } 
1434                     elsif action.kind_of?(String)
1435                       { "METHOD" => "post", "ACTION" => action,
1436                         "ENCTYPE" => enctype } 
1437                     else
1438                       unless action.has_key?("METHOD")
1439                         action["METHOD"] = "post"
1440                       end
1441                       unless action.has_key?("ENCTYPE")
1442                         action["ENCTYPE"] = enctype
1443                       end
1444                       action
1445                     end
1446        if block_given?
1447          form(attributes){ yield }
1448        else
1449          form(attributes)
1450        end
1451      end
1452  
1453  
1454  =begin
1455  === PASSWORD_FIELD
1456    password_field("name")
1457      # <INPUT TYPE="password" NAME="name" SIZE="40">
1458  
1459    password_field("name", "value")
1460      # <INPUT TYPE="password" NAME="name" VALUE="value" SIZE="40">
1461  
1462    password_field("password", "value", 80, 200)
1463      # <INPUT TYPE="password" NAME="name" VALUE="value" SIZE="80" MAXLENGTH="200">
1464  
1465    password_field({ "NAME" => "name", "VALUE" => "value" })
1466      # <INPUT TYPE="password" NAME="name" VALUE="value">
1467  =end
1468      def password_field(name = "", value = nil, size = 40, maxlength = nil)
1469        attributes = if name.kind_of?(String)
1470                       { "TYPE" => "password", "NAME" => name,
1471                         "VALUE" => value, "SIZE" => size.to_s }
1472                     else
1473                       name["TYPE"] = "password"
1474                       name
1475                     end
1476        attributes["MAXLENGTH"] = maxlength.to_s if maxlength
1477        input(attributes)
1478      end
1479  
1480  
1481  =begin
1482  === POPUP_MENU
1483    popup_menu("name", "foo", "bar", "baz")
1484      # <SELECT NAME="name">
1485      #   <OPTION VALUE="foo">foo</OPTION>
1486      #   <OPTION VALUE="bar">bar</OPTION>
1487      #   <OPTION VALUE="baz">baz</OPTION>
1488      # </SELECT>
1489  
1490    popup_menu("name", ["foo"], ["bar", true], "baz")
1491      # <SELECT NAME="name">
1492      #   <OPTION VALUE="foo">foo</OPTION>
1493      #   <OPTION VALUE="bar" SELECTED>bar</OPTION>
1494      #   <OPTION VALUE="baz">baz</OPTION>
1495      # </SELECT>
1496  
1497    popup_menu("name", ["1", "Foo"], ["2", "Bar", true], "Baz")
1498      # <SELECT NAME="name">
1499      #   <OPTION VALUE="1">Foo</OPTION>
1500      #   <OPTION SELECTED VALUE="2">Bar</OPTION>
1501      #   <OPTION VALUE="Baz">Baz</OPTION>
1502      # </SELECT>
1503  
1504    popup_menu({"NAME" => "name", "SIZE" => 2, "MULTIPLE" => true,
1505                "VALUES" => [["1", "Foo"], ["2", "Bar", true], "Baz"] })
1506      # <SELECT NAME="name" MULTIPLE SIZE="2">
1507      #   <OPTION VALUE="1">Foo</OPTION>
1508      #   <OPTION SELECTED VALUE="2">Bar</OPTION>
1509      #   <OPTION VALUE="Baz">Baz</OPTION>
1510      # </SELECT>
1511  =end
1512      def popup_menu(name = "", *values)
1513  
1514        if name.kind_of?(Hash)
1515          values   = name["VALUES"]
1516          size     = name["SIZE"].to_s if name["SIZE"]
1517          multiple = name["MULTIPLE"]
1518          name     = name["NAME"]
1519        else
1520          size = nil
1521          multiple = nil
1522        end
1523  
1524        select({ "NAME" => name, "SIZE" => size,
1525                 "MULTIPLE" => multiple }){
1526          values.collect{|value|
1527            if value.kind_of?(String)
1528              option({ "VALUE" => value }){ value }
1529            else
1530              if value[value.size - 1] == true
1531                option({ "VALUE" => value[0], "SELECTED" => true }){
1532                  value[value.size - 2]
1533                }
1534              else
1535                option({ "VALUE" => value[0] }){
1536                  value[value.size - 1]
1537                }
1538              end
1539            end
1540          }.to_s
1541        }
1542  
1543      end
1544  
1545  
1546  =begin
1547  === RADIO_BUTTON
1548    radio_button("name", "value")
1549      # <INPUT TYPE="radio" NAME="name" VALUE="value">
1550  
1551    radio_button("name", "value", true)
1552      # <INPUT TYPE="radio" NAME="name" VALUE="value" CHECKED>
1553  
1554    radio_button({ "NAME" => "name", "VALUE" => "value", "ID" => "foo" })
1555      # <INPUT TYPE="radio" NAME="name" VALUE="value" ID="foo">
1556  =end
1557      def radio_button(name = "", value = nil, checked = nil)
1558        attributes = if name.kind_of?(String)
1559                       { "TYPE" => "radio", "NAME" => name,
1560                         "VALUE" => value, "CHECKED" => checked }
1561                     else
1562                       name["TYPE"] = "radio"
1563                       name
1564                     end
1565        input(attributes)
1566      end
1567  
1568  
1569  =begin
1570  === RADIO_GROUP
1571    radio_group("name", "foo", "bar", "baz")
1572      # <INPUT TYPE="radio" NAME="name" VALUE="foo">foo
1573      # <INPUT TYPE="radio" NAME="name" VALUE="bar">bar
1574      # <INPUT TYPE="radio" NAME="name" VALUE="baz">baz
1575  
1576    radio_group("name", ["foo"], ["bar", true], "baz")
1577      # <INPUT TYPE="radio" NAME="name" VALUE="foo">foo
1578      # <INPUT TYPE="radio" CHECKED NAME="name" VALUE="bar">bar
1579      # <INPUT TYPE="radio" NAME="name" VALUE="baz">baz
1580  
1581    radio_group("name", ["1", "Foo"], ["2", "Bar", true], "Baz")
1582      # <INPUT TYPE="radio" NAME="name" VALUE="1">Foo
1583      # <INPUT TYPE="radio" CHECKED NAME="name" VALUE="2">Bar
1584      # <INPUT TYPE="radio" NAME="name" VALUE="Baz">Baz
1585  
1586    radio_group({ "NAME" => "name",
1587                  "VALUES" => ["foo", "bar", "baz"] })
1588  
1589    radio_group({ "NAME" => "name",
1590                  "VALUES" => [["foo"], ["bar", true], "baz"] })
1591  
1592    radio_group({ "NAME" => "name",
1593                  "VALUES" => [["1", "Foo"], ["2", "Bar", true], "Baz"] })
1594  =end
1595      def radio_group(name = "", *values)
1596        if name.kind_of?(Hash)
1597          values = name["VALUES"]
1598          name = name["NAME"]
1599        end
1600        values.collect{|value|
1601          if value.kind_of?(String)
1602            radio_button(name, value) + value
1603          else
1604            if value[value.size - 1] == true
1605              radio_button(name, value[0], true) +
1606              value[value.size - 2]
1607            else
1608              radio_button(name, value[0]) +
1609              value[value.size - 1]
1610            end
1611          end
1612        }.to_s
1613      end
1614  
1615  
1616  =begin
1617  === RESET BUTTON
1618    reset
1619      # <INPUT TYPE="reset">
1620  
1621    reset("reset")
1622      # <INPUT TYPE="reset" VALUE="reset">
1623  
1624    reset({ "VALUE" => "reset", "ID" => "foo" })
1625      # <INPUT TYPE="reset" VALUE="reset" ID="foo">
1626  =end
1627      def reset(value = nil, name = nil)
1628        attributes = if (not value) or value.kind_of?(String)
1629                       { "TYPE" => "reset", "VALUE" => value, "NAME" => name }
1630                     else
1631                       value["TYPE"] = "reset"
1632                       value
1633                     end
1634        input(attributes)
1635      end
1636  
1637  
1638  =begin
1639  === SCROLLING_LIST
1640    scrolling_list({"NAME" => "name", "SIZE" => 2, "MULTIPLE" => true,
1641                    "VALUES" => [["1", "Foo"], ["2", "Bar", true], "Baz"] })
1642      # <SELECT NAME="name" MULTIPLE SIZE="2">
1643      #   <OPTION VALUE="1">Foo</OPTION>
1644      #   <OPTION SELECTED VALUE="2">Bar</OPTION>
1645      #   <OPTION VALUE="Baz">Baz</OPTION>
1646      # </SELECT>
1647  =end
1648      alias scrolling_list popup_menu
1649  
1650  
1651  =begin
1652  === SUBMIT BUTTON
1653    submit
1654      # <INPUT TYPE="submit">
1655  
1656    submit("ok")
1657      # <INPUT TYPE="submit" VALUE="ok">
1658  
1659    submit("ok", "button1")
1660      # <INPUT TYPE="submit" VALUE="ok" NAME="button1">
1661  
1662    submit({ "VALUE" => "ok", "NAME" => "button1", "ID" => "foo" })
1663      # <INPUT TYPE="submit" VALUE="ok" NAME="button1" ID="foo">
1664  =end
1665      def submit(value = nil, name = nil)
1666        attributes = if (not value) or value.kind_of?(String)
1667                       { "TYPE" => "submit", "VALUE" => value, "NAME" => name }
1668                     else
1669                       value["TYPE"] = "submit"
1670                       value
1671                     end
1672        input(attributes)
1673      end
1674  
1675  
1676  =begin
1677  === TEXT_FIELD
1678    text_field("name")
1679      # <INPUT TYPE="text" NAME="name" SIZE="40">
1680  
1681    text_field("name", "value")
1682      # <INPUT TYPE="text" NAME="name" VALUE="value" SIZE="40">
1683  
1684    text_field("name", "value", 80)
1685      # <INPUT TYPE="text" NAME="name" VALUE="value" SIZE="80">
1686  
1687    text_field("name", "value", 80, 200)
1688      # <INPUT TYPE="text" NAME="name" VALUE="value" SIZE="80" MAXLENGTH="200">
1689  
1690    text_field({ "NAME" => "name", "VALUE" => "value" })
1691      # <INPUT TYPE="text" NAME="name" VALUE="value">
1692  =end
1693      def text_field(name = "", value = nil, size = 40, maxlength = nil)
1694        attributes = if name.kind_of?(String)
1695                       { "TYPE" => "text", "NAME" => name, "VALUE" => value,
1696                         "SIZE" => size.to_s }
1697                     else
1698                       name["TYPE"] = "text"
1699                       name
1700                     end
1701        attributes["MAXLENGTH"] = maxlength.to_s if maxlength
1702        input(attributes)
1703      end
1704  
1705  
1706  =begin
1707  === TEXTAREA ELEMENT
1708    textarea("name")
1709      # = textarea({ "NAME" => "name", "COLS" => 70, "ROWS" => 10 })
1710  
1711    textarea("name", 40, 5)
1712      # = textarea({ "NAME" => "name", "COLS" => 40, "ROWS" => 5 })
1713  =end
1714      def textarea(name = "", cols = 70, rows = 10)
1715        attributes = if name.kind_of?(String)
1716                       { "NAME" => name, "COLS" => cols.to_s,
1717                         "ROWS" => rows.to_s }
1718                     else
1719                       name
1720                     end
1721        if block_given?
1722          super(attributes){ yield }
1723        else
1724          super(attributes)
1725        end
1726      end
1727  
1728    end # HtmlExtension
1729  
1730  
1731    module Html3
1732  
1733      def doctype
1734        %|<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">|
1735      end
1736  
1737      def element_init
1738        extend TagMaker
1739        methods = ""
1740        # - -
1741        for element in %w[ A TT I B U STRIKE BIG SMALL SUB SUP EM STRONG
1742            DFN CODE SAMP KBD VAR CITE FONT ADDRESS DIV center MAP
1743            APPLET PRE XMP LISTING DL OL UL DIR MENU SELECT table TITLE
1744            STYLE SCRIPT H1 H2 H3 H4 H5 H6 TEXTAREA FORM BLOCKQUOTE
1745            CAPTION ]
1746          methods += <<-BEGIN + nn_element_def(element) + <<-END
1747            def #{element.downcase}(attributes = {})
1748          BEGIN
1749            end
1750          END
1751        end
1752  
1753        # - O EMPTY
1754        for element in %w[ IMG BASE BASEFONT BR AREA LINK PARAM HR INPUT
1755            ISINDEX META ]
1756          methods += <<-BEGIN + nOE_element_def(element) + <<-END
1757            def #{element.downcase}(attributes = {})
1758          BEGIN
1759            end
1760          END
1761        end
1762  
1763        # O O or - O
1764        for element in %w[ HTML HEAD BODY P PLAINTEXT DT DD LI OPTION tr
1765            th td ]
1766          methods += <<-BEGIN + nO_element_def(element) + <<-END
1767            def #{element.downcase}(attributes = {})
1768          BEGIN
1769            end
1770          END
1771        end
1772        eval(methods)
1773      end
1774  
1775    end # Html3
1776  
1777  
1778    module Html4
1779  
1780      def doctype
1781        %|<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">|
1782      end
1783  
1784      def element_init
1785        extend TagMaker
1786        methods = ""
1787        # - -
1788        for element in %w[ TT I B BIG SMALL EM STRONG DFN CODE SAMP KBD
1789          VAR CITE ABBR ACRONYM SUB SUP SPAN BDO ADDRESS DIV MAP OBJECT
1790          H1 H2 H3 H4 H5 H6 PRE Q INS DEL DL OL UL LABEL SELECT OPTGROUP
1791          FIELDSET LEGEND BUTTON TABLE TITLE STYLE SCRIPT NOSCRIPT
1792          TEXTAREA FORM A BLOCKQUOTE CAPTION ]
1793          methods += <<-BEGIN + nn_element_def(element) + <<-END
1794            def #{element.downcase}(attributes = {})
1795          BEGIN
1796            end
1797          END
1798        end
1799  
1800        # - O EMPTY
1801        for element in %w[ IMG BASE BR AREA LINK PARAM HR INPUT COL META ]
1802          methods += <<-BEGIN + nOE_element_def(element) + <<-END
1803            def #{element.downcase}(attributes = {})
1804          BEGIN
1805            end
1806          END
1807        end
1808  
1809        # O O or - O
1810        for element in %w[ HTML BODY P DT DD LI OPTION THEAD TFOOT TBODY
1811            COLGROUP TR TH TD HEAD]
1812          methods += <<-BEGIN + nO_element_def(element) + <<-END
1813            def #{element.downcase}(attributes = {})
1814          BEGIN
1815            end
1816          END
1817        end
1818        eval(methods)
1819      end
1820  
1821    end # Html4
1822  
1823  
1824    module Html4Tr
1825  
1826      def doctype
1827        %|<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">|
1828      end
1829  
1830      def element_init
1831        extend TagMaker
1832        methods = ""
1833        # - -
1834        for element in %w[ TT I B U S STRIKE BIG SMALL EM STRONG DFN
1835            CODE SAMP KBD VAR CITE ABBR ACRONYM FONT SUB SUP SPAN BDO
1836            ADDRESS DIV CENTER MAP OBJECT APPLET H1 H2 H3 H4 H5 H6 PRE Q
1837            INS DEL DL OL UL DIR MENU LABEL SELECT OPTGROUP FIELDSET
1838            LEGEND BUTTON TABLE IFRAME NOFRAMES TITLE STYLE SCRIPT
1839            NOSCRIPT TEXTAREA FORM A BLOCKQUOTE CAPTION ]
1840          methods += <<-BEGIN + nn_element_def(element) + <<-END
1841            def #{element.downcase}(attributes = {})
1842          BEGIN
1843            end
1844          END
1845        end
1846  
1847        # - O EMPTY
1848        for element in %w[ IMG BASE BASEFONT BR AREA LINK PARAM HR INPUT
1849            COL ISINDEX META ]
1850          methods += <<-BEGIN + nOE_element_def(element) + <<-END
1851            def #{element.downcase}(attributes = {})
1852          BEGIN
1853            end
1854          END
1855        end
1856  
1857        # O O or - O
1858        for element in %w[ HTML BODY P DT DD LI OPTION THEAD TFOOT TBODY
1859            COLGROUP TR TH TD HEAD ]
1860          methods += <<-BEGIN + nO_element_def(element) + <<-END
1861            def #{element.downcase}(attributes = {})
1862          BEGIN
1863            end
1864          END
1865        end
1866        eval(methods)
1867      end
1868  
1869    end # Html4Tr
1870  
1871  
1872    module Html4Fr
1873  
1874      def doctype
1875        %|<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">|
1876      end
1877  
1878      def element_init
1879        methods = ""
1880        # - -
1881        for element in %w[ FRAMESET ]
1882          methods += <<-BEGIN + nn_element_def(element) + <<-END
1883            def #{element.downcase}(attributes = {})
1884          BEGIN
1885            end
1886          END
1887        end
1888  
1889        # - O EMPTY
1890        for element in %w[ FRAME ]
1891          methods += <<-BEGIN + nOE_element_def(element) + <<-END
1892            def #{element.downcase}(attributes = {})
1893          BEGIN
1894            end
1895          END
1896        end
1897        eval(methods)
1898      end
1899  
1900    end # Html4Fr
1901  
1902  
1903    def initialize(type = "query")
1904      if defined?(MOD_RUBY) && !ENV.key?("GATEWAY_INTERFACE")
1905        Apache.request.setup_cgi_env
1906      end
1907  
1908      extend QueryExtension
1909      if "POST" != env_table['REQUEST_METHOD']
1910        initialize_query()  # set @params, @cookies
1911      else
1912        if defined?(CGI_PARAMS)
1913          @params  = CGI_PARAMS.nil?  ? nil : CGI_PARAMS.dup
1914          @cookies = CGI_COOKIES.nil? ? nil : CGI_COOKIES.dup
1915        else
1916          initialize_query()  # set @params, @cookies
1917          eval "CGI_PARAMS  = @params.nil?  ? nil : @params.dup"
1918          eval "CGI_COOKIES = @cookies.nil? ? nil : @cookies.dup"
1919          if defined?(MOD_RUBY) and (RUBY_VERSION < "1.4.3")
1920            raise "Please, use ruby1.4.3 or later."
1921          else
1922            at_exit() do
1923              if defined?(CGI_PARAMS)
1924                self.class.class_eval("remove_const(:CGI_PARAMS)")
1925                self.class.class_eval("remove_const(:CGI_COOKIES)")
1926              end
1927            end
1928          end
1929        end
1930      end
1931      @output_cookies = nil
1932      @output_hidden = nil
1933  
1934      case type
1935      when "html3"
1936        extend Html3
1937        element_init()
1938        extend HtmlExtension
1939      when "html4"
1940        extend Html4
1941        element_init()
1942        extend HtmlExtension
1943      when "html4Tr"
1944        extend Html4Tr
1945        element_init()
1946        extend HtmlExtension
1947      when "html4Fr"
1948        extend Html4Tr
1949        element_init()
1950        extend Html4Fr
1951        element_init()
1952        extend HtmlExtension
1953      end
1954  
1955    end
1956  end
1957  
1958  
1959  =begin
1960  
1961  == HISTORY
1962  
1963  delete. see cvs log.
1964  
1965  
1966  =end
1967  
1968  # vi:set tw=0: