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 |