TeX and CSS tweaks.
Sync with latest Instiki Trunk (Updates Rails to 1.2.2)
This commit is contained in:
parent
0ac586ee25
commit
c358389f25
443 changed files with 24218 additions and 9823 deletions
|
@ -27,13 +27,6 @@ class CGI #:nodoc:
|
|||
def request_parameters
|
||||
CGIMethods.parse_request_parameters(params, env_table)
|
||||
end
|
||||
|
||||
def redirect(where)
|
||||
header({
|
||||
"Status" => "302 Moved",
|
||||
"location" => "#{where}"
|
||||
})
|
||||
end
|
||||
|
||||
def session(parameters = nil)
|
||||
parameters = {} if parameters.nil?
|
||||
|
|
|
@ -1,217 +1,211 @@
|
|||
require 'cgi'
|
||||
require 'action_controller/vendor/xml_simple'
|
||||
require 'action_controller/vendor/xml_node'
|
||||
require 'strscan'
|
||||
|
||||
# Static methods for parsing the query and request parameters that can be used in
|
||||
# a CGI extension class or testing in isolation.
|
||||
class CGIMethods #:nodoc:
|
||||
public
|
||||
# Returns a hash with the pairs from the query string. The implicit hash construction that is done in
|
||||
# parse_request_params is not done here.
|
||||
def CGIMethods.parse_query_parameters(query_string)
|
||||
parsed_params = {}
|
||||
|
||||
query_string.split(/[&;]/).each { |p|
|
||||
# Ignore repeated delimiters.
|
||||
next if p.empty?
|
||||
class << self
|
||||
# DEPRECATED: Use parse_form_encoded_parameters
|
||||
def parse_query_parameters(query_string)
|
||||
pairs = query_string.split('&').collect do |chunk|
|
||||
next if chunk.empty?
|
||||
key, value = chunk.split('=', 2)
|
||||
next if key.empty?
|
||||
value = (value.nil? || value.empty?) ? nil : CGI.unescape(value)
|
||||
[ CGI.unescape(key), value ]
|
||||
end.compact
|
||||
|
||||
k, v = p.split('=',2)
|
||||
v = nil if (v && v.empty?)
|
||||
|
||||
k = CGI.unescape(k) if k
|
||||
v = CGI.unescape(v) if v
|
||||
|
||||
unless k.include?(?[)
|
||||
parsed_params[k] = v
|
||||
else
|
||||
keys = split_key(k)
|
||||
last_key = keys.pop
|
||||
last_key = keys.pop if (use_array = last_key.empty?)
|
||||
parent = keys.inject(parsed_params) {|h, k| h[k] ||= {}}
|
||||
|
||||
if use_array then (parent[last_key] ||= []) << v
|
||||
else parent[last_key] = v
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
parsed_params
|
||||
FormEncodedPairParser.new(pairs).result
|
||||
end
|
||||
|
||||
# Returns the request (POST/GET) parameters in a parsed form where pairs such as "customer[address][street]" /
|
||||
# "Somewhere cool!" are translated into a full hash hierarchy, like
|
||||
# { "customer" => { "address" => { "street" => "Somewhere cool!" } } }
|
||||
def CGIMethods.parse_request_parameters(params)
|
||||
parsed_params = {}
|
||||
# DEPRECATED: Use parse_form_encoded_parameters
|
||||
def parse_request_parameters(params)
|
||||
parser = FormEncodedPairParser.new
|
||||
|
||||
for key, value in params
|
||||
value = [value] if key =~ /.*\[\]$/
|
||||
unless key.include?('[')
|
||||
# much faster to test for the most common case first (GET)
|
||||
# and avoid the call to build_deep_hash
|
||||
parsed_params[key] = get_typed_value(value[0])
|
||||
else
|
||||
build_deep_hash(get_typed_value(value[0]), parsed_params, get_levels(key))
|
||||
params = params.dup
|
||||
until params.empty?
|
||||
for key, value in params
|
||||
if key.blank?
|
||||
params.delete key
|
||||
elsif !key.include?('[')
|
||||
# much faster to test for the most common case first (GET)
|
||||
# and avoid the call to build_deep_hash
|
||||
parser.result[key] = get_typed_value(value[0])
|
||||
params.delete key
|
||||
elsif value.is_a?(Array)
|
||||
parser.parse(key, get_typed_value(value.shift))
|
||||
params.delete key if value.empty?
|
||||
else
|
||||
raise TypeError, "Expected array, found #{value.inspect}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
parsed_params
|
||||
parser.result
|
||||
end
|
||||
|
||||
def self.parse_formatted_request_parameters(mime_type, raw_post_data)
|
||||
params = case strategy = ActionController::Base.param_parsers[mime_type]
|
||||
def parse_formatted_request_parameters(mime_type, raw_post_data)
|
||||
case strategy = ActionController::Base.param_parsers[mime_type]
|
||||
when Proc
|
||||
strategy.call(raw_post_data)
|
||||
when :xml_simple
|
||||
raw_post_data.blank? ? nil :
|
||||
typecast_xml_value(XmlSimple.xml_in(raw_post_data,
|
||||
'forcearray' => false,
|
||||
'forcecontent' => true,
|
||||
'keeproot' => true,
|
||||
'contentkey' => '__content__'))
|
||||
raw_post_data.blank? ? {} : Hash.from_xml(raw_post_data)
|
||||
when :yaml
|
||||
YAML.load(raw_post_data)
|
||||
when :xml_node
|
||||
node = XmlNode.from_xml(raw_post_data)
|
||||
{ node.node_name => node }
|
||||
end
|
||||
|
||||
dasherize_keys(params || {})
|
||||
rescue Object => e
|
||||
rescue Exception => e # YAML, XML or Ruby code block errors
|
||||
{ "exception" => "#{e.message} (#{e.class})", "backtrace" => e.backtrace,
|
||||
"raw_post_data" => raw_post_data, "format" => mime_type }
|
||||
end
|
||||
|
||||
def self.typecast_xml_value(value)
|
||||
case value
|
||||
when Hash
|
||||
if value.has_key?("__content__")
|
||||
content = translate_xml_entities(value["__content__"])
|
||||
case value["type"]
|
||||
when "integer" then content.to_i
|
||||
when "boolean" then content == "true"
|
||||
when "datetime" then Time.parse(content)
|
||||
when "date" then Date.parse(content)
|
||||
else content
|
||||
end
|
||||
else
|
||||
value.empty? ? nil : value.inject({}) do |h,(k,v)|
|
||||
h[k] = typecast_xml_value(v)
|
||||
h
|
||||
end
|
||||
end
|
||||
when Array
|
||||
value.map! { |i| typecast_xml_value(i) }
|
||||
case value.length
|
||||
when 0 then nil
|
||||
when 1 then value.first
|
||||
else value
|
||||
end
|
||||
else
|
||||
raise "can't typecast #{value.inspect}"
|
||||
end
|
||||
end
|
||||
private
|
||||
def get_typed_value(value)
|
||||
case value
|
||||
when String
|
||||
value
|
||||
when NilClass
|
||||
''
|
||||
when Array
|
||||
value.map { |v| get_typed_value(v) }
|
||||
else
|
||||
# Uploaded file provides content type and filename.
|
||||
if value.respond_to?(:content_type) &&
|
||||
!value.content_type.blank? &&
|
||||
!value.original_filename.blank?
|
||||
unless value.respond_to?(:full_original_filename)
|
||||
class << value
|
||||
alias_method :full_original_filename, :original_filename
|
||||
|
||||
private
|
||||
|
||||
def self.translate_xml_entities(value)
|
||||
value.gsub(/</, "<").
|
||||
gsub(/>/, ">").
|
||||
gsub(/"/, '"').
|
||||
gsub(/'/, "'").
|
||||
gsub(/&/, "&")
|
||||
end
|
||||
|
||||
def self.dasherize_keys(params)
|
||||
case params.class.to_s
|
||||
when "Hash"
|
||||
params.inject({}) do |h,(k,v)|
|
||||
h[k.to_s.tr("-", "_")] = dasherize_keys(v)
|
||||
h
|
||||
end
|
||||
when "Array"
|
||||
params.map { |v| dasherize_keys(v) }
|
||||
else
|
||||
params
|
||||
end
|
||||
end
|
||||
|
||||
# Splits the given key into several pieces. Example keys are 'name', 'person[name]',
|
||||
# 'person[name][first]', and 'people[]'. In each instance, an Array instance is returned.
|
||||
# 'person[name][first]' produces ['person', 'name', 'first']; 'people[]' produces ['people', '']
|
||||
def CGIMethods.split_key(key)
|
||||
if /^([^\[]+)((?:\[[^\]]*\])+)$/ =~ key
|
||||
keys = [$1]
|
||||
|
||||
keys.concat($2[1..-2].split(']['))
|
||||
keys << '' if key[-2..-1] == '[]' # Have to add it since split will drop empty strings
|
||||
|
||||
keys
|
||||
else
|
||||
[key]
|
||||
end
|
||||
end
|
||||
|
||||
def CGIMethods.get_typed_value(value)
|
||||
# test most frequent case first
|
||||
if value.is_a?(String)
|
||||
value
|
||||
elsif value.respond_to?(:content_type) && ! value.content_type.blank?
|
||||
# Uploaded file
|
||||
unless value.respond_to?(:full_original_filename)
|
||||
class << value
|
||||
alias_method :full_original_filename, :original_filename
|
||||
|
||||
# Take the basename of the upload's original filename.
|
||||
# This handles the full Windows paths given by Internet Explorer
|
||||
# (and perhaps other broken user agents) without affecting
|
||||
# those which give the lone filename.
|
||||
# The Windows regexp is adapted from Perl's File::Basename.
|
||||
def original_filename
|
||||
if md = /^(?:.*[:\\\/])?(.*)/m.match(full_original_filename)
|
||||
md.captures.first
|
||||
else
|
||||
File.basename full_original_filename
|
||||
# Take the basename of the upload's original filename.
|
||||
# This handles the full Windows paths given by Internet Explorer
|
||||
# (and perhaps other broken user agents) without affecting
|
||||
# those which give the lone filename.
|
||||
# The Windows regexp is adapted from Perl's File::Basename.
|
||||
def original_filename
|
||||
if md = /^(?:.*[:\\\/])?(.*)/m.match(full_original_filename)
|
||||
md.captures.first
|
||||
else
|
||||
File.basename full_original_filename
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Return the same value after overriding original_filename.
|
||||
value
|
||||
|
||||
# Multipart values may have content type, but no filename.
|
||||
elsif value.respond_to?(:read)
|
||||
result = value.read
|
||||
value.rewind
|
||||
result
|
||||
|
||||
# Unknown value, neither string nor multipart.
|
||||
else
|
||||
raise "Unknown form value: #{value.inspect}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class FormEncodedPairParser < StringScanner #:nodoc:
|
||||
attr_reader :top, :parent, :result
|
||||
|
||||
def initialize(pairs = [])
|
||||
super('')
|
||||
@result = {}
|
||||
pairs.each { |key, value| parse(key, value) }
|
||||
end
|
||||
|
||||
KEY_REGEXP = %r{([^\[\]=&]+)}
|
||||
BRACKETED_KEY_REGEXP = %r{\[([^\[\]=&]+)\]}
|
||||
|
||||
# Parse the query string
|
||||
def parse(key, value)
|
||||
self.string = key
|
||||
@top, @parent = result, nil
|
||||
|
||||
# First scan the bare key
|
||||
key = scan(KEY_REGEXP) or return
|
||||
key = post_key_check(key)
|
||||
|
||||
# Then scan as many nestings as present
|
||||
until eos?
|
||||
r = scan(BRACKETED_KEY_REGEXP) or return
|
||||
key = self[1]
|
||||
key = post_key_check(key)
|
||||
end
|
||||
|
||||
bind(key, value)
|
||||
end
|
||||
|
||||
private
|
||||
# After we see a key, we must look ahead to determine our next action. Cases:
|
||||
#
|
||||
# [] follows the key. Then the value must be an array.
|
||||
# = follows the key. (A value comes next)
|
||||
# & or the end of string follows the key. Then the key is a flag.
|
||||
# otherwise, a hash follows the key.
|
||||
def post_key_check(key)
|
||||
if scan(/\[\]/) # a[b][] indicates that b is an array
|
||||
container(key, Array)
|
||||
nil
|
||||
elsif check(/\[[^\]]/) # a[b] indicates that a is a hash
|
||||
container(key, Hash)
|
||||
nil
|
||||
else # End of key? We do nothing.
|
||||
key
|
||||
end
|
||||
end
|
||||
|
||||
# Add a container to the stack.
|
||||
#
|
||||
def container(key, klass)
|
||||
type_conflict! klass, top[key] if top.is_a?(Hash) && top.key?(key) && ! top[key].is_a?(klass)
|
||||
value = bind(key, klass.new)
|
||||
type_conflict! klass, value unless value.is_a?(klass)
|
||||
push(value)
|
||||
end
|
||||
|
||||
# Push a value onto the 'stack', which is actually only the top 2 items.
|
||||
def push(value)
|
||||
@parent, @top = @top, value
|
||||
end
|
||||
|
||||
# Bind a key (which may be nil for items in an array) to the provided value.
|
||||
def bind(key, value)
|
||||
if top.is_a? Array
|
||||
if key
|
||||
if top[-1].is_a?(Hash) && ! top[-1].key?(key)
|
||||
top[-1][key] = value
|
||||
else
|
||||
top << {key => value}.with_indifferent_access
|
||||
push top.last
|
||||
end
|
||||
else
|
||||
top << value
|
||||
end
|
||||
elsif top.is_a? Hash
|
||||
key = CGI.unescape(key)
|
||||
parent << (@top = {}) if top.key?(key) && parent.is_a?(Array)
|
||||
return top[key] ||= value
|
||||
else
|
||||
raise ArgumentError, "Don't know what to do: top is #{top.inspect}"
|
||||
end
|
||||
|
||||
# Return the same value after overriding original_filename.
|
||||
value
|
||||
|
||||
elsif value.respond_to?(:read)
|
||||
# Value as part of a multipart request
|
||||
value.read
|
||||
elsif value.class == Array
|
||||
value.collect { |v| CGIMethods.get_typed_value(v) }
|
||||
else
|
||||
# other value (neither string nor a multipart request)
|
||||
value.to_s
|
||||
return value
|
||||
end
|
||||
end
|
||||
|
||||
PARAMS_HASH_RE = /^([^\[]+)(\[.*\])?(.)?.*$/
|
||||
def CGIMethods.get_levels(key)
|
||||
all, main, bracketed, trailing = PARAMS_HASH_RE.match(key).to_a
|
||||
if main.nil?
|
||||
[]
|
||||
elsif trailing
|
||||
[key]
|
||||
elsif bracketed
|
||||
[main] + bracketed.slice(1...-1).split('][')
|
||||
else
|
||||
[main]
|
||||
end
|
||||
end
|
||||
|
||||
def CGIMethods.build_deep_hash(value, hash, levels)
|
||||
if levels.length == 0
|
||||
value
|
||||
elsif hash.nil?
|
||||
{ levels.first => CGIMethods.build_deep_hash(value, nil, levels[1..-1]) }
|
||||
else
|
||||
hash.update({ levels.first => CGIMethods.build_deep_hash(value, hash[levels.first], levels[1..-1]) })
|
||||
|
||||
def type_conflict!(klass, value)
|
||||
raise TypeError,
|
||||
"Conflicting types for parameter containers. " +
|
||||
"Expected an instance of #{klass}, but found an instance of #{value.class}. " +
|
||||
"This can be caused by passing Array and Hash based paramters qs[]=value&qs[key]=value. "
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,27 +1,48 @@
|
|||
class CGI #:nodoc:
|
||||
# Add @request.env['RAW_POST_DATA'] for the vegans.
|
||||
module QueryExtension
|
||||
# Initialize the data from the query.
|
||||
#
|
||||
# Handles multipart forms (in particular, forms that involve file uploads).
|
||||
# Reads query parameters in the @params field, and cookies into @cookies.
|
||||
def initialize_query()
|
||||
def initialize_query
|
||||
@cookies = CGI::Cookie::parse(env_table['HTTP_COOKIE'] || env_table['COOKIE'])
|
||||
|
||||
#fix some strange request environments
|
||||
# Fix some strange request environments.
|
||||
if method = env_table['REQUEST_METHOD']
|
||||
method = method.to_s.downcase.intern
|
||||
else
|
||||
method = :get
|
||||
end
|
||||
|
||||
if method == :post && (boundary = multipart_form_boundary)
|
||||
@multipart = true
|
||||
@params = read_multipart(boundary, Integer(env_table['CONTENT_LENGTH']))
|
||||
else
|
||||
@multipart = false
|
||||
@params = CGI::parse(read_query_params(method) || "")
|
||||
# POST assumes missing Content-Type is application/x-www-form-urlencoded.
|
||||
content_type = env_table['CONTENT_TYPE']
|
||||
if content_type.blank? && method == :post
|
||||
content_type = 'application/x-www-form-urlencoded'
|
||||
end
|
||||
|
||||
# Force content length to zero if missing.
|
||||
content_length = env_table['CONTENT_LENGTH'].to_i
|
||||
|
||||
# Set multipart to false by default.
|
||||
@multipart = false
|
||||
|
||||
# POST and PUT may have params in entity body. If content type is
|
||||
# missing for POST, assume urlencoded. If content type is missing
|
||||
# for PUT, don't assume anything and don't parse the parameters:
|
||||
# it's likely binary data.
|
||||
#
|
||||
# The other HTTP methods have their params in the query string.
|
||||
if method == :post || method == :put
|
||||
if boundary = extract_multipart_form_boundary(content_type)
|
||||
@multipart = true
|
||||
@params = read_multipart(boundary, content_length)
|
||||
elsif content_type.blank? || content_type !~ %r{application/x-www-form-urlencoded}i
|
||||
read_params(method, content_length)
|
||||
@params = {}
|
||||
end
|
||||
end
|
||||
|
||||
@params ||= CGI.parse(read_params(method, content_length))
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -29,16 +50,16 @@ class CGI #:nodoc:
|
|||
MULTIPART_FORM_BOUNDARY_RE = %r|\Amultipart/form-data.*boundary=\"?([^\";,]+)\"?|n #"
|
||||
end
|
||||
|
||||
def multipart_form_boundary
|
||||
MULTIPART_FORM_BOUNDARY_RE.match(env_table['CONTENT_TYPE']).to_a.pop
|
||||
def extract_multipart_form_boundary(content_type)
|
||||
MULTIPART_FORM_BOUNDARY_RE.match(content_type).to_a.pop
|
||||
end
|
||||
|
||||
if defined? MOD_RUBY
|
||||
def read_params_from_query
|
||||
def read_query
|
||||
Apache::request.args || ''
|
||||
end
|
||||
else
|
||||
def read_params_from_query
|
||||
def read_query
|
||||
# fixes CGI querystring parsing for lighttpd
|
||||
env_qs = env_table['QUERY_STRING']
|
||||
if env_qs.blank? && !(uri = env_table['REQUEST_URI']).blank?
|
||||
|
@ -49,25 +70,25 @@ class CGI #:nodoc:
|
|||
end
|
||||
end
|
||||
|
||||
def read_params_from_post
|
||||
def read_body(content_length)
|
||||
stdinput.binmode if stdinput.respond_to?(:binmode)
|
||||
content = stdinput.read(Integer(env_table['CONTENT_LENGTH'])) || ''
|
||||
# fix for Safari Ajax postings that always append \000
|
||||
content = stdinput.read(content_length) || ''
|
||||
# Fix for Safari Ajax postings that always append \000
|
||||
content.chop! if content[-1] == 0
|
||||
content.gsub! /&_=$/, ''
|
||||
content.gsub!(/&_=$/, '')
|
||||
env_table['RAW_POST_DATA'] = content.freeze
|
||||
end
|
||||
|
||||
def read_query_params(method)
|
||||
def read_params(method, content_length)
|
||||
case method
|
||||
when :get
|
||||
read_params_from_query
|
||||
read_query
|
||||
when :post, :put
|
||||
read_params_from_post
|
||||
read_body(content_length)
|
||||
when :cmd
|
||||
read_from_cmdline
|
||||
else # when :head, :delete, :options
|
||||
read_params_from_query
|
||||
else # :head, :delete, :options, :trace, :connect
|
||||
read_query
|
||||
end
|
||||
end
|
||||
end # module QueryExtension
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue