rubysl-cgi 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.travis.yml +8 -0
  4. data/Gemfile +4 -0
  5. data/LICENSE +25 -0
  6. data/README.md +29 -0
  7. data/Rakefile +2 -0
  8. data/lib/cgi.rb +1 -0
  9. data/lib/rubysl/cgi.rb +2 -0
  10. data/lib/rubysl/cgi/cgi.rb +2333 -0
  11. data/lib/rubysl/cgi/version.rb +5 -0
  12. data/rubysl-cgi.gemspec +23 -0
  13. data/spec/cookie/domain_spec.rb +22 -0
  14. data/spec/cookie/expires_spec.rb +22 -0
  15. data/spec/cookie/initialize_spec.rb +146 -0
  16. data/spec/cookie/name_spec.rb +22 -0
  17. data/spec/cookie/parse_spec.rb +17 -0
  18. data/spec/cookie/path_spec.rb +22 -0
  19. data/spec/cookie/secure_spec.rb +69 -0
  20. data/spec/cookie/to_s_spec.rb +28 -0
  21. data/spec/cookie/value_spec.rb +79 -0
  22. data/spec/escapeElement_spec.rb +19 -0
  23. data/spec/escapeHTML_spec.rb +25 -0
  24. data/spec/escape_spec.rb +13 -0
  25. data/spec/header_spec.rb +7 -0
  26. data/spec/htmlextension/a_spec.rb +48 -0
  27. data/spec/htmlextension/base_spec.rb +32 -0
  28. data/spec/htmlextension/blockquote_spec.rb +32 -0
  29. data/spec/htmlextension/br_spec.rb +21 -0
  30. data/spec/htmlextension/caption_spec.rb +32 -0
  31. data/spec/htmlextension/checkbox_group_spec.rb +77 -0
  32. data/spec/htmlextension/checkbox_spec.rb +76 -0
  33. data/spec/htmlextension/doctype_spec.rb +26 -0
  34. data/spec/htmlextension/file_field_spec.rb +73 -0
  35. data/spec/htmlextension/fixtures/common.rb +16 -0
  36. data/spec/htmlextension/form_spec.rb +57 -0
  37. data/spec/htmlextension/frame_spec.rb +13 -0
  38. data/spec/htmlextension/frameset_spec.rb +13 -0
  39. data/spec/htmlextension/hidden_spec.rb +58 -0
  40. data/spec/htmlextension/html_spec.rb +65 -0
  41. data/spec/htmlextension/image_button_spec.rb +68 -0
  42. data/spec/htmlextension/img_spec.rb +84 -0
  43. data/spec/htmlextension/multipart_form_spec.rb +63 -0
  44. data/spec/htmlextension/password_field_spec.rb +83 -0
  45. data/spec/htmlextension/popup_menu_spec.rb +7 -0
  46. data/spec/htmlextension/radio_button_spec.rb +76 -0
  47. data/spec/htmlextension/radio_group_spec.rb +78 -0
  48. data/spec/htmlextension/reset_spec.rb +56 -0
  49. data/spec/htmlextension/scrolling_list_spec.rb +7 -0
  50. data/spec/htmlextension/shared/popup_menu.rb +94 -0
  51. data/spec/htmlextension/submit_spec.rb +56 -0
  52. data/spec/htmlextension/text_field_spec.rb +83 -0
  53. data/spec/htmlextension/textarea_spec.rb +72 -0
  54. data/spec/http_header_spec.rb +9 -0
  55. data/spec/initialize_spec.rb +171 -0
  56. data/spec/out_spec.rb +50 -0
  57. data/spec/parse_spec.rb +23 -0
  58. data/spec/pretty_spec.rb +23 -0
  59. data/spec/print_spec.rb +25 -0
  60. data/spec/queryextension/accept_charset_spec.rb +21 -0
  61. data/spec/queryextension/accept_encoding_spec.rb +21 -0
  62. data/spec/queryextension/accept_language_spec.rb +21 -0
  63. data/spec/queryextension/accept_spec.rb +21 -0
  64. data/spec/queryextension/auth_type_spec.rb +21 -0
  65. data/spec/queryextension/cache_control_spec.rb +21 -0
  66. data/spec/queryextension/content_length_spec.rb +25 -0
  67. data/spec/queryextension/content_type_spec.rb +21 -0
  68. data/spec/queryextension/cookies_spec.rb +9 -0
  69. data/spec/queryextension/element_reference_spec.rb +33 -0
  70. data/spec/queryextension/from_spec.rb +21 -0
  71. data/spec/queryextension/gateway_interface_spec.rb +21 -0
  72. data/spec/queryextension/has_key_spec.rb +6 -0
  73. data/spec/queryextension/host_spec.rb +21 -0
  74. data/spec/queryextension/include_spec.rb +6 -0
  75. data/spec/queryextension/key_spec.rb +6 -0
  76. data/spec/queryextension/keys_spec.rb +19 -0
  77. data/spec/queryextension/multipart_spec.rb +39 -0
  78. data/spec/queryextension/negotiate_spec.rb +21 -0
  79. data/spec/queryextension/params_spec.rb +44 -0
  80. data/spec/queryextension/path_info_spec.rb +21 -0
  81. data/spec/queryextension/path_translated_spec.rb +21 -0
  82. data/spec/queryextension/pragma_spec.rb +21 -0
  83. data/spec/queryextension/query_string_spec.rb +21 -0
  84. data/spec/queryextension/raw_cookie2_spec.rb +21 -0
  85. data/spec/queryextension/raw_cookie_spec.rb +21 -0
  86. data/spec/queryextension/referer_spec.rb +21 -0
  87. data/spec/queryextension/remote_addr_spec.rb +21 -0
  88. data/spec/queryextension/remote_host_spec.rb +21 -0
  89. data/spec/queryextension/remote_ident_spec.rb +21 -0
  90. data/spec/queryextension/remote_user_spec.rb +21 -0
  91. data/spec/queryextension/request_method_spec.rb +21 -0
  92. data/spec/queryextension/script_name_spec.rb +21 -0
  93. data/spec/queryextension/server_name_spec.rb +21 -0
  94. data/spec/queryextension/server_port_spec.rb +25 -0
  95. data/spec/queryextension/server_protocol_spec.rb +21 -0
  96. data/spec/queryextension/server_software_spec.rb +21 -0
  97. data/spec/queryextension/shared/has_key.rb +19 -0
  98. data/spec/queryextension/user_agent_spec.rb +21 -0
  99. data/spec/rfc1123_date_spec.rb +9 -0
  100. data/spec/shared/http_header.rb +111 -0
  101. data/spec/unescapeElement_spec.rb +19 -0
  102. data/spec/unescapeHTML_spec.rb +33 -0
  103. data/spec/unescape_spec.rb +14 -0
  104. metadata +293 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ca1e8dbe8f76a7d86da0d860b5a3c41b53056b5b
4
+ data.tar.gz: f661b2e445100662dba142d2e30e383985c6b892
5
+ SHA512:
6
+ metadata.gz: c81bd82dfec68d8f91b3957375fb0484e3dce0da709d168f839c85d5277f8df6bd1aebb6f373f1f35a1f1b24b687de64a703392c3a6471d0aff8de1b122e0fcf
7
+ data.tar.gz: 760e91d5984724defc82b925025fcc9a0f205a3e32c48db01f4da306d47bb34c73591ce12b685257c7b5c09ced959cbe4e667106882fffe86e2f90229ebcaeb6
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .rbx
data/.travis.yml ADDED
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ before_install:
3
+ - gem update --system
4
+ - gem --version
5
+ - gem install rubysl-bundler
6
+ script: bundle exec mspec spec
7
+ rvm:
8
+ - rbx-nightly-18mode
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rubysl-cgi.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,25 @@
1
+ Copyright (c) 2013, Brian Shirai
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+
7
+ 1. Redistributions of source code must retain the above copyright notice, this
8
+ list of conditions and the following disclaimer.
9
+ 2. Redistributions in binary form must reproduce the above copyright notice,
10
+ this list of conditions and the following disclaimer in the documentation
11
+ and/or other materials provided with the distribution.
12
+ 3. Neither the name of the library nor the names of its contributors may be
13
+ used to endorse or promote products derived from this software without
14
+ specific prior written permission.
15
+
16
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
+ DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY DIRECT,
20
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21
+ BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23
+ OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
25
+ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # RubySL::Cgi
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'rubysl-cgi'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install rubysl-cgi
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
data/lib/cgi.rb ADDED
@@ -0,0 +1 @@
1
+ require "rubysl/cgi"
data/lib/rubysl/cgi.rb ADDED
@@ -0,0 +1,2 @@
1
+ require "rubysl/cgi/cgi"
2
+ require "rubysl/cgi/version"
@@ -0,0 +1,2333 @@
1
+ #
2
+ # cgi.rb - cgi support library
3
+ #
4
+ # Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
5
+ #
6
+ # Copyright (C) 2000 Information-technology Promotion Agency, Japan
7
+ #
8
+ # Author: Wakou Aoyama <wakou@ruby-lang.org>
9
+ #
10
+ # Documentation: Wakou Aoyama (RDoc'd and embellished by William Webber)
11
+ #
12
+ # == Overview
13
+ #
14
+ # The Common Gateway Interface (CGI) is a simple protocol
15
+ # for passing an HTTP request from a web server to a
16
+ # standalone program, and returning the output to the web
17
+ # browser. Basically, a CGI program is called with the
18
+ # parameters of the request passed in either in the
19
+ # environment (GET) or via $stdin (POST), and everything
20
+ # it prints to $stdout is returned to the client.
21
+ #
22
+ # This file holds the +CGI+ class. This class provides
23
+ # functionality for retrieving HTTP request parameters,
24
+ # managing cookies, and generating HTML output. See the
25
+ # class documentation for more details and examples of use.
26
+ #
27
+ # The file cgi/session.rb provides session management
28
+ # functionality; see that file for more details.
29
+ #
30
+ # See http://www.w3.org/CGI/ for more information on the CGI
31
+ # protocol.
32
+
33
+ raise "Please, use ruby 1.5.4 or later." if RUBY_VERSION < "1.5.4"
34
+
35
+ require 'English'
36
+
37
+ # CGI class. See documentation for the file cgi.rb for an overview
38
+ # of the CGI protocol.
39
+ #
40
+ # == Introduction
41
+ #
42
+ # CGI is a large class, providing several categories of methods, many of which
43
+ # are mixed in from other modules. Some of the documentation is in this class,
44
+ # some in the modules CGI::QueryExtension and CGI::HtmlExtension. See
45
+ # CGI::Cookie for specific information on handling cookies, and cgi/session.rb
46
+ # (CGI::Session) for information on sessions.
47
+ #
48
+ # For queries, CGI provides methods to get at environmental variables,
49
+ # parameters, cookies, and multipart request data. For responses, CGI provides
50
+ # methods for writing output and generating HTML.
51
+ #
52
+ # Read on for more details. Examples are provided at the bottom.
53
+ #
54
+ # == Queries
55
+ #
56
+ # The CGI class dynamically mixes in parameter and cookie-parsing
57
+ # functionality, environmental variable access, and support for
58
+ # parsing multipart requests (including uploaded files) from the
59
+ # CGI::QueryExtension module.
60
+ #
61
+ # === Environmental Variables
62
+ #
63
+ # The standard CGI environmental variables are available as read-only
64
+ # attributes of a CGI object. The following is a list of these variables:
65
+ #
66
+ #
67
+ # AUTH_TYPE HTTP_HOST REMOTE_IDENT
68
+ # CONTENT_LENGTH HTTP_NEGOTIATE REMOTE_USER
69
+ # CONTENT_TYPE HTTP_PRAGMA REQUEST_METHOD
70
+ # GATEWAY_INTERFACE HTTP_REFERER SCRIPT_NAME
71
+ # HTTP_ACCEPT HTTP_USER_AGENT SERVER_NAME
72
+ # HTTP_ACCEPT_CHARSET PATH_INFO SERVER_PORT
73
+ # HTTP_ACCEPT_ENCODING PATH_TRANSLATED SERVER_PROTOCOL
74
+ # HTTP_ACCEPT_LANGUAGE QUERY_STRING SERVER_SOFTWARE
75
+ # HTTP_CACHE_CONTROL REMOTE_ADDR
76
+ # HTTP_FROM REMOTE_HOST
77
+ #
78
+ #
79
+ # For each of these variables, there is a corresponding attribute with the
80
+ # same name, except all lower case and without a preceding HTTP_.
81
+ # +content_length+ and +server_port+ are integers; the rest are strings.
82
+ #
83
+ # === Parameters
84
+ #
85
+ # The method #params() returns a hash of all parameters in the request as
86
+ # name/value-list pairs, where the value-list is an Array of one or more
87
+ # values. The CGI object itself also behaves as a hash of parameter names
88
+ # to values, but only returns a single value (as a String) for each
89
+ # parameter name.
90
+ #
91
+ # For instance, suppose the request contains the parameter
92
+ # "favourite_colours" with the multiple values "blue" and "green". The
93
+ # following behaviour would occur:
94
+ #
95
+ # cgi.params["favourite_colours"] # => ["blue", "green"]
96
+ # cgi["favourite_colours"] # => "blue"
97
+ #
98
+ # If a parameter does not exist, the former method will return an empty
99
+ # array, the latter an empty string. The simplest way to test for existence
100
+ # of a parameter is by the #has_key? method.
101
+ #
102
+ # === Cookies
103
+ #
104
+ # HTTP Cookies are automatically parsed from the request. They are available
105
+ # from the #cookies() accessor, which returns a hash from cookie name to
106
+ # CGI::Cookie object.
107
+ #
108
+ # === Multipart requests
109
+ #
110
+ # If a request's method is POST and its content type is multipart/form-data,
111
+ # then it may contain uploaded files. These are stored by the QueryExtension
112
+ # module in the parameters of the request. The parameter name is the name
113
+ # attribute of the file input field, as usual. However, the value is not
114
+ # a string, but an IO object, either an IOString for small files, or a
115
+ # Tempfile for larger ones. This object also has the additional singleton
116
+ # methods:
117
+ #
118
+ # #local_path():: the path of the uploaded file on the local filesystem
119
+ # #original_filename():: the name of the file on the client computer
120
+ # #content_type():: the content type of the file
121
+ #
122
+ # == Responses
123
+ #
124
+ # The CGI class provides methods for sending header and content output to
125
+ # the HTTP client, and mixes in methods for programmatic HTML generation
126
+ # from CGI::HtmlExtension and CGI::TagMaker modules. The precise version of HTML
127
+ # to use for HTML generation is specified at object creation time.
128
+ #
129
+ # === Writing output
130
+ #
131
+ # The simplest way to send output to the HTTP client is using the #out() method.
132
+ # This takes the HTTP headers as a hash parameter, and the body content
133
+ # via a block. The headers can be generated as a string using the #header()
134
+ # method. The output stream can be written directly to using the #print()
135
+ # method.
136
+ #
137
+ # === Generating HTML
138
+ #
139
+ # Each HTML element has a corresponding method for generating that
140
+ # element as a String. The name of this method is the same as that
141
+ # of the element, all lowercase. The attributes of the element are
142
+ # passed in as a hash, and the body as a no-argument block that evaluates
143
+ # to a String. The HTML generation module knows which elements are
144
+ # always empty, and silently drops any passed-in body. It also knows
145
+ # which elements require matching closing tags and which don't. However,
146
+ # it does not know what attributes are legal for which elements.
147
+ #
148
+ # There are also some additional HTML generation methods mixed in from
149
+ # the CGI::HtmlExtension module. These include individual methods for the
150
+ # different types of form inputs, and methods for elements that commonly
151
+ # take particular attributes where the attributes can be directly specified
152
+ # as arguments, rather than via a hash.
153
+ #
154
+ # == Examples of use
155
+ #
156
+ # === Get form values
157
+ #
158
+ # require "cgi"
159
+ # cgi = CGI.new
160
+ # value = cgi['field_name'] # <== value string for 'field_name'
161
+ # # if not 'field_name' included, then return "".
162
+ # fields = cgi.keys # <== array of field names
163
+ #
164
+ # # returns true if form has 'field_name'
165
+ # cgi.has_key?('field_name')
166
+ # cgi.has_key?('field_name')
167
+ # cgi.include?('field_name')
168
+ #
169
+ # CAUTION! cgi['field_name'] returned an Array with the old
170
+ # cgi.rb(included in ruby 1.6)
171
+ #
172
+ # === Get form values as hash
173
+ #
174
+ # require "cgi"
175
+ # cgi = CGI.new
176
+ # params = cgi.params
177
+ #
178
+ # cgi.params is a hash.
179
+ #
180
+ # cgi.params['new_field_name'] = ["value"] # add new param
181
+ # cgi.params['field_name'] = ["new_value"] # change value
182
+ # cgi.params.delete('field_name') # delete param
183
+ # cgi.params.clear # delete all params
184
+ #
185
+ #
186
+ # === Save form values to file
187
+ #
188
+ # require "pstore"
189
+ # db = PStore.new("query.db")
190
+ # db.transaction do
191
+ # db["params"] = cgi.params
192
+ # end
193
+ #
194
+ #
195
+ # === Restore form values from file
196
+ #
197
+ # require "pstore"
198
+ # db = PStore.new("query.db")
199
+ # db.transaction do
200
+ # cgi.params = db["params"]
201
+ # end
202
+ #
203
+ #
204
+ # === Get multipart form values
205
+ #
206
+ # require "cgi"
207
+ # cgi = CGI.new
208
+ # value = cgi['field_name'] # <== value string for 'field_name'
209
+ # value.read # <== body of value
210
+ # value.local_path # <== path to local file of value
211
+ # value.original_filename # <== original filename of value
212
+ # value.content_type # <== content_type of value
213
+ #
214
+ # and value has StringIO or Tempfile class methods.
215
+ #
216
+ # === Get cookie values
217
+ #
218
+ # require "cgi"
219
+ # cgi = CGI.new
220
+ # values = cgi.cookies['name'] # <== array of 'name'
221
+ # # if not 'name' included, then return [].
222
+ # names = cgi.cookies.keys # <== array of cookie names
223
+ #
224
+ # and cgi.cookies is a hash.
225
+ #
226
+ # === Get cookie objects
227
+ #
228
+ # require "cgi"
229
+ # cgi = CGI.new
230
+ # for name, cookie in cgi.cookies
231
+ # cookie.expires = Time.now + 30
232
+ # end
233
+ # cgi.out("cookie" => cgi.cookies) {"string"}
234
+ #
235
+ # cgi.cookies # { "name1" => cookie1, "name2" => cookie2, ... }
236
+ #
237
+ # require "cgi"
238
+ # cgi = CGI.new
239
+ # cgi.cookies['name'].expires = Time.now + 30
240
+ # cgi.out("cookie" => cgi.cookies['name']) {"string"}
241
+ #
242
+ # === Print http header and html string to $DEFAULT_OUTPUT ($>)
243
+ #
244
+ # require "cgi"
245
+ # cgi = CGI.new("html3") # add HTML generation methods
246
+ # cgi.out() do
247
+ # cgi.html() do
248
+ # cgi.head{ cgi.title{"TITLE"} } +
249
+ # cgi.body() do
250
+ # cgi.form() do
251
+ # cgi.textarea("get_text") +
252
+ # cgi.br +
253
+ # cgi.submit
254
+ # end +
255
+ # cgi.pre() do
256
+ # CGI::escapeHTML(
257
+ # "params: " + cgi.params.inspect + "\n" +
258
+ # "cookies: " + cgi.cookies.inspect + "\n" +
259
+ # ENV.collect() do |key, value|
260
+ # key + " --> " + value + "\n"
261
+ # end.join("")
262
+ # )
263
+ # end
264
+ # end
265
+ # end
266
+ # end
267
+ #
268
+ # # add HTML generation methods
269
+ # CGI.new("html3") # html3.2
270
+ # CGI.new("html4") # html4.01 (Strict)
271
+ # CGI.new("html4Tr") # html4.01 Transitional
272
+ # CGI.new("html4Fr") # html4.01 Frameset
273
+ #
274
+ class CGI
275
+
276
+ # :stopdoc:
277
+
278
+ # String for carriage return
279
+ CR = "\015"
280
+
281
+ # String for linefeed
282
+ LF = "\012"
283
+
284
+ # Standard internet newline sequence
285
+ EOL = CR + LF
286
+
287
+ REVISION = '$Id: cgi.rb 17817 2008-07-02 10:06:58Z shyouhei $' #:nodoc:
288
+
289
+ NEEDS_BINMODE = true if /WIN/ni.match(RUBY_PLATFORM)
290
+
291
+ # Path separators in different environments.
292
+ PATH_SEPARATOR = {'UNIX'=>'/', 'WINDOWS'=>'\\', 'MACINTOSH'=>':'}
293
+
294
+ # HTTP status codes.
295
+ HTTP_STATUS = {
296
+ "OK" => "200 OK",
297
+ "PARTIAL_CONTENT" => "206 Partial Content",
298
+ "MULTIPLE_CHOICES" => "300 Multiple Choices",
299
+ "MOVED" => "301 Moved Permanently",
300
+ "REDIRECT" => "302 Found",
301
+ "NOT_MODIFIED" => "304 Not Modified",
302
+ "BAD_REQUEST" => "400 Bad Request",
303
+ "AUTH_REQUIRED" => "401 Authorization Required",
304
+ "FORBIDDEN" => "403 Forbidden",
305
+ "NOT_FOUND" => "404 Not Found",
306
+ "METHOD_NOT_ALLOWED" => "405 Method Not Allowed",
307
+ "NOT_ACCEPTABLE" => "406 Not Acceptable",
308
+ "LENGTH_REQUIRED" => "411 Length Required",
309
+ "PRECONDITION_FAILED" => "412 Rrecondition Failed",
310
+ "SERVER_ERROR" => "500 Internal Server Error",
311
+ "NOT_IMPLEMENTED" => "501 Method Not Implemented",
312
+ "BAD_GATEWAY" => "502 Bad Gateway",
313
+ "VARIANT_ALSO_VARIES" => "506 Variant Also Negotiates"
314
+ }
315
+
316
+ # Abbreviated day-of-week names specified by RFC 822
317
+ RFC822_DAYS = %w[ Sun Mon Tue Wed Thu Fri Sat ]
318
+
319
+ # Abbreviated month names specified by RFC 822
320
+ RFC822_MONTHS = %w[ Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec ]
321
+
322
+ # :startdoc:
323
+
324
+ def env_table
325
+ ENV
326
+ end
327
+
328
+ def stdinput
329
+ $stdin
330
+ end
331
+
332
+ def stdoutput
333
+ $DEFAULT_OUTPUT
334
+ end
335
+
336
+ private :env_table, :stdinput, :stdoutput
337
+
338
+ # URL-encode a string.
339
+ # url_encoded_string = CGI::escape("'Stop!' said Fred")
340
+ # # => "%27Stop%21%27+said+Fred"
341
+ def CGI::escape(string)
342
+ string.gsub(/([^ a-zA-Z0-9_.-]+)/n) do
343
+ '%' + $1.unpack('H2' * $1.size).join('%').upcase
344
+ end.tr(' ', '+')
345
+ end
346
+
347
+
348
+ # URL-decode a string.
349
+ # string = CGI::unescape("%27Stop%21%27+said+Fred")
350
+ # # => "'Stop!' said Fred"
351
+ def CGI::unescape(string)
352
+ string.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n) do
353
+ [$1.delete('%')].pack('H*')
354
+ end
355
+ end
356
+
357
+
358
+ # Escape special characters in HTML, namely &\"<>
359
+ # CGI::escapeHTML('Usage: foo "bar" <baz>')
360
+ # # => "Usage: foo &quot;bar&quot; &lt;baz&gt;"
361
+ def CGI::escapeHTML(string)
362
+ string.gsub(/&/n, '&amp;').gsub(/\"/n, '&quot;').gsub(/>/n, '&gt;').gsub(/</n, '&lt;')
363
+ end
364
+
365
+
366
+ # Unescape a string that has been HTML-escaped
367
+ # CGI::unescapeHTML("Usage: foo &quot;bar&quot; &lt;baz&gt;")
368
+ # # => "Usage: foo \"bar\" <baz>"
369
+ def CGI::unescapeHTML(string)
370
+ string.gsub(/&(amp|quot|gt|lt|\#[0-9]+|\#x[0-9A-Fa-f]+);/n) do
371
+ match = $1.dup
372
+ case match
373
+ when 'amp' then '&'
374
+ when 'quot' then '"'
375
+ when 'gt' then '>'
376
+ when 'lt' then '<'
377
+ when /\A#0*(\d+)\z/n then
378
+ if Integer($1) < 256
379
+ Integer($1).chr
380
+ else
381
+ if Integer($1) < 65536 and ($KCODE[0] == ?u or $KCODE[0] == ?U)
382
+ [Integer($1)].pack("U")
383
+ else
384
+ "&##{$1};"
385
+ end
386
+ end
387
+ when /\A#x([0-9a-f]+)\z/ni then
388
+ if $1.hex < 256
389
+ $1.hex.chr
390
+ else
391
+ if $1.hex < 65536 and ($KCODE[0] == ?u or $KCODE[0] == ?U)
392
+ [$1.hex].pack("U")
393
+ else
394
+ "&#x#{$1};"
395
+ end
396
+ end
397
+ else
398
+ "&#{match};"
399
+ end
400
+ end
401
+ end
402
+
403
+
404
+ # Escape only the tags of certain HTML elements in +string+.
405
+ #
406
+ # Takes an element or elements or array of elements. Each element
407
+ # is specified by the name of the element, without angle brackets.
408
+ # This matches both the start and the end tag of that element.
409
+ # The attribute list of the open tag will also be escaped (for
410
+ # instance, the double-quotes surrounding attribute values).
411
+ #
412
+ # print CGI::escapeElement('<BR><A HREF="url"></A>', "A", "IMG")
413
+ # # "<BR>&lt;A HREF=&quot;url&quot;&gt;&lt;/A&gt"
414
+ #
415
+ # print CGI::escapeElement('<BR><A HREF="url"></A>', ["A", "IMG"])
416
+ # # "<BR>&lt;A HREF=&quot;url&quot;&gt;&lt;/A&gt"
417
+ def CGI::escapeElement(string, *elements)
418
+ elements = elements[0] if elements[0].kind_of?(Array)
419
+ unless elements.empty?
420
+ string.gsub(/<\/?(?:#{elements.join("|")})(?!\w)(?:.|\n)*?>/ni) do
421
+ CGI::escapeHTML($&)
422
+ end
423
+ else
424
+ string
425
+ end
426
+ end
427
+
428
+
429
+ # Undo escaping such as that done by CGI::escapeElement()
430
+ #
431
+ # print CGI::unescapeElement(
432
+ # CGI::escapeHTML('<BR><A HREF="url"></A>'), "A", "IMG")
433
+ # # "&lt;BR&gt;<A HREF="url"></A>"
434
+ #
435
+ # print CGI::unescapeElement(
436
+ # CGI::escapeHTML('<BR><A HREF="url"></A>'), ["A", "IMG"])
437
+ # # "&lt;BR&gt;<A HREF="url"></A>"
438
+ def CGI::unescapeElement(string, *elements)
439
+ elements = elements[0] if elements[0].kind_of?(Array)
440
+ unless elements.empty?
441
+ string.gsub(/&lt;\/?(?:#{elements.join("|")})(?!\w)(?:.|\n)*?&gt;/ni) do
442
+ CGI::unescapeHTML($&)
443
+ end
444
+ else
445
+ string
446
+ end
447
+ end
448
+
449
+
450
+ # Format a +Time+ object as a String using the format specified by RFC 1123.
451
+ #
452
+ # CGI::rfc1123_date(Time.now)
453
+ # # Sat, 01 Jan 2000 00:00:00 GMT
454
+ def CGI::rfc1123_date(time)
455
+ t = time.clone.gmtime
456
+ return format("%s, %.2d %s %.4d %.2d:%.2d:%.2d GMT",
457
+ RFC822_DAYS[t.wday], t.day, RFC822_MONTHS[t.month-1], t.year,
458
+ t.hour, t.min, t.sec)
459
+ end
460
+
461
+
462
+ # Create an HTTP header block as a string.
463
+ #
464
+ # Includes the empty line that ends the header block.
465
+ #
466
+ # +options+ can be a string specifying the Content-Type (defaults
467
+ # to text/html), or a hash of header key/value pairs. The following
468
+ # header keys are recognized:
469
+ #
470
+ # type:: the Content-Type header. Defaults to "text/html"
471
+ # charset:: the charset of the body, appended to the Content-Type header.
472
+ # nph:: a boolean value. If true, prepend protocol string and status code, and
473
+ # date; and sets default values for "server" and "connection" if not
474
+ # explicitly set.
475
+ # status:: the HTTP status code, returned as the Status header. See the
476
+ # list of available status codes below.
477
+ # server:: the server software, returned as the Server header.
478
+ # connection:: the connection type, returned as the Connection header (for
479
+ # instance, "close".
480
+ # length:: the length of the content that will be sent, returned as the
481
+ # Content-Length header.
482
+ # language:: the language of the content, returned as the Content-Language
483
+ # header.
484
+ # expires:: the time on which the current content expires, as a +Time+
485
+ # object, returned as the Expires header.
486
+ # cookie:: a cookie or cookies, returned as one or more Set-Cookie headers.
487
+ # The value can be the literal string of the cookie; a CGI::Cookie
488
+ # object; an Array of literal cookie strings or Cookie objects; or a
489
+ # hash all of whose values are literal cookie strings or Cookie objects.
490
+ # These cookies are in addition to the cookies held in the
491
+ # @output_cookies field.
492
+ #
493
+ # Other header lines can also be set; they are appended as key: value.
494
+ #
495
+ # header
496
+ # # Content-Type: text/html
497
+ #
498
+ # header("text/plain")
499
+ # # Content-Type: text/plain
500
+ #
501
+ # header("nph" => true,
502
+ # "status" => "OK", # == "200 OK"
503
+ # # "status" => "200 GOOD",
504
+ # "server" => ENV['SERVER_SOFTWARE'],
505
+ # "connection" => "close",
506
+ # "type" => "text/html",
507
+ # "charset" => "iso-2022-jp",
508
+ # # Content-Type: text/html; charset=iso-2022-jp
509
+ # "length" => 103,
510
+ # "language" => "ja",
511
+ # "expires" => Time.now + 30,
512
+ # "cookie" => [cookie1, cookie2],
513
+ # "my_header1" => "my_value"
514
+ # "my_header2" => "my_value")
515
+ #
516
+ # The status codes are:
517
+ #
518
+ # "OK" --> "200 OK"
519
+ # "PARTIAL_CONTENT" --> "206 Partial Content"
520
+ # "MULTIPLE_CHOICES" --> "300 Multiple Choices"
521
+ # "MOVED" --> "301 Moved Permanently"
522
+ # "REDIRECT" --> "302 Found"
523
+ # "NOT_MODIFIED" --> "304 Not Modified"
524
+ # "BAD_REQUEST" --> "400 Bad Request"
525
+ # "AUTH_REQUIRED" --> "401 Authorization Required"
526
+ # "FORBIDDEN" --> "403 Forbidden"
527
+ # "NOT_FOUND" --> "404 Not Found"
528
+ # "METHOD_NOT_ALLOWED" --> "405 Method Not Allowed"
529
+ # "NOT_ACCEPTABLE" --> "406 Not Acceptable"
530
+ # "LENGTH_REQUIRED" --> "411 Length Required"
531
+ # "PRECONDITION_FAILED" --> "412 Precondition Failed"
532
+ # "SERVER_ERROR" --> "500 Internal Server Error"
533
+ # "NOT_IMPLEMENTED" --> "501 Method Not Implemented"
534
+ # "BAD_GATEWAY" --> "502 Bad Gateway"
535
+ # "VARIANT_ALSO_VARIES" --> "506 Variant Also Negotiates"
536
+ #
537
+ # This method does not perform charset conversion.
538
+ #
539
+ def header(options = "text/html")
540
+
541
+ buf = ""
542
+
543
+ case options
544
+ when String
545
+ options = { "type" => options }
546
+ when Hash
547
+ options = options.dup
548
+ end
549
+
550
+ unless options.has_key?("type")
551
+ options["type"] = "text/html"
552
+ end
553
+
554
+ if options.has_key?("charset")
555
+ options["type"] += "; charset=" + options.delete("charset")
556
+ end
557
+
558
+ options.delete("nph") if defined?(MOD_RUBY)
559
+ if options.delete("nph") or
560
+ (/IIS\/(\d+)/n.match(env_table['SERVER_SOFTWARE']) and $1.to_i < 5)
561
+ buf += (env_table["SERVER_PROTOCOL"] or "HTTP/1.0") + " " +
562
+ (HTTP_STATUS[options["status"]] or options["status"] or "200 OK") +
563
+ EOL +
564
+ "Date: " + CGI::rfc1123_date(Time.now) + EOL
565
+
566
+ unless options.has_key?("server")
567
+ options["server"] = (env_table['SERVER_SOFTWARE'] or "")
568
+ end
569
+
570
+ unless options.has_key?("connection")
571
+ options["connection"] = "close"
572
+ end
573
+
574
+ options.delete("status")
575
+ end
576
+
577
+ if options.has_key?("status")
578
+ buf += "Status: " +
579
+ (HTTP_STATUS[options["status"]] or options["status"]) + EOL
580
+ options.delete("status")
581
+ end
582
+
583
+ if options.has_key?("server")
584
+ buf += "Server: " + options.delete("server") + EOL
585
+ end
586
+
587
+ if options.has_key?("connection")
588
+ buf += "Connection: " + options.delete("connection") + EOL
589
+ end
590
+
591
+ buf += "Content-Type: " + options.delete("type") + EOL
592
+
593
+ if options.has_key?("length")
594
+ buf += "Content-Length: " + options.delete("length").to_s + EOL
595
+ end
596
+
597
+ if options.has_key?("language")
598
+ buf += "Content-Language: " + options.delete("language") + EOL
599
+ end
600
+
601
+ if options.has_key?("expires")
602
+ buf += "Expires: " + CGI::rfc1123_date( options.delete("expires") ) + EOL
603
+ end
604
+
605
+ if options.has_key?("cookie")
606
+ if options["cookie"].kind_of?(String) or
607
+ options["cookie"].kind_of?(Cookie)
608
+ buf += "Set-Cookie: " + options.delete("cookie").to_s + EOL
609
+ elsif options["cookie"].kind_of?(Array)
610
+ options.delete("cookie").each{|cookie|
611
+ buf += "Set-Cookie: " + cookie.to_s + EOL
612
+ }
613
+ elsif options["cookie"].kind_of?(Hash)
614
+ options.delete("cookie").each_value{|cookie|
615
+ buf += "Set-Cookie: " + cookie.to_s + EOL
616
+ }
617
+ end
618
+ end
619
+ if @output_cookies
620
+ for cookie in @output_cookies
621
+ buf += "Set-Cookie: " + cookie.to_s + EOL
622
+ end
623
+ end
624
+
625
+ options.each{|key, value|
626
+ buf += key + ": " + value.to_s + EOL
627
+ }
628
+
629
+ if defined?(MOD_RUBY)
630
+ table = Apache::request.headers_out
631
+ buf.scan(/([^:]+): (.+)#{EOL}/n){ |name, value|
632
+ warn sprintf("name:%s value:%s\n", name, value) if $DEBUG
633
+ case name
634
+ when 'Set-Cookie'
635
+ table.add(name, value)
636
+ when /^status$/ni
637
+ Apache::request.status_line = value
638
+ Apache::request.status = value.to_i
639
+ when /^content-type$/ni
640
+ Apache::request.content_type = value
641
+ when /^content-encoding$/ni
642
+ Apache::request.content_encoding = value
643
+ when /^location$/ni
644
+ if Apache::request.status == 200
645
+ Apache::request.status = 302
646
+ end
647
+ Apache::request.headers_out[name] = value
648
+ else
649
+ Apache::request.headers_out[name] = value
650
+ end
651
+ }
652
+ Apache::request.send_http_header
653
+ ''
654
+ else
655
+ buf + EOL
656
+ end
657
+
658
+ end # header()
659
+
660
+
661
+ # Print an HTTP header and body to $DEFAULT_OUTPUT ($>)
662
+ #
663
+ # The header is provided by +options+, as for #header().
664
+ # The body of the document is that returned by the passed-
665
+ # in block. This block takes no arguments. It is required.
666
+ #
667
+ # cgi = CGI.new
668
+ # cgi.out{ "string" }
669
+ # # Content-Type: text/html
670
+ # # Content-Length: 6
671
+ # #
672
+ # # string
673
+ #
674
+ # cgi.out("text/plain") { "string" }
675
+ # # Content-Type: text/plain
676
+ # # Content-Length: 6
677
+ # #
678
+ # # string
679
+ #
680
+ # cgi.out("nph" => true,
681
+ # "status" => "OK", # == "200 OK"
682
+ # "server" => ENV['SERVER_SOFTWARE'],
683
+ # "connection" => "close",
684
+ # "type" => "text/html",
685
+ # "charset" => "iso-2022-jp",
686
+ # # Content-Type: text/html; charset=iso-2022-jp
687
+ # "language" => "ja",
688
+ # "expires" => Time.now + (3600 * 24 * 30),
689
+ # "cookie" => [cookie1, cookie2],
690
+ # "my_header1" => "my_value",
691
+ # "my_header2" => "my_value") { "string" }
692
+ #
693
+ # Content-Length is automatically calculated from the size of
694
+ # the String returned by the content block.
695
+ #
696
+ # If ENV['REQUEST_METHOD'] == "HEAD", then only the header
697
+ # is outputted (the content block is still required, but it
698
+ # is ignored).
699
+ #
700
+ # If the charset is "iso-2022-jp" or "euc-jp" or "shift_jis" then
701
+ # the content is converted to this charset, and the language is set
702
+ # to "ja".
703
+ def out(options = "text/html") # :yield:
704
+
705
+ options = { "type" => options } if options.kind_of?(String)
706
+ content = yield
707
+
708
+ if options.has_key?("charset")
709
+ require "nkf"
710
+ case options["charset"]
711
+ when /iso-2022-jp/ni
712
+ content = NKF::nkf('-m0 -x -j', content)
713
+ options["language"] = "ja" unless options.has_key?("language")
714
+ when /euc-jp/ni
715
+ content = NKF::nkf('-m0 -x -e', content)
716
+ options["language"] = "ja" unless options.has_key?("language")
717
+ when /shift_jis/ni
718
+ content = NKF::nkf('-m0 -x -s', content)
719
+ options["language"] = "ja" unless options.has_key?("language")
720
+ end
721
+ end
722
+
723
+ options["length"] = content.length.to_s
724
+ output = stdoutput
725
+ output.binmode if defined? output.binmode
726
+ output.print header(options)
727
+ output.print content unless "HEAD" == env_table['REQUEST_METHOD']
728
+ end
729
+
730
+
731
+ # Print an argument or list of arguments to the default output stream
732
+ #
733
+ # cgi = CGI.new
734
+ # cgi.print # default: cgi.print == $DEFAULT_OUTPUT.print
735
+ def print(*options)
736
+ stdoutput.print(*options)
737
+ end
738
+
739
+ require "delegate"
740
+
741
+ # Class representing an HTTP cookie.
742
+ #
743
+ # In addition to its specific fields and methods, a Cookie instance
744
+ # is a delegator to the array of its values.
745
+ #
746
+ # See RFC 2965.
747
+ #
748
+ # == Examples of use
749
+ # cookie1 = CGI::Cookie::new("name", "value1", "value2", ...)
750
+ # cookie1 = CGI::Cookie::new("name" => "name", "value" => "value")
751
+ # cookie1 = CGI::Cookie::new('name' => 'name',
752
+ # 'value' => ['value1', 'value2', ...],
753
+ # 'path' => 'path', # optional
754
+ # 'domain' => 'domain', # optional
755
+ # 'expires' => Time.now, # optional
756
+ # 'secure' => true # optional
757
+ # )
758
+ #
759
+ # cgi.out("cookie" => [cookie1, cookie2]) { "string" }
760
+ #
761
+ # name = cookie1.name
762
+ # values = cookie1.value
763
+ # path = cookie1.path
764
+ # domain = cookie1.domain
765
+ # expires = cookie1.expires
766
+ # secure = cookie1.secure
767
+ #
768
+ # cookie1.name = 'name'
769
+ # cookie1.value = ['value1', 'value2', ...]
770
+ # cookie1.path = 'path'
771
+ # cookie1.domain = 'domain'
772
+ # cookie1.expires = Time.now + 30
773
+ # cookie1.secure = true
774
+ class Cookie < DelegateClass(Array)
775
+
776
+ # Create a new CGI::Cookie object.
777
+ #
778
+ # The contents of the cookie can be specified as a +name+ and one
779
+ # or more +value+ arguments. Alternatively, the contents can
780
+ # be specified as a single hash argument. The possible keywords of
781
+ # this hash are as follows:
782
+ #
783
+ # name:: the name of the cookie. Required.
784
+ # value:: the cookie's value or list of values.
785
+ # path:: the path for which this cookie applies. Defaults to the
786
+ # base directory of the CGI script.
787
+ # domain:: the domain for which this cookie applies.
788
+ # expires:: the time at which this cookie expires, as a +Time+ object.
789
+ # secure:: whether this cookie is a secure cookie or not (default to
790
+ # false). Secure cookies are only transmitted to HTTPS
791
+ # servers.
792
+ #
793
+ # These keywords correspond to attributes of the cookie object.
794
+ def initialize(name = "", *value)
795
+ if name.kind_of?(String)
796
+ @name = name
797
+ @value = value
798
+ %r|^(.*/)|.match(ENV["SCRIPT_NAME"])
799
+ @path = ($1 or "")
800
+ @secure = false
801
+ return super(@value)
802
+ end
803
+
804
+ options = name
805
+ unless options.has_key?("name")
806
+ raise ArgumentError, "`name' required"
807
+ end
808
+
809
+ @name = options["name"]
810
+ @value = Array(options["value"])
811
+ # simple support for IE
812
+ if options["path"]
813
+ @path = options["path"]
814
+ else
815
+ %r|^(.*/)|.match(ENV["SCRIPT_NAME"])
816
+ @path = ($1 or "")
817
+ end
818
+ @domain = options["domain"]
819
+ @expires = options["expires"]
820
+ @secure = options["secure"] == true ? true : false
821
+
822
+ super(@value)
823
+ end
824
+
825
+ attr_accessor("name", "path", "domain", "expires")
826
+ attr_reader("secure")
827
+
828
+ def value=(val)
829
+ @value = Array(val)
830
+ replace(@value)
831
+ end
832
+
833
+ def value
834
+ @value
835
+ end
836
+
837
+ # Set whether the Cookie is a secure cookie or not.
838
+ #
839
+ # +val+ must be a boolean.
840
+ def secure=(val)
841
+ @secure = val if val == true or val == false
842
+ @secure
843
+ end
844
+
845
+ # Convert the Cookie to its string representation.
846
+ def to_s
847
+ buf = ""
848
+ buf += @name + '='
849
+
850
+ if @value.kind_of?(String)
851
+ buf += CGI::escape(@value)
852
+ else
853
+ buf += @value.collect{|v| CGI::escape(v) }.join("&")
854
+ end
855
+
856
+ if @domain
857
+ buf += '; domain=' + @domain
858
+ end
859
+
860
+ if @path
861
+ buf += '; path=' + @path
862
+ end
863
+
864
+ if @expires
865
+ buf += '; expires=' + CGI::rfc1123_date(@expires)
866
+ end
867
+
868
+ if @secure == true
869
+ buf += '; secure'
870
+ end
871
+
872
+ buf
873
+ end
874
+
875
+ end # class Cookie
876
+
877
+
878
+ # Parse a raw cookie string into a hash of cookie-name=>Cookie
879
+ # pairs.
880
+ #
881
+ # cookies = CGI::Cookie::parse("raw_cookie_string")
882
+ # # { "name1" => cookie1, "name2" => cookie2, ... }
883
+ #
884
+ def Cookie::parse(raw_cookie)
885
+ cookies = Hash.new([])
886
+ return cookies unless raw_cookie
887
+
888
+ raw_cookie.split(/[;,]\s?/).each do |pairs|
889
+ name, values = pairs.split('=',2)
890
+ next unless name and values
891
+ name = CGI::unescape(name)
892
+ values ||= ""
893
+ values = values.split('&').collect{|v| CGI::unescape(v) }
894
+ if cookies.has_key?(name)
895
+ values = cookies[name].value + values
896
+ end
897
+ cookies[name] = Cookie::new(name, *values)
898
+ end
899
+
900
+ cookies
901
+ end
902
+
903
+ # Parse an HTTP query string into a hash of key=>value pairs.
904
+ #
905
+ # params = CGI::parse("query_string")
906
+ # # {"name1" => ["value1", "value2", ...],
907
+ # # "name2" => ["value1", "value2", ...], ... }
908
+ #
909
+ def CGI::parse(query)
910
+ params = Hash.new([].freeze)
911
+
912
+ query.split(/[&;]/n).each do |pairs|
913
+ key, value = pairs.split('=',2).collect{|v| CGI::unescape(v) }
914
+ if params.has_key?(key)
915
+ params[key].push(value)
916
+ else
917
+ params[key] = [value]
918
+ end
919
+ end
920
+
921
+ params
922
+ end
923
+
924
+ # Mixin module. It provides the follow functionality groups:
925
+ #
926
+ # 1. Access to CGI environment variables as methods. See
927
+ # documentation to the CGI class for a list of these variables.
928
+ #
929
+ # 2. Access to cookies, including the cookies attribute.
930
+ #
931
+ # 3. Access to parameters, including the params attribute, and overloading
932
+ # [] to perform parameter value lookup by key.
933
+ #
934
+ # 4. The initialize_query method, for initialising the above
935
+ # mechanisms, handling multipart forms, and allowing the
936
+ # class to be used in "offline" mode.
937
+ #
938
+ module QueryExtension
939
+
940
+ %w[ CONTENT_LENGTH SERVER_PORT ].each do |env|
941
+ define_method(env.sub(/^HTTP_/n, '').downcase) do
942
+ (val = env_table[env]) && Integer(val)
943
+ end
944
+ end
945
+
946
+ %w[ AUTH_TYPE CONTENT_TYPE GATEWAY_INTERFACE PATH_INFO
947
+ PATH_TRANSLATED QUERY_STRING REMOTE_ADDR REMOTE_HOST
948
+ REMOTE_IDENT REMOTE_USER REQUEST_METHOD SCRIPT_NAME
949
+ SERVER_NAME SERVER_PROTOCOL SERVER_SOFTWARE
950
+
951
+ HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_ACCEPT_ENCODING
952
+ HTTP_ACCEPT_LANGUAGE HTTP_CACHE_CONTROL HTTP_FROM HTTP_HOST
953
+ HTTP_NEGOTIATE HTTP_PRAGMA HTTP_REFERER HTTP_USER_AGENT ].each do |env|
954
+ define_method(env.sub(/^HTTP_/n, '').downcase) do
955
+ env_table[env]
956
+ end
957
+ end
958
+
959
+ # Get the raw cookies as a string.
960
+ def raw_cookie
961
+ env_table["HTTP_COOKIE"]
962
+ end
963
+
964
+ # Get the raw RFC2965 cookies as a string.
965
+ def raw_cookie2
966
+ env_table["HTTP_COOKIE2"]
967
+ end
968
+
969
+ # Get the cookies as a hash of cookie-name=>Cookie pairs.
970
+ attr_accessor("cookies")
971
+
972
+ # Get the parameters as a hash of name=>values pairs, where
973
+ # values is an Array.
974
+ attr("params")
975
+
976
+ # Set all the parameters.
977
+ def params=(hash)
978
+ @params.clear
979
+ @params.update(hash)
980
+ end
981
+
982
+ def read_multipart(boundary, content_length)
983
+ params = Hash.new([])
984
+ boundary = "--" + boundary
985
+ quoted_boundary = Regexp.quote(boundary)
986
+ buf = ""
987
+ bufsize = 10 * 1024
988
+ boundary_end=""
989
+
990
+ # start multipart/form-data
991
+ stdinput.binmode if defined? stdinput.binmode
992
+ boundary_size = boundary.size + EOL.size
993
+ content_length -= boundary_size
994
+ status = stdinput.read(boundary_size)
995
+ if nil == status
996
+ raise EOFError, "no content body"
997
+ elsif boundary + EOL != status
998
+ raise EOFError, "bad content body"
999
+ end
1000
+
1001
+ loop do
1002
+ head = nil
1003
+ if 10240 < content_length
1004
+ require "tempfile"
1005
+ body = Tempfile.new("CGI")
1006
+ else
1007
+ begin
1008
+ require "stringio"
1009
+ body = StringIO.new
1010
+ rescue LoadError
1011
+ require "tempfile"
1012
+ body = Tempfile.new("CGI")
1013
+ end
1014
+ end
1015
+ body.binmode if defined? body.binmode
1016
+
1017
+ until head and /#{quoted_boundary}(?:#{EOL}|--)/n.match(buf)
1018
+
1019
+ if (not head) and /#{EOL}#{EOL}/n.match(buf)
1020
+ buf = buf.sub(/\A((?:.|\n)*?#{EOL})#{EOL}/n) do
1021
+ head = $1.dup
1022
+ ""
1023
+ end
1024
+ next
1025
+ end
1026
+
1027
+ if head and ( (EOL + boundary + EOL).size < buf.size )
1028
+ body.print buf[0 ... (buf.size - (EOL + boundary + EOL).size)]
1029
+ buf[0 ... (buf.size - (EOL + boundary + EOL).size)] = ""
1030
+ end
1031
+
1032
+ c = if bufsize < content_length
1033
+ stdinput.read(bufsize)
1034
+ else
1035
+ stdinput.read(content_length)
1036
+ end
1037
+ if c.nil? || c.empty?
1038
+ raise EOFError, "bad content body"
1039
+ end
1040
+ buf.concat(c)
1041
+ content_length -= c.size
1042
+ end
1043
+
1044
+ buf = buf.sub(/\A((?:.|\n)*?)(?:[\r\n]{1,2})?#{quoted_boundary}([\r\n]{1,2}|--)/n) do
1045
+ body.print $1
1046
+ if "--" == $2
1047
+ content_length = -1
1048
+ end
1049
+ boundary_end = $2.dup
1050
+ ""
1051
+ end
1052
+
1053
+ body.rewind
1054
+
1055
+ /Content-Disposition:.* filename=(?:"((?:\\.|[^\"])*)"|([^;\s]*))/ni.match(head)
1056
+ filename = ($1 or $2 or "")
1057
+ if /Mac/ni.match(env_table['HTTP_USER_AGENT']) and
1058
+ /Mozilla/ni.match(env_table['HTTP_USER_AGENT']) and
1059
+ (not /MSIE/ni.match(env_table['HTTP_USER_AGENT']))
1060
+ filename = CGI::unescape(filename)
1061
+ end
1062
+
1063
+ /Content-Type: ([\s]*)/ni.match(head)
1064
+ content_type = ($1 or "")
1065
+
1066
+ (class << body; self; end).class_eval do
1067
+ alias local_path path
1068
+ define_method(:original_filename) {filename.dup.taint}
1069
+ define_method(:content_type) {content_type.dup.taint}
1070
+ end
1071
+
1072
+ /Content-Disposition:.* name="?([^\";\s]*)"?/ni.match(head)
1073
+ name = $1.dup
1074
+
1075
+ if params.has_key?(name)
1076
+ params[name].push(body)
1077
+ else
1078
+ params[name] = [body]
1079
+ end
1080
+ break if buf.size == 0
1081
+ break if content_length == -1
1082
+ end
1083
+ raise EOFError, "bad boundary end of body part" unless boundary_end=~/--/
1084
+
1085
+ params
1086
+ end # read_multipart
1087
+ private :read_multipart
1088
+
1089
+ # offline mode. read name=value pairs on standard input.
1090
+ def read_from_cmdline
1091
+ require "shellwords"
1092
+
1093
+ string = unless ARGV.empty?
1094
+ ARGV.join(' ')
1095
+ else
1096
+ if STDIN.tty?
1097
+ STDERR.print(
1098
+ %|(offline mode: enter name=value pairs on standard input)\n|
1099
+ )
1100
+ end
1101
+ readlines.join(' ').gsub(/\n/n, '')
1102
+ end.gsub(/\\=/n, '%3D').gsub(/\\&/n, '%26')
1103
+
1104
+ words = Shellwords.shellwords(string)
1105
+
1106
+ if words.find{|x| /=/n.match(x) }
1107
+ words.join('&')
1108
+ else
1109
+ words.join('+')
1110
+ end
1111
+ end
1112
+ private :read_from_cmdline
1113
+
1114
+ # Initialize the data from the query.
1115
+ #
1116
+ # Handles multipart forms (in particular, forms that involve file uploads).
1117
+ # Reads query parameters in the @params field, and cookies into @cookies.
1118
+ def initialize_query()
1119
+ if ("POST" == env_table['REQUEST_METHOD']) and
1120
+ %r|\Amultipart/form-data.*boundary=\"?([^\";,]+)\"?|n.match(env_table['CONTENT_TYPE'])
1121
+ boundary = $1.dup
1122
+ @multipart = true
1123
+ @params = read_multipart(boundary, Integer(env_table['CONTENT_LENGTH']))
1124
+ else
1125
+ @multipart = false
1126
+ @params = CGI::parse(
1127
+ case env_table['REQUEST_METHOD']
1128
+ when "GET", "HEAD"
1129
+ if defined?(MOD_RUBY)
1130
+ Apache::request.args or ""
1131
+ else
1132
+ env_table['QUERY_STRING'] or ""
1133
+ end
1134
+ when "POST"
1135
+ stdinput.binmode if defined? stdinput.binmode
1136
+ stdinput.read(Integer(env_table['CONTENT_LENGTH'])) or ''
1137
+ else
1138
+ read_from_cmdline
1139
+ end
1140
+ )
1141
+ end
1142
+
1143
+ @cookies = CGI::Cookie::parse((env_table['HTTP_COOKIE'] or env_table['COOKIE']))
1144
+ end
1145
+ private :initialize_query
1146
+
1147
+ def multipart?
1148
+ @multipart
1149
+ end
1150
+
1151
+ module Value # :nodoc:
1152
+ def set_params(params)
1153
+ @params = params
1154
+ end
1155
+ def [](idx, *args)
1156
+ if args.size == 0
1157
+ warn "#{caller(1)[0]}:CAUTION! cgi['key'] == cgi.params['key'][0]; if want Array, use cgi.params['key']"
1158
+ @params[idx]
1159
+ else
1160
+ super[idx,*args]
1161
+ end
1162
+ end
1163
+ def first
1164
+ warn "#{caller(1)[0]}:CAUTION! cgi['key'] == cgi.params['key'][0]; if want Array, use cgi.params['key']"
1165
+ self
1166
+ end
1167
+ alias last first
1168
+ def to_a
1169
+ @params || [self]
1170
+ end
1171
+ alias to_ary to_a # to be rhs of multiple assignment
1172
+ end
1173
+
1174
+ # Get the value for the parameter with a given key.
1175
+ #
1176
+ # If the parameter has multiple values, only the first will be
1177
+ # retrieved; use #params() to get the array of values.
1178
+ def [](key)
1179
+ params = @params[key]
1180
+ return '' unless params
1181
+ value = params[0]
1182
+ if @multipart
1183
+ if value
1184
+ return value
1185
+ elsif defined? StringIO
1186
+ StringIO.new("")
1187
+ else
1188
+ Tempfile.new("CGI")
1189
+ end
1190
+ else
1191
+ str = if value then value.dup else "" end
1192
+ str.extend(Value)
1193
+ str.set_params(params)
1194
+ str
1195
+ end
1196
+ end
1197
+
1198
+ # Return all parameter keys as an array.
1199
+ def keys(*args)
1200
+ @params.keys(*args)
1201
+ end
1202
+
1203
+ # Returns true if a given parameter key exists in the query.
1204
+ def has_key?(*args)
1205
+ @params.has_key?(*args)
1206
+ end
1207
+ alias key? has_key?
1208
+ alias include? has_key?
1209
+
1210
+ end # QueryExtension
1211
+
1212
+
1213
+ # Prettify (indent) an HTML string.
1214
+ #
1215
+ # +string+ is the HTML string to indent. +shift+ is the indentation
1216
+ # unit to use; it defaults to two spaces.
1217
+ #
1218
+ # print CGI::pretty("<HTML><BODY></BODY></HTML>")
1219
+ # # <HTML>
1220
+ # # <BODY>
1221
+ # # </BODY>
1222
+ # # </HTML>
1223
+ #
1224
+ # print CGI::pretty("<HTML><BODY></BODY></HTML>", "\t")
1225
+ # # <HTML>
1226
+ # # <BODY>
1227
+ # # </BODY>
1228
+ # # </HTML>
1229
+ #
1230
+ def CGI::pretty(string, shift = " ")
1231
+ lines = string.gsub(/(?!\A)<(?:.|\n)*?>/n, "\n\\0").gsub(/<(?:.|\n)*?>(?!\n)/n, "\\0\n")
1232
+ end_pos = 0
1233
+ while end_pos = lines.index(/^<\/(\w+)/n, end_pos)
1234
+ element = $1.dup
1235
+ start_pos = lines.rindex(/^\s*<#{element}/ni, end_pos)
1236
+ lines[start_pos ... end_pos] = "__" + lines[start_pos ... end_pos].gsub(/\n(?!\z)/n, "\n" + shift) + "__"
1237
+ end
1238
+ lines.gsub(/^((?:#{Regexp::quote(shift)})*)__(?=<\/?\w)/n, '\1')
1239
+ end
1240
+
1241
+
1242
+ # Base module for HTML-generation mixins.
1243
+ #
1244
+ # Provides methods for code generation for tags following
1245
+ # the various DTD element types.
1246
+ module TagMaker # :nodoc:
1247
+ extend self
1248
+
1249
+ # Generate code for an element with required start and end tags.
1250
+ #
1251
+ # - -
1252
+ def nn_element_def(element)
1253
+ nOE_element_def(element, <<-END)
1254
+ if block_given?
1255
+ yield.to_s
1256
+ else
1257
+ ""
1258
+ end +
1259
+ "</#{element.upcase}>"
1260
+ END
1261
+ end
1262
+
1263
+ # Generate code for an empty element.
1264
+ #
1265
+ # - O EMPTY
1266
+ def nOE_element_def(element, append = nil)
1267
+ s = <<-END
1268
+ "<#{element.upcase}" + attributes.collect{|name, value|
1269
+ next unless value
1270
+ " " + CGI::escapeHTML(name) +
1271
+ if true == value
1272
+ ""
1273
+ else
1274
+ '="' + CGI::escapeHTML(value) + '"'
1275
+ end
1276
+ }.to_s + ">"
1277
+ END
1278
+ s.sub!(/\Z/, " +") << append if append
1279
+ s
1280
+ end
1281
+
1282
+ # Generate code for an element for which the end (and possibly the
1283
+ # start) tag is optional.
1284
+ #
1285
+ # O O or - O
1286
+ def nO_element_def(element)
1287
+ nOE_element_def(element, <<-END)
1288
+ if block_given?
1289
+ yield.to_s + "</#{element.upcase}>"
1290
+ else
1291
+ ""
1292
+ end
1293
+ END
1294
+ end
1295
+
1296
+ end # TagMaker
1297
+
1298
+
1299
+ #
1300
+ # Mixin module providing HTML generation methods.
1301
+ #
1302
+ # For example,
1303
+ # cgi.a("http://www.example.com") { "Example" }
1304
+ # # => "<A HREF=\"http://www.example.com\">Example</A>"
1305
+ #
1306
+ # Modules Http3, Http4, etc., contain more basic HTML-generation methods
1307
+ # (:title, :center, etc.).
1308
+ #
1309
+ # See class CGI for a detailed example.
1310
+ #
1311
+ module HtmlExtension
1312
+
1313
+
1314
+ # Generate an Anchor element as a string.
1315
+ #
1316
+ # +href+ can either be a string, giving the URL
1317
+ # for the HREF attribute, or it can be a hash of
1318
+ # the element's attributes.
1319
+ #
1320
+ # The body of the element is the string returned by the no-argument
1321
+ # block passed in.
1322
+ #
1323
+ # a("http://www.example.com") { "Example" }
1324
+ # # => "<A HREF=\"http://www.example.com\">Example</A>"
1325
+ #
1326
+ # a("HREF" => "http://www.example.com", "TARGET" => "_top") { "Example" }
1327
+ # # => "<A HREF=\"http://www.example.com\" TARGET=\"_top\">Example</A>"
1328
+ #
1329
+ def a(href = "") # :yield:
1330
+ attributes = if href.kind_of?(String)
1331
+ { "HREF" => href }
1332
+ else
1333
+ href
1334
+ end
1335
+ if block_given?
1336
+ super(attributes){ yield }
1337
+ else
1338
+ super(attributes)
1339
+ end
1340
+ end
1341
+
1342
+ # Generate a Document Base URI element as a String.
1343
+ #
1344
+ # +href+ can either by a string, giving the base URL for the HREF
1345
+ # attribute, or it can be a has of the element's attributes.
1346
+ #
1347
+ # The passed-in no-argument block is ignored.
1348
+ #
1349
+ # base("http://www.example.com/cgi")
1350
+ # # => "<BASE HREF=\"http://www.example.com/cgi\">"
1351
+ def base(href = "") # :yield:
1352
+ attributes = if href.kind_of?(String)
1353
+ { "HREF" => href }
1354
+ else
1355
+ href
1356
+ end
1357
+ if block_given?
1358
+ super(attributes){ yield }
1359
+ else
1360
+ super(attributes)
1361
+ end
1362
+ end
1363
+
1364
+ # Generate a BlockQuote element as a string.
1365
+ #
1366
+ # +cite+ can either be a string, give the URI for the source of
1367
+ # the quoted text, or a hash, giving all attributes of the element,
1368
+ # or it can be omitted, in which case the element has no attributes.
1369
+ #
1370
+ # The body is provided by the passed-in no-argument block
1371
+ #
1372
+ # blockquote("http://www.example.com/quotes/foo.html") { "Foo!" }
1373
+ # #=> "<BLOCKQUOTE CITE=\"http://www.example.com/quotes/foo.html\">Foo!</BLOCKQUOTE>
1374
+ def blockquote(cite = nil) # :yield:
1375
+ attributes = if cite.kind_of?(String)
1376
+ { "CITE" => cite }
1377
+ else
1378
+ cite or ""
1379
+ end
1380
+ if block_given?
1381
+ super(attributes){ yield }
1382
+ else
1383
+ super(attributes)
1384
+ end
1385
+ end
1386
+
1387
+
1388
+ # Generate a Table Caption element as a string.
1389
+ #
1390
+ # +align+ can be a string, giving the alignment of the caption
1391
+ # (one of top, bottom, left, or right). It can be a hash of
1392
+ # all the attributes of the element. Or it can be omitted.
1393
+ #
1394
+ # The body of the element is provided by the passed-in no-argument block.
1395
+ #
1396
+ # caption("left") { "Capital Cities" }
1397
+ # # => <CAPTION ALIGN=\"left\">Capital Cities</CAPTION>
1398
+ def caption(align = nil) # :yield:
1399
+ attributes = if align.kind_of?(String)
1400
+ { "ALIGN" => align }
1401
+ else
1402
+ align or ""
1403
+ end
1404
+ if block_given?
1405
+ super(attributes){ yield }
1406
+ else
1407
+ super(attributes)
1408
+ end
1409
+ end
1410
+
1411
+
1412
+ # Generate a Checkbox Input element as a string.
1413
+ #
1414
+ # The attributes of the element can be specified as three arguments,
1415
+ # +name+, +value+, and +checked+. +checked+ is a boolean value;
1416
+ # if true, the CHECKED attribute will be included in the element.
1417
+ #
1418
+ # Alternatively, the attributes can be specified as a hash.
1419
+ #
1420
+ # checkbox("name")
1421
+ # # = checkbox("NAME" => "name")
1422
+ #
1423
+ # checkbox("name", "value")
1424
+ # # = checkbox("NAME" => "name", "VALUE" => "value")
1425
+ #
1426
+ # checkbox("name", "value", true)
1427
+ # # = checkbox("NAME" => "name", "VALUE" => "value", "CHECKED" => true)
1428
+ def checkbox(name = "", value = nil, checked = nil)
1429
+ attributes = if name.kind_of?(String)
1430
+ { "TYPE" => "checkbox", "NAME" => name,
1431
+ "VALUE" => value, "CHECKED" => checked }
1432
+ else
1433
+ name["TYPE"] = "checkbox"
1434
+ name
1435
+ end
1436
+ input(attributes)
1437
+ end
1438
+
1439
+ # Generate a sequence of checkbox elements, as a String.
1440
+ #
1441
+ # The checkboxes will all have the same +name+ attribute.
1442
+ # Each checkbox is followed by a label.
1443
+ # There will be one checkbox for each value. Each value
1444
+ # can be specified as a String, which will be used both
1445
+ # as the value of the VALUE attribute and as the label
1446
+ # for that checkbox. A single-element array has the
1447
+ # same effect.
1448
+ #
1449
+ # Each value can also be specified as a three-element array.
1450
+ # The first element is the VALUE attribute; the second is the
1451
+ # label; and the third is a boolean specifying whether this
1452
+ # checkbox is CHECKED.
1453
+ #
1454
+ # Each value can also be specified as a two-element
1455
+ # array, by omitting either the value element (defaults
1456
+ # to the same as the label), or the boolean checked element
1457
+ # (defaults to false).
1458
+ #
1459
+ # checkbox_group("name", "foo", "bar", "baz")
1460
+ # # <INPUT TYPE="checkbox" NAME="name" VALUE="foo">foo
1461
+ # # <INPUT TYPE="checkbox" NAME="name" VALUE="bar">bar
1462
+ # # <INPUT TYPE="checkbox" NAME="name" VALUE="baz">baz
1463
+ #
1464
+ # checkbox_group("name", ["foo"], ["bar", true], "baz")
1465
+ # # <INPUT TYPE="checkbox" NAME="name" VALUE="foo">foo
1466
+ # # <INPUT TYPE="checkbox" CHECKED NAME="name" VALUE="bar">bar
1467
+ # # <INPUT TYPE="checkbox" NAME="name" VALUE="baz">baz
1468
+ #
1469
+ # checkbox_group("name", ["1", "Foo"], ["2", "Bar", true], "Baz")
1470
+ # # <INPUT TYPE="checkbox" NAME="name" VALUE="1">Foo
1471
+ # # <INPUT TYPE="checkbox" CHECKED NAME="name" VALUE="2">Bar
1472
+ # # <INPUT TYPE="checkbox" NAME="name" VALUE="Baz">Baz
1473
+ #
1474
+ # checkbox_group("NAME" => "name",
1475
+ # "VALUES" => ["foo", "bar", "baz"])
1476
+ #
1477
+ # checkbox_group("NAME" => "name",
1478
+ # "VALUES" => [["foo"], ["bar", true], "baz"])
1479
+ #
1480
+ # checkbox_group("NAME" => "name",
1481
+ # "VALUES" => [["1", "Foo"], ["2", "Bar", true], "Baz"])
1482
+ def checkbox_group(name = "", *values)
1483
+ if name.kind_of?(Hash)
1484
+ values = name["VALUES"]
1485
+ name = name["NAME"]
1486
+ end
1487
+ values.collect{|value|
1488
+ if value.kind_of?(String)
1489
+ checkbox(name, value) + value
1490
+ else
1491
+ if value[value.size - 1] == true
1492
+ checkbox(name, value[0], true) +
1493
+ value[value.size - 2]
1494
+ elsif value[value.size - 1] == false
1495
+ checkbox(name, value[0], false) +
1496
+ value[value.size - 2]
1497
+ else
1498
+ checkbox(name, value[0]) +
1499
+ value[value.size - 1]
1500
+ end
1501
+ end
1502
+ }.to_s
1503
+ end
1504
+
1505
+
1506
+ # Generate an File Upload Input element as a string.
1507
+ #
1508
+ # The attributes of the element can be specified as three arguments,
1509
+ # +name+, +size+, and +maxlength+. +maxlength+ is the maximum length
1510
+ # of the file's _name_, not of the file's _contents_.
1511
+ #
1512
+ # Alternatively, the attributes can be specified as a hash.
1513
+ #
1514
+ # See #multipart_form() for forms that include file uploads.
1515
+ #
1516
+ # file_field("name")
1517
+ # # <INPUT TYPE="file" NAME="name" SIZE="20">
1518
+ #
1519
+ # file_field("name", 40)
1520
+ # # <INPUT TYPE="file" NAME="name" SIZE="40">
1521
+ #
1522
+ # file_field("name", 40, 100)
1523
+ # # <INPUT TYPE="file" NAME="name" SIZE="40" MAXLENGTH="100">
1524
+ #
1525
+ # file_field("NAME" => "name", "SIZE" => 40)
1526
+ # # <INPUT TYPE="file" NAME="name" SIZE="40">
1527
+ def file_field(name = "", size = 20, maxlength = nil)
1528
+ attributes = if name.kind_of?(String)
1529
+ { "TYPE" => "file", "NAME" => name,
1530
+ "SIZE" => size.to_s }
1531
+ else
1532
+ name["TYPE"] = "file"
1533
+ name["SIZE"] = name["SIZE"].to_s if name["SIZE"]
1534
+ name["MAXLENGTH"] = name["MAXLENGTH"].to_s if name["MAXLENGTH"]
1535
+ name
1536
+ end
1537
+ attributes["MAXLENGTH"] = maxlength.to_s if maxlength
1538
+ input(attributes)
1539
+ end
1540
+
1541
+
1542
+ # Generate a Form element as a string.
1543
+ #
1544
+ # +method+ should be either "get" or "post", and defaults to the latter.
1545
+ # +action+ defaults to the current CGI script name. +enctype+
1546
+ # defaults to "application/x-www-form-urlencoded".
1547
+ #
1548
+ # Alternatively, the attributes can be specified as a hash.
1549
+ #
1550
+ # See also #multipart_form() for forms that include file uploads.
1551
+ #
1552
+ # form{ "string" }
1553
+ # # <FORM METHOD="post" ENCTYPE="application/x-www-form-urlencoded">string</FORM>
1554
+ #
1555
+ # form("get") { "string" }
1556
+ # # <FORM METHOD="get" ENCTYPE="application/x-www-form-urlencoded">string</FORM>
1557
+ #
1558
+ # form("get", "url") { "string" }
1559
+ # # <FORM METHOD="get" ACTION="url" ENCTYPE="application/x-www-form-urlencoded">string</FORM>
1560
+ #
1561
+ # form("METHOD" => "post", "ENCTYPE" => "enctype") { "string" }
1562
+ # # <FORM METHOD="post" ENCTYPE="enctype">string</FORM>
1563
+ def form(method = "post", action = script_name, enctype = "application/x-www-form-urlencoded")
1564
+ attributes = if method.kind_of?(String)
1565
+ { "METHOD" => method, "ACTION" => action,
1566
+ "ENCTYPE" => enctype }
1567
+ else
1568
+ unless method.has_key?("METHOD")
1569
+ method["METHOD"] = "post"
1570
+ end
1571
+ unless method.has_key?("ENCTYPE")
1572
+ method["ENCTYPE"] = enctype
1573
+ end
1574
+ method
1575
+ end
1576
+ if block_given?
1577
+ body = yield
1578
+ else
1579
+ body = ""
1580
+ end
1581
+ if @output_hidden
1582
+ body += @output_hidden.collect{|k,v|
1583
+ "<INPUT TYPE=\"HIDDEN\" NAME=\"#{k}\" VALUE=\"#{v}\">"
1584
+ }.to_s
1585
+ end
1586
+ super(attributes){body}
1587
+ end
1588
+
1589
+ # Generate a Hidden Input element as a string.
1590
+ #
1591
+ # The attributes of the element can be specified as two arguments,
1592
+ # +name+ and +value+.
1593
+ #
1594
+ # Alternatively, the attributes can be specified as a hash.
1595
+ #
1596
+ # hidden("name")
1597
+ # # <INPUT TYPE="hidden" NAME="name">
1598
+ #
1599
+ # hidden("name", "value")
1600
+ # # <INPUT TYPE="hidden" NAME="name" VALUE="value">
1601
+ #
1602
+ # hidden("NAME" => "name", "VALUE" => "reset", "ID" => "foo")
1603
+ # # <INPUT TYPE="hidden" NAME="name" VALUE="value" ID="foo">
1604
+ def hidden(name = "", value = nil)
1605
+ attributes = if name.kind_of?(String)
1606
+ { "TYPE" => "hidden", "NAME" => name, "VALUE" => value }
1607
+ else
1608
+ name["TYPE"] = "hidden"
1609
+ name
1610
+ end
1611
+ input(attributes)
1612
+ end
1613
+
1614
+ # Generate a top-level HTML element as a string.
1615
+ #
1616
+ # The attributes of the element are specified as a hash. The
1617
+ # pseudo-attribute "PRETTY" can be used to specify that the generated
1618
+ # HTML string should be indented. "PRETTY" can also be specified as
1619
+ # a string as the sole argument to this method. The pseudo-attribute
1620
+ # "DOCTYPE", if given, is used as the leading DOCTYPE SGML tag; it
1621
+ # should include the entire text of this tag, including angle brackets.
1622
+ #
1623
+ # The body of the html element is supplied as a block.
1624
+ #
1625
+ # html{ "string" }
1626
+ # # <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><HTML>string</HTML>
1627
+ #
1628
+ # html("LANG" => "ja") { "string" }
1629
+ # # <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"><HTML LANG="ja">string</HTML>
1630
+ #
1631
+ # html("DOCTYPE" => false) { "string" }
1632
+ # # <HTML>string</HTML>
1633
+ #
1634
+ # html("DOCTYPE" => '<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">') { "string" }
1635
+ # # <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"><HTML>string</HTML>
1636
+ #
1637
+ # html("PRETTY" => " ") { "<BODY></BODY>" }
1638
+ # # <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
1639
+ # # <HTML>
1640
+ # # <BODY>
1641
+ # # </BODY>
1642
+ # # </HTML>
1643
+ #
1644
+ # html("PRETTY" => "\t") { "<BODY></BODY>" }
1645
+ # # <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
1646
+ # # <HTML>
1647
+ # # <BODY>
1648
+ # # </BODY>
1649
+ # # </HTML>
1650
+ #
1651
+ # html("PRETTY") { "<BODY></BODY>" }
1652
+ # # = html("PRETTY" => " ") { "<BODY></BODY>" }
1653
+ #
1654
+ # html(if $VERBOSE then "PRETTY" end) { "HTML string" }
1655
+ #
1656
+ def html(attributes = {}) # :yield:
1657
+ if nil == attributes
1658
+ attributes = {}
1659
+ elsif "PRETTY" == attributes
1660
+ attributes = { "PRETTY" => true }
1661
+ end
1662
+ pretty = attributes.delete("PRETTY")
1663
+ pretty = " " if true == pretty
1664
+ buf = ""
1665
+
1666
+ if attributes.has_key?("DOCTYPE")
1667
+ if attributes["DOCTYPE"]
1668
+ buf += attributes.delete("DOCTYPE")
1669
+ else
1670
+ attributes.delete("DOCTYPE")
1671
+ end
1672
+ else
1673
+ buf += doctype
1674
+ end
1675
+
1676
+ if block_given?
1677
+ buf += super(attributes){ yield }
1678
+ else
1679
+ buf += super(attributes)
1680
+ end
1681
+
1682
+ if pretty
1683
+ CGI::pretty(buf, pretty)
1684
+ else
1685
+ buf
1686
+ end
1687
+
1688
+ end
1689
+
1690
+ # Generate an Image Button Input element as a string.
1691
+ #
1692
+ # +src+ is the URL of the image to use for the button. +name+
1693
+ # is the input name. +alt+ is the alternative text for the image.
1694
+ #
1695
+ # Alternatively, the attributes can be specified as a hash.
1696
+ #
1697
+ # image_button("url")
1698
+ # # <INPUT TYPE="image" SRC="url">
1699
+ #
1700
+ # image_button("url", "name", "string")
1701
+ # # <INPUT TYPE="image" SRC="url" NAME="name" ALT="string">
1702
+ #
1703
+ # image_button("SRC" => "url", "ATL" => "strng")
1704
+ # # <INPUT TYPE="image" SRC="url" ALT="string">
1705
+ def image_button(src = "", name = nil, alt = nil)
1706
+ attributes = if src.kind_of?(String)
1707
+ { "TYPE" => "image", "SRC" => src, "NAME" => name,
1708
+ "ALT" => alt }
1709
+ else
1710
+ src["TYPE"] = "image"
1711
+ src["SRC"] ||= ""
1712
+ src
1713
+ end
1714
+ input(attributes)
1715
+ end
1716
+
1717
+
1718
+ # Generate an Image element as a string.
1719
+ #
1720
+ # +src+ is the URL of the image. +alt+ is the alternative text for
1721
+ # the image. +width+ is the width of the image, and +height+ is
1722
+ # its height.
1723
+ #
1724
+ # Alternatively, the attributes can be specified as a hash.
1725
+ #
1726
+ # img("src", "alt", 100, 50)
1727
+ # # <IMG SRC="src" ALT="alt" WIDTH="100" HEIGHT="50">
1728
+ #
1729
+ # img("SRC" => "src", "ALT" => "alt", "WIDTH" => 100, "HEIGHT" => 50)
1730
+ # # <IMG SRC="src" ALT="alt" WIDTH="100" HEIGHT="50">
1731
+ def img(src = "", alt = "", width = nil, height = nil)
1732
+ attributes = if src.kind_of?(String)
1733
+ { "SRC" => src, "ALT" => alt }
1734
+ else
1735
+ src["WIDTH"] = src["WIDTH"].to_s if src["WIDTH"]
1736
+ src["HEIGHT"] = src["HEIGHT"].to_s if src["HEIGHT"]
1737
+ src
1738
+ end
1739
+ attributes["WIDTH"] = width.to_s if width
1740
+ attributes["HEIGHT"] = height.to_s if height
1741
+ super(attributes)
1742
+ end
1743
+
1744
+
1745
+ # Generate a Form element with multipart encoding as a String.
1746
+ #
1747
+ # Multipart encoding is used for forms that include file uploads.
1748
+ #
1749
+ # +action+ is the action to perform. +enctype+ is the encoding
1750
+ # type, which defaults to "multipart/form-data".
1751
+ #
1752
+ # Alternatively, the attributes can be specified as a hash.
1753
+ #
1754
+ # multipart_form{ "string" }
1755
+ # # <FORM METHOD="post" ENCTYPE="multipart/form-data">string</FORM>
1756
+ #
1757
+ # multipart_form("url") { "string" }
1758
+ # # <FORM METHOD="post" ACTION="url" ENCTYPE="multipart/form-data">string</FORM>
1759
+ def multipart_form(action = nil, enctype = "multipart/form-data")
1760
+ attributes = if action == nil
1761
+ { "METHOD" => "post", "ENCTYPE" => enctype }
1762
+ elsif action.kind_of?(String)
1763
+ { "METHOD" => "post", "ACTION" => action,
1764
+ "ENCTYPE" => enctype }
1765
+ else
1766
+ unless action.has_key?("METHOD")
1767
+ action["METHOD"] = "post"
1768
+ end
1769
+ unless action.has_key?("ENCTYPE")
1770
+ action["ENCTYPE"] = enctype
1771
+ end
1772
+ action
1773
+ end
1774
+ if block_given?
1775
+ form(attributes){ yield }
1776
+ else
1777
+ form(attributes)
1778
+ end
1779
+ end
1780
+
1781
+
1782
+ # Generate a Password Input element as a string.
1783
+ #
1784
+ # +name+ is the name of the input field. +value+ is its default
1785
+ # value. +size+ is the size of the input field display. +maxlength+
1786
+ # is the maximum length of the inputted password.
1787
+ #
1788
+ # Alternatively, attributes can be specified as a hash.
1789
+ #
1790
+ # password_field("name")
1791
+ # # <INPUT TYPE="password" NAME="name" SIZE="40">
1792
+ #
1793
+ # password_field("name", "value")
1794
+ # # <INPUT TYPE="password" NAME="name" VALUE="value" SIZE="40">
1795
+ #
1796
+ # password_field("password", "value", 80, 200)
1797
+ # # <INPUT TYPE="password" NAME="name" VALUE="value" SIZE="80" MAXLENGTH="200">
1798
+ #
1799
+ # password_field("NAME" => "name", "VALUE" => "value")
1800
+ # # <INPUT TYPE="password" NAME="name" VALUE="value">
1801
+ def password_field(name = "", value = nil, size = 40, maxlength = nil)
1802
+ attributes = if name.kind_of?(String)
1803
+ { "TYPE" => "password", "NAME" => name,
1804
+ "VALUE" => value, "SIZE" => size.to_s }
1805
+ else
1806
+ name["TYPE"] = "password"
1807
+ name
1808
+ end
1809
+ attributes["MAXLENGTH"] = maxlength.to_s if maxlength
1810
+ input(attributes)
1811
+ end
1812
+
1813
+ # Generate a Select element as a string.
1814
+ #
1815
+ # +name+ is the name of the element. The +values+ are the options that
1816
+ # can be selected from the Select menu. Each value can be a String or
1817
+ # a one, two, or three-element Array. If a String or a one-element
1818
+ # Array, this is both the value of that option and the text displayed for
1819
+ # it. If a three-element Array, the elements are the option value, displayed
1820
+ # text, and a boolean value specifying whether this option starts as selected.
1821
+ # The two-element version omits either the option value (defaults to the same
1822
+ # as the display text) or the boolean selected specifier (defaults to false).
1823
+ #
1824
+ # The attributes and options can also be specified as a hash. In this
1825
+ # case, options are specified as an array of values as described above,
1826
+ # with the hash key of "VALUES".
1827
+ #
1828
+ # popup_menu("name", "foo", "bar", "baz")
1829
+ # # <SELECT NAME="name">
1830
+ # # <OPTION VALUE="foo">foo</OPTION>
1831
+ # # <OPTION VALUE="bar">bar</OPTION>
1832
+ # # <OPTION VALUE="baz">baz</OPTION>
1833
+ # # </SELECT>
1834
+ #
1835
+ # popup_menu("name", ["foo"], ["bar", true], "baz")
1836
+ # # <SELECT NAME="name">
1837
+ # # <OPTION VALUE="foo">foo</OPTION>
1838
+ # # <OPTION VALUE="bar" SELECTED>bar</OPTION>
1839
+ # # <OPTION VALUE="baz">baz</OPTION>
1840
+ # # </SELECT>
1841
+ #
1842
+ # popup_menu("name", ["1", "Foo"], ["2", "Bar", true], "Baz")
1843
+ # # <SELECT NAME="name">
1844
+ # # <OPTION VALUE="1">Foo</OPTION>
1845
+ # # <OPTION SELECTED VALUE="2">Bar</OPTION>
1846
+ # # <OPTION VALUE="Baz">Baz</OPTION>
1847
+ # # </SELECT>
1848
+ #
1849
+ # popup_menu("NAME" => "name", "SIZE" => 2, "MULTIPLE" => true,
1850
+ # "VALUES" => [["1", "Foo"], ["2", "Bar", true], "Baz"])
1851
+ # # <SELECT NAME="name" MULTIPLE SIZE="2">
1852
+ # # <OPTION VALUE="1">Foo</OPTION>
1853
+ # # <OPTION SELECTED VALUE="2">Bar</OPTION>
1854
+ # # <OPTION VALUE="Baz">Baz</OPTION>
1855
+ # # </SELECT>
1856
+ def popup_menu(name = "", *values)
1857
+
1858
+ if name.kind_of?(Hash)
1859
+ values = name["VALUES"]
1860
+ size = name["SIZE"].to_s if name["SIZE"]
1861
+ multiple = name["MULTIPLE"]
1862
+ name = name["NAME"]
1863
+ else
1864
+ size = nil
1865
+ multiple = nil
1866
+ end
1867
+
1868
+ select({ "NAME" => name, "SIZE" => size,
1869
+ "MULTIPLE" => multiple }){
1870
+ values.collect{|value|
1871
+ if value.kind_of?(String)
1872
+ option({ "VALUE" => value }){ value }
1873
+ else
1874
+ if value[value.size - 1] == true
1875
+ option({ "VALUE" => value[0], "SELECTED" => true }){
1876
+ value[value.size - 2]
1877
+ }
1878
+ else
1879
+ option({ "VALUE" => value[0] }){
1880
+ value[value.size - 1]
1881
+ }
1882
+ end
1883
+ end
1884
+ }.to_s
1885
+ }
1886
+
1887
+ end
1888
+
1889
+ # Generates a radio-button Input element.
1890
+ #
1891
+ # +name+ is the name of the input field. +value+ is the value of
1892
+ # the field if checked. +checked+ specifies whether the field
1893
+ # starts off checked.
1894
+ #
1895
+ # Alternatively, the attributes can be specified as a hash.
1896
+ #
1897
+ # radio_button("name", "value")
1898
+ # # <INPUT TYPE="radio" NAME="name" VALUE="value">
1899
+ #
1900
+ # radio_button("name", "value", true)
1901
+ # # <INPUT TYPE="radio" NAME="name" VALUE="value" CHECKED>
1902
+ #
1903
+ # radio_button("NAME" => "name", "VALUE" => "value", "ID" => "foo")
1904
+ # # <INPUT TYPE="radio" NAME="name" VALUE="value" ID="foo">
1905
+ def radio_button(name = "", value = nil, checked = nil)
1906
+ attributes = if name.kind_of?(String)
1907
+ { "TYPE" => "radio", "NAME" => name,
1908
+ "VALUE" => value, "CHECKED" => checked }
1909
+ else
1910
+ name["TYPE"] = "radio"
1911
+ name
1912
+ end
1913
+ input(attributes)
1914
+ end
1915
+
1916
+ # Generate a sequence of radio button Input elements, as a String.
1917
+ #
1918
+ # This works the same as #checkbox_group(). However, it is not valid
1919
+ # to have more than one radiobutton in a group checked.
1920
+ #
1921
+ # radio_group("name", "foo", "bar", "baz")
1922
+ # # <INPUT TYPE="radio" NAME="name" VALUE="foo">foo
1923
+ # # <INPUT TYPE="radio" NAME="name" VALUE="bar">bar
1924
+ # # <INPUT TYPE="radio" NAME="name" VALUE="baz">baz
1925
+ #
1926
+ # radio_group("name", ["foo"], ["bar", true], "baz")
1927
+ # # <INPUT TYPE="radio" NAME="name" VALUE="foo">foo
1928
+ # # <INPUT TYPE="radio" CHECKED NAME="name" VALUE="bar">bar
1929
+ # # <INPUT TYPE="radio" NAME="name" VALUE="baz">baz
1930
+ #
1931
+ # radio_group("name", ["1", "Foo"], ["2", "Bar", true], "Baz")
1932
+ # # <INPUT TYPE="radio" NAME="name" VALUE="1">Foo
1933
+ # # <INPUT TYPE="radio" CHECKED NAME="name" VALUE="2">Bar
1934
+ # # <INPUT TYPE="radio" NAME="name" VALUE="Baz">Baz
1935
+ #
1936
+ # radio_group("NAME" => "name",
1937
+ # "VALUES" => ["foo", "bar", "baz"])
1938
+ #
1939
+ # radio_group("NAME" => "name",
1940
+ # "VALUES" => [["foo"], ["bar", true], "baz"])
1941
+ #
1942
+ # radio_group("NAME" => "name",
1943
+ # "VALUES" => [["1", "Foo"], ["2", "Bar", true], "Baz"])
1944
+ def radio_group(name = "", *values)
1945
+ if name.kind_of?(Hash)
1946
+ values = name["VALUES"]
1947
+ name = name["NAME"]
1948
+ end
1949
+ values.collect{|value|
1950
+ if value.kind_of?(String)
1951
+ radio_button(name, value) + value
1952
+ else
1953
+ if value[value.size - 1] == true
1954
+ radio_button(name, value[0], true) +
1955
+ value[value.size - 2]
1956
+ elsif value[value.size - 1] == false
1957
+ radio_button(name, value[0], false) +
1958
+ value[value.size - 2]
1959
+ else
1960
+ radio_button(name, value[0]) +
1961
+ value[value.size - 1]
1962
+ end
1963
+ end
1964
+ }.to_s
1965
+ end
1966
+
1967
+ # Generate a reset button Input element, as a String.
1968
+ #
1969
+ # This resets the values on a form to their initial values. +value+
1970
+ # is the text displayed on the button. +name+ is the name of this button.
1971
+ #
1972
+ # Alternatively, the attributes can be specified as a hash.
1973
+ #
1974
+ # reset
1975
+ # # <INPUT TYPE="reset">
1976
+ #
1977
+ # reset("reset")
1978
+ # # <INPUT TYPE="reset" VALUE="reset">
1979
+ #
1980
+ # reset("VALUE" => "reset", "ID" => "foo")
1981
+ # # <INPUT TYPE="reset" VALUE="reset" ID="foo">
1982
+ def reset(value = nil, name = nil)
1983
+ attributes = if (not value) or value.kind_of?(String)
1984
+ { "TYPE" => "reset", "VALUE" => value, "NAME" => name }
1985
+ else
1986
+ value["TYPE"] = "reset"
1987
+ value
1988
+ end
1989
+ input(attributes)
1990
+ end
1991
+
1992
+ alias scrolling_list popup_menu
1993
+
1994
+ # Generate a submit button Input element, as a String.
1995
+ #
1996
+ # +value+ is the text to display on the button. +name+ is the name
1997
+ # of the input.
1998
+ #
1999
+ # Alternatively, the attributes can be specified as a hash.
2000
+ #
2001
+ # submit
2002
+ # # <INPUT TYPE="submit">
2003
+ #
2004
+ # submit("ok")
2005
+ # # <INPUT TYPE="submit" VALUE="ok">
2006
+ #
2007
+ # submit("ok", "button1")
2008
+ # # <INPUT TYPE="submit" VALUE="ok" NAME="button1">
2009
+ #
2010
+ # submit("VALUE" => "ok", "NAME" => "button1", "ID" => "foo")
2011
+ # # <INPUT TYPE="submit" VALUE="ok" NAME="button1" ID="foo">
2012
+ def submit(value = nil, name = nil)
2013
+ attributes = if (not value) or value.kind_of?(String)
2014
+ { "TYPE" => "submit", "VALUE" => value, "NAME" => name }
2015
+ else
2016
+ value["TYPE"] = "submit"
2017
+ value
2018
+ end
2019
+ input(attributes)
2020
+ end
2021
+
2022
+ # Generate a text field Input element, as a String.
2023
+ #
2024
+ # +name+ is the name of the input field. +value+ is its initial
2025
+ # value. +size+ is the size of the input area. +maxlength+
2026
+ # is the maximum length of input accepted.
2027
+ #
2028
+ # Alternatively, the attributes can be specified as a hash.
2029
+ #
2030
+ # text_field("name")
2031
+ # # <INPUT TYPE="text" NAME="name" SIZE="40">
2032
+ #
2033
+ # text_field("name", "value")
2034
+ # # <INPUT TYPE="text" NAME="name" VALUE="value" SIZE="40">
2035
+ #
2036
+ # text_field("name", "value", 80)
2037
+ # # <INPUT TYPE="text" NAME="name" VALUE="value" SIZE="80">
2038
+ #
2039
+ # text_field("name", "value", 80, 200)
2040
+ # # <INPUT TYPE="text" NAME="name" VALUE="value" SIZE="80" MAXLENGTH="200">
2041
+ #
2042
+ # text_field("NAME" => "name", "VALUE" => "value")
2043
+ # # <INPUT TYPE="text" NAME="name" VALUE="value">
2044
+ def text_field(name = "", value = nil, size = 40, maxlength = nil)
2045
+ attributes = if name.kind_of?(String)
2046
+ { "TYPE" => "text", "NAME" => name, "VALUE" => value,
2047
+ "SIZE" => size.to_s }
2048
+ else
2049
+ name["TYPE"] = "text"
2050
+ name
2051
+ end
2052
+ attributes["MAXLENGTH"] = maxlength.to_s if maxlength
2053
+ input(attributes)
2054
+ end
2055
+
2056
+ # Generate a TextArea element, as a String.
2057
+ #
2058
+ # +name+ is the name of the textarea. +cols+ is the number of
2059
+ # columns and +rows+ is the number of rows in the display.
2060
+ #
2061
+ # Alternatively, the attributes can be specified as a hash.
2062
+ #
2063
+ # The body is provided by the passed-in no-argument block
2064
+ #
2065
+ # textarea("name")
2066
+ # # = textarea("NAME" => "name", "COLS" => 70, "ROWS" => 10)
2067
+ #
2068
+ # textarea("name", 40, 5)
2069
+ # # = textarea("NAME" => "name", "COLS" => 40, "ROWS" => 5)
2070
+ def textarea(name = "", cols = 70, rows = 10) # :yield:
2071
+ attributes = if name.kind_of?(String)
2072
+ { "NAME" => name, "COLS" => cols.to_s,
2073
+ "ROWS" => rows.to_s }
2074
+ else
2075
+ name
2076
+ end
2077
+ if block_given?
2078
+ super(attributes){ yield }
2079
+ else
2080
+ super(attributes)
2081
+ end
2082
+ end
2083
+
2084
+ end # HtmlExtension
2085
+
2086
+
2087
+ # Mixin module for HTML version 3 generation methods.
2088
+ module Html3 # :nodoc:
2089
+
2090
+ # The DOCTYPE declaration for this version of HTML
2091
+ def doctype
2092
+ %|<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">|
2093
+ end
2094
+
2095
+ # Initialise the HTML generation methods for this version.
2096
+ def element_init
2097
+ extend TagMaker
2098
+ methods = ""
2099
+ # - -
2100
+ for element in %w[ A TT I B U STRIKE BIG SMALL SUB SUP EM STRONG
2101
+ DFN CODE SAMP KBD VAR CITE FONT ADDRESS DIV center MAP
2102
+ APPLET PRE XMP LISTING DL OL UL DIR MENU SELECT table TITLE
2103
+ STYLE SCRIPT H1 H2 H3 H4 H5 H6 TEXTAREA FORM BLOCKQUOTE
2104
+ CAPTION ]
2105
+ methods += <<-BEGIN + nn_element_def(element) + <<-END
2106
+ def #{element.downcase}(attributes = {})
2107
+ BEGIN
2108
+ end
2109
+ END
2110
+ end
2111
+
2112
+ # - O EMPTY
2113
+ for element in %w[ IMG BASE BASEFONT BR AREA LINK PARAM HR INPUT
2114
+ ISINDEX META ]
2115
+ methods += <<-BEGIN + nOE_element_def(element) + <<-END
2116
+ def #{element.downcase}(attributes = {})
2117
+ BEGIN
2118
+ end
2119
+ END
2120
+ end
2121
+
2122
+ # O O or - O
2123
+ for element in %w[ HTML HEAD BODY P PLAINTEXT DT DD LI OPTION tr
2124
+ th td ]
2125
+ methods += <<-BEGIN + nO_element_def(element) + <<-END
2126
+ def #{element.downcase}(attributes = {})
2127
+ BEGIN
2128
+ end
2129
+ END
2130
+ end
2131
+ eval(methods)
2132
+ end
2133
+
2134
+ end # Html3
2135
+
2136
+
2137
+ # Mixin module for HTML version 4 generation methods.
2138
+ module Html4 # :nodoc:
2139
+
2140
+ # The DOCTYPE declaration for this version of HTML
2141
+ def doctype
2142
+ %|<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">|
2143
+ end
2144
+
2145
+ def element_init
2146
+ # did this is the module body
2147
+ end
2148
+
2149
+ # Initialise the HTML generation methods for this version.
2150
+ def self.element_init_methods
2151
+ methods = ""
2152
+ # - -
2153
+ for element in %w[ TT I B BIG SMALL EM STRONG DFN CODE SAMP KBD
2154
+ VAR CITE ABBR ACRONYM SUB SUP SPAN BDO ADDRESS DIV MAP OBJECT
2155
+ H1 H2 H3 H4 H5 H6 PRE Q INS DEL DL OL UL LABEL SELECT OPTGROUP
2156
+ FIELDSET LEGEND BUTTON TABLE TITLE STYLE SCRIPT NOSCRIPT
2157
+ TEXTAREA FORM A BLOCKQUOTE CAPTION ]
2158
+ methods += <<-BEGIN + TagMaker.nn_element_def(element) + <<-END
2159
+ def #{element.downcase}(attributes = {})
2160
+ BEGIN
2161
+ end
2162
+ END
2163
+ end
2164
+
2165
+ # - O EMPTY
2166
+ for element in %w[ IMG BASE BR AREA LINK PARAM HR INPUT COL META ]
2167
+ methods += <<-BEGIN + TagMaker.nOE_element_def(element) + <<-END
2168
+ def #{element.downcase}(attributes = {})
2169
+ BEGIN
2170
+ end
2171
+ END
2172
+ end
2173
+
2174
+ # O O or - O
2175
+ for element in %w[ HTML BODY P DT DD LI OPTION THEAD TFOOT TBODY
2176
+ COLGROUP TR TH TD HEAD]
2177
+ methods += <<-BEGIN + TagMaker.nO_element_def(element) + <<-END
2178
+ def #{element.downcase}(attributes = {})
2179
+ BEGIN
2180
+ end
2181
+ END
2182
+ end
2183
+ eval(methods)
2184
+ end
2185
+
2186
+ element_init_methods()
2187
+
2188
+ end # Html4
2189
+
2190
+
2191
+ # Mixin module for HTML version 4 transitional generation methods.
2192
+ module Html4Tr # :nodoc:
2193
+
2194
+ # The DOCTYPE declaration for this version of HTML
2195
+ def doctype
2196
+ %|<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">|
2197
+ end
2198
+
2199
+ # Initialise the HTML generation methods for this version.
2200
+ def element_init
2201
+ extend TagMaker
2202
+ methods = ""
2203
+ # - -
2204
+ for element in %w[ TT I B U S STRIKE BIG SMALL EM STRONG DFN
2205
+ CODE SAMP KBD VAR CITE ABBR ACRONYM FONT SUB SUP SPAN BDO
2206
+ ADDRESS DIV CENTER MAP OBJECT APPLET H1 H2 H3 H4 H5 H6 PRE Q
2207
+ INS DEL DL OL UL DIR MENU LABEL SELECT OPTGROUP FIELDSET
2208
+ LEGEND BUTTON TABLE IFRAME NOFRAMES TITLE STYLE SCRIPT
2209
+ NOSCRIPT TEXTAREA FORM A BLOCKQUOTE CAPTION ]
2210
+ methods += <<-BEGIN + nn_element_def(element) + <<-END
2211
+ def #{element.downcase}(attributes = {})
2212
+ BEGIN
2213
+ end
2214
+ END
2215
+ end
2216
+
2217
+ # - O EMPTY
2218
+ for element in %w[ IMG BASE BASEFONT BR AREA LINK PARAM HR INPUT
2219
+ COL ISINDEX META ]
2220
+ methods += <<-BEGIN + nOE_element_def(element) + <<-END
2221
+ def #{element.downcase}(attributes = {})
2222
+ BEGIN
2223
+ end
2224
+ END
2225
+ end
2226
+
2227
+ # O O or - O
2228
+ for element in %w[ HTML BODY P DT DD LI OPTION THEAD TFOOT TBODY
2229
+ COLGROUP TR TH TD HEAD ]
2230
+ methods += <<-BEGIN + nO_element_def(element) + <<-END
2231
+ def #{element.downcase}(attributes = {})
2232
+ BEGIN
2233
+ end
2234
+ END
2235
+ end
2236
+ eval(methods)
2237
+ end
2238
+
2239
+ end # Html4Tr
2240
+
2241
+
2242
+ # Mixin module for generating HTML version 4 with framesets.
2243
+ module Html4Fr # :nodoc:
2244
+
2245
+ # The DOCTYPE declaration for this version of HTML
2246
+ def doctype
2247
+ %|<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">|
2248
+ end
2249
+
2250
+ # Initialise the HTML generation methods for this version.
2251
+ def element_init
2252
+ methods = ""
2253
+ # - -
2254
+ for element in %w[ FRAMESET ]
2255
+ methods += <<-BEGIN + nn_element_def(element) + <<-END
2256
+ def #{element.downcase}(attributes = {})
2257
+ BEGIN
2258
+ end
2259
+ END
2260
+ end
2261
+
2262
+ # - O EMPTY
2263
+ for element in %w[ FRAME ]
2264
+ methods += <<-BEGIN + nOE_element_def(element) + <<-END
2265
+ def #{element.downcase}(attributes = {})
2266
+ BEGIN
2267
+ end
2268
+ END
2269
+ end
2270
+ eval(methods)
2271
+ end
2272
+
2273
+ end # Html4Fr
2274
+
2275
+
2276
+ # Creates a new CGI instance.
2277
+ #
2278
+ # +type+ specifies which version of HTML to load the HTML generation
2279
+ # methods for. The following versions of HTML are supported:
2280
+ #
2281
+ # html3:: HTML 3.x
2282
+ # html4:: HTML 4.0
2283
+ # html4Tr:: HTML 4.0 Transitional
2284
+ # html4Fr:: HTML 4.0 with Framesets
2285
+ #
2286
+ # If not specified, no HTML generation methods will be loaded.
2287
+ #
2288
+ # If the CGI object is not created in a standard CGI call environment
2289
+ # (that is, it can't locate REQUEST_METHOD in its environment), then
2290
+ # it will run in "offline" mode. In this mode, it reads its parameters
2291
+ # from the command line or (failing that) from standard input. Otherwise,
2292
+ # cookies and other parameters are parsed automatically from the standard
2293
+ # CGI locations, which varies according to the REQUEST_METHOD.
2294
+ def initialize(type = "query")
2295
+ if defined?(MOD_RUBY) && !ENV.key?("GATEWAY_INTERFACE")
2296
+ Apache.request.setup_cgi_env
2297
+ end
2298
+
2299
+ extend QueryExtension
2300
+ @multipart = false
2301
+ if defined?(CGI_PARAMS)
2302
+ warn "do not use CGI_PARAMS and CGI_COOKIES"
2303
+ @params = CGI_PARAMS.dup
2304
+ @cookies = CGI_COOKIES.dup
2305
+ else
2306
+ initialize_query() # set @params, @cookies
2307
+ end
2308
+ @output_cookies = nil
2309
+ @output_hidden = nil
2310
+
2311
+ case type
2312
+ when "html3"
2313
+ extend Html3
2314
+ element_init()
2315
+ extend HtmlExtension
2316
+ when "html4"
2317
+ extend Html4
2318
+ element_init()
2319
+ extend HtmlExtension
2320
+ when "html4Tr"
2321
+ extend Html4Tr
2322
+ element_init()
2323
+ extend HtmlExtension
2324
+ when "html4Fr"
2325
+ extend Html4Tr
2326
+ element_init()
2327
+ extend Html4Fr
2328
+ element_init()
2329
+ extend HtmlExtension
2330
+ end
2331
+ end
2332
+
2333
+ end # class CGI