Index: lib/technoweenie/attachment_fu.rb =================================================================== --- lib/technoweenie/attachment_fu.rb (.../vendor/attachment_fu/2007_11_27) (revision 626) +++ lib/technoweenie/attachment_fu.rb (.../place_gather/trunk/vendor/plugins/attachment_fu) (revision 631) @@ -1,8 +1,8 @@ module Technoweenie # :nodoc: module AttachmentFu # :nodoc: - @@default_processors = %w(ImageScience Rmagick MiniMagick) - @@tempfile_path = File.join(RAILS_ROOT, 'tmp', 'attachment_fu') - @@content_types = ['image/jpeg', 'image/pjpeg', 'image/gif', 'image/png', 'image/x-png', 'image/jpg'] + @@default_processors = %w(ImageScience Rmagick MiniMagick) + @@tempfile_path = File.join(RAILS_ROOT, 'tmp', 'attachment_fu') + @@content_types = ['image/jpeg', 'image/pjpeg', 'image/gif', 'image/png', 'image/x-png', 'image/jpg'] mattr_reader :content_types, :tempfile_path, :default_processors mattr_writer :tempfile_path @@ -12,6 +12,7 @@ module ActMethods # Options: # * :content_type - Allowed content types. Allows all by default. Use :image to allow all standard image types. + # * :content_type_validation - Validate that the uploaded file actually has data of the allowed content_types. Unix only. Defaults to false. # * :min_size - Minimum size allowed. 1 byte is the default. # * :max_size - Maximum size allowed. 1.megabyte is the default. # * :size - Range of sizes allowed. (1..1.megabyte) is the default. This overrides the :min_size and :max_size options. @@ -45,6 +46,7 @@ options[:thumbnail_class] ||= self options[:s3_access] ||= :public_read options[:content_type] = [options[:content_type]].flatten.collect! { |t| t == :image ? Technoweenie::AttachmentFu.content_types : t }.flatten unless options[:content_type].nil? + options[:content_type_validation] ||= false unless options[:thumbnails].is_a?(Hash) raise ArgumentError, ":thumbnails option should be a hash: e.g. :thumbnails => { :foo => '50x50' }" @@ -57,7 +59,7 @@ # only need to define these once on a class unless included_modules.include?(InstanceMethods) attr_accessor :thumbnail_resize_options - + attachment_options[:content_type_regexp] = Regexp.new(attachment_options[:content_type].join('|')) attachment_options[:storage] ||= (attachment_options[:file_system_path] || attachment_options[:path_prefix]) ? :file_system : :db_file attachment_options[:path_prefix] ||= attachment_options[:file_system_path] if attachment_options[:path_prefix].nil? @@ -97,7 +99,7 @@ puts "Problems loading #{options[:processor]}Processor: #{$!}" end end - after_validation :process_attachment + before_save :process_attachment end end end @@ -109,6 +111,7 @@ def validates_as_attachment validates_presence_of :size, :content_type, :filename validate :attachment_attributes_valid? + validate :attachment_content_type_valid? if self.attachment_options[:content_type_validation] end # Returns true or false if the given content type is recognized as an image. @@ -352,6 +355,27 @@ end end + # + # Validates that the uploaded file's data is actually of + # an acceptable mime type. + # This is necessary because the CGI mime-type identifier just uses + # the filename, which can be inaccurate. + # + # Unix-dependent method. Calls unix file(1) command to determine mime type by + # investigating the first bytes of the file. + def attachment_content_type_valid? + cmd = "file -i #{self.temp_path}" + output = `#{cmd}` + exit_code = $? + # if the whole job failed, report that to the log + unless exit_code.success? + logger.warn("Failed to validate attachment with command: [#{cmd}]. Exit code: #{exit_code}, Output: #{output}") + end + unless exit_code.success? && (! output.blank?) && (output =~ attachment_options[:content_type_regexp]) + errors.add_to_base("File must have a valid content type") + end + end + # Initializes a new thumbnail with the given suffix. def find_or_initialize_thumbnail(file_name_suffix) respond_to?(:parent_id) ?