lib/cgi-lib.rb


DEFINITIONS

This source file includes following functions.


   1  =begin
   2  
   3  = simple CGI support library
   4  
   5  = example
   6  
   7  == get form values
   8  
   9          require "cgi-lib.rb"
  10          query = CGI.new
  11          query['field']   # <== value of 'field'
  12          query.keys       # <== array of fields
  13  
  14  and query has Hash class methods
  15  
  16  
  17  == get cookie values
  18  
  19          require "cgi-lib.rb"
  20          query = CGI.new
  21          query.cookie['name']  # <== cookie value of 'name'
  22          query.cookie.keys     # <== all cookie names
  23  
  24  and query.cookie has Hash class methods
  25  
  26  
  27  == print HTTP header and HTML string to $>
  28  
  29          require "cgi-lib.rb"
  30          CGI::print{
  31            CGI::tag("HTML"){
  32              CGI::tag("HEAD"){ CGI::tag("TITLE"){"TITLE"} } +
  33              CGI::tag("BODY"){
  34                CGI::tag("FORM", {"ACTION"=>"test.rb", "METHOD"=>"POST"}){
  35                  CGI::tag("INPUT", {"TYPE"=>"submit", "VALUE"=>"submit"})
  36                } +
  37                CGI::tag("HR")
  38              }
  39            }
  40          }
  41  
  42  
  43  == make raw cookie string
  44  
  45          require "cgi-lib.rb"
  46          cookie1 = CGI::cookie({'name'    => 'name',
  47                                 'value'   => 'value',
  48                                 'path'    => 'path',   # optional
  49                                 'domain'  => 'domain', # optional
  50                                 'expires' => Time.now, # optional
  51                                 'secure'  => true      # optional
  52                                })
  53  
  54          CGI::print("Content-Type: text/html", cookie1, cookie2){ "string" }
  55  
  56  
  57  == print HTTP header and string to $>
  58  
  59          require "cgi-lib.rb"
  60          CGI::print{ "string" }
  61            # == CGI::print("Content-Type: text/html"){ "string" }
  62          CGI::print("Content-Type: text/html", cookie1, cookie2){ "string" }
  63  
  64  
  65  === NPH (no-parse-header) mode
  66  
  67          require "cgi-lib.rb"
  68          CGI::print("nph"){ "string" }
  69            # == CGI::print("nph", "Content-Type: text/html"){ "string" }
  70          CGI::print("nph", "Content-Type: text/html", cookie1, cookie2){ "string" }
  71  
  72  
  73  == make HTML tag string
  74  
  75          require "cgi-lib.rb"
  76          CGI::tag("element", {"attribute_name"=>"attribute_value"}){"content"}
  77  
  78  
  79  == make HTTP header string
  80  
  81          require "cgi-lib.rb"
  82          CGI::header # == CGI::header("Content-Type: text/html")
  83          CGI::header("Content-Type: text/html", cookie1, cookie2)
  84  
  85  
  86  === NPH (no-parse-header) mode
  87  
  88          CGI::header("nph") # == CGI::header("nph", "Content-Type: text/html")
  89          CGI::header("nph", "Content-Type: text/html", cookie1, cookie2)
  90  
  91  
  92  == escape url encode
  93  
  94          require "cgi-lib.rb"
  95          url_encoded_string = CGI::escape("string")
  96  
  97  
  98  == unescape url encoded
  99  
 100          require "cgi-lib.rb"
 101          string = CGI::unescape("url encoded string")
 102  
 103  
 104  == escape HTML &"<>
 105  
 106          require "cgi-lib.rb"
 107          CGI::escapeHTML("string")
 108  
 109  
 110  =end
 111  
 112  require "delegate"
 113  
 114  class CGI < SimpleDelegator
 115  
 116    CR  = "\015"
 117    LF  = "\012"
 118    EOL = CR + LF
 119  
 120    RFC822_DAYS = %w[ Sun Mon Tue Wed Thu Fri Sat ]
 121    RFC822_MONTHS = %w[ Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec ]
 122  
 123    # make rfc1123 date string
 124    def CGI::rfc1123_date(time)
 125      t = time.clone.gmtime
 126      return format("%s, %.2d %s %d %.2d:%.2d:%.2d GMT",
 127                  RFC822_DAYS[t.wday], t.day, RFC822_MONTHS[t.month-1], t.year,
 128                  t.hour, t.min, t.sec)
 129    end
 130  
 131    # escape url encode
 132    def CGI::escape(str)
 133      str.gsub(/[^a-zA-Z0-9_\-.]/n){ sprintf("%%%02X", $&.unpack("C")[0]) }
 134    end
 135  
 136    # unescape url encoded
 137    def CGI::unescape(str)
 138      str.gsub(/\+/, ' ').gsub(/%([0-9a-fA-F]{2})/){ [$1.hex].pack("c") }
 139    end
 140  
 141    # escape HTML
 142    def CGI::escapeHTML(str)
 143      str.gsub(/&/, "&amp;").gsub(/\"/, "&quot;").gsub(/>/, "&gt;").gsub(/</, "&lt;")
 144    end
 145  
 146    # offline mode. read name=value pairs on standard input.
 147    def read_from_cmdline
 148      require "shellwords.rb"
 149      words = Shellwords.shellwords(
 150                if not ARGV.empty?
 151                  ARGV.join(' ')
 152                else
 153                  STDERR.print "(offline mode: enter name=value pairs on standard input)\n" if STDIN.tty?
 154                  readlines.join(' ').gsub(/\n/, '')
 155                end.gsub(/\\=/, '%3D').gsub(/\\&/, '%26'))
 156  
 157      if words.find{|x| x =~ /=/} then words.join('&') else words.join('+') end
 158    end
 159  
 160    def initialize(input = $stdin)
 161  
 162      @inputs = {}
 163      @cookie = {}
 164  
 165      case ENV['REQUEST_METHOD']
 166      when "GET"
 167        ENV['QUERY_STRING'] or ""
 168      when "POST"
 169        input.read(Integer(ENV['CONTENT_LENGTH'])) or ""
 170      else
 171        read_from_cmdline
 172      end.split(/[&;]/).each do |x|
 173        key, val = x.split(/=/,2).collect{|x|CGI::unescape(x)}
 174        if @inputs.include?(key)
 175          @inputs[key] += "\0" + (val or "")
 176        else
 177          @inputs[key] = (val or "")
 178        end
 179      end
 180  
 181      super(@inputs)
 182  
 183      if ENV.has_key?('HTTP_COOKIE') or ENV.has_key?('COOKIE')
 184        (ENV['HTTP_COOKIE'] or ENV['COOKIE']).split("; ").each do |x|
 185          key, val = x.split(/=/,2)
 186          key = CGI::unescape(key)
 187          val = val.split(/&/).collect{|x|CGI::unescape(x)}.join("\0")
 188          if @cookie.include?(key)
 189            @cookie[key] += "\0" + val
 190          else
 191            @cookie[key] = val
 192          end
 193        end
 194      end
 195    end
 196  
 197    attr("inputs")
 198    attr("cookie")
 199  
 200    # make HTML tag string
 201    def CGI::tag(element, attributes = {})
 202      "<" + escapeHTML(element) + attributes.collect{|name, value|
 203        " " + escapeHTML(name) + '="' + escapeHTML(value) + '"'
 204      }.to_s + ">" +
 205      (iterator? ? yield.to_s + "</" + escapeHTML(element) + ">" : "")
 206    end
 207  
 208    # make raw cookie string
 209    def CGI::cookie(options)
 210      "Set-Cookie: " + options['name'] + '=' + escape(options['value']) +
 211      (options['domain']  ? '; domain='  + options['domain'] : '') +
 212      (options['path']    ? '; path='    + options['path']   : '') +
 213      (options['expires'] ? '; expires=' + rfc1123_date(options['expires']) : '') +
 214      (options['secure']  ? '; secure' : '')
 215    end
 216  
 217    # make HTTP header string
 218    def CGI::header(*options)
 219      if defined?(MOD_RUBY)
 220        options.each{|option|
 221          option.sub(/(.*?): (.*)/){
 222            Apache::request.headers_out[$1] = $2
 223          }
 224        }
 225        Apache::request.send_http_header
 226        ''
 227      else
 228        if options.delete("nph") or (ENV['SERVER_SOFTWARE'] =~ /IIS/)
 229          [(ENV['SERVER_PROTOCOL'] or "HTTP/1.0") + " 200 OK",
 230           "Date: " + rfc1123_date(Time.now),
 231           "Server: " + (ENV['SERVER_SOFTWARE'] or ""),
 232           "Connection: close"] +
 233          (options.empty? ? ["Content-Type: text/html"] : options)
 234        else
 235          options.empty? ? ["Content-Type: text/html"] : options
 236        end.join(EOL) + EOL + EOL
 237      end
 238    end
 239  
 240    # print HTTP header and string to $>
 241    def CGI::print(*options)
 242      $>.print CGI::header(*options) + yield.to_s
 243    end
 244  
 245    # print message to $>
 246    def CGI::message(message, title = "", header = ["Content-Type: text/html"])
 247      if message.kind_of?(Hash)
 248        title   = message['title']
 249        header  = message['header']
 250        message = message['body']
 251      end
 252      CGI::print(*header){
 253        CGI::tag("HTML"){
 254          CGI::tag("HEAD"){ CGI.tag("TITLE"){ title } } +
 255          CGI::tag("BODY"){ message }
 256        }
 257      }
 258      true
 259    end
 260  
 261    # print error message to $> and exit
 262    def CGI::error
 263      CGI::message({'title'=>'ERROR', 'body'=>
 264        CGI::tag("PRE"){
 265          "ERROR: " + CGI::tag("STRONG"){ escapeHTML($!.to_s) } + "\n" + escapeHTML($@.join("\n"))
 266        }
 267      })
 268      exit
 269    end
 270  end