=begin == NAME cgi.rb - cgi support library Version 2.1.1 Copyright (C) 2000 Network Applied Communication Laboratory, Inc. Copyright (C) 2000 Information-technology Promotion Agency, Japan Wakou Aoyama == EXAMPLE === GET FORM VALUES require "cgi" cgi = CGI.new values = cgi['field_name'] # <== array of 'field_name' # if not 'field_name' included, then return []. fields = cgi.keys # <== array of field names # returns true if form has 'field_name' cgi.has_key?('field_name') cgi.has_key?('field_name') cgi.include?('field_name') === GET FORM VALUES AS HASH require "cgi" cgi = CGI.new params = cgi.params cgi.params is a hash. cgi.params['new_field_name'] = ["value"] # add new param cgi.params['field_name'] = ["new_value"] # change value cgi.params.delete('field_name') # delete param cgi.params.clear # delete all params === SAVE FORM VALUES TO FILE require "pstore" db = PStore.new("query.db") db.transaction do db["params"] = cgi.params end === RESTORE FORM VALUES FROM FILE require "pstore" db = PStore.new("query.db") db.transaction do cgi.params = db["params"] end === GET MULTIPART FORM VALUES require "cgi" cgi = CGI.new values = cgi['field_name'] # <== array of 'field_name' values[0].read # <== body of values[0] values[0].local_path # <== path to local file of values[0] values[0].original_filename # <== original filename of values[0] values[0].content_type # <== content_type of values[0] and values[0] has Tempfile class methods. (Tempfile class object has File class methods) === GET COOKIE VALUES require "cgi" cgi = CGI.new values = cgi.cookies['name'] # <== array of 'name' # if not 'name' included, then return []. names = cgi.cookies.keys # <== array of cookie names and cgi.cookies is a hash. === GET COOKIE OBJECTS require "cgi" cgi = CGI.new for name, cookie in cgi.cookies cookie.expires = Time.now + 30 end cgi.out("cookie" => cgi.cookies){"string"} cgi.cookies # { "name1" => cookie1, "name2" => cookie2, ... } require "cgi" cgi = CGI.new cgi.cookies['name'].expires = Time.now + 30 cgi.out("cookie" => cgi.cookies['name']){"string"} and see MAKE COOKIE OBJECT. === GET ENVIRONMENT VALUE require "cgi" cgi = CGI.new value = cgi.auth_type # ENV["AUTH_TYPE"] see http://www.w3.org/CGI/ AUTH_TYPE CONTENT_LENGTH CONTENT_TYPE GATEWAY_INTERFACE PATH_INFO PATH_TRANSLATED QUERY_STRING REMOTE_ADDR REMOTE_HOST REMOTE_IDENT REMOTE_USER REQUEST_METHOD SCRIPT_NAME SERVER_NAME SERVER_PORT SERVER_PROTOCOL SERVER_SOFTWARE content_length and server_port return Integer. and the others return String. and HTTP_COOKIE, HTTP_COOKIE2 value = cgi.raw_cookie # ENV["HTTP_COOKIE"] value = cgi.raw_cookie2 # ENV["HTTP_COOKIE2"] and other HTTP_* value = cgi.accept # ENV["HTTP_ACCEPT"] value = cgi.accept_charset # ENV["HTTP_ACCEPT_CHARSET"] HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_ACCEPT_ENCODING HTTP_ACCEPT_LANGUAGE HTTP_CACHE_CONTROL HTTP_FROM HTTP_HOST HTTP_NEGOTIATE HTTP_PRAGMA HTTP_REFERER HTTP_USER_AGENT === PRINT HTTP HEADER AND HTML STRING TO $DEFAULT_OUTPUT ($>) require "cgi" cgi = CGI.new("html3") # add HTML generation methods cgi.out() do cgi.html() do cgi.head{ cgi.title{"TITLE"} } + cgi.body() do cgi.form() do cgi.textarea("get_text") + cgi.br + cgi.submit end + cgi.pre() do CGI::escapeHTML( "params: " + cgi.params.inspect + "\n" + "cookies: " + cgi.cookies.inspect + "\n" + ENV.collect() do |key, value| key + " --> " + value + "\n" end.join("") ) end end end end # add HTML generation methods CGI.new("html3") # html3.2 CGI.new("html4") # html4.0 (Strict) CGI.new("html4Tr") # html4.0 Transitional CGI.new("html4Fr") # html4.0 Frameset =end raise "Please, use ruby1.5.4 or later." if RUBY_VERSION < "1.5.4" require 'English' class CGI CR = "\015" LF = "\012" EOL = CR + LF VERSION = "2.1.1" RELEASE_DATE = "2000-12-14" VERSION_CODE = 211 RELEASE_CODE = 20001214 NEEDS_BINMODE = true if /WIN/ni === RUBY_PLATFORM PATH_SEPARATOR = {'UNIX'=>'/', 'WINDOWS'=>'\\', 'MACINTOSH'=>':'} HTTP_STATUS = { "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 = %w[ Sun Mon Tue Wed Thu Fri Sat ] RFC822_MONTHS = %w[ Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec ] def env_table ENV end def stdinput $stdin end def stdoutput $DEFAULT_OUTPUT end private :env_table, :stdinput, :stdoutput =begin == METHODS =end =begin === ESCAPE URL ENCODE url_encoded_string = CGI::escape("string") =end def CGI::escape(string) string.gsub(/([^ a-zA-Z0-9_.-]+)/n) do '%' + Regexp::last_match[1].unpack('H2' * Regexp::last_match[1].size).join('%').upcase end.tr(' ', '+') end =begin === UNESCAPE URL ENCODED string = CGI::unescape("url encoded string") =end def CGI::unescape(string) string.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n) do [Regexp::last_match[1].delete('%')].pack('H*') end end =begin === ESCAPE HTML &\"<> CGI::escapeHTML("string") =end def CGI::escapeHTML(string) string.gsub(/&/n, '&').gsub(/\"/n, '"').gsub(/>/n, '>').gsub(/' when /\Alt\z/ni then '<' when /\A#(\d+)\z/n then if Integer(Regexp::last_match[1]) < 256 Integer(Regexp::last_match[1]).chr else if Integer(Regexp::last_match[1]) < 65536 and ($KCODE[0] == ?u or $KCODE[0] == ?U) [Integer(Regexp::last_match[1])].pack("U") else "&##{Regexp::last_match[1]};" end end when /\A#x([0-9a-f]+)\z/ni then if Regexp::last_match[1].hex < 256 Regexp::last_match[1].hex.chr else if Regexp::last_match[1].hex < 65536 and ($KCODE[0] == ?u or $KCODE[0] == ?U) [Regexp::last_match[1].hex].pack("U") else "&#x#{Regexp::last_match[1]};" end end else "&#{Regexp::last_match[1]};" end end end =begin === ESCAPE ELEMENT print CGI::escapeElement('
', "A", "IMG") # "
<A HREF="url"></A>" print CGI::escapeElement('
', ["A", "IMG"]) # "
<A HREF="url"></A>" =end def CGI::escapeElement(string, *element) unless element.empty? string.gsub(/<\/?(?:#{element.join("|")})(?!\w)(?:.|\n)*?>/ni) do CGI::escapeHTML(Regexp::last_match[0]) end else string end end =begin === UNESCAPE ELEMENT print CGI::unescapeElement( CGI::escapeHTML('
'), "A", "IMG") # "<BR>" print CGI::unescapeElement( CGI::escapeHTML('
'), ["A", "IMG"]) # "<BR>" =end def CGI::unescapeElement(string, *element) string.gsub(/<\/?(?:#{element.join("|")})(?!\w)(?:.|\n)*?>/ni) do CGI::unescapeHTML(Regexp::last_match[0]) end end =begin === MAKE RFC1123 DATE STRING CGI::rfc1123_date(Time.now) # Sat, 1 Jan 2000 00:00:00 GMT =end def CGI::rfc1123_date(time) t = time.clone.gmtime return format("%s, %d %s %d %.2d:%.2d:%.2d GMT", RFC822_DAYS[t.wday], t.day, RFC822_MONTHS[t.month-1], t.year, t.hour, t.min, t.sec) end =begin === MAKE HTTP HEADER STRING 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 "language" => "ja", "expires" => Time.now + 30, "cookie" => [cookie1, cookie2], "my_header1" => "my_value" "my_header2" => "my_value"}) header will not convert charset. status: "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" =end def header(options = "text/html") buf = "" if options.kind_of?(String) options = { "type" => options } end unless options.has_key?("type") options["type"] = "text/html" end if options.has_key?("charset") options["type"].concat( "; charset=" ) options["type"].concat( options.delete("charset") ) end if options.delete("nph") or (/IIS/n === env_table['SERVER_SOFTWARE']) buf.concat( (env_table["SERVER_PROTOCOL"] or "HTTP/1.0") + " " ) buf.concat( (HTTP_STATUS[options["status"]] or options["status"] or "200 OK" ) + EOL ) buf.concat( "Date: " + CGI::rfc1123_date(Time.now) + EOL ) unless options.has_key?("server") options["server"] = (env_table['SERVER_SOFTWARE'] or "") end unless options.has_key?("connection") options["connection"] = "close" end options.delete("status") end if options.has_key?("status") buf.concat("Status: " + options.delete("status") + EOL) end if options.has_key?("server") buf.concat("Server: " + options.delete("server") + EOL) end if options.has_key?("connection") buf.concat("Connection: " + options.delete("connection") + EOL) end buf.concat("Content-Type: " + options.delete("type") + EOL) if options.has_key?("length") buf.concat("Content-Length: " + options.delete("length").to_s + EOL) end if options.has_key?("language") buf.concat("Content-Language: " + options.delete("language") + EOL) end if options.has_key?("expires") buf.concat("Expires: " + CGI::rfc1123_date( options.delete("expires") ) + EOL) end if options.has_key?("cookie") if options["cookie"].kind_of?(String) or options["cookie"].kind_of?(Cookie) buf.concat("Set-Cookie: " + options.delete("cookie").to_s + EOL) elsif options["cookie"].kind_of?(Array) options.delete("cookie").each{|cookie| buf.concat("Set-Cookie: " + cookie.to_s + EOL) } elsif options["cookie"].kind_of?(Hash) options.delete("cookie").each_value{|cookie| buf.concat("Set-Cookie: " + cookie.to_s + EOL) } end end if @output_cookies for cookie in @output_cookies buf.concat("Set-Cookie: " + cookie.to_s + EOL) end end options.each{|key, value| buf.concat(key + ": " + value + EOL) } if defined?(MOD_RUBY) buf.scan(/([^:]+): (.+)#{EOL}/n){ Apache::request[Regexp::last_match[1]] = Regexp::last_match[2] } Apache::request.send_http_header '' else buf + EOL end end # header() =begin === PRINT HTTP HEADER AND STRING TO $DEFAULT_OUTPUT ($>) 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" } if "HEAD" == REQUEST_METHOD then output only HTTP header. if charset is "iso-2022-jp" or "euc-jp" or "shift_jis" then convert string charset, and set language to "ja". =end def out(options = "text/html") options = { "type" => options } if options.kind_of?(String) content = yield if options.has_key?("charset") require "nkf" case options["charset"] when /iso-2022-jp/ni content = NKF::nkf('-j', content) options["language"] = "ja" unless options.has_key?("language") when /euc-jp/ni content = NKF::nkf('-e', content) options["language"] = "ja" unless options.has_key?("language") when /shift_jis/ni content = NKF::nkf('-s', content) options["language"] = "ja" unless options.has_key?("language") end end options["length"] = content.length.to_s output = stdoutput output.binmode if defined? output.binmode output.print header(options) output.print content unless "HEAD" == env_table['REQUEST_METHOD'] end =begin === PRINT cgi = CGI.new cgi.print # default: cgi.print == $DEFAULT_OUTPUT.print =end def print(*options) stdoutput.print(*options) end =begin === MAKE COOKIE OBJECT cookie1 = CGI::Cookie::new("name", "value1", "value2", ...) cookie1 = CGI::Cookie::new({"name" => "name", "value" => "value"}) cookie1 = CGI::Cookie::new({'name' => 'name', 'value' => ['value1', 'value2', ...], 'path' => 'path', # optional 'domain' => 'domain', # optional 'expires' => Time.now, # optional 'secure' => true # optional }) cgi.out({"cookie" => [cookie1, cookie2]}){ "string" } name = cookie1.name values = cookie1.value path = cookie1.path domain = cookie1.domain expires = cookie1.expires secure = cookie1.secure cookie1.name = 'name' cookie1.value = ['value1', 'value2', ...] cookie1.path = 'path' cookie1.domain = 'domain' cookie1.expires = Time.now + 30 cookie1.secure = true =end require "delegate" class Cookie < SimpleDelegator def initialize(name = "", *value) options = if name.kind_of?(String) { "name" => name, "value" => value } else name end unless options.has_key?("name") raise ArgumentError, "`name' required" end @name = options["name"] @value = Array(options["value"]) # simple support for IE if options["path"] @path = options["path"] elsif ENV["REQUEST_URI"] @path = ENV["REQUEST_URI"].sub(/\?.*/n,'') if ENV["PATH_INFO"] @path = @path[0...@path.rindex(ENV["PATH_INFO"])] end else @path = (ENV["SCRIPT_NAME"] or "") end @domain = options["domain"] @expires = options["expires"] @secure = options["secure"] == true ? true : false super(@value) end attr_accessor("name", "value", "path", "domain", "expires") attr_reader("secure") def secure=(val) @secure = val if val == true or val == false @secure end def to_s buf = "" buf.concat(@name + '=') if @value.kind_of?(String) buf.concat CGI::escape(@value) else buf.concat(@value.collect{|v| CGI::escape(v) }.join("&")) end if @domain buf.concat('; domain=' + @domain) end if @path buf.concat('; path=' + @path) end if @expires buf.concat('; expires=' + CGI::rfc1123_date(@expires)) end if @secure == true buf.concat('; secure') end buf end end # class Cookie =begin === PARSE RAW COOKIE STRING cookies = CGI::Cookie::parse("raw_cookie_string") # { "name1" => cookie1, "name2" => cookie2, ... } =end def Cookie::parse(raw_cookie) cookies = Hash.new([]) return cookies unless raw_cookie raw_cookie.split('; ').each do |pairs| name, values = pairs.split('=',2) name = CGI::unescape(name) values ||= "" values = values.split('&').collect{|v| CGI::unescape(v) } if cookies.has_key?(name) cookies[name].value.push(*values) else cookies[name] = Cookie::new({ "name" => name, "value" => values }) end end cookies end =begin === PARSE QUERY STRING params = CGI::parse("query_string") # {"name1" => ["value1", "value2", ...], # "name2" => ["value1", "value2", ...], ... } =end def CGI::parse(query) params = Hash.new([]) query.split(/[&;]/n).each do |pairs| key, value = pairs.split('=',2).collect{|v| CGI::unescape(v) } if params.has_key?(key) params[key].push(value) else params[key] = [value] end end params end module QueryExtension for env in %w[ CONTENT_LENGTH SERVER_PORT ] eval( <<-END ) def #{env.sub(/^HTTP_/n, '').downcase} env_table["#{env}"] && Integer(env_table["#{env}"]) end END end for env in %w[ AUTH_TYPE CONTENT_TYPE GATEWAY_INTERFACE PATH_INFO PATH_TRANSLATED QUERY_STRING REMOTE_ADDR REMOTE_HOST REMOTE_IDENT REMOTE_USER REQUEST_METHOD SCRIPT_NAME SERVER_NAME SERVER_PROTOCOL SERVER_SOFTWARE HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_ACCEPT_ENCODING HTTP_ACCEPT_LANGUAGE HTTP_CACHE_CONTROL HTTP_FROM HTTP_HOST HTTP_NEGOTIATE HTTP_PRAGMA HTTP_REFERER HTTP_USER_AGENT ] eval( <<-END ) def #{env.sub(/^HTTP_/n, '').downcase} env_table["#{env}"] end END end def raw_cookie env_table["HTTP_COOKIE"] end def raw_cookie2 env_table["HTTP_COOKIE2"] end attr_accessor("cookies") attr("params") def params=(hash) @params.clear @params.update(hash) end def read_multipart(boundary, content_length) params = Hash.new([]) boundary = "--" + boundary buf = "" bufsize = 10 * 1024 # start multipart/form-data stdinput.binmode boundary_size = boundary.size + EOL.size content_length -= boundary_size status = stdinput.read(boundary_size) if nil == status raise EOFError, "no content body" end require "tempfile" until -1 == content_length head = nil body = Tempfile.new("CGI") body.binmode until head and (/#{boundary}(?:#{EOL}|--)/n === buf) if (not head) and (/#{EOL}#{EOL}/n === buf) buf = buf.sub(/\A((?:.|\n)*?#{EOL})#{EOL}/n) do head = Regexp::last_match[1].dup "" end next end if head and ( (EOL + boundary + EOL).size < buf.size ) body.print buf[0 ... (buf.size - (EOL + boundary + EOL).size)] buf[0 ... (buf.size - (EOL + boundary + EOL).size)] = "" end c = if bufsize < content_length stdinput.read(bufsize) or '' else stdinput.read(content_length) or '' end buf.concat c content_length -= c.size end buf = buf.sub(/\A((?:.|\n)*?)(?:#{EOL})?#{boundary}(#{EOL}|--)/n) do body.print Regexp::last_match[1] if "--" == Regexp::last_match[2] content_length = -1 end "" end body.rewind eval <<-END def body.local_path #{body.path.dump} end END /Content-Disposition:.* filename="?([^\";]*)"?/ni === head eval <<-END def body.original_filename #{ filename = (Regexp::last_match[1] or "").dup if (/Mac/ni === env_table['HTTP_USER_AGENT']) and (/Mozilla/ni === env_table['HTTP_USER_AGENT']) and (not /MSIE/ni === env_table['HTTP_USER_AGENT']) CGI::unescape(filename) else filename end.dump.untaint }.taint end END /Content-Type: (.*)/ni === head eval <<-END def body.content_type #{(Regexp::last_match[1] or "").dump.untaint}.taint end END /Content-Disposition:.* name="?([^\";]*)"?/ni === head name = Regexp::last_match[1].dup if params.has_key?(name) params[name].push(body) else params[name] = [body] end end params end # read_multipart private :read_multipart # offline mode. read name=value pairs on standard input. def read_from_cmdline require "shellwords" string = unless ARGV.empty? ARGV.join(' ') else if STDIN.tty? STDERR.print( %|(offline mode: enter name=value pairs on standard input)\n| ) end readlines.join(' ').gsub(/\n/n, '') end.gsub(/\\=/n, '%3D').gsub(/\\&/n, '%26') words = Shellwords.shellwords(string) if words.find{|x| /=/n === x } words.join('&') else words.join('+') end end private :read_from_cmdline def initialize_query() if ("POST" == env_table['REQUEST_METHOD']) and (%r|\Amultipart/form-data.*boundary=\"?([^\";,]+)\"?|n === env_table['CONTENT_TYPE']) boundary = Regexp::last_match[1].dup @params = read_multipart(boundary, Integer(env_table['CONTENT_LENGTH'])) else @params = CGI::parse( case env_table['REQUEST_METHOD'] when "GET", "HEAD" if defined?(MOD_RUBY) Apache::request.args or "" else env_table['QUERY_STRING'] or "" end when "POST" stdinput.binmode stdinput.read(Integer(env_table['CONTENT_LENGTH'])) or '' else read_from_cmdline end ) end @cookies = CGI::Cookie::parse((env_table['HTTP_COOKIE'] or env_table['COOKIE'])) end private :initialize_query def [](*args) @params[*args] end def keys(*args) @params.keys(*args) end def has_key?(*args) @params.has_key?(*args) end alias key? has_key? alias include? has_key? end # QueryExtension =begin === HTML PRETTY FORMAT print CGI::pretty("") # # # # print CGI::pretty("", "\t") # # # # =end 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 = Regexp::last_match[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 =begin == HTML ELEMENTS cgi = CGI.new("html3") # add HTML generation methods cgi.element cgi.element{ "string" } cgi.element({ "ATTRILUTE1" => "value1", "ATTRIBUTE2" => "value2" }) cgi.element({ "ATTRILUTE1" => "value1", "ATTRIBUTE2" => "value2" }){ "string" } # add HTML generation methods CGI.new("html3") # html3.2 CGI.new("html4") # html4.0 (Strict) CGI.new("html4Tr") # html4.0 Transitional CGI.new("html4Fr") # html4.0 Frameset =end module TagMaker # - - def nn_element_def(element) <<-END.gsub(/element\.downcase/n, element.downcase).gsub(/element\.upcase/n, element.upcase) "" + if block_given? yield.to_s else "" end + "" END end # - O EMPTY def nOE_element_def(element) <<-END.gsub(/element\.downcase/n, element.downcase).gsub(/element\.upcase/n, element.upcase) "" END end # O O or - O def nO_element_def(element) <<-END.gsub(/element\.downcase/n, element.downcase).gsub(/element\.upcase/n, element.upcase) "" + if block_given? yield.to_s + "" else "" end END end end # TagMaker module HtmlExtension =begin === A ELEMENT a("url") # = a({ "HREF" => "url" }) =end def a(href = "") attributes = if href.kind_of?(String) { "HREF" => href } else href end if block_given? super(attributes){ yield } else super(attributes) end end =begin === BASE ELEMENT base("url") # = base({ "HREF" => "url" }) =end def base(href = "") attributes = if href.kind_of?(String) { "HREF" => href } else href end if block_given? super(attributes){ yield } else super(attributes) end end =begin === BLOCKQUOTE ELEMENT blockquote("url"){ "string" } # = blockquote({ "CITE" => "url" }){ "string" } =end def blockquote(cite = nil) attributes = if cite.kind_of?(String) { "CITE" => cite } else cite or "" end if block_given? super(attributes){ yield } else super(attributes) end end =begin === CAPTION ELEMENT caption("align"){ "string" } # = caption({ "ALIGN" => "align" }){ "string" } =end def caption(align = nil) attributes = if align.kind_of?(String) { "ALIGN" => align } else align or "" end if block_given? super(attributes){ yield } else super(attributes) end end =begin === CHECKBOX checkbox("name") # = checkbox({ "NAME" => "name" }) checkbox("name", "value") # = checkbox({ "NAME" => "name", "VALUE" => "value" }) checkbox("name", "value", true) # = checkbox({ "NAME" => "name", "VALUE" => "value", "CHECKED" => true }) =end def checkbox(name = "", value = nil, checked = nil) attributes = if name.kind_of?(String) { "TYPE" => "checkbox", "NAME" => name, "VALUE" => value, "CHECKED" => checked } else name["TYPE"] = "checkbox" name end input(attributes) end =begin === CHECKBOX_GROUP checkbox_group("name", "foo", "bar", "baz") # foo # bar # baz checkbox_group("name", ["foo"], ["bar", true], "baz") # foo # bar # baz checkbox_group("name", ["1", "Foo"], ["2", "Bar", true], "Baz") # Foo # Bar # Baz checkbox_group({ "NAME" => "name", "VALUES" => ["foo", "bar", "baz"] }) checkbox_group({ "NAME" => "name", "VALUES" => [["foo"], ["bar", true], "baz"] }) checkbox_group({ "NAME" => "name", "VALUES" => [["1", "Foo"], ["2", "Bar", true], "Baz"] }) =end def checkbox_group(name = "", *values) if name.kind_of?(Hash) values = name["VALUES"] name = name["NAME"] end values.collect{|value| if value.kind_of?(String) checkbox(name, value) + value else if value[value.size - 1] == true checkbox(name, value[0], true) + value[value.size - 2] else checkbox(name, value[0]) + value[value.size - 1] end end }.to_s end =begin === FILE_FIELD file_field("name") # file_field("name", 40) # file_field("name", 40, 100) # file_field({ "NAME" => "name", "SIZE" => 40 }) # =end def file_field(name = "", size = 20, maxlength = nil) attributes = if name.kind_of?(String) { "TYPE" => "file", "NAME" => name, "SIZE" => size.to_s } else name["TYPE"] = "file" name end attributes["MAXLENGTH"] = maxlength.to_s if maxlength input(attributes) end =begin === FORM ELEMENT form{ "string" } #
string
form("get"){ "string" } #
string
form("get", "url"){ "string" } #
string
form({"METHOD" => "post", ENCTYPE => "enctype"}){ "string" } #
string
=end def form(method = "post", action = nil, enctype = "application/x-www-form-urlencoded") attributes = if method.kind_of?(String) { "METHOD" => method, "ACTION" => action, "ENCTYPE" => enctype } else unless method.has_key?("METHOD") method["METHOD"] = method end unless method.has_key?("ENCTYPE") method["ENCTYPE"] = enctype end method end if block_given? body = yield else body = "" end if @output_hidden hidden = @output_hidden.collect{|k,v| "" }.to_s body.concat hidden end super(attributes){body} end =begin === HIDDEN FIELD hidden("name") # hidden("name", "value") # hidden({ "NAME" => "name", "VALUE" => "reset", "ID" => "foo" }) # =end def hidden(name = "", value = nil) attributes = if name.kind_of?(String) { "TYPE" => "hidden", "NAME" => name, "VALUE" => value } else name["TYPE"] = "hidden" name end input(attributes) end =begin === HTML ELEMENT html{ "string" } # string html({ "LANG" => "ja" }){ "string" } # string html({ "DOCTYPE" => false }){ "string" } # string html({ "DOCTYPE" => '' }){ "string" } # string html({ "PRETTY" => " " }){ "" } # # # # # html({ "PRETTY" => "\t" }){ "" } # # # # # html("PRETTY"){ "" } # = html({ "PRETTY" => " " }){ "" } html(if $VERBOSE then "PRETTY" end){ "HTML string" } =end def html(attributes = {}) if nil == attributes attributes = {} elsif "PRETTY" == attributes attributes = { "PRETTY" => true } end pretty = attributes.delete("PRETTY") pretty = " " if true == pretty buf = "" if attributes.has_key?("DOCTYPE") if attributes["DOCTYPE"] buf.concat( attributes.delete("DOCTYPE") ) else attributes.delete("DOCTYPE") end else buf.concat( doctype ) end if block_given? buf.concat( super(attributes){ yield } ) else buf.concat( super(attributes) ) end if pretty CGI::pretty(buf, pretty) else buf end end =begin === IMAGE_BUTTON image_button("url") # image_button("url", "name", "string") # image_button({ "SRC" => "url", "ATL" => "strng" }) # =end def image_button(src = "", name = nil, alt = nil) attributes = if src.kind_of?(String) { "TYPE" => "image", "SRC" => src, "NAME" => name, "ALT" => alt } else src["TYPE"] = "image" src["SRC"] ||= "" src end input(attributes) end =begin === IMG ELEMENT img("src", "alt", 100, 50) # alt img({ "SRC" => "src", "ALT" => "alt", "WIDTH" => 100, "HEIGHT" => 50 }) # alt =end def img(src = "", alt = "", width = nil, height = nil) attributes = if src.kind_of?(String) { "SRC" => src, "ALT" => alt } else src end attributes["WIDTH"] = width.to_s if width attributes["HEIGHT"] = height.to_s if height super(attributes) end =begin === MULTIPART FORM multipart_form{ "string" } #
string
multipart_form("url"){ "string" } #
string
=end def multipart_form(action = nil, enctype = "multipart/form-data") attributes = if action == nil { "METHOD" => "post", "ENCTYPE" => enctype } elsif action.kind_of?(String) { "METHOD" => "post", "ACTION" => action, "ENCTYPE" => enctype } else unless action.has_key?("METHOD") action["METHOD"] = "post" end unless action.has_key?("ENCTYPE") action["ENCTYPE"] = enctype end action end if block_given? form(attributes){ yield } else form(attributes) end end =begin === PASSWORD_FIELD password_field("name") # password_field("name", "value") # password_field("password", "value", 80, 200) # password_field({ "NAME" => "name", "VALUE" => "value" }) # =end def password_field(name = "", value = nil, size = 40, maxlength = nil) attributes = if name.kind_of?(String) { "TYPE" => "password", "NAME" => name, "VALUE" => value, "SIZE" => size.to_s } else name["TYPE"] = "password" name end attributes["MAXLENGTH"] = maxlength.to_s if maxlength input(attributes) end =begin === POPUP_MENU popup_menu("name", "foo", "bar", "baz") # popup_menu("name", ["foo"], ["bar", true], "baz") # popup_menu("name", ["1", "Foo"], ["2", "Bar", true], "Baz") # popup_menu({"NAME" => "name", "SIZE" => 2, "MULTIPLE" => true, "VALUES" => [["1", "Foo"], ["2", "Bar", true], "Baz"] }) # =end def popup_menu(name = "", *values) if name.kind_of?(Hash) values = name["VALUES"] size = name["SIZE"].to_s if name["SIZE"] multiple = name["MULTIPLE"] name = name["NAME"] else size = nil multiple = nil end select({ "NAME" => name, "SIZE" => size, "MULTIPLE" => multiple }){ values.collect{|value| if value.kind_of?(String) option({ "VALUE" => value }){ value } else if value[value.size - 1] == true option({ "VALUE" => value[0], "SELECTED" => true }){ value[value.size - 2] } else option({ "VALUE" => value[0] }){ value[value.size - 1] } end end }.to_s } end =begin === RADIO_BUTTON radio_button("name", "value") # radio_button("name", "value", true) # radio_button({ "NAME" => "name", "VALUE" => "value", "ID" => "foo" }) # =end def radio_button(name = "", value = nil, checked = nil) attributes = if name.kind_of?(String) { "TYPE" => "radio", "NAME" => name, "VALUE" => value, "CHECKED" => checked } else name["TYPE"] = "radio" name end input(attributes) end =begin === RADIO_GROUP radio_group("name", "foo", "bar", "baz") # foo # bar # baz radio_group("name", ["foo"], ["bar", true], "baz") # foo # bar # baz radio_group("name", ["1", "Foo"], ["2", "Bar", true], "Baz") # Foo # Bar # Baz radio_group({ "NAME" => "name", "VALUES" => ["foo", "bar", "baz"] }) radio_group({ "NAME" => "name", "VALUES" => [["foo"], ["bar", true], "baz"] }) radio_group({ "NAME" => "name", "VALUES" => [["1", "Foo"], ["2", "Bar", true], "Baz"] }) =end def radio_group(name = "", *values) if name.kind_of?(Hash) values = name["VALUES"] name = name["NAME"] end values.collect{|value| if value.kind_of?(String) radio_button(name, value) + value else if value[value.size - 1] == true radio_button(name, value[0], true) + value[value.size - 2] else radio_button(name, value[0]) + value[value.size - 1] end end }.to_s end =begin === RESET BUTTON reset # reset("reset") # reset({ "VALUE" => "reset", "ID" => "foo" }) # =end def reset(value = nil, name = nil) attributes = if (not value) or value.kind_of?(String) { "TYPE" => "reset", "VALUE" => value, "NAME" => name } else value["TYPE"] = "reset" value end input(attributes) end =begin === SCROLLING_LIST scrolling_list({"NAME" => "name", "SIZE" => 2, "MULTIPLE" => true, "VALUES" => [["1", "Foo"], ["2", "Bar", true], "Baz"] }) # =end alias scrolling_list popup_menu =begin === SUBMIT BUTTON submit # submit("ok") # submit("ok", "button1") # submit({ "VALUE" => "ok", "NAME" => "button1", "ID" => "foo" }) # =end def submit(value = nil, name = nil) attributes = if (not value) or value.kind_of?(String) { "TYPE" => "submit", "VALUE" => value, "NAME" => name } else value["TYPE"] = "submit" value end input(attributes) end =begin === TEXT_FIELD text_field("name") # text_field("name", "value") # text_field("name", "value", 80) # text_field("name", "value", 80, 200) # text_field({ "NAME" => "name", "VALUE" => "value" }) # =end def text_field(name = "", value = nil, size = 40, maxlength = nil) attributes = if name.kind_of?(String) { "TYPE" => "text", "NAME" => name, "VALUE" => value, "SIZE" => size.to_s } else name["TYPE"] = "text" name end attributes["MAXLENGTH"] = maxlength.to_s if maxlength input(attributes) end =begin === TEXTAREA ELEMENT textarea("name") # = textarea({ "NAME" => "name", "COLS" => 70, "ROWS" => 10 }) textarea("name", 40, 5) # = textarea({ "NAME" => "name", "COLS" => 40, "ROWS" => 5 }) =end def textarea(name = "", cols = 70, rows = 10) attributes = if name.kind_of?(String) { "NAME" => name, "COLS" => cols.to_s, "ROWS" => rows.to_s } else name end if block_given? super(attributes){ yield } else super(attributes) end end end # HtmlExtension module Html3 def doctype %|| end def element_init extend TagMaker methods = "" # - - for element in %w[ A TT I B U STRIKE BIG SMALL SUB SUP EM STRONG DFN CODE SAMP KBD VAR CITE FONT ADDRESS DIV center MAP APPLET PRE XMP LISTING DL OL UL DIR MENU SELECT table TITLE STYLE SCRIPT H1 H2 H3 H4 H5 H6 TEXTAREA FORM BLOCKQUOTE CAPTION ] methods.concat( <<-BEGIN + nn_element_def(element) + <<-END ) def #{element.downcase}(attributes = {}) BEGIN end END end # - O EMPTY for element in %w[ IMG BASE BASEFONT BR AREA LINK PARAM HR INPUT ISINDEX META ] methods.concat( <<-BEGIN + nOE_element_def(element) + <<-END ) def #{element.downcase}(attributes = {}) BEGIN end END end # O O or - O for element in %w[ HTML HEAD BODY P PLAINTEXT DT DD LI OPTION tr th td ] methods.concat( <<-BEGIN + nO_element_def(element) + <<-END ) def #{element.downcase}(attributes = {}) BEGIN end END end eval(methods) end end # Html3 module Html4 def doctype %|| end def element_init extend TagMaker methods = "" # - - for element in %w[ TT I B BIG SMALL EM STRONG DFN CODE SAMP KBD VAR CITE ABBR ACRONYM SUB SUP SPAN BDO ADDRESS DIV MAP OBJECT H1 H2 H3 H4 H5 H6 PRE Q INS DEL DL OL UL LABEL SELECT OPTGROUP FIELDSET LEGEND BUTTON TABLE TITLE STYLE SCRIPT NOSCRIPT TEXTAREA FORM A BLOCKQUOTE CAPTION ] methods.concat( <<-BEGIN + nn_element_def(element) + <<-END ) def #{element.downcase}(attributes = {}) BEGIN end END end # - O EMPTY for element in %w[ IMG BASE BR AREA LINK PARAM HR INPUT COL META ] methods.concat( <<-BEGIN + nOE_element_def(element) + <<-END ) def #{element.downcase}(attributes = {}) BEGIN end END end # O O or - O for element in %w[ HTML BODY P DT DD LI OPTION THEAD TFOOT TBODY COLGROUP TR TH TD HEAD] methods.concat( <<-BEGIN + nO_element_def(element) + <<-END ) def #{element.downcase}(attributes = {}) BEGIN end END end eval(methods) end end # Html4 module Html4Tr def doctype %|| end def element_init extend TagMaker methods = "" # - - for element in %w[ TT I B U S STRIKE BIG SMALL EM STRONG DFN CODE SAMP KBD VAR CITE ABBR ACRONYM FONT SUB SUP SPAN BDO ADDRESS DIV CENTER MAP OBJECT APPLET H1 H2 H3 H4 H5 H6 PRE Q INS DEL DL OL UL DIR MENU LABEL SELECT OPTGROUP FIELDSET LEGEND BUTTON TABLE IFRAME NOFRAMES TITLE STYLE SCRIPT NOSCRIPT TEXTAREA FORM A BLOCKQUOTE CAPTION ] methods.concat( <<-BEGIN + nn_element_def(element) + <<-END ) def #{element.downcase}(attributes = {}) BEGIN end END end # - O EMPTY for element in %w[ IMG BASE BASEFONT BR AREA LINK PARAM HR INPUT COL ISINDEX META ] methods.concat( <<-BEGIN + nOE_element_def(element) + <<-END ) def #{element.downcase}(attributes = {}) BEGIN end END end # O O or - O for element in %w[ HTML BODY P DT DD LI OPTION THEAD TFOOT TBODY COLGROUP TR TH TD HEAD ] methods.concat( <<-BEGIN + nO_element_def(element) + <<-END ) def #{element.downcase}(attributes = {}) BEGIN end END end eval(methods) end end # Html4Tr module Html4Fr def doctype %|| end def element_init extend TagMaker extend Html4Tr element_init() methods = "" # - - for element in %w[ FRAMESET ] methods.concat( <<-BEGIN + nn_element_def(element) + <<-END ) def #{element.downcase}(attributes = {}) BEGIN end END end # - O EMPTY for element in %w[ FRAME ] methods.concat( <<-BEGIN + nOE_element_def(element) + <<-END ) def #{element.downcase}(attributes = {}) BEGIN end END end eval(methods) end end # Html4Fr def initialize(type = "query") extend QueryExtension if defined?(CGI_PARAMS) @params = CGI_PARAMS.nil? ? nil : CGI_PARAMS.dup @cookies = CGI_COOKIES.nil? ? nil : CGI_COOKIES.dup else initialize_query() # set @params, @cookies eval "CGI_PARAMS = @params.nil? ? nil : @params.dup" eval "CGI_COOKIES = @cookies.nil? ? nil : @cookies.dup" end @output_cookies = nil @output_hidden = nil 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 Html4Fr element_init() extend HtmlExtension end end if defined?(MOD_RUBY) and (RUBY_VERSION < "1.4.3") raise "Please, use ruby1.4.3 or later." else at_exit() do if defined?(CGI_PARAMS) remove_const(:CGI_PARAMS) remove_const(:CGI_COOKIES) end end end end =begin == HISTORY * Mon Dec 11 00:16:51 JST 2000 - wakou * version 2.1.1 * support -T1 on ruby 1.6.2 * body.original_filename: eval(str.dump.untaint).taint * body.content_type: eval(str.dump.untaint).taint * $& --> Regexp::last_match[0] * $1 --> Regexp::last_match[1] * $2 --> Regexp::last_match[2] * Thu Oct 12 01:16:59 JST 2000 - wakou * version 2.1.0 * bug fix: CGI::html(): PRETTY option didn't work. thanks to akira yamada * Wed Sep 13 06:09:26 JST 2000 - wakou * version 2.0.1 * bug fix: CGI::header(): output status header. thanks to Yasuhiro Fukuma * Tue Sep 12 06:56:51 JST 2000 - wakou * version 2.0.0 * require ruby1.5.4 or later. (ruby1.4 doesn't have block_given? method.) * improvement: CGI::escape(), CGI::unescape(). thanks to WATANABE Hirofumi * bug fix: CGI::escapeElement(). * improvement: CGI::unescapeHTML(). thanks to Kazuhiro NISHIYAMA * 2000/08/09 04:32:22 - matz * improvement: CGI::pretty() * 2000/06/23 07:01:34 - matz * change: iterator? --> block_given? * Sun Jun 18 23:31:44 JST 2000 - wakou * version 1.7.0 * change: version syntax. old: x.yz, now: x.y.z * 2000/06/13 15:49:27 - wakou * version 1.61 * read_multipart(): if no content body then raise EOFError. * 2000/06/03 18:16:17 - wakou * version 1.60 * improve: CGI::pretty() * 2000/05/30 19:04:08 - wakou * version 1.50 * CGI#out(): if "HEAD" == REQUEST_METHOD then output only HTTP header. * 2000/05/24 06:58:51 - wakou * version 1.40 * typo: CGI::Cookie::new() * bug fix: CGI::escape(): bad: " " --> "%2B"; true: " " --> "+"; thanks to Ryunosuke Ohshima * 2000/05/08 21:51:30 - wakou * version 1.31 * improvement of time forming new CGI object accompanied with HTML generation methods. * 2000/05/07 21:51:14 - wakou * version 1.30 * require English.rb * improvement of load time. * 2000/05/02 21:44:12 - wakou * version 1.21 * support for ruby 1.5.3 (2000-05-01) (Array#filter --> Array#collect!) * 2000/04/03 18:31:42 - wakou * version 1.20 * bug fix: CGI#image_button() can't get Hash option. thanks to Takashi Ikeda * CGI::unescapeHTML(): simple support for "〹" * CGI::Cookie::new(): simple support for IE * CGI::escape(): ' ' replaced by '+' * 1999/12/06 20:16:34 - wakou * version 1.10 * can make many CGI objects. * if use mod_ruby, then require ruby1.4.3 or later. * 1999/11/29 21:35:58 - wakou * version 1.01 * support for ruby 1.5.0 (1999-11-20) * 1999/09/13 23:00:58 - wakou * version 1.00 * COUTION! name change. CGI.rb --> cgi.rb * CGI#auth_type, CGI#content_length, CGI#content_type, ... if not ENV included it, then return nil. * CGI#content_length and CGI#server_port return Integer. * if not CGI#params.include?('name'), then CGI#params['name'] return []. * if not CGI#cookies.include?('name'), then CGI#cookies['name'] return []. * 1999/08/05 18:04:59 - wakou * version 0.41 * typo. thanks to MJ Ray HTTP_STATUS["NOT_INPLEMENTED"] --> HTTP_STATUS["NOT_IMPLEMENTED"] * 1999/07/20 20:44:31 - wakou * version 0.40 * COUTION! incompatible change. sorry, but probably this change is last big incompatible change. * CGI::print --> CGI#out cgi = CGI.new cgi.out{"string"} # old: CGI::print{"string"} * CGI::cookie --> CGI::Cookie::new cookie1 = CGI::Cookie::new # old: CGI::cookie * CGI::header --> CGI#header * 1999/06/29 06:50:21 - wakou * version 0.30 * COUTION! incompatible change. query = CGI.new cookies = query.cookies # old: query.cookie values = query.cookies[name] # old: query.cookie[name] * 1999/06/21 21:05:57 - wakou * version 0.24 * CGI::Cookie::parse() return { name => CGI::Cookie object } pairs. * 1999/06/20 23:29:12 - wakou * version 0.23 * modified a bit to clear module separation. * Mon Jun 14 17:49:32 JST 1999 - matz * version 0.22 * Cookies are now CGI::Cookie objects. * Cookie modeled after CGI::Cookie.pm. * Fri Jun 11 11:19:11 JST 1999 - matz * version 0.21 * modified a bit to clear module separation. * 1999/06/03 06:48:15 - wakou * version 0.20 * support for multipart form. * 1999/05/24 07:05:41 - wakou * version 0.10 * first release. $Date$ =end