Feature #43484 » 0004-Detect-attachment-content-type-from-file-contents-in.patch
| app/assets/javascripts/attachments.js | ||
|---|---|---|
| 120 | 120 |
uploadUrl = uploadUrl + '?attachment_id=' + attachmentId; |
| 121 | 121 |
if (blob instanceof window.Blob) {
|
| 122 | 122 |
uploadUrl += '&filename=' + encodeURIComponent(blob.name); |
| 123 |
uploadUrl += '&content_type=' + encodeURIComponent(blob.type); |
|
| 124 | 123 |
} |
| 125 | 124 | |
| 126 | 125 |
return $.ajax(uploadUrl, {
|
| app/controllers/attachments_controller.rb | ||
|---|---|---|
| 108 | 108 |
@attachment = Attachment.new(:file => raw_request_body) |
| 109 | 109 |
@attachment.author = User.current |
| 110 | 110 |
@attachment.filename = params[:filename].presence || Redmine::Utils.random_hex(16) |
| 111 |
@attachment.content_type = params[:content_type].presence |
|
| 112 | 111 |
saved = @attachment.save |
| 113 | 112 | |
| 114 | 113 |
respond_to do |format| |
| app/models/attachment.rb | ||
|---|---|---|
| 83 | 83 |
cattr_accessor :thumbnails_storage_path |
| 84 | 84 |
@@thumbnails_storage_path = File.join(Rails.root, "tmp", "thumbnails") |
| 85 | 85 | |
| 86 |
before_create :files_to_final_location |
|
| 86 |
before_create :set_content_type, :files_to_final_location
|
|
| 87 | 87 |
after_commit :delete_from_disk, :on => :destroy |
| 88 | 88 |
after_commit :reuse_existing_file_if_possible, :on => :create |
| 89 | 89 |
after_rollback :delete_from_disk, :on => :create |
| ... | ... | |
| 118 | 118 |
self.filename = @temp_file.original_filename |
| 119 | 119 |
self.filename.force_encoding("UTF-8")
|
| 120 | 120 |
end |
| 121 |
if @temp_file.respond_to?(:content_type) |
|
| 122 |
self.content_type = @temp_file.content_type.to_s.chomp |
|
| 123 |
end |
|
| 124 | 121 |
self.filesize = @temp_file.size |
| 125 | 122 |
end |
| 126 | 123 |
end |
| ... | ... | |
| 134 | 131 |
filename |
| 135 | 132 |
end |
| 136 | 133 | |
| 134 |
def set_content_type |
|
| 135 |
return unless @temp_file |
|
| 136 | ||
| 137 |
io = @temp_file.respond_to?(:read) ? @temp_file : StringIO.new(@temp_file) |
|
| 138 |
self.content_type = Marcel::MimeType.for(io, name: filename) |
|
| 139 |
@temp_file.rewind if @temp_file.respond_to?(:rewind) |
|
| 140 |
end |
|
| 141 | ||
| 137 | 142 |
# Copies the temporary file to its final location |
| 138 | 143 |
# and computes its hash |
| 139 | 144 |
def files_to_final_location |
| ... | ... | |
| 157 | 162 |
self.digest = sha.hexdigest |
| 158 | 163 |
end |
| 159 | 164 |
@temp_file = nil |
| 160 | ||
| 161 |
if content_type.blank? && filename.present? |
|
| 162 |
self.content_type = Redmine::MimeType.of(filename) |
|
| 163 |
end |
|
| 164 |
# Don't save the content type if it's longer than the authorized length |
|
| 165 |
if self.content_type && self.content_type.length > 255 |
|
| 166 |
self.content_type = nil |
|
| 167 |
end |
|
| 168 | 165 |
end |
| 169 | 166 | |
| 170 | 167 |
# Deletes the file from the file system if it's not referenced by other attachments |
| test/integration/api_test/attachments_test.rb | ||
|---|---|---|
| 142 | 142 |
assert_nil attachment.container |
| 143 | 143 |
assert_equal 2, attachment.author_id |
| 144 | 144 |
assert_equal 'File content'.size, attachment.filesize |
| 145 |
assert attachment.content_type.blank?
|
|
| 145 |
assert_equal 'application/octet-stream', attachment.content_type
|
|
| 146 | 146 |
assert attachment.filename.present? |
| 147 | 147 |
assert_match %r{\d+_[0-9a-z]+}, attachment.diskfile
|
| 148 | 148 |
assert File.exist?(attachment.diskfile) |
| test/integration/attachments_test.rb | ||
|---|---|---|
| 33 | 33 |
assert_equal 'text/plain', attachment.content_type |
| 34 | 34 |
end |
| 35 | 35 | |
| 36 |
def test_upload_should_accept_content_type_param
|
|
| 36 |
def test_upload_should_detect_file_content_type
|
|
| 37 | 37 |
log_user('jsmith', 'jsmith')
|
| 38 | 38 |
assert_difference 'Attachment.count' do |
| 39 | 39 |
post( |
| 40 |
"/uploads.js?attachment_id=1&filename=foo&content_type=image/jpeg",
|
|
| 41 |
:params => "File content",
|
|
| 40 |
"/uploads.js?attachment_id=1&filename=foo", |
|
| 41 |
:params => "\xFF\xD8\xFF\xD9", # JPEG file signature
|
|
| 42 | 42 |
:headers => {"CONTENT_TYPE" => 'application/octet-stream'})
|
| 43 | 43 |
assert_response :success |
| 44 | 44 |
end |
| test/unit/attachment_test.rb | ||
|---|---|---|
| 54 | 54 |
assert_equal 59, File.size(a.diskfile) |
| 55 | 55 |
end |
| 56 | 56 | |
| 57 |
def test_create_should_clear_content_type_if_too_long
|
|
| 57 |
def test_create_should_not_trust_given_content_type_attribute
|
|
| 58 | 58 |
a = Attachment.new(:container => Issue.find(1), |
| 59 |
:file => uploaded_test_file("testfile.txt", "text/plain"),
|
|
| 59 |
:file => uploaded_test_file("testfile.txt", nil),
|
|
| 60 | 60 |
:author => User.find(1), |
| 61 | 61 |
:content_type => 'a'*300) |
| 62 | 62 |
assert a.save |
| 63 | 63 |
a.reload |
| 64 |
assert_nil a.content_type |
|
| 64 |
assert_equal 'text/plain', a.content_type |
|
| 65 |
end |
|
| 66 | ||
| 67 |
def test_create_should_not_trust_client_declared_content_type |
|
| 68 |
a = Attachment.new(:container => Issue.find(1), |
|
| 69 |
:file => uploaded_test_file("testfile.txt", "image/jpeg"),
|
|
| 70 |
:author => User.find(1)) |
|
| 71 |
assert a.save |
|
| 72 |
a.reload |
|
| 73 |
assert_equal 'text/plain', a.content_type |
|
| 65 | 74 |
end |
| 66 | 75 | |
| 67 | 76 |
def test_shorted_filename_if_too_long |
- « Previous
- 1
- 2
- 3
- 4
- Next »