From 27ab5f471c346f501bee392980d507563b71f3cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9gis=20Hanol?= Date: Wed, 10 Jul 2013 22:54:05 +0200 Subject: [PATCH] support arbitrary attachments --- app/controllers/uploads_controller.rb | 3 +- app/models/site_setting.rb | 17 ++++++++++ app/models/upload.rb | 47 ++++++++++----------------- lib/local_store.rb | 26 +++++++++++++-- lib/s3.rb | 6 ++-- 5 files changed, 61 insertions(+), 38 deletions(-) diff --git a/app/controllers/uploads_controller.rb b/app/controllers/uploads_controller.rb index ceff160e3..bd812f535 100644 --- a/app/controllers/uploads_controller.rb +++ b/app/controllers/uploads_controller.rb @@ -4,8 +4,7 @@ class UploadsController < ApplicationController def create file = params[:file] || params[:files].first - # only supports images for now - return render status: 415, json: failed_json unless file.content_type =~ /^image\/.+/ + return render status: 415, json: failed_json unless SiteSetting.authorized_file?(file) upload = Upload.create_for(current_user.id, file) diff --git a/app/models/site_setting.rb b/app/models/site_setting.rb index 3a961ebb8..021eb2d47 100644 --- a/app/models/site_setting.rb +++ b/app/models/site_setting.rb @@ -274,6 +274,23 @@ class SiteSetting < ActiveRecord::Base top_menu_items.map { |item| item.name }.select{ |item| list.include?(item) }.first end + def self.authorized_file?(file) + file.original_filename =~ /\.(#{authorized_extensions.tr(". ", "")})$/i + end + + def self.images + @images ||= ["jpg", "jpeg", "png", "gif", "tif", "tiff", "bmp"] + end + + def self.authorized_image?(file) + authorized_images = authorized_extensions + .tr(". ", "") + .split("|") + .select { |extension| images.include?(extension) } + .join("|") + file.original_filename =~ /\.(#{authorized_images})$/i + end + end # == Schema Information diff --git a/app/models/upload.rb b/app/models/upload.rb index 38d5a8592..02c3d0c2f 100644 --- a/app/models/upload.rb +++ b/app/models/upload.rb @@ -1,9 +1,9 @@ require 'digest/sha1' require 'image_sizer' -require 's3' -require 'local_store' require 'tempfile' require 'pathname' +require_dependency 's3' +require_dependency 'local_store' class Upload < ActiveRecord::Base belongs_to :user @@ -48,24 +48,25 @@ class Upload < ActiveRecord::Base sha1 = Digest::SHA1.file(file.tempfile).hexdigest # check if the file has already been uploaded unless upload = Upload.where(sha1: sha1).first - # retrieve image info - image_info = FastImage.new(file.tempfile, raise_on_failure: true) - # compute image aspect ratio - width, height = ImageSizer.resize(*image_info.size) # create a db record (so we can use the id) upload = Upload.create!({ user_id: user_id, original_filename: file.original_filename, filesize: File.size(file.tempfile), sha1: sha1, - width: width, - height: height, url: "" }) - # make sure we're at the beginning of the file (FastImage is moving the pointer) - file.rewind + # deal with width & heights for images + if SiteSetting.authorized_image?(file) + # retrieve image info + image_info = FastImage.new(file.tempfile, raise_on_failure: true) + # compute image aspect ratio + upload.width, upload.height = ImageSizer.resize(*image_info.size) + # make sure we're at the beginning of the file (FastImage is moving the pointer) + file.rewind + end # store the file and update its url - upload.url = Upload.store_file(file, sha1, image_info, upload.id) + upload.url = Upload.store_file(file, sha1, upload.id) # save the url upload.save end @@ -73,9 +74,9 @@ class Upload < ActiveRecord::Base upload end - def self.store_file(file, sha1, image_info, upload_id) - return S3.store_file(file, sha1, image_info, upload_id) if SiteSetting.enable_s3_uploads? - return LocalStore.store_file(file, sha1, image_info, upload_id) + def self.store_file(file, sha1, upload_id) + return S3.store_file(file, sha1, upload_id) if SiteSetting.enable_s3_uploads? + return LocalStore.store_file(file, sha1, upload_id) end def self.remove_file(url) @@ -92,29 +93,15 @@ class Upload < ActiveRecord::Base end def self.is_local?(url) - url.start_with?(base_url) + !SiteSetting.enable_s3_uploads? && url.start_with?(LocalStore.base_url) end def self.is_on_s3?(url) SiteSetting.enable_s3_uploads? && url.start_with?(S3.base_url) end - def self.base_url - asset_host.present? ? asset_host : Discourse.base_url_no_prefix - end - - def self.asset_host - ActionController::Base.asset_host - end - def self.get_from_url(url) - if has_been_uploaded?(url) - if m = LocalStore.uploaded_regex.match(url) - Upload.where(id: m[:upload_id]).first - elsif is_on_s3?(url) - Upload.where(url: url).first - end - end + Upload.where(url: url).first if has_been_uploaded?(url) end end diff --git a/lib/local_store.rb b/lib/local_store.rb index f893461da..a87ff8a0e 100644 --- a/lib/local_store.rb +++ b/lib/local_store.rb @@ -1,18 +1,21 @@ module LocalStore - def self.store_file(file, sha1, image_info, upload_id) - clean_name = Digest::SHA1.hexdigest("#{Time.now.to_s}#{file.original_filename}")[0,16] + ".#{image_info.type}" + def self.store_file(file, sha1, upload_id) + unique_sha1 = Digest::SHA1.hexdigest("#{Time.now.to_s}#{file.original_filename}")[0,16] + extension = File.extname(file.original_filename) + clean_name = "#{unique_sha1}#{extension}" url_root = "/uploads/#{RailsMultisite::ConnectionManagement.current_db}/#{upload_id}" path = "#{Rails.root}/public#{url_root}" FileUtils.mkdir_p path + # not using cause mv, cause permissions are no good on move File.open("#{path}/#{clean_name}", "wb") do |f| f.write File.read(file.tempfile) end # url - return Discourse::base_uri + "#{url_root}/#{clean_name}" + Discourse::base_uri + "#{url_root}/#{clean_name}" end def self.remove_file(url) @@ -24,4 +27,21 @@ module LocalStore /\/uploads\/#{RailsMultisite::ConnectionManagement.current_db}\/(?\d+)\/[0-9a-f]{16}\.(png|jpg|jpeg|gif|tif|tiff|bmp)/ end + def self.base_url + url = asset_host.present? ? asset_host : Discourse.base_url_no_prefix + "#{url}#{directory}" + end + + def self.base_path + "#{Rails.root}/public#{directory}" + end + + def self.directory + "/uploads/#{RailsMultisite::ConnectionManagement.current_db}" + end + + def self.asset_host + ActionController::Base.asset_host + end + end diff --git a/lib/s3.rb b/lib/s3.rb index a880cfd0e..09d485a72 100644 --- a/lib/s3.rb +++ b/lib/s3.rb @@ -1,11 +1,11 @@ module S3 - def self.store_file(file, sha1, image_info, upload_id) + def self.store_file(file, sha1, upload_id) S3.check_missing_site_settings directory = S3.get_or_create_directory(SiteSetting.s3_upload_bucket) - - remote_filename = "#{upload_id}#{sha1}.#{image_info.type}" + extension = File.extname(file.original_filename) + remote_filename = "#{upload_id}#{sha1}#{extension}" # if this fails, it will throw an exception file = S3.upload(file, remote_filename, directory)