Project

General

Profile

Defect #35539 » 0001-ensure-unique-attachment-filenames.patch

Jens Krämer, 2021-07-07 07:20

View differences:

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
    (1-1/1)