Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Encode Content-Disposition filenames according to RFC 2231
Closes #30134.
- Loading branch information
Showing
with
74 additions
and 4 deletions.
- +1 −1 activestorage/app/models/active_storage/blob.rb
- +4 −0 activestorage/app/models/active_storage/filename.rb
- +34 −0 activestorage/app/models/active_storage/filename/parameters.rb
- +2 −2 activestorage/test/controllers/disk_controller_test.rb
- +1 −1 activestorage/test/models/blob_test.rb
- +32 −0 activestorage/test/models/filename/parameters_test.rb
@@ -0,0 +1,34 @@ | ||
class ActiveStorage::Filename::Parameters | ||
attr_reader :filename | ||
|
||
def initialize(filename) | ||
@filename = filename | ||
end | ||
|
||
def combined | ||
"#{ascii}; #{utf8}" | ||
end | ||
|
||
TRADITIONAL_ESCAPED_CHAR = /[^ A-Za-z0-9!#$+.^_`|~-]/ | ||
|
||
def ascii | ||
'filename="' + percent_escape(I18n.transliterate(filename.sanitized), TRADITIONAL_ESCAPED_CHAR) + '"' | ||
end | ||
|
||
RFC_5987_ESCAPED_CHAR = /[^A-Za-z0-9!#$&+.^_`|~-]/ | ||
|
||
def utf8 | ||
"filename*=UTF-8''" + percent_escape(filename.sanitized, RFC_5987_ESCAPED_CHAR) | ||
end | ||
|
||
def to_s | ||
combined | ||
end | ||
|
||
private | ||
def percent_escape(string, pattern) | ||
string.gsub(pattern) do |char| | ||
char.bytes.map { |byte| "%%%02X" % byte }.join | ||
end | ||
end | ||
end |
@@ -0,0 +1,32 @@ | ||
# frozen_string_literal: true | ||
|
||
require "test_helper" | ||
|
||
class ActiveStorage::Filename::ParametersTest < ActiveSupport::TestCase | ||
test "parameterizing a Latin filename" do | ||
filename = ActiveStorage::Filename.new("racecar.jpg") | ||
|
||
assert_equal %(filename="racecar.jpg"), filename.parameters.ascii | ||
assert_equal "filename*=UTF-8''racecar.jpg", filename.parameters.utf8 | ||
assert_equal "#{filename.parameters.ascii}; #{filename.parameters.utf8}", filename.parameters.combined | ||
assert_equal filename.parameters.combined, filename.parameters.to_s | ||
end | ||
|
||
test "parameterizing a Latin filename with accented characters" do | ||
filename = ActiveStorage::Filename.new("råcëçâr.jpg") | ||
|
||
assert_equal %(filename="racecar.jpg"), filename.parameters.ascii | ||
assert_equal "filename*=UTF-8''r%C3%A5c%C3%AB%C3%A7%C3%A2r.jpg", filename.parameters.utf8 | ||
assert_equal "#{filename.parameters.ascii}; #{filename.parameters.utf8}", filename.parameters.combined | ||
assert_equal filename.parameters.combined, filename.parameters.to_s | ||
end | ||
|
||
test "parameterizing a non-Latin filename" do | ||
filename = ActiveStorage::Filename.new("автомобиль.jpg") | ||
|
||
assert_equal %(filename="%3F%3F%3F%3F%3F%3F%3F%3F%3F%3F.jpg"), filename.parameters.ascii | ||
assert_equal "filename*=UTF-8''%D0%B0%D0%B2%D1%82%D0%BE%D0%BC%D0%BE%D0%B1%D0%B8%D0%BB%D1%8C.jpg", filename.parameters.utf8 | ||
assert_equal "#{filename.parameters.ascii}; #{filename.parameters.utf8}", filename.parameters.combined | ||
assert_equal filename.parameters.combined, filename.parameters.to_s | ||
end | ||
end |