diff options
-rw-r--r-- | lib/rack/multipart.rb | 8 | ||||
-rw-r--r-- | lib/rack/multipart/parser.rb | 23 | ||||
-rw-r--r-- | test/multipart/filename_with_single_quote | 7 | ||||
-rw-r--r-- | test/spec_multipart.rb | 6 |
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) |