Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package rubygem-cgi for openSUSE:Factory checked in at 2026-09-29 17:46:04 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/rubygem-cgi (Old) and /work/SRC/openSUSE:Factory/.rubygem-cgi.new.383539 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "rubygem-cgi" Tue Sep 29 17:46:04 2026 rev:2 rq:1380830 version:0.5.2 Changes: -------- --- /work/SRC/openSUSE:Factory/rubygem-cgi/rubygem-cgi.changes 2026-01-05 14:51:36.110385065 +0100 +++ /work/SRC/openSUSE:Factory/.rubygem-cgi.new.383539/rubygem-cgi.changes 2026-09-29 17:46:24.385755733 +0200 @@ -1,0 +2,17 @@ +Sat Sep 26 16:25:38 UTC 2026 - Andrea Manzini <[email protected]> + +- Update to 0.5.2 (also covers skipped 0.5.1): + * Handle a POST request with a missing/empty Content-Length instead + of raising TypeError/ArgumentError (gh#ruby/cgi#56) + * Fix CGI.unescapeHTML raising Encoding::CompatibilityError in the + pure-Ruby fallback on mixed non-ASCII input (gh#ruby/cgi#103) + * Fix escape_html/h/unescape_html aliases to actually dispatch to + the C extension instead of the slower pure-Ruby implementation + * Harden CGI::Session's file-store filename hashing: use SHA-256 + instead of MD5, and add a configurable :digest option (default + stays MD5 for backward compatibility) + * Various documentation improvements +- Run spec-cleaner (tag order, License operator casing normalized to + the SPDX "AND") + +------------------------------------------------------------------- Old: ---- cgi-0.5.0.gem New: ---- cgi-0.5.2.gem ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ rubygem-cgi.spec ++++++ --- /var/tmp/diff_new_pack.TJ0Z9d/_old 2026-09-29 17:46:30.076993820 +0200 +++ /var/tmp/diff_new_pack.TJ0Z9d/_new 2026-09-29 17:46:30.078993904 +0200 @@ -16,26 +16,25 @@ # +%define mod_name cgi +%define mod_full_name %{mod_name}-%{version} # # This file was generated with a gem2rpm.yml and not just plain gem2rpm. # All sections marked as MANUAL, license headers, summaries and descriptions # can be maintained in that file. Please consult this file before editing any # of those fields # - Name: rubygem-cgi -Version: 0.5.0 +Version: 0.5.2 Release: 0 -%define mod_name cgi -%define mod_full_name %{mod_name}-%{version} -BuildRequires: ruby-macros >= 5 -BuildRequires: %{rubydevel >= 2.5.0} -BuildRequires: %{rubygem gem2rpm} +Summary: Support for the Common Gateway Interface protocol +License: Ruby AND BSD-2-Clause URL: https://github.com/ruby/cgi Source: https://rubygems.org/gems/%{mod_full_name}.gem Source1: gem2rpm.yml -Summary: Support for the Common Gateway Interface protocol -License: Ruby and BSD-2-Clause +BuildRequires: %{rubydevel >= 2.5.0} +BuildRequires: %{rubygem gem2rpm} +BuildRequires: ruby-macros >= 5 %description Support for the Common Gateway Interface protocol. @@ -49,7 +48,7 @@ --no-rdoc --no-ri \ --doc-files="COPYING README.md" \ -f -%gem_cleanup +%{gem_cleanup} %gem_packages ++++++ cgi-0.5.0.gem -> cgi-0.5.2.gem ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/README.md new/README.md --- old/README.md 2025-06-04 05:38:29.000000000 +0200 +++ new/README.md 2026-06-23 09:06:24.000000000 +0200 @@ -22,11 +22,15 @@ And then execute: - $ bundle +```bash +bundle +``` Or install it yourself as: - $ gem install cgi +```bash +gem install cgi +``` ## Usage @@ -73,7 +77,6 @@ end ``` - ### Restore form values from file ```ruby Binary files old/checksums.yaml.gz and new/checksums.yaml.gz differ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/ext/cgi/escape/escape.c new/ext/cgi/escape/escape.c --- old/ext/cgi/escape/escape.c 2025-06-04 05:38:29.000000000 +0200 +++ new/ext/cgi/escape/escape.c 2026-06-23 09:06:24.000000000 +0200 @@ -45,6 +45,7 @@ static VALUE optimized_escape_html(VALUE str) { + VALUE escaped; VALUE vbuf; char *buf = ALLOCV_N(char, vbuf, escaped_length(str)); const char *cstr = RSTRING_PTR(str); @@ -63,7 +64,6 @@ } } - VALUE escaped; if (RSTRING_LEN(str) < (dest - buf)) { escaped = rb_str_new(buf, dest - buf); preserve_original_state(str, escaped); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/cgi/cookie.rb new/lib/cgi/cookie.rb --- old/lib/cgi/cookie.rb 2025-06-04 05:38:29.000000000 +0200 +++ new/lib/cgi/cookie.rb 2026-06-23 09:06:24.000000000 +0200 @@ -40,9 +40,11 @@ class Cookie < Array @@accept_charset="UTF-8" unless defined?(@@accept_charset) + # :stopdoc: TOKEN_RE = %r"\A[[!-~]&&[^()<>@,;:\\\"/?=\[\]{}]]+\z" PATH_VALUE_RE = %r"\A[[ -~]&&[^;]]*\z" DOMAIN_VALUE_RE = %r"\A\.?(?<label>(?!-)[-A-Za-z0-9]+(?<!-))(?:\.\g<label>)*\z" + # :startdoc: # Create a new CGI::Cookie object. # diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/cgi/core.rb new/lib/cgi/core.rb --- old/lib/cgi/core.rb 2025-06-04 05:38:29.000000000 +0200 +++ new/lib/cgi/core.rb 2026-06-23 09:06:24.000000000 +0200 @@ -72,91 +72,267 @@ private :env_table, :stdinput, :stdoutput - # Create an HTTP header block as a string. - # # :call-seq: - # http_header(content_type_string="text/html") - # http_header(headers_hash) - # - # Includes the empty line that ends the header block. + # http_header(content_type = 'text/html') -> string + # http_header(headers) -> string # - # +content_type_string+:: - # If this form is used, this string is the <tt>Content-Type</tt> - # +headers_hash+:: - # A Hash of header values. The following header keys are recognized: + # Creates and returns an HTTP header section as a multi-line string. # - # type:: The Content-Type header. Defaults to "text/html" - # charset:: The charset of the body, appended to the Content-Type header. - # nph:: A boolean value. If true, prepend protocol string and status - # code, and date; and sets default values for "server" and - # "connection" if not explicitly set. - # status:: - # The HTTP status code as a String, returned as the Status header. The - # values are: - # - # OK:: 200 OK - # PARTIAL_CONTENT:: 206 Partial Content - # MULTIPLE_CHOICES:: 300 Multiple Choices - # MOVED:: 301 Moved Permanently - # REDIRECT:: 302 Found - # NOT_MODIFIED:: 304 Not Modified - # BAD_REQUEST:: 400 Bad Request - # AUTH_REQUIRED:: 401 Authorization Required - # FORBIDDEN:: 403 Forbidden - # NOT_FOUND:: 404 Not Found - # METHOD_NOT_ALLOWED:: 405 Method Not Allowed - # NOT_ACCEPTABLE:: 406 Not Acceptable - # LENGTH_REQUIRED:: 411 Length Required - # PRECONDITION_FAILED:: 412 Precondition Failed - # SERVER_ERROR:: 500 Internal Server Error - # NOT_IMPLEMENTED:: 501 Method Not Implemented - # BAD_GATEWAY:: 502 Bad Gateway - # VARIANT_ALSO_VARIES:: 506 Variant Also Negotiates - # - # server:: The server software, returned as the Server header. - # connection:: The connection type, returned as the Connection header (for - # instance, "close". - # length:: The length of the content that will be sent, returned as the - # Content-Length header. - # language:: The language of the content, returned as the Content-Language - # header. - # expires:: The time on which the current content expires, as a +Time+ - # object, returned as the Expires header. - # cookie:: - # A cookie or cookies, returned as one or more Set-Cookie headers. The - # value can be the literal string of the cookie; a CGI::Cookie object; - # an Array of literal cookie strings or Cookie objects; or a hash all of - # whose values are literal cookie strings or Cookie objects. + # The string always includes: # - # These cookies are in addition to the cookies held in the - # @output_cookies field. + # - Header +Content-Type+ (with a default value if none given). + # - A trailing newline, which delimits the header block; + # that last line is omitted from the examples below. + # + # <b>In Brief</b> + # + # headers = { + # 'charset' => 'iso-2022-jp', + # 'connection' => 'keep-alive', + # 'cookie' => 'foo=0', + # 'expires' => Time.now + (60 * 60 * 24 * 365), + # 'language' => 'en-US, en-CA', + # 'length' => 4096, + # 'nph' => true, + # 'server' => 'Apache/2.4.1 (Unix)', + # 'status' => 'OK', + # 'type' => 'text/xml', + # MyHeader: true + # } + # + # puts cgi.http_header(headers) + # HTTP/1.0 200 OK + # Date: Mon, 01 Dec 2025 22:08:22 GMT + # Server: Apache/2.4.1 (Unix) + # Connection: keep-alive + # Content-Type: text/xml; charset=iso-2022-jp + # Content-Length: 4096 + # Content-Language: en-US, en-CA + # Expires: Tue, 01 Dec 2026 22:05:30 GMT + # Set-Cookie: foo=0 + # MyHeader: true + # + # headers.delete('nph') + # + # puts cgi.http_header(headers) + # Status: 200 OK + # Server: Apache/2.4.1 (Unix) + # Connection: keep-alive + # Content-Type: text/xml; charset=iso-2022-jp + # Content-Length: 4096 + # Content-Language: en-US, en-CA + # Expires: Tue, 01 Dec 2026 22:05:30 GMT + # Set-Cookie: foo=0 + # MyHeader: true + # + # <b>Arguments</b> + # + # With no argument given, + # includes only header +Content-Type+ with its default value <tt>'text/html'</tt>: + # + # puts cgi.http_header + # Content-Type: text/html + # + # With string argument +content_type+ given, + # includes header +Content-Type+ with the given value: + # + # puts cgi.http_header('text/xml') + # Content-Type: text/xml + # + # With hash argument +headers+ given, + # includes a header for hash entry, whose name is based on the entry's key, + # and whose value is the entry's value. + # + # <i>Recognized Keys</i> + # + # The following keys are recognized; + # each is a lowercase string: + # + # <tt>'charset'</tt>:: + # The character set of the body; appended to the +Content-Type+ header: + # + # puts cgi.http_header('charset' => 'iso-2022-jp') + # Content-Type: text/html; charset=iso-2022-jp + # + # <tt>'connection'</tt>:: + # Sets header +Connection+ to the given string: + # + # puts cgi.http_header('connection' => 'keep-alive') + # Connection: keep-alive + # Content-Type: text/html + # + # <tt>'cookie'</tt>:: + # Sets one or more +Set-Cookie+ headers to the given value, which may be: + # + # - String cookie. + # - CGI::Cookie object. + # - Array of string cookies and CGI::Cookie objects. + # - A hash whose values are string cookies and CGI::Cookie objects + # (the keys are not used). + # + # Examples: + # + # foo_string = 'foo=0' + # bar_string = 'bar=1' + # foo_cookie = CGI::Cookie.new('foo', '0') + # bar_cookie = CGI::Cookie.new('bar', '1') + # + # puts cgi.http_header('cookie' => foo_string) + # Content-Type: text/html + # Set-Cookie: foo=0 + # + # puts cgi.http_header('cookie' => foo_cookie) + # Content-Type: text/html + # Set-Cookie: foo=0; path= + # + # puts cgi.http_header('cookie' => [foo_cookie, bar_string]) + # Content-Type: text/html + # Set-Cookie: foo=0; path= + # Set-Cookie: bar=1 + # + # puts cgi.http_header('cookie' => {foo: foo_cookie, bar: bar_string}) + # Content-Type: text/html + # Set-Cookie: foo=0; path= + # Set-Cookie: bar=1 + # + # These cookies are in addition to the cookies held + # in the <tt>@output_cookies</tt> variable. + # + # <tt>'expires'</tt>:: + # Sets header +Expires+ to the given time, + # which must be a {Time}[https://docs.ruby-lang.org/en/master/Time.html] object: + # + # puts cgi.http_header('expires' => Time.now + (60 * 60 * 24 * 365)) + # Content-Type: text/html + # Expires: Tue, 01 Dec 2026 23:42:37 GMT + # + # <tt>'language'</tt>:: + # Sets header +Content-Language+ to the given string: + # + # puts cgi.http_header('language' => 'en-US, en-CA') + # Content-Type: text/html + # Content-Language: en-US, en-CA + # + # <tt>'length'</tt>:: + # Sets header +Content-Length+ to the given value, + # which may be an integer or a string: + # + # puts cgi.http_header('length' => 4096) + # Content-Type: text/html + # Content-Length: 4096 + # + # puts cgi.http_header('length' => '4096') + # Content-Type: text/html + # Content-Length: 4096 + # + # <tt>'nph'</tt>:: + # If +true+: + # + # - Adds protocol string and status code as first line. + # - Adds date as second line. + # - Adds headers +Server+ with no value, + # and +Connection+ with default value <tt>'close'</tt>; + # either or both values may be overridden with explicit values. + # + # Examples: + # + # puts cgi.http_header('nph' => true) + # HTTP/1.0 200 OK + # Date: Mon, 01 Dec 2025 19:42:22 GMT + # Server: + # Connection: close + # Content-Type: text/html + # + # puts cgi.http_header('nph' => true, 'server' => 'Apache/2.4.1 (Unix)', 'connection' => 'keep-alive') + # HTTP/1.0 200 OK + # Date: Mon, 01 Dec 2025 20:00:41 GMT + # Server: Apache/2.4.1 (Unix) + # Connection: keep-alive + # Content-Type: text/html + # + # <tt>'server'</tt>:: + # Sets header +Server+ to the given string: + # + # puts cgi.http_header('server' => 'Apache/2.4.1 (Unix)') + # Server: Apache/2.4.1 (Unix) + # Content-Type: text/html + # + # <tt>'status'</tt>:: + # Sets header +Status+ to the given string: + # + # puts cgi.http_header('status' => '666 MyVeryOwnStatus') + # Status: 666 MyVeryOwnStatus + # Content-Type: text/html + # + # If the given string is a key in the hash constant +CGI::HTTP_STATUS+, + # the status becomes the value for that key: + # + # CGI::HTTP_STATUS + # # => + # {"OK" => "200 OK", + # "PARTIAL_CONTENT" => "206 Partial Content", + # "MULTIPLE_CHOICES" => "300 Multiple Choices", + # "MOVED" => "301 Moved Permanently", + # "REDIRECT" => "302 Found", + # "NOT_MODIFIED" => "304 Not Modified", + # "BAD_REQUEST" => "400 Bad Request", + # "AUTH_REQUIRED" => "401 Authorization Required", + # "FORBIDDEN" => "403 Forbidden", + # "NOT_FOUND" => "404 Not Found", + # "METHOD_NOT_ALLOWED" => "405 Method Not Allowed", + # "NOT_ACCEPTABLE" => "406 Not Acceptable", + # "LENGTH_REQUIRED" => "411 Length Required", + # "PRECONDITION_FAILED" => "412 Precondition Failed", + # "SERVER_ERROR" => "500 Internal Server Error", + # "NOT_IMPLEMENTED" => "501 Method Not Implemented", + # "BAD_GATEWAY" => "502 Bad Gateway", + # "VARIANT_ALSO_VARIES" => "506 Variant Also Negotiates"} + # + # puts cgi.http_header('status' => 'OK') + # Status: 200 OK + # Content-Type: text/html + # + # puts cgi.http_header('status' => 'NOT_FOUND') + # Status: 404 Not Found + # Content-Type: text/html + # + # <tt>'type'</tt>:: + # Sets +Content-Type+, overriding the default value <tt>'text/html'</tt>: + # + # puts cgi.http_header('type' => 'text/xml') + # Content-Type: text/xml + # + # <i>Unrecognized Keys</i> + # + # Headers may also be set for unrecognized keys; + # an unrecognized key becomes a header name with the given value: + # + # puts cgi.http_header('length' => 0) # Recognized key (lowercase string). + # Content-Type: text/html + # Content-Length: 0 + # + # puts cgi.http_header('Length' => 0) # Unrecognized key (string key not lowercase). + # Content-Type: text/html + # Length: 0 + # + # puts cgi.http_header(length: 0) # Unrecognized key (symbol key not string) + # Content-Type: text/html + # length: 0 # - # Other headers can also be set; they are appended as key: value. + # This method does not perform charset conversion. # - # Examples: + # It's best to use this method (CGI#http_header), not its aliased method +header+, + # which is provided only for backward compatibility. # - # http_header - # # Content-Type: text/html + # Method CGI#http_header is preferred because when +tag_maker+ is <tt>'html5'</tt>, + # calling method +header+ generates an HTML +header+ element: # - # http_header("text/plain") - # # Content-Type: text/plain + # cgi = CGI.new(tag_maker: 'html5') + # puts cgi.http_header # Works as expected. + # Content-Type: text/html + # puts cgi.header # Maybe a surprise. + # <HEADER></HEADER> # - # http_header("nph" => true, - # "status" => "OK", # == "200 OK" - # # "status" => "200 GOOD", - # "server" => ENV['SERVER_SOFTWARE'], - # "connection" => "close", - # "type" => "text/html", - # "charset" => "iso-2022-jp", - # # Content-Type: text/html; charset=iso-2022-jp - # "length" => 103, - # "language" => "ja", - # "expires" => Time.now + 30, - # "cookie" => [cookie1, cookie2], - # "my_header1" => "my_value", - # "my_header2" => "my_value") - # - # This method does not perform charset conversion. def http_header(options='text/html') if options.is_a?(String) content_type = options @@ -384,11 +560,14 @@ stdoutput.print(*options) end - # Parse an HTTP query string into a hash of key=>value pairs. + # :call-seq: + # CGI.parse(query_string) -> hash # - # params = CGI.parse("query_string") - # # {"name1" => ["value1", "value2", ...], - # # "name2" => ["value1", "value2", ...], ... } + # Returns a new hash built from name/value pairs in the given +query_string+: + # + # query = 'foo=0&bar=1&foo=2&bar=3' + # CGI.parse(query) + # # => {"foo" => ["0", "2"], "bar" => ["1", "3"]} # def self.parse(query) params = {} @@ -478,7 +657,7 @@ ## # Parses multipart form elements according to - # http://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.2 + # https://www.w3.org/TR/html401/interact/forms.html#h-17.13.4.2 # # Returns a hash of multipart form parameters with bodies of type StringIO or # Tempfile depending on whether the multipart form element exceeds 10 KB @@ -629,8 +808,8 @@ string = unless ARGV.empty? ARGV.join(' ') else - if STDIN.tty? - STDERR.print( + if stdinput.tty? + $stderr.print( %|(offline mode: enter name=value pairs on standard input)\n| ) end @@ -659,13 +838,16 @@ # Handles multipart forms (in particular, forms that involve file uploads). # Reads query parameters in the @params field, and cookies into @cookies. def initialize_query() + content_length = env_table['CONTENT_LENGTH'] + content_length = nil if content_length == '' if ("POST" == env_table['REQUEST_METHOD']) and %r|\Amultipart/form-data.*boundary=\"?([^\";,]+)\"?| =~ env_table['CONTENT_TYPE'] current_max_multipart_length = @max_multipart_length.respond_to?(:call) ? @max_multipart_length.call : @max_multipart_length raise StandardError.new("too large multipart data.") if env_table['CONTENT_LENGTH'].to_i > current_max_multipart_length + raise StandardError.new("no content length for multipart data.") if content_length.nil? boundary = $1.dup @multipart = true - @params = read_multipart(boundary, Integer(env_table['CONTENT_LENGTH'])) + @params = read_multipart(boundary, Integer(content_length)) else @multipart = false @params = CGI.parse( @@ -678,7 +860,11 @@ end when "POST" stdinput.binmode if defined? stdinput.binmode - stdinput.read(Integer(env_table['CONTENT_LENGTH'])) or '' + if content_length.nil? + stdinput.read or '' + else + stdinput.read(Integer(content_length)) or '' + end else read_from_cmdline end.dup.force_encoding(@accept_charset) @@ -755,12 +941,75 @@ # @@accept_charset="UTF-8" if false # needed for rdoc? - # Return the accept character set for all new CGI instances. + # :call-seq: + # CGI.accept_charset -> encoding + # + # Returns the default accept character set to be used for new \CGI instances; + # see CGI.accept_charset=. def self.accept_charset @@accept_charset end - # Set the accept character set for all new CGI instances. + # :call-seq: + # CGI.accept_charset = encoding + # + # Sets the default accept character set to be used for new \CGI instances; + # returns the argument. + # + # The argument may be an Encoding object or an Encoding name; + # see {Encodings}[https://docs.ruby-lang.org/en/master/language/encodings_rdoc.html]: + # + # # The initial value. + # CGI.accept_charset # => #<Encoding:UTF-8> + # CGI.new + # # => + # #<CGI:0x0000018991db6ae8 + # @accept_charset=#<Encoding:UTF-8>, + # @accept_charset_error_block=nil, + # @cookies={}, + # @max_multipart_length=134217728, + # @multipart=false, + # @options={accept_charset: #<Encoding:UTF-8>, max_multipart_length: 134217728}, + # @output_cookies=nil, + # @output_hidden=nil, + # @params={}> + # + # # Set by Encoding name. + # CGI.accept_charset = 'US-ASCII' # => "US-ASCII" + # CGI.new + # # => + # #<CGI:0x0000018991cf4100 + # @accept_charset="US-ASCII", + # @accept_charset_error_block=nil, + # @cookies={}, + # @max_multipart_length=134217728, + # @multipart=false, + # @options={accept_charset: "US-ASCII", max_multipart_length: 134217728}, + # @output_cookies=nil, + # @output_hidden=nil, + # @params={}> + # + # # Set by Encoding object. + # CGI.accept_charset = Encoding::ASCII_8BIT # => #<Encoding:BINARY (ASCII-8BIT)> + # CGI.new + # # => + # #<CGI:0x0000018991cfc800 + # @accept_charset=#<Encoding:BINARY (ASCII-8BIT)>, + # @accept_charset_error_block=nil, + # @cookies={}, + # @max_multipart_length=134217728, + # @multipart=false, + # @options={accept_charset: #<Encoding:BINARY (ASCII-8BIT)>, max_multipart_length: 134217728}, + # @output_cookies=nil, + # @output_hidden=nil, + # @params={}> + # + # The given encoding is not checked in this method, + # but if it is invalid, a call to CGI.new will fail: + # + # CGI.accept_charset = 'foo' + # CGI.new # Raises ArgumentError: unknown encoding name - foo + # def self.accept_charset=(accept_charset) @@accept_charset=accept_charset end @@ -778,75 +1027,199 @@ # @@max_multipart_length= 128 * 1024 * 1024 - # Create a new CGI instance. - # # :call-seq: - # CGI.new(tag_maker) { block } - # CGI.new(options_hash = {}) { block } + # CGI.new(options = {}) -> new_cgi + # CGI.new(tag_maker) -> new_cgi + # CGI.new(options = {}) {|name, value| ... } -> new_cgi + # CGI.new(tag_maker) {|name, value| ... } -> new_cgi + # + # Returns a new \CGI object. + # + # The behavior of this method depends _strongly_ on whether it is called + # within a standard \CGI call environment; + # that is, whether <tt>ENV['REQUEST_METHOD']</tt> is defined. + # + # <b>Within a Standard Call Environment</b> # + # This section assumes that <tt>ENV['REQUEST_METHOD']</tt> is defined; + # for example: # - # <tt>tag_maker</tt>:: - # This is the same as using the +options_hash+ form with the value <tt>{ - # :tag_maker => tag_maker }</tt> Note that it is recommended to use the - # +options_hash+ form, since it also allows you specify the charset you - # will accept. - # <tt>options_hash</tt>:: - # A Hash that recognizes three options: + # ENV['REQUEST_METHOD'] # => "GET" # - # <tt>:accept_charset</tt>:: - # specifies encoding of received query string. If omitted, - # <tt>@@accept_charset</tt> is used. If the encoding is not valid, a - # CGI::InvalidEncoding will be raised. + # With no argument and no block given, returns a new \CGI object with default values: + # + # cgi = CGI.new + # cgi + # # => + # #<CGI:0x00000189917aff00 + # @accept_charset=#<Encoding:UTF-8>, + # @accept_charset_error_block=nil, + # @cookies={}, + # @max_multipart_length=134217728, + # @multipart=false, + # @options={accept_charset: #<Encoding:UTF-8>, max_multipart_length: 134217728}, + # @output_cookies=nil, + # @output_hidden=nil, + # @params={}> + # + # With hash argument +options+ given and no block given, + # returns a new \CGI object with the given options. + # + # The options may be: + # + # - <tt>accept_charset: _encoding_</tt>: + # specifies the encoding of the received query string. + # + # Value _encoding_ may be + # an {Encoding object}[https://docs.ruby-lang.org/en/master/encodings_rdoc.html#label-Encoding+Objects] + # or an {encoding name}[https://docs.ruby-lang.org/en/master/encodings_rdoc.html#label-Names+and+Aliases]: + # + # CGI.new(accept_charset: 'EUC-JP') + # + # If the option is not given, + # the default value is the class default encoding. + # + # <em>Note:</em> The <tt>accept_charset</tt> method returns the HTTP Accept-Charset + # header value, not the configured encoding. The configured encoding is used + # internally for query string parsing. + # + # - <tt>max_multipart_length: _size_</tt>: + # specifies maximum size (in bytes) of multipart data. + # + # The _size_ may be: + # + # - A positive integer. + # + # CGI.new(max_multipart_length: 1024 * 1024) # - # Example. Suppose <tt>@@accept_charset</tt> is "UTF-8" # - # when not specified: + # - A lambda to be evaluated when the request is parsed. + # This is useful when determining whether to accept multipart data + # (e.g. by consulting a registered user's upload allowance). # - # cgi=CGI.new # @accept_charset # => "UTF-8" + # CGI.new(max_multipart_length: -> {check_filesystem}) # - # when specified as "EUC-JP": + # If the option is not given, the default is +134217728+, specifying a maximum size of 128 megabytes. # - # cgi=CGI.new(:accept_charset => "EUC-JP") # => "EUC-JP" + # <em>Note:</em> This option configures internal behavior only. + # There is no public method to retrieve this value after initialization. # - # <tt>:tag_maker</tt>:: - # String that specifies which version of the HTML generation methods to - # use. If not specified, no HTML generation methods will be loaded. + # - <tt>tag_maker: _html_version_</tt>: + # specifies a version of HTML; + # this determines which tag-generating instance methods + # (such as +html+, +head+, +body+, etc.) are to be loaded. # - # The following values are supported: + # Value _html_version_ may be one of: # - # "html3":: HTML 3.x - # "html4":: HTML 4.0 - # "html4Tr":: HTML 4.0 Transitional - # "html4Fr":: HTML 4.0 with Framesets - # "html5":: HTML 5 + # - <tt>'html3'</tt>: {HTML version 3}[https://www.w3.org/MarkUp/html3/Contents.html]. + # - <tt>'html4'</tt>: {HTML version 4}[https://www.w3.org/TR/html4]. + # - <tt>'html4Tr'</tt>: {HTML 4.0 Transitional}[https://www.w3.org/TR/html4/sgml/loosedtd.html]. + # - <tt>'html4Fr'</tt>: {HTML 4.0 with Framesets}[https://www.w3.org/TR/html4/present/frames.html]. + # - <tt>'html5'</tt>: {HTML version 5}[https://html.spec.whatwg.org/multipage]. # - # <tt>:max_multipart_length</tt>:: - # Specifies maximum length of multipart data. Can be an Integer scalar or - # a lambda, that will be evaluated when the request is parsed. This - # allows more complex logic to be set when determining whether to accept - # multipart data (e.g. consult a registered users upload allowance) + # Example: # - # Default is 128 * 1024 * 1024 bytes + # CGI.new(tag_maker: 'html5') # - # cgi=CGI.new(:max_multipart_length => 268435456) # simple scalar + # If the option is not given (or if an invalid value is given), + # no tag-generating methods are loaded. # - # cgi=CGI.new(:max_multipart_length => -> {check_filesystem}) # lambda + # Examples: # - # <tt>block</tt>:: - # If provided, the block is called when an invalid encoding is - # encountered. For example: + # CGI.new.respond_to?(:html) # => false # Tag-generating methods not loaded. + # CGI.new(tag_maker: 'html3').respond_to?(:html) # => true # Tag-generating methods loaded. + # # Tag 'button' is new in HTML 4. + # CGI.new(tag_maker: 'html3').respond_to?(:button) # => false + # CGI.new(tag_maker: 'html4').respond_to?(:button) # => true + # # Tag 'canvas' is new in HTML 5. + # CGI.new(tag_maker: 'html4').respond_to?(:canvas) # => false + # CGI.new(tag_maker: 'html5').respond_to?(:canvas) # => true + # # Value is case-sensitive. + # CGI.new(tag_maker: 'HTML4').respond_to?(:html) # => false # - # encoding_errors={} - # cgi=CGI.new(:accept_charset=>"EUC-JP") do |name,value| - # encoding_errors[name] = value - # end + # You can determine exactly which methods have been loaded; + # this example captures the methods loaded by <tt>'html4'</tt>: + # + # methods_loaded = CGI.new('html4').methods - CGI.new.methods + # methods_loaded.size # => 98 + # methods_loaded.sort.take(10) + # # => [:a, :abbr, :acronym, :address, :area, :b, :base, :bdo, :big, :blockquote] + # + # With string argument +tag_maker+ given as _tag_maker_ and no block given, + # equivalent to <tt>CGI.new(tag_maker: _tag_maker_)</tt>: + # + # CGI.new('html5') + # + # <b>Outside a Standard Call Environment</b> + # + # This section assumes that <tt>ENV['REQUEST_METHOD']</tt> is not defined; + # for example: + # + # ENV['REQUEST_METHOD'] # => nil + # + # In this mode, the method reads its parameters + # from the command line or (failing that) from standard input; + # returns a new \CGI object. + # + # Parameters from command line: + # + # $ cat t.rb + # require 'cgi' + # cgi = CGI.new + # p cgi.params + # ruby t.rb foo=0 bar=1 foo=2 bar=3 + # {"foo" => ["0", "2"], "bar" => ["1", "3"]} + # + # Parameters from standard input: + # + # cgi = CGI.new + # (offline mode: enter name=value pairs on standard input) + # foo=0 + # bar=1 + # ^D + # cgi.params + # # => {"foo" => ["0"], "bar" => ["1"]} + # + # The end-of-file character is Ctrl-D on a Unix-like system (as above), + # or Ctrl-Z on Windows. + # + # Otherwise, cookies and other parameters are parsed automatically from the standard CGI locations, + # which vary according to the request method. + # + # <b>Options vs Public Methods</b> + # + # Some initialization options configure internal behavior only and do not provide + # corresponding public getter methods: + # + # - <tt>accept_charset</tt>: Configures internal encoding for parsing. + # The <tt>accept_charset</tt> method returns the HTTP Accept-Charset header. + # - <tt>max_multipart_length</tt>: Configures internal multipart size limits. + # No public getter method is available. + # - <tt>tag_maker</tt>: Loads HTML generation methods (publicly accessible). + # + # <b>Block</b> + # + # If a block is given, its code is stored as a Proc; + # whenever CGI::InvalidEncoding would be raised, the proc is called instead. + # + # In this example, the proc simply saves the error: + # + # encoding_errors = {} + # CGI.new(accept_charset: 'EUC-JP') do |name,value| + # encoding_errors[name] = value + # end + # # => + # #<CGI:0x000002b0ec11bcd8 + # @accept_charset="EUC-JP", + # @accept_charset_error_block=#<Proc:0x000002b0ed2ee190 (irb):146>, + # @cookies={}, + # @max_multipart_length=134217728, + # @multipart=false, + # @options={accept_charset: "EUC-JP", max_multipart_length: 134217728}, + # @output_cookies=nil, + # @output_hidden=nil, + # @params={}> # - # Finally, if the CGI object is not created in a standard CGI call - # environment (that is, it can't locate REQUEST_METHOD in its environment), - # then it will run in "offline" mode. In this mode, it reads its parameters - # from the command line or (failing that) from standard input. Otherwise, - # cookies and other parameters are parsed automatically from the standard - # CGI locations, which varies according to the REQUEST_METHOD. def initialize(options = {}, &block) # :yields: name, value @accept_charset_error_block = block_given? ? block : nil @options={ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/cgi/escape.rb new/lib/cgi/escape.rb --- old/lib/cgi/escape.rb 2025-06-04 05:38:29.000000000 +0200 +++ new/lib/cgi/escape.rb 2026-06-23 09:06:24.000000000 +0200 @@ -1,11 +1,15 @@ # frozen_string_literal: true +# :stopdoc class CGI module Escape; end include Escape extend Escape + module EscapeExt; end # :nodoc: end +# :startdoc: +# Escape/unescape for CGI, HTML, URI. module CGI::Escape @@accept_charset = Encoding::UTF_8 unless defined?(@@accept_charset) @@ -128,41 +132,32 @@ end string = string.b string.gsub!(/&(apos|amp|quot|gt|lt|\#[0-9]+|\#[xX][0-9A-Fa-f]+);/) do - match = $1.dup + match = $1 case match when 'apos' then "'" when 'amp' then '&' when 'quot' then '"' when 'gt' then '>' when 'lt' then '<' - when /\A#0*(\d+)\z/ - n = $1.to_i - if n < charlimit - n.chr(enc) - else - "&##{$1};" - end - when /\A#x([0-9a-f]+)\z/i - n = $1.hex - if n < charlimit - n.chr(enc) + else + # Numeric character reference. Decode into the binary buffer so that a + # non-ASCII byte already present in it never triggers an encoding + # compatibility error on concatenation; the trailing force_encoding + # re-tags the whole buffer. This mirrors the C extension's + # optimized_unescape_html. + n = match.start_with?('#x', '#X') ? match[2..-1].hex : match[1..-1].to_i + if n >= charlimit + "&#{match};" # out of range: keep the reference verbatim + elsif charlimit > 256 + [n].pack('U').b # UTF-8: code point bytes, surrogates included (like rb_enc_mbcput) else - "&#x#{$1};" + n.chr.b # ISO-8859-1 / ASCII: single byte end - else - "&#{match};" end end string.force_encoding enc end - # Synonym for CGI.escapeHTML(str) - alias escape_html escapeHTML - alias h escapeHTML - - # Synonym for CGI.unescapeHTML(str) - alias unescape_html unescapeHTML - # TruffleRuby runs the pure-Ruby variant faster, do not use the C extension there unless RUBY_ENGINE == 'truffleruby' begin @@ -171,6 +166,14 @@ end end + # Aliases must be defined on EscapeExt so they resolve to the C methods. + target = defined?(CGI::EscapeExt) && CGI::EscapeExt.method_defined?(:escapeHTML) ? CGI::EscapeExt : self + target.module_eval do + alias escape_html escapeHTML + alias h escapeHTML + alias unescape_html unescapeHTML + end + # Escape only the tags of certain HTML elements in +string+. # # Takes an element or elements or array of elements. Each element diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/cgi/html.rb new/lib/cgi/html.rb --- old/lib/cgi/html.rb 2025-06-04 05:38:29.000000000 +0200 +++ new/lib/cgi/html.rb 2026-06-23 09:06:24.000000000 +0200 @@ -1006,27 +1006,32 @@ end # Html5 + # HTML version 3 generation class. class HTML3 include Html3 include HtmlExtension end + # HTML version 4 generation class. class HTML4 include Html4 include HtmlExtension end + # HTML version 4 transitional generation class. class HTML4Tr include Html4Tr include HtmlExtension end + # HTML version 4 with framesets generation class. class HTML4Fr include Html4Tr include Html4Fr include HtmlExtension end + # HTML version 5 generation class. class HTML5 include Html5 include HtmlExtension diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/cgi/session.rb new/lib/cgi/session.rb --- old/lib/cgi/session.rb 2025-06-04 05:38:29.000000000 +0200 +++ new/lib/cgi/session.rb 2026-06-23 09:06:24.000000000 +0200 @@ -206,19 +206,28 @@ # on Unix systems). # prefix:: the prefix to add to the session id when generating # the filename for this session's FileStore file. - # Defaults to "cgi_sid_". + # Defaults to the empty string. # suffix:: the prefix to add to the session id when generating # the filename for this session's FileStore file. # Defaults to the empty string. + # digest:: the digest algorithm to hash the session id when + # generating the filename for this session's FileStore + # file. Defaults to "SHA256". + # length:: the length of the session id part of the filestore, + # excluding +prefix+ and +suffix+ parts. Defaults to 16. def new_store_file(option={}) # :nodoc: dir = option['tmpdir'] || Dir::tmpdir prefix = option['prefix'] suffix = option['suffix'] - require 'digest/md5' - md5 = Digest::MD5.hexdigest(session_id)[0,16] + algorithm = option['digest'] || 'SHA256' + if String === algorithm + require 'digest' + algorithm = Digest(algorithm) + end + digest = algorithm.hexdigest(session_id)[0, option['length'] || 16] path = dir+"/" path << prefix if prefix - path << md5 + path << digest path << suffix if suffix if File::exist? path hash = nil @@ -410,6 +419,9 @@ # suffix:: the prefix to add to the session id when generating # the filename for this session's FileStore file. # Defaults to the empty string. + # digest:: the digest algorithm to hash the session id when + # generating the filename for this session's FileStore + # file. Defaults to "MD5". # # This session's FileStore file will be created if it does # not exist, or opened if it does. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/cgi/util.rb new/lib/cgi/util.rb --- old/lib/cgi/util.rb 2025-06-04 05:38:29.000000000 +0200 +++ new/lib/cgi/util.rb 2026-06-23 09:06:24.000000000 +0200 @@ -5,6 +5,7 @@ extend Util end +# Utility methods for CGI. module CGI::Util # Format a +Time+ object as a String using the format specified by RFC 1123. # diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/lib/cgi.rb new/lib/cgi.rb --- old/lib/cgi.rb 2025-06-04 05:38:29.000000000 +0200 +++ new/lib/cgi.rb 2026-06-23 09:06:24.000000000 +0200 @@ -26,7 +26,7 @@ # The file CGI::Session provides session management functionality; see that # class for more details. # -# See http://www.w3.org/CGI/ for more information on the CGI protocol. +# See https://www.w3.org/CGI/ for more information on the CGI protocol. # # == Introduction # @@ -42,6 +42,17 @@ # # Read on for more details. Examples are provided at the bottom. # +# == About the Examples +# +# Examples on this page assume that \CGI has been required: +# +# require 'cgi' +# +# Unless otherwise stated, examples also assume that environment variable 'REQUEST_METHOD' exists +# (which prevents CGI.new from entering its online mode): +# +# ENV.include?('REQUEST_METHOD') # => true +# # == Queries # # The CGI class dynamically mixes in parameter and cookie-parsing @@ -148,59 +159,62 @@ # Escape and unescape methods are defined in cgi/escape.rb. # And when include, you can use utility methods like a function. # -# == Examples of use +# == Examples of Use # -# === Get form values +# === Form Values # -# require "cgi" -# cgi = CGI.new -# value = cgi['field_name'] # <== value string for 'field_name' -# # if not 'field_name' included, then return "". -# fields = cgi.keys # <== array of field names -# -# # returns true if form has 'field_name' -# cgi.has_key?('field_name') -# cgi.has_key?('field_name') -# cgi.include?('field_name') +# ==== Get Form Values # -# CAUTION! <code>cgi['field_name']</code> returned an Array with the old -# cgi.rb(included in Ruby 1.6) +# You can use method +cgi.params+ to retrieve form values +# in a {Hash}[https://docs.ruby-lang.org/en/3.4/Hash.html]: # -# === Get form values as hash -# -# require "cgi" +# ENV.update( +# 'REQUEST_METHOD' => 'GET', +# 'QUERY_STRING' => 'a=111&&b=222&c&d=' +# ) # cgi = CGI.new -# params = cgi.params -# -# cgi.params is a hash. -# -# cgi.params['new_field_name'] = ["value"] # add new param -# cgi.params['field_name'] = ["new_value"] # change value -# cgi.params.delete('field_name') # delete param -# cgi.params.clear # delete all params -# -# -# === Save form values to file -# -# require "pstore" -# db = PStore.new("query.db") -# db.transaction do -# db["params"] = cgi.params +# cgi.params.class # => Hash +# cgi.params # => {"a" => ["111"], "b" => ["222"], "c" => [], "d" => [""]} +# cgi.params.keys # => ["a", "b", "c", "d"] +# cgi.params['a'] # => ["111"] # Returns an array. +# cgi.params['d'] # => [""] # Returns empty string in array if no value. +# cgi.params['x'] # => [] # Returns empty array if no key. +# +# A \CGI instance has these convenience methods: +# +# # Convenience method for cgi.params.keys. +# cgi.keys # => ["a", "b", "c", "d"] +# # Convenience method for cgi.params[key].first. +# cgi['a'] # => "111" # Returns string, not array. +# cgi['d'] # => "" # Returns empty string if no value. +# cgi['x'] # => "" # Returns empty string if no key. +# # Convenience method for cgi.params.include?. +# cgi.include?('a') # => true +# cgi.include?('x') # => false +# +# ==== Save and Restore Form Values +# +# This example uses {Pstore}[https://docs.ruby-lang.org/en/3.4/PStore.html] +# to store and retrieve form values: +# +# ENV.update( +# 'REQUEST_METHOD' => 'GET', +# 'QUERY_STRING' => 'a=111&&b=222&c&d=' +# ) +# cgi = CGI.new +# require 'pstore' +# store = PStore.new('params.store') +# store.transaction do +# store['params'] = cgi.params # end -# -# -# === Restore form values from file -# -# require "pstore" -# db = PStore.new("query.db") -# db.transaction do -# cgi.params = db["params"] +# cgi.params.clear # Oops! Lost my params! +# store.transaction do +# cgi.params = store['params'] # end +# cgi.params # => {"a" => ["111"], "b" => ["222"], "c" => [], "d" => [""]} # +# ==== Get multipart form values # -# === Get multipart form values -# -# require "cgi" # cgi = CGI.new # value = cgi['field_name'] # <== value string for 'field_name' # value.read # <== body of value @@ -212,7 +226,6 @@ # # === Get cookie values # -# require "cgi" # cgi = CGI.new # values = cgi.cookies['name'] # <== array of 'name' # # if not 'name' included, then return []. @@ -222,7 +235,6 @@ # # === Get cookie objects # -# require "cgi" # cgi = CGI.new # for name, cookie in cgi.cookies # cookie.expires = Time.now + 30 @@ -231,14 +243,12 @@ # # cgi.cookies # { "name1" => cookie1, "name2" => cookie2, ... } # -# require "cgi" # cgi = CGI.new # cgi.cookies['name'].expires = Time.now + 30 # cgi.out("cookie" => cgi.cookies['name']) {"string"} # # === Print http header and html string to $DEFAULT_OUTPUT ($>) # -# require "cgi" # cgi = CGI.new("html4") # add HTML generation methods # cgi.out do # cgi.html do @@ -289,7 +299,8 @@ # class CGI - VERSION = "0.5.0" + # The version string + VERSION = "0.5.2" end require 'cgi/util' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/metadata new/metadata --- old/metadata 2025-06-04 05:38:29.000000000 +0200 +++ new/metadata 2026-06-23 09:06:24.000000000 +0200 @@ -1,13 +1,13 @@ --- !ruby/object:Gem::Specification name: cgi version: !ruby/object:Gem::Version - version: 0.5.0 + version: 0.5.2 platform: ruby authors: - Yukihiro Matsumoto bindir: bin cert_chain: [] -date: 2025-06-04 00:00:00.000000000 Z +date: 2026-06-23 00:00:00.000000000 Z dependencies: [] description: Support for the Common Gateway Interface protocol. email: @@ -38,6 +38,7 @@ metadata: homepage_uri: https://github.com/ruby/cgi source_code_uri: https://github.com/ruby/cgi + changelog_uri: https://github.com/ruby/cgi/releases rdoc_options: [] require_paths: - lib
