Class: CGI

Inherits:
Object
  • Object
show all
Includes:
QueryExtension
Defined in:
lib/cgialt.rb,
lib/cgialt/core.rb,
lib/cgialt/html.rb,
lib/cgialt/util.rb,
lib/cgialt/cookie.rb

Overview

Copyright © 2000 Network Applied Communication Laboratory, Inc.

Copyright © 2000 Information-technology Promotion Agency, Japan

Original Author: Wakou Aoyama <[email protected]>

Documentation: Wakou Aoyama (RDoc’d and embellished by William Webber)

$Rev: 32 $ $Release: 0.0.2 $ copyright© 2007 kuwata-lab.com all rights reserved.

Defined Under Namespace

Modules: Html3, Html4, Html4Fr, Html4Tr, HtmlExtension, QueryExtension, TagMaker Classes: Cookie

Constant Summary collapse

RELEASE =
('$Release: 0.0.2 $' =~ /[.\d]+/) && $&
EOL =

for FCGI support

"\r\n"
HTTP_STATUS =

HTTP status codes.

{
  "OK"                  => "200 OK",
  "PARTIAL_CONTENT"     => "206 Partial Content",
  "MULTIPLE_CHOICES"    => "300 Multiple Choices",
  "MOVED"               => "301 Moved Permanently",
  "REDIRECT"            => "302 Found",
  "NOT_MODIFIED"        => "304 Not Modified",
  "BAD_REQUEST"         => "400 Bad Request",
  "AUTH_REQUIRED"       => "401 Authorization Required",
  "FORBIDDEN"           => "403 Forbidden",
  "NOT_FOUND"           => "404 Not Found",
  "METHOD_NOT_ALLOWED"  => "405 Method Not Allowed",
  "NOT_ACCEPTABLE"      => "406 Not Acceptable",
  "LENGTH_REQUIRED"     => "411 Length Required",
  "PRECONDITION_FAILED" => "412 Rrecondition Failed",
  "SERVER_ERROR"        => "500 Internal Server Error",
  "NOT_IMPLEMENTED"     => "501 Method Not Implemented",
  "BAD_GATEWAY"         => "502 Bad Gateway",
  "VARIANT_ALSO_VARIES" => "506 Variant Also Negotiates",
}
RFC822_DAYS =

Abbreviated day-of-week names specified by RFC 822

%w[ Sun Mon Tue Wed Thu Fri Sat ]
RFC822_MONTHS =

Abbreviated month names specified by RFC 822

%w[ Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec ]
MAX_CONTENT_LENGTH =

Maximum content length of post data

2 * 1024 * 1024
MAX_MULTIPART_LENGTH =

Maximum content length of multipart data

128 * 1024 * 1024
MAX_MULTIPART_COUNT =

Maximum number of request parameters when multipart

128

Instance Attribute Summary

Attributes included from QueryExtension

#cookies, #params

Class Method Summary collapse

Instance Method Summary collapse

Methods included from QueryExtension

#[], #accept, #accept_charset, #accept_encoding, #accept_language, #auth_type, #cache_control, #content_length, #content_type, #create_body, #from, #gateway_interface, #has_key?, #host, #keys, #multipart?, #negotiate, #path_info, #path_translated, #pragma, #query_string, #raw_cookie, #raw_cookie2, #referer, #remote_addr, #remote_host, #remote_ident, #remote_user, #request_method, #script_name, #server_name, #server_port, #server_protocol, #server_software, #unescape_filename?, #user_agent

Constructor Details

#initialize(type = nil) ⇒ CGI

Creates a new CGI instance.

type specifies which version of HTML to load the HTML generation methods for. The following versions of HTML are supported:

html3

HTML 3.x

html4

HTML 4.0

html4Tr

HTML 4.0 Transitional

html4Fr

HTML 4.0 with Framesets

If not specified, no HTML generation methods will be loaded.

If the CGI object is not created in a standard CGI call environment (that is, it can’t locate REQUEST_METHOD in its environment), then it will run in “offline” mode. In this mode, it reads its parameters from the command line or (failing that) from standard input. Otherwise, cookies and other parameters are parsed automatically from the standard CGI locations, which varies according to the REQUEST_METHOD.



1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
# File 'lib/cgialt/core.rb', line 1398

def initialize(type=nil)
  if defined?(MOD_RUBY) && !$CGI_ENV['GATEWAY_INTERFACE']
    Apache.request.setup_cgi_env
  end
  ##
  #extend QueryExtension
  if defined?(CGI_PARAMS)
    warn "do not use CGI_PARAMS and CGI_COOKIES"
    @params = CGI_PARAMS.dup
    @cookies = CGI_COOKIES.dup
    @multipart = false
  else
    initialize_query()  # set @params, @cookies, and @multipart
  end
  @output_cookies = nil
  @output_hidden = nil
  ##
  if type
    require 'cgialt/html' unless defined?(HtmlExtension)
    case type
    when 'html3'
      extend Html3; element_init()
      extend HtmlExtension
    when 'html4'
      extend Html4; element_init()
      extend HtmlExtension
    when 'html4Tr'
      extend Html4Tr; element_init()
      extend HtmlExtension
    when 'html4Fr'
      extend Html4Tr; element_init()
      extend Html4Fr; element_init()
      extend HtmlExtension
    end
  end
end

Class Method Details

.escape(string) ⇒ Object

URL-encode a string.

url_encoded_string = CGI::escape("'Stop!' said Fred")
   # => "%27Stop%21%27+said+Fred"


16
17
18
19
20
# File 'lib/cgialt/util.rb', line 16

def CGI::escape(string)
  string.gsub(/([^ a-zA-Z0-9_.-]+)/n) do
    '%' + $1.unpack('H2' * $1.size).join('%').upcase
  end.tr(' ', '+')
end

.escapeElement(string, *elements) ⇒ Object

Escape only the tags of certain HTML elements in string.

Takes an element or elements or array of elements. Each element is specified by the name of the element, without angle brackets. This matches both the start and the end tag of that element. The attribute list of the open tag will also be escaped (for instance, the double-quotes surrounding attribute values).

print CGI::escapeElement('<BR><A HREF="url"></A>', "A", "IMG")
  # "<BR>&lt;A HREF=&quot;url&quot;&gt;&lt;/A&gt"

print CGI::escapeElement('<BR><A HREF="url"></A>', ["A", "IMG"])
  # "<BR>&lt;A HREF=&quot;url&quot;&gt;&lt;/A&gt"


92
93
94
95
96
97
98
99
100
101
# File 'lib/cgialt/util.rb', line 92

def CGI::escapeElement(string, *elements)
  elements = elements[0] if elements[0].kind_of?(Array)
  unless elements.empty?
    string.gsub(/<\/?(?:#{elements.join("|")})(?!\w)(?:.|\n)*?>/ni) do
      CGI::escapeHTML($&)
    end
  else
    string
  end
end

.escapeHTML(string) ⇒ Object

Escape special characters in HTML, namely &"<>

CGI::escapeHTML('Usage: foo "bar" <baz>')
   # => "Usage: foo &quot;bar&quot; &lt;baz&gt;"


36
37
38
# File 'lib/cgialt/util.rb', line 36

def CGI::escapeHTML(string)
  string.gsub(/&/n, '&amp;').gsub(/\"/n, '&quot;').gsub(/>/n, '&gt;').gsub(/</n, '&lt;')
end

.parse(query) ⇒ Object

Parse an HTTP query string into a hash of key=>value pairs.

params = CGI::parse("query_string")
  # {"name1" => ["value1", "value2", ...],
  #  "name2" => ["value1", "value2", ...], ... }


785
786
787
788
789
790
791
792
793
# File 'lib/cgialt/core.rb', line 785

def CGI::parse(query)
  params = {}
  query.split(/[&;]/n).each do |pair|
    key, value = pair.split('=', 2)
    (params[CGI.unescape(key)] ||= []) << CGI.unescape(value)
  end
  params.default = [].freeze
  return params
end

.pretty(string, shift = " ") ⇒ Object

Prettify (indent) an HTML string.

string is the HTML string to indent. shift is the indentation unit to use; it defaults to two spaces.

print CGI::pretty("<HTML><BODY></BODY></HTML>")
  # <HTML>
  #   <BODY>
  #   </BODY>
  # </HTML>

print CGI::pretty("<HTML><BODY></BODY></HTML>", "\t")
  # <HTML>
  #         <BODY>
  #         </BODY>
  # </HTML>


154
155
156
157
158
159
160
161
162
163
# File 'lib/cgialt/util.rb', line 154

def CGI::pretty(string, shift = "  ")
  lines = string.gsub(/(?!\A)<(?:.|\n)*?>/n, "\n\\0").gsub(/<(?:.|\n)*?>(?!\n)/n, "\\0\n")
  end_pos = 0
  while end_pos = lines.index(/^<\/(\w+)/n, end_pos)
    element = $1.dup
    start_pos = lines.rindex(/^\s*<#{element}/ni, end_pos)
    lines[start_pos ... end_pos] = "__" + lines[start_pos ... end_pos].gsub(/\n(?!\z)/n, "\n" + shift) + "__"
  end
  lines.gsub(/^((?:#{Regexp::quote(shift)})*)__(?=<\/?\w)/n, '\1')
end

.rfc1123_date(time) ⇒ Object

Format a Time object as a String using the format specified by RFC 1123.

CGI::rfc1123_date(Time.now)
  # Sat, 01 Jan 2000 00:00:00 GMT


129
130
131
132
133
134
# File 'lib/cgialt/util.rb', line 129

def CGI::rfc1123_date(time)
  t = time.clone.gmtime
  return format("%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT",
              RFC822_DAYS[t.wday], t.day, RFC822_MONTHS[t.month-1], t.year,
              t.hour, t.min, t.sec)
end

.unescape(string) ⇒ Object

URL-decode a string.

string = CGI::unescape("%27Stop%21%27+said+Fred")
   # => "'Stop!' said Fred"


26
27
28
29
30
# File 'lib/cgialt/util.rb', line 26

def CGI::unescape(string)
  string.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n) do
    [$1.delete('%')].pack('H*')
  end
end

.unescapeElement(string, *elements) ⇒ Object

Undo escaping such as that done by CGI::escapeElement()

print CGI::unescapeElement(
        CGI::escapeHTML('<BR><A HREF="url"></A>'), "A", "IMG")
  # "&lt;BR&gt;<A HREF="url"></A>"

print CGI::unescapeElement(
        CGI::escapeHTML('<BR><A HREF="url"></A>'), ["A", "IMG"])
  # "&lt;BR&gt;<A HREF="url"></A>"


113
114
115
116
117
118
119
120
121
122
# File 'lib/cgialt/util.rb', line 113

def CGI::unescapeElement(string, *elements)
  elements = elements[0] if elements[0].kind_of?(Array)
  unless elements.empty?
    string.gsub(/&lt;\/?(?:#{elements.join("|")})(?!\w)(?:.|\n)*?&gt;/ni) do
      CGI::unescapeHTML($&)
    end
  else
    string
  end
end

.unescapeHTML(string) ⇒ Object

Unescape a string that has been HTML-escaped

CGI::unescapeHTML("Usage: foo &quot;bar&quot; &lt;baz&gt;")
   # => "Usage: foo \"bar\" <baz>"


44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/cgialt/util.rb', line 44

def CGI::unescapeHTML(string)
  string.gsub(/&(amp|quot|gt|lt|\#[0-9]+|\#x[0-9A-Fa-f]+);/n) do
    match = $1.dup
    case match
    when 'amp'                 then '&'
    when 'quot'                then '"'
    when 'gt'                  then '>'
    when 'lt'                  then '<'
    when /\A#0*(\d+)\z/n       then
      if Integer($1) < 256
        Integer($1).chr
      else
        if Integer($1) < 65536 and ($KCODE[0] == ?u or $KCODE[0] == ?U)
          [Integer($1)].pack("U")
        else
          "&##{$1};"
        end
      end
    when /\A#x([0-9a-f]+)\z/ni then
      if $1.hex < 256
        $1.hex.chr
      else
        if $1.hex < 65536 and ($KCODE[0] == ?u or $KCODE[0] == ?U)
          [$1.hex].pack("U")
        else
          "&#x#{$1};"
        end
      end
    else
      "&#{match};"
    end
  end
end

Instance Method Details

#header(options = 'text/html') ⇒ Object

Create an HTTP header block as a string.

Includes the empty line that ends the header block.

options can be a string specifying the Content-Type (defaults to text/html), or a hash of header key/value pairs. The following header keys are recognized:

type

the Content-Type header. Defaults to “text/html”

charset

the charset of the body, appended to the Content-Type header.

nph

a boolean value. If true, prepend protocol string and status code, and date; and sets default values for “server” and “connection” if not explicitly set.

status

the HTTP status code, returned as the Status header. See the list of available status codes below.

server

the server software, returned as the Server header.

connection

the connection type, returned as the Connection header (for instance, “close”.

length

the length of the content that will be sent, returned as the Content-Length header.

language

the language of the content, returned as the Content-Language header.

expires

the time on which the current content expires, as a Time object, returned as the Expires header.

cookie

a cookie or cookies, returned as one or more Set-Cookie headers. The value can be the literal string of the cookie; a CGI::Cookie object; an Array of literal cookie strings or Cookie objects; or a hash all of whose values are literal cookie strings or Cookie objects. These cookies are in addition to the cookies held in the @output_cookies field.

Other header lines can also be set; they are appended as key: value.

header
  # Content-Type: text/html

header("text/plain")
  # Content-Type: text/plain

header("nph"        => true,
       "status"     => "OK",  # == "200 OK"
         # "status"     => "200 GOOD",
       "server"     => ENV['SERVER_SOFTWARE'],
       "connection" => "close",
       "type"       => "text/html",
       "charset"    => "iso-2022-jp",
         # Content-Type: text/html; charset=iso-2022-jp
       "length"     => 103,
       "language"   => "ja",
       "expires"    => Time.now + 30,
       "cookie"     => [cookie1, cookie2],
       "my_header1" => "my_value"
       "my_header2" => "my_value")

The status codes are:

"OK"                  --> "200 OK"
"PARTIAL_CONTENT"     --> "206 Partial Content"
"MULTIPLE_CHOICES"    --> "300 Multiple Choices"
"MOVED"               --> "301 Moved Permanently"
"REDIRECT"            --> "302 Found"
"NOT_MODIFIED"        --> "304 Not Modified"
"BAD_REQUEST"         --> "400 Bad Request"
"AUTH_REQUIRED"       --> "401 Authorization Required"
"FORBIDDEN"           --> "403 Forbidden"
"NOT_FOUND"           --> "404 Not Found"
"METHOD_NOT_ALLOWED"  --> "405 Method Not Allowed"
"NOT_ACCEPTABLE"      --> "406 Not Acceptable"
"LENGTH_REQUIRED"     --> "411 Length Required"
"PRECONDITION_FAILED" --> "412 Precondition Failed"
"SERVER_ERROR"        --> "500 Internal Server Error"
"NOT_IMPLEMENTED"     --> "501 Method Not Implemented"
"BAD_GATEWAY"         --> "502 Bad Gateway"
"VARIANT_ALSO_VARIES" --> "506 Variant Also Negotiates"

This method does not perform charset conversion.



414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
# File 'lib/cgialt/core.rb', line 414

def header(options='text/html')
  if options.is_a?(String)
    content_type = options
    buf = _header_for_string(content_type)
  elsif options.is_a?(Hash)
    if options.size == 1 && options.has_key?('type')
      content_type = options['type']
      buf = _header_for_string(content_type)
    else
      buf = _header_for_hash(options.dup)
    end
  else
    raise ArgumentError.new("expected String or Hash but got #{options.class}")
  end
  if defined?(MOD_RUBY)
    _header_for_modruby(buf)
    return ''
  else
    buf << EOL    # empty line of separator
    return buf
  end
end

#nph?Boolean

:nodoc:

Returns:

  • (Boolean)


509
510
511
# File 'lib/cgialt/core.rb', line 509

def nph?  #:nodoc:
  return /IIS\/(\d+)/n.match($CGI_ENV['SERVER_SOFTWARE']) && $1.to_i < 5
end

#out(options = 'text/html') ⇒ Object

Print an HTTP header and body to $DEFAULT_OUTPUT ($>)

The header is provided by options, as for #header(). The body of the document is that returned by the passed- in block. This block takes no arguments. It is required.

cgi = CGI.new
cgi.out{ "string" }
  # Content-Type: text/html
  # Content-Length: 6
  #
  # string

cgi.out("text/plain") { "string" }
  # Content-Type: text/plain
  # Content-Length: 6
  #
  # string

cgi.out("nph"        => true,
        "status"     => "OK",  # == "200 OK"
        "server"     => ENV['SERVER_SOFTWARE'],
        "connection" => "close",
        "type"       => "text/html",
        "charset"    => "iso-2022-jp",
          # Content-Type: text/html; charset=iso-2022-jp
        "language"   => "ja",
        "expires"    => Time.now + (3600 * 24 * 30),
        "cookie"     => [cookie1, cookie2],
        "my_header1" => "my_value",
        "my_header2" => "my_value") { "string" }

Content-Length is automatically calculated from the size of the String returned by the content block.

If ENV == “HEAD”, then only the header is outputted (the content block is still required, but it is ignored).

If the charset is “iso-2022-jp” or “euc-jp” or “shift_jis” then the content is converted to this charset, and the language is set to “ja”.



704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
# File 'lib/cgialt/core.rb', line 704

def out(options='text/html') # :yield:
  options = { 'type' => options } if options.kind_of?(String)
  stdout = $stdout
  stdout.binmode if defined? stdout.binmode
  #if ENV['REQUEST_METHOD'] == 'HEAD'
  #  charset = options['charset']
  #  options['language'] ||= 'ja' if charset && charset =~ /iso-2022-jp|euc-jp|shift_jis/ni
  #  stdout.print header(options)
  #  return
  #end
  content = yield
  content = convert_content(content, options)
  options['length'] = content.length.to_s
  stdout.print header(options)
  stdout.print content unless $CGI_ENV['REQUEST_METHOD'] == 'HEAD'
end

Print an argument or list of arguments to the default output stream

cgi = CGI.new
cgi.print    # default:  cgi.print == $DEFAULT_OUTPUT.print


771
772
773
774
775
776
# File 'lib/cgialt/core.rb', line 771

def print(*options)
  $stdout.print(*options)
  #*** original
  #*stdoutput.print(*options)
  #*** /original
end