summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/rack/multipart.rb8
-rw-r--r--lib/rack/multipart/parser.rb23
-rw-r--r--test/multipart/filename_with_single_quote7
-rw-r--r--test/spec_multipart.rb6
4 files changed, 29 insertions, 15 deletions
diff --git a/lib/rack/multipart.rb b/lib/rack/multipart.rb
index 97f7a77e..b1625437 100644
--- a/lib/rack/multipart.rb
+++ b/lib/rack/multipart.rb
@@ -19,19 +19,19 @@ module Rack
MULTIPART_CONTENT_DISPOSITION = /Content-Disposition:.*\s+name="?([^\";]*)"?/ni
MULTIPART_CONTENT_ID = /Content-ID:\s*([^#{EOL}]*)/ni
# Updated definitions from RFC 2231
- ATTRIBUTE_CHAR = %r{[^ \t)(><@,;:\\"/\[\]?=]}
+ ATTRIBUTE_CHAR = %r{[^ \t\v\n\r)(><@,;:\\"/\[\]?='*%]}
ATTRIBUTE = /#{ATTRIBUTE_CHAR}+/
SECTION = /\*[0-9]+/
REGULAR_PARAMETER_NAME = /#{ATTRIBUTE}#{SECTION}?/
REGULAR_PARAMETER = /(#{REGULAR_PARAMETER_NAME})=(#{VALUE})/
EXTENDED_OTHER_NAME = /#{ATTRIBUTE}\*[1-9][0-9]*\*/
EXTENDED_OTHER_VALUE = /%[0-9a-fA-F]{2}|#{ATTRIBUTE_CHAR}/
- EXTENDED_OTHER_PARAMETER = /#{EXTENDED_OTHER_NAME}=#{EXTENDED_OTHER_VALUE}*/
+ EXTENDED_OTHER_PARAMETER = /(#{EXTENDED_OTHER_NAME})=(#{EXTENDED_OTHER_VALUE}*)/
EXTENDED_INITIAL_NAME = /#{ATTRIBUTE}(?:\*0)?\*/
EXTENDED_INITIAL_VALUE = /[a-zA-Z0-9\-]*'[a-zA-Z0-9\-]*'#{EXTENDED_OTHER_VALUE}*/
- EXTENDED_INITIAL_PARAMETER = /#{EXTENDED_INITIAL_NAME}=#{EXTENDED_INITIAL_VALUE}/
+ EXTENDED_INITIAL_PARAMETER = /(#{EXTENDED_INITIAL_NAME})=(#{EXTENDED_INITIAL_VALUE})/
EXTENDED_PARAMETER = /#{EXTENDED_INITIAL_PARAMETER}|#{EXTENDED_OTHER_PARAMETER}/
- DISPPARM = /;\s*(?:#{REGULAR_PARAMETER}|#{EXTENDED_PARAMETER})/
+ DISPPARM = /;\s*(?:#{REGULAR_PARAMETER}|#{EXTENDED_PARAMETER})\s*/
RFC2183 = /^#{CONDISP}(#{DISPPARM})+$/i
class << self
diff --git a/lib/rack/multipart/parser.rb b/lib/rack/multipart/parser.rb
index 9b97d7e5..50f6695a 100644
--- a/lib/rack/multipart/parser.rb
+++ b/lib/rack/multipart/parser.rb
@@ -152,13 +152,16 @@ module Rack
def get_filename(head)
filename = nil
case head
+ when RFC2183
+ params = Hash[*head.scan(DISPPARM).flat_map(&:compact)]
+
+ if filename = params['filename']
+ filename = $1 if filename =~ /^"(.*)"$/
+ elsif filename = params['filename*']
+ encoding, locale, filename = filename.split("'", 3)
+ end
when BROKEN_QUOTED, BROKEN_UNQUOTED
filename = $1
- when RFC2183
- params = Hash[head.scan(DISPPARM)]
- filename = params['filename']
- filename ||= params['filename*']
- filename = $1 if filename and filename =~ /^"(.*)"$/
end
return unless filename
@@ -173,13 +176,11 @@ module Rack
filename = filename.gsub(/\\(.)/, '\1')
end
- encoding, locale, name = filename.split("'",3)
-
- if locale.nil? && name.nil?
- name = encoding
- else
- name.force_encoding ::Encoding.find(encoding)
+ if encoding
+ filename.force_encoding ::Encoding.find(encoding)
end
+
+ filename
end
def scrub_filename(filename)
diff --git a/test/multipart/filename_with_single_quote b/test/multipart/filename_with_single_quote
new file mode 100644
index 00000000..f7220abe
--- /dev/null
+++ b/test/multipart/filename_with_single_quote
@@ -0,0 +1,7 @@
+--AaB03x
+Content-Type: image/jpeg
+Content-Disposition: attachment; name="files"; filename="bob's flowers.jpg"
+Content-Description: a complete map of the human genome
+
+contents
+--AaB03x--
diff --git a/test/spec_multipart.rb b/test/spec_multipart.rb
index 6121fdbb..b0ad2cb9 100644
--- a/test/spec_multipart.rb
+++ b/test/spec_multipart.rb
@@ -279,6 +279,12 @@ describe Rack::Multipart do
params["files"][:filename].must_equal "файл"
end
+ it "parse multipart form with a single quote in the filename" do
+ env = Rack::MockRequest.env_for '/', multipart_fixture(:filename_with_single_quote)
+ params = Rack::Multipart.parse_multipart(env)
+ params["files"][:filename].must_equal "bob's flowers.jpg"
+ end
+
it "not include file params if no file was selected" do
env = Rack::MockRequest.env_for("/", multipart_fixture(:none))
params = Rack::Multipart.parse_multipart(env)