/usr/lib/ruby/vendor_ruby/merb-core/dispatch/request_parsers.rb is in ruby-merb-core 1.1.3+dfsg-2.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 | module Merb
module Parse
# ==== Parameters
# query_string<String>:: The query string.
# delimiter<String>:: The query string divider. Defaults to "&".
# preserve_order<Boolean>:: Preserve order of args. Defaults to false.
#
# ==== Returns
# Mash:: The parsed query string (Dictionary if preserve_order is set).
#
# ==== Examples
# Merb::Parse.query("bar=nik&post[body]=heya")
# # => { :bar => "nik", :post => { :body => "heya" } }
#
# :api: plugin
def self.query(query_string, delimiter = '&;', preserve_order = false)
query = preserve_order ? Dictionary.new : {}
for pair in (query_string || '').split(/[#{delimiter}] */n)
key, value = unescape(pair).split('=',2)
next if key.nil?
if key.include?('[')
normalize_params(query, key, value)
else
query[key] = value
end
end
preserve_order ? query : query.to_mash
end
NAME_REGEX = /Content-Disposition:.* name="?([^\";]*)"?/ni.freeze
CONTENT_TYPE_REGEX = /Content-Type: (.*)\r\n/ni.freeze
FILENAME_REGEX = /Content-Disposition:.* filename="?([^\";]*)"?/ni.freeze
CRLF = "\r\n".freeze
EOL = CRLF
# ==== Parameters
# request<IO>:: The raw request.
# boundary<String>:: The boundary string.
# content_length<Fixnum>:: The length of the content.
#
# ==== Raises
# ControllerExceptions::MultiPartParseError:: Failed to parse request.
#
# ==== Returns
# Hash:: The parsed request.
#
# :api: plugin
def self.multipart(request, boundary, content_length)
boundary = "--#{boundary}"
paramhsh = {}
buf = ""
input = request
input.binmode if defined? input.binmode
boundary_size = boundary.size + EOL.size
bufsize = 16384
content_length -= boundary_size
key_memo = []
# status is boundary delimiter line
status = input.read(boundary_size)
return {} if status == nil || status.empty?
raise ControllerExceptions::MultiPartParseError, "bad content body:\n'#{status}' should == '#{boundary + EOL}'" unless status == boundary + EOL
rx = /(?:#{EOL})?#{Regexp.quote(boundary)}(#{EOL}|--)/n
loop {
head = nil
body = ''
filename = content_type = name = nil
read_size = 0
until head && buf =~ rx
i = buf.index("\r\n\r\n")
if( i == nil && read_size == 0 && content_length == 0 )
content_length = -1
break
end
if !head && i
head = buf.slice!(0, i+2) # First \r\n
buf.slice!(0, 2) # Second \r\n
# String#[] with 2nd arg here is returning
# a group from match data
filename = head[FILENAME_REGEX, 1]
content_type = head[CONTENT_TYPE_REGEX, 1]
name = head[NAME_REGEX, 1]
if filename && !filename.empty?
body = Tempfile.new(:Merb)
body.binmode if defined? body.binmode
end
next
end
# Save the read body part.
if head && (boundary_size+4 < buf.size)
body << buf.slice!(0, buf.size - (boundary_size+4))
end
read_size = bufsize < content_length ? bufsize : content_length
if( read_size > 0 )
c = input.read(read_size)
raise ControllerExceptions::MultiPartParseError, "bad content body" if c.nil? || c.empty?
buf << c
content_length -= c.size
end
end
# Save the rest.
if i = buf.index(rx)
# correct value of i for some edge cases
if (i > 2) && (j = buf.index(rx, i-2)) && (j < i)
i = j
end
body << buf.slice!(0, i)
buf.slice!(0, boundary_size+2)
content_length = -1 if $1 == "--"
end
if filename && !filename.empty?
body.rewind
data = {
:filename => File.basename(filename),
:content_type => content_type,
:tempfile => body,
:size => File.size(body.path)
}
else
data = body
end
unless key_memo.include?(name) && name !~ /\[\]/
paramhsh = normalize_params(paramhsh,name,data)
end
# Prevent from double processing files but process other params
key_memo << name if filename && !filename.empty?
break if buf.empty? || content_length == -1
}
paramhsh
end
# ==== Parameters
# value<Array, Hash, Dictionary ~to_s>:: The value for the query string.
# prefix<~to_s>:: The prefix to add to the query string keys.
#
# ==== Returns
# String:: The query string.
#
# ==== Alternatives
# If the value is a string, the prefix will be used as the key.
#
# ==== Examples
# params_to_query_string(10, "page")
# # => "page=10"
# params_to_query_string({ :page => 10, :word => "ruby" })
# # => "page=10&word=ruby"
# params_to_query_string({ :page => 10, :word => "ruby" }, "search")
# # => "search[page]=10&search[word]=ruby"
# params_to_query_string([ "ice-cream", "cake" ], "shopping_list")
# # => "shopping_list[]=ice-cream&shopping_list[]=cake"
#
# :api: plugin
def self.params_to_query_string(value, prefix = nil)
case value
when Array
value.map { |v|
params_to_query_string(v, "#{prefix}[]")
} * "&"
when Hash, Dictionary
value.map { |k, v|
params_to_query_string(v, prefix ? "#{prefix}[#{escape(k)}]" : escape(k))
} * "&"
else
"#{prefix}=#{escape(value)}"
end
end
# ==== Parameters
# s<String>:: String to URL escape.
#
# ==== returns
# String:: The escaped string.
#
# :api: public
def self.escape(s)
s.to_s.gsub(/([^ a-zA-Z0-9_.-]+)/n) {
'%'+$1.unpack('H2'*$1.size).join('%').upcase
}.tr(' ', '+')
end
# ==== Parameter
# s<String>:: String to URL unescape.
# encoding<String>:: Encoding which we force to return. Only for
# Ruby 1.9. If encoding is not passed it defaults
# to Encoding.default_internal. When this is nil
# (default) no encoding forcing is done.
#
# ==== returns
# String:: The unescaped string.
#
# :api: public
def self.unescape(s, encoding = nil)
s = s.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n){
[$1.delete('%')].pack('H*')
}
if RUBY_VERSION >= '1.9'
encoding ||= Encoding.default_internal
s.force_encoding(encoding) if encoding
end
s
end
# ==== Parameters
# s<String>:: String to XML escape.
#
# ==== returns
# String:: The escaped string.
#
# :api: public
def self.escape_xml(s)
Erubis::XmlHelper.escape_xml(s)
end
private
# Converts a query string snippet to a hash and adds it to existing
# parameters.
#
# ==== Parameters
# parms<Hash>:: Parameters to add the normalized parameters to.
# name<String>:: The key of the parameter to normalize.
# val<String>:: The value of the parameter.
#
# ==== Returns
# Hash:: Normalized parameters
#
# :api: private
def self.normalize_params(parms, name, val=nil)
name =~ %r([\[\]]*([^\[\]]+)\]*)
key = $1 || ''
after = $' || ''
if after == ""
parms[key] = val
elsif after == "[]"
(parms[key] ||= []) << val
elsif after =~ %r(^\[\]\[([^\[\]]+)\]$)
child_key = $1
parms[key] ||= []
if parms[key].last.is_a?(Hash) && !parms[key].last.key?(child_key)
parms[key].last.update(child_key => val)
else
parms[key] << { child_key => val }
end
else
parms[key] ||= {}
parms[key] = normalize_params(parms[key], after, val)
end
parms
end
end
end
|