Defect #35539 » 0001-ensure-unique-attachment-filenames.patch
| app/models/attachment.rb | ||
|---|---|---|
| 138 | 138 |
def files_to_final_location |
| 139 | 139 |
if @temp_file |
| 140 | 140 |
self.disk_directory = target_directory |
| 141 |
self.disk_filename = Attachment.disk_filename(filename, disk_directory) |
|
| 142 |
logger.info("Saving attachment '#{self.diskfile}' (#{@temp_file.size} bytes)") if logger
|
|
| 143 |
path = File.dirname(diskfile) |
|
| 144 |
unless File.directory?(path) |
|
| 145 |
FileUtils.mkdir_p(path) |
|
| 146 |
end |
|
| 147 | 141 |
sha = Digest::SHA256.new |
| 148 |
File.open(diskfile, "wb") do |f| |
|
| 142 |
Attachment.create_diskfile(filename, disk_directory) do |f| |
|
| 143 |
self.disk_filename = File.basename f.path |
|
| 144 |
logger.info("Saving attachment '#{self.diskfile}' (#{@temp_file.size} bytes)") if logger
|
|
| 149 | 145 |
if @temp_file.respond_to?(:read) |
| 150 | 146 |
buffer = "" |
| 151 | 147 |
while (buffer = @temp_file.read(8192)) |
| ... | ... | |
| 557 | 553 | |
| 558 | 554 |
# Singleton class method is public |
| 559 | 555 |
class << self |
| 560 |
# Returns an ASCII or hashed filename that do not |
|
| 561 |
# exists yet in the given subdirectory |
|
| 562 |
def disk_filename(filename, directory=nil) |
|
| 556 |
# Claims a unique ASCII or hashed filename, yields the open file handle |
|
| 557 |
def create_diskfile(filename, directory=nil, &block) |
|
| 563 | 558 |
timestamp = DateTime.now.strftime("%y%m%d%H%M%S")
|
| 564 | 559 |
ascii = '' |
| 565 | 560 |
if %r{^[a-zA-Z0-9_\.\-]*$}.match?(filename) && filename.length <= 50
|
| ... | ... | |
| 569 | 564 |
# keep the extension if any |
| 570 | 565 |
ascii << $1 if filename =~ %r{(\.[a-zA-Z0-9]+)$}
|
| 571 | 566 |
end |
| 572 |
while File.exist?(File.join(storage_path, directory.to_s, |
|
| 573 |
"#{timestamp}_#{ascii}"))
|
|
| 567 | ||
| 568 |
path = File.join storage_path, directory.to_s |
|
| 569 |
FileUtils.mkdir_p(path) unless File.directory?(path) |
|
| 570 |
begin |
|
| 571 |
name = "#{timestamp}_#{ascii}"
|
|
| 572 |
File.open( |
|
| 573 |
File.join(path, name), |
|
| 574 |
flags: File::CREAT | File::EXCL | File::BINARY | File::WRONLY, |
|
| 575 |
binmode: true, |
|
| 576 |
&block |
|
| 577 |
) |
|
| 578 |
rescue Errno::EEXIST |
|
| 574 | 579 |
timestamp.succ! |
| 580 |
retry |
|
| 575 | 581 |
end |
| 576 |
"#{timestamp}_#{ascii}"
|
|
| 577 | 582 |
end |
| 578 | 583 |
end |
| 579 | 584 |
end |
| test/unit/attachment_test.rb | ||
|---|---|---|
| 275 | 275 |
assert_equal 'valid_[] invalid_chars', a.filename |
| 276 | 276 |
end |
| 277 | 277 | |
| 278 |
def test_diskfilename |
|
| 279 |
assert Attachment.disk_filename("test_file.txt") =~ /^\d{12}_test_file.txt$/
|
|
| 280 |
assert_equal 'test_file.txt', Attachment.disk_filename("test_file.txt")[13..-1]
|
|
| 281 |
assert_equal '770c509475505f37c2b8fb6030434d6b.txt', Attachment.disk_filename("test_accentué.txt")[13..-1]
|
|
| 282 |
assert_equal 'f8139524ebb8f32e51976982cd20a85d', Attachment.disk_filename("test_accentué")[13..-1]
|
|
| 283 |
assert_equal 'cbb5b0f30978ba03731d61f9f6d10011', Attachment.disk_filename("test_accentué.ça")[13..-1]
|
|
| 278 |
def test_create_diskfile |
|
| 279 |
Attachment.create_diskfile("test_file.txt") do |f|
|
|
| 280 |
path = f.path |
|
| 281 |
assert_match(/^\d{12}_test_file.txt$/, File.basename(path))
|
|
| 282 |
assert_equal 'test_file.txt', File.basename(path)[13..-1] |
|
| 283 |
File.unlink f.path |
|
| 284 |
end |
|
| 285 | ||
| 286 |
Attachment.create_diskfile("test_accentué.txt") do |f|
|
|
| 287 |
assert_equal '770c509475505f37c2b8fb6030434d6b.txt', File.basename(f.path)[13..-1] |
|
| 288 |
File.unlink f.path |
|
| 289 |
end |
|
| 290 | ||
| 291 |
Attachment.create_diskfile("test_accentué") do |f|
|
|
| 292 |
assert_equal 'f8139524ebb8f32e51976982cd20a85d', File.basename(f.path)[13..-1] |
|
| 293 |
File.unlink f.path |
|
| 294 |
end |
|
| 295 | ||
| 296 |
Attachment.create_diskfile("test_accentué.ça") do |f|
|
|
| 297 |
assert_equal 'cbb5b0f30978ba03731d61f9f6d10011', File.basename(f.path)[13..-1] |
|
| 298 |
File.unlink f.path |
|
| 299 |
end |
|
| 284 | 300 |
end |
| 285 | 301 | |
| 286 | 302 |
def test_title |