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(/&/, "&").gsub(/\"/, """).gsub(/>/, ">").gsub(/</, "<")
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