paperclip 3.5.4 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of paperclip might be problematic. Click here for more details.
- checksums.yaml +4 -4
 - data/Gemfile +1 -0
 - data/LICENSE +1 -3
 - data/NEWS +25 -21
 - data/README.md +35 -1
 - data/features/step_definitions/attachment_steps.rb +2 -0
 - data/features/step_definitions/rails_steps.rb +1 -0
 - data/lib/paperclip.rb +1 -0
 - data/lib/paperclip/attachment.rb +25 -1
 - data/lib/paperclip/callbacks.rb +1 -1
 - data/lib/paperclip/content_type_detector.rb +1 -13
 - data/lib/paperclip/errors.rb +5 -0
 - data/lib/paperclip/has_attached_file.rb +5 -0
 - data/lib/paperclip/io_adapters/abstract_adapter.rb +1 -1
 - data/lib/paperclip/io_adapters/attachment_adapter.rb +4 -4
 - data/lib/paperclip/io_adapters/data_uri_adapter.rb +4 -9
 - data/lib/paperclip/io_adapters/stringio_adapter.rb +10 -8
 - data/lib/paperclip/media_type_spoof_detector.rb +36 -0
 - data/lib/paperclip/tempfile_factory.rb +5 -1
 - data/lib/paperclip/validators.rb +6 -1
 - data/lib/paperclip/validators/attachment_content_type_validator.rb +4 -0
 - data/lib/paperclip/validators/attachment_file_name_validator.rb +80 -0
 - data/lib/paperclip/validators/attachment_file_type_ignorance_validator.rb +29 -0
 - data/lib/paperclip/validators/attachment_presence_validator.rb +4 -0
 - data/lib/paperclip/validators/attachment_size_validator.rb +4 -0
 - data/lib/paperclip/validators/media_type_spoof_detection_validator.rb +27 -0
 - data/lib/paperclip/version.rb +1 -1
 - data/test/attachment_definitions_test.rb +1 -0
 - data/test/attachment_test.rb +40 -43
 - data/test/content_type_detector_test.rb +0 -10
 - data/test/fixtures/empty.html +1 -0
 - data/test/has_attached_file_test.rb +3 -1
 - data/test/helper.rb +11 -4
 - data/test/io_adapters/abstract_adapter_test.rb +1 -0
 - data/test/io_adapters/attachment_adapter_test.rb +1 -1
 - data/test/io_adapters/data_uri_adapter_test.rb +2 -2
 - data/test/io_adapters/file_adapter_test.rb +0 -11
 - data/test/io_adapters/http_url_proxy_adapter_test.rb +2 -3
 - data/test/io_adapters/stringio_adapter_test.rb +1 -1
 - data/test/matchers/have_attached_file_matcher_test.rb +3 -2
 - data/test/matchers/validate_attachment_content_type_matcher_test.rb +13 -12
 - data/test/matchers/validate_attachment_presence_matcher_test.rb +8 -7
 - data/test/matchers/validate_attachment_size_matcher_test.rb +12 -11
 - data/test/media_type_spoof_detector_test.rb +28 -0
 - data/test/meta_class_test.rb +2 -2
 - data/test/schema_test.rb +6 -0
 - data/test/storage/fog_test.rb +4 -4
 - data/test/storage/s3_test.rb +32 -31
 - data/test/tempfile_factory_test.rb +13 -1
 - data/test/validators/attachment_file_name_validator_test.rb +162 -0
 - data/test/validators/attachment_presence_validator_test.rb +1 -1
 - data/test/validators/media_type_spoof_detection_validator_test.rb +12 -0
 - data/test/validators_test.rb +43 -3
 - metadata +14 -2
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA1:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 748ce579f297e0d58f7ee6e72b2968341af560be
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 47ed54affca1e4c7736eddcb33dfac09763d871d
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 1337da62c00c9b10e151b8a1efd10b611d92c0601c3471bab99b57acbdf7c532e9714ce271c1bc01dbbc499aa71938ba5865b15ff44f1f68d7c0e919e24d00b8
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: e8fe2a23b8bc5fbbfc9e5ead740a42a6ff7ccb93c9cc452e10cd443b510560ac826d6ef1d3a6dcbefe5ce13bf4b8b7799fd7a6f290c26b27874ac166c89d501d
         
     | 
    
        data/Gemfile
    CHANGED
    
    
    
        data/LICENSE
    CHANGED
    
    | 
         @@ -3,7 +3,7 @@ LICENSE 
     | 
|
| 
       3 
3 
     | 
    
         | 
| 
       4 
4 
     | 
    
         
             
            The MIT License
         
     | 
| 
       5 
5 
     | 
    
         | 
| 
       6 
     | 
    
         
            -
            Copyright (c) 2008- 
     | 
| 
      
 6 
     | 
    
         
            +
            Copyright (c) 2008-2014 Jon Yurek and thoughtbot, inc.
         
     | 
| 
       7 
7 
     | 
    
         | 
| 
       8 
8 
     | 
    
         
             
            Permission is hereby granted, free of charge, to any person obtaining a copy
         
     | 
| 
       9 
9 
     | 
    
         
             
            of this software and associated documentation files (the "Software"), to deal
         
     | 
| 
         @@ -22,5 +22,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
     | 
|
| 
       22 
22 
     | 
    
         
             
            LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
         
     | 
| 
       23 
23 
     | 
    
         
             
            OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
         
     | 
| 
       24 
24 
     | 
    
         
             
            THE SOFTWARE.
         
     | 
| 
       25 
     | 
    
         
            -
             
     | 
| 
       26 
     | 
    
         
            -
             
     | 
    
        data/NEWS
    CHANGED
    
    | 
         @@ -1,28 +1,32 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            New in  
     | 
| 
      
 1 
     | 
    
         
            +
            New in 4.0.0:
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
     | 
    
         
            -
             
     | 
| 
      
 3 
     | 
    
         
            +
            * Security: Attachments are checked to make sure they're not pulling a fast one.
         
     | 
| 
      
 4 
     | 
    
         
            +
            * Security: It is now *enforced* that every attachment has a file/mime validation.
         
     | 
| 
      
 5 
     | 
    
         
            +
            * Bug Fix: Removed a call to IOAdapter#close that was causing issues.
         
     | 
| 
      
 6 
     | 
    
         
            +
            * Improvement: Added bullets to the 3.5.3 list of changes. Very important.
         
     | 
| 
      
 7 
     | 
    
         
            +
            * Improcement: Updated the copyright to 2014
         
     | 
| 
       4 
8 
     | 
    
         | 
| 
       5 
9 
     | 
    
         
             
            New in 3.5.3:
         
     | 
| 
       6 
10 
     | 
    
         | 
| 
       7 
     | 
    
         
            -
            Improvement: After three long, hard years... we know how to upgrade
         
     | 
| 
       8 
     | 
    
         
            -
            Bug Fix: #expiring_url returns 'missing' urls if nothing is attached
         
     | 
| 
       9 
     | 
    
         
            -
            Improvement: Lots of documentation fixes
         
     | 
| 
       10 
     | 
    
         
            -
            Improvement: Lots of fixes for Ruby warnings
         
     | 
| 
       11 
     | 
    
         
            -
            Improvement: Test the most appropriate Ruby/Rails comobinations on Travis
         
     | 
| 
       12 
     | 
    
         
            -
            Improvement: Delegate more IO methods through IOAdapters
         
     | 
| 
       13 
     | 
    
         
            -
            Improvement: Remove Rails 4 deprecations
         
     | 
| 
       14 
     | 
    
         
            -
            Improvement: Both S3's and Fog's #expiring_url can take a Time or Int
         
     | 
| 
       15 
     | 
    
         
            -
            Bug Fix: Both S3's and Fog's expiring_url respect style when missing the file
         
     | 
| 
       16 
     | 
    
         
            -
            Bug Fix: Timefiles will have a reasonable-length name. They're all MD5 hashes now
         
     | 
| 
       17 
     | 
    
         
            -
            Bug Fix: Don't delete files off S3 when reprocessing due to AWS inconsistencies
         
     | 
| 
       18 
     | 
    
         
            -
            Bug Fix: "swallow_stream" isn't thread dafe. Use :swallow_stderr
         
     | 
| 
       19 
     | 
    
         
            -
            Improvement: Regexps use \A and \Z instead of ^ and $
         
     | 
| 
       20 
     | 
    
         
            -
            Improvement: :s3_credentials can take a lambda as an argument
         
     | 
| 
       21 
     | 
    
         
            -
            Improvement: Search up the class heirarchy for attachments
         
     | 
| 
       22 
     | 
    
         
            -
            Improvement: deep_merge options instead of regular merge
         
     | 
| 
       23 
     | 
    
         
            -
            Bug Fix: Prevent file deletion on transaction rollback
         
     | 
| 
       24 
     | 
    
         
            -
            Test Improvement: Ensure more files are properly closed during tests
         
     | 
| 
       25 
     | 
    
         
            -
            Test Bug Fix: Return the gemfile's syntax to normal
         
     | 
| 
      
 11 
     | 
    
         
            +
            * Improvement: After three long, hard years... we know how to upgrade
         
     | 
| 
      
 12 
     | 
    
         
            +
            * Bug Fix: #expiring_url returns 'missing' urls if nothing is attached
         
     | 
| 
      
 13 
     | 
    
         
            +
            * Improvement: Lots of documentation fixes
         
     | 
| 
      
 14 
     | 
    
         
            +
            * Improvement: Lots of fixes for Ruby warnings
         
     | 
| 
      
 15 
     | 
    
         
            +
            * Improvement: Test the most appropriate Ruby/Rails comobinations on Travis
         
     | 
| 
      
 16 
     | 
    
         
            +
            * Improvement: Delegate more IO methods through IOAdapters
         
     | 
| 
      
 17 
     | 
    
         
            +
            * Improvement: Remove Rails 4 deprecations
         
     | 
| 
      
 18 
     | 
    
         
            +
            * Improvement: Both S3's and Fog's #expiring_url can take a Time or Int
         
     | 
| 
      
 19 
     | 
    
         
            +
            * Bug Fix: Both S3's and Fog's expiring_url respect style when missing the file
         
     | 
| 
      
 20 
     | 
    
         
            +
            * Bug Fix: Timefiles will have a reasonable-length name. They're all MD5 hashes now
         
     | 
| 
      
 21 
     | 
    
         
            +
            * Bug Fix: Don't delete files off S3 when reprocessing due to AWS inconsistencies
         
     | 
| 
      
 22 
     | 
    
         
            +
            * Bug Fix: "swallow_stream" isn't thread dafe. Use :swallow_stderr
         
     | 
| 
      
 23 
     | 
    
         
            +
            * Improvement: Regexps use \A and \Z instead of ^ and $
         
     | 
| 
      
 24 
     | 
    
         
            +
            * Improvement: :s3_credentials can take a lambda as an argument
         
     | 
| 
      
 25 
     | 
    
         
            +
            * Improvement: Search up the class heirarchy for attachments
         
     | 
| 
      
 26 
     | 
    
         
            +
            * Improvement: deep_merge options instead of regular merge
         
     | 
| 
      
 27 
     | 
    
         
            +
            * Bug Fix: Prevent file deletion on transaction rollback
         
     | 
| 
      
 28 
     | 
    
         
            +
            * Test Improvement: Ensure more files are properly closed during tests
         
     | 
| 
      
 29 
     | 
    
         
            +
            * Test Bug Fix: Return the gemfile's syntax to normal
         
     | 
| 
       26 
30 
     | 
    
         | 
| 
       27 
31 
     | 
    
         
             
            New in 3.5.2:
         
     | 
| 
       28 
32 
     | 
    
         | 
    
        data/README.md
    CHANGED
    
    | 
         @@ -104,6 +104,7 @@ Quick Start 
     | 
|
| 
       104 
104 
     | 
    
         
             
            class User < ActiveRecord::Base
         
     | 
| 
       105 
105 
     | 
    
         
             
              attr_accessible :avatar
         
     | 
| 
       106 
106 
     | 
    
         
             
              has_attached_file :avatar, :styles => { :medium => "300x300>", :thumb => "100x100>" }, :default_url => "/images/:style/missing.png"
         
     | 
| 
      
 107 
     | 
    
         
            +
              validates_attachment_content_type :avatar, :content_type => /\Aimage\/.*\Z/
         
     | 
| 
       107 
108 
     | 
    
         
             
            end
         
     | 
| 
       108 
109 
     | 
    
         
             
            ```
         
     | 
| 
       109 
110 
     | 
    
         | 
| 
         @@ -112,6 +113,7 @@ end 
     | 
|
| 
       112 
113 
     | 
    
         
             
            ```ruby
         
     | 
| 
       113 
114 
     | 
    
         
             
            class User < ActiveRecord::Base
         
     | 
| 
       114 
115 
     | 
    
         
             
              has_attached_file :avatar, :styles => { :medium => "300x300>", :thumb => "100x100>" }, :default_url => "/images/:style/missing.png"
         
     | 
| 
      
 116 
     | 
    
         
            +
              validates_attachment_content_type :avatar, :content_type => /\Aimage\/.*\Z/
         
     | 
| 
       115 
117 
     | 
    
         
             
            end
         
     | 
| 
       116 
118 
     | 
    
         
             
            ```
         
     | 
| 
       117 
119 
     | 
    
         | 
| 
         @@ -302,6 +304,38 @@ validates_attachment :avatar, 
     | 
|
| 
       302 
304 
     | 
    
         
             
            `Paperclip::ContentTypeDetector` will attempt to match a file's extension to an
         
     | 
| 
       303 
305 
     | 
    
         
             
            inferred content_type, regardless of the actual contents of the file.
         
     | 
| 
       304 
306 
     | 
    
         | 
| 
      
 307 
     | 
    
         
            +
            Security Validations
         
     | 
| 
      
 308 
     | 
    
         
            +
            ====================
         
     | 
| 
      
 309 
     | 
    
         
            +
             
     | 
| 
      
 310 
     | 
    
         
            +
            NOTE: Starting at version 4.0.0, all attachments are *required* to include a
         
     | 
| 
      
 311 
     | 
    
         
            +
            content_type validation, a file_name validation, or to explicitly state that
         
     | 
| 
      
 312 
     | 
    
         
            +
            they're not going to have either. *Paperclip will raise an error* if you do not
         
     | 
| 
      
 313 
     | 
    
         
            +
            do this.
         
     | 
| 
      
 314 
     | 
    
         
            +
             
     | 
| 
      
 315 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 316 
     | 
    
         
            +
            class ActiveRecord::Base
         
     | 
| 
      
 317 
     | 
    
         
            +
              has_attached_file :avatar
         
     | 
| 
      
 318 
     | 
    
         
            +
            # Validate content type
         
     | 
| 
      
 319 
     | 
    
         
            +
              validates_attachment_content_type :avatar, :content_type => /\Aimage/
         
     | 
| 
      
 320 
     | 
    
         
            +
            # Validate filename
         
     | 
| 
      
 321 
     | 
    
         
            +
              validates_attachment_file_name :avatar, :matches => [/png\Z/, /jpe?g\Z/]
         
     | 
| 
      
 322 
     | 
    
         
            +
            # Explicitly do not validate
         
     | 
| 
      
 323 
     | 
    
         
            +
              do_not_validate_attachment_file_type :avatar
         
     | 
| 
      
 324 
     | 
    
         
            +
            end
         
     | 
| 
      
 325 
     | 
    
         
            +
            ```
         
     | 
| 
      
 326 
     | 
    
         
            +
             
     | 
| 
      
 327 
     | 
    
         
            +
            This keeps Paperclip secure-by-default, and will prevent people trying to mess
         
     | 
| 
      
 328 
     | 
    
         
            +
            with your filesystem.
         
     | 
| 
      
 329 
     | 
    
         
            +
             
     | 
| 
      
 330 
     | 
    
         
            +
            NOTE: Also starting at version 4.0.0, Paperclip has another validation that
         
     | 
| 
      
 331 
     | 
    
         
            +
            cannot be turned off. This validation will prevent content type spoofing. That
         
     | 
| 
      
 332 
     | 
    
         
            +
            is, uploading, say, a PHP document as part of the EXIF tags of a well-formed
         
     | 
| 
      
 333 
     | 
    
         
            +
            JPEG. This check is limited to the media type (the first part of the MIME type,
         
     | 
| 
      
 334 
     | 
    
         
            +
            so, 'text' in 'text/plain'). This will prevent HTML documents from being
         
     | 
| 
      
 335 
     | 
    
         
            +
            uploaded as JPEGs, but will not prevent GIFs from being uploaded with a .jpg
         
     | 
| 
      
 336 
     | 
    
         
            +
            extension. This validation will only add validation errors to the form. It will
         
     | 
| 
      
 337 
     | 
    
         
            +
            not cause Errors to be raised.
         
     | 
| 
      
 338 
     | 
    
         
            +
             
     | 
| 
       305 
339 
     | 
    
         
             
            Defaults
         
     | 
| 
       306 
340 
     | 
    
         
             
            --------
         
     | 
| 
       307 
341 
     | 
    
         
             
            Global defaults for all your paperclip attachments can be defined by changing the Paperclip::Attachment.default_options Hash, this can be useful for setting your default storage settings per example so you won't have to define them in every has_attached_file definition.
         
     | 
| 
         @@ -754,5 +788,5 @@ The names and logos for thoughtbot are trademarks of thoughtbot, inc. 
     | 
|
| 
       754 
788 
     | 
    
         
             
            License
         
     | 
| 
       755 
789 
     | 
    
         
             
            -------
         
     | 
| 
       756 
790 
     | 
    
         | 
| 
       757 
     | 
    
         
            -
            Paperclip is Copyright © 2008- 
     | 
| 
      
 791 
     | 
    
         
            +
            Paperclip is Copyright © 2008-2014 thoughtbot, inc. It is free software, and may be
         
     | 
| 
       758 
792 
     | 
    
         
             
            redistributed under the terms specified in the MIT-LICENSE file.
         
     | 
| 
         @@ -11,8 +11,10 @@ World(AttachmentHelpers) 
     | 
|
| 
       11 
11 
     | 
    
         | 
| 
       12 
12 
     | 
    
         
             
            When /^I modify my attachment definition to:$/ do |definition|
         
     | 
| 
       13 
13 
     | 
    
         
             
              content = in_current_dir { File.read("app/models/user.rb") }
         
     | 
| 
      
 14 
     | 
    
         
            +
              name = content[/has_attached_file :\w+/][/:\w+/]
         
     | 
| 
       14 
15 
     | 
    
         
             
              content.gsub!(/has_attached_file.+end/m, <<-FILE)
         
     | 
| 
       15 
16 
     | 
    
         
             
                  #{definition}
         
     | 
| 
      
 17 
     | 
    
         
            +
                  do_not_validate_attachment_file_type #{name}
         
     | 
| 
       16 
18 
     | 
    
         
             
                end
         
     | 
| 
       17 
19 
     | 
    
         
             
              FILE
         
     | 
| 
       18 
20 
     | 
    
         | 
| 
         @@ -69,6 +69,7 @@ def attach_attachment(name, definition = nil) 
     | 
|
| 
       69 
69 
     | 
    
         
             
                snippet += ", \n"
         
     | 
| 
       70 
70 
     | 
    
         
             
                snippet += definition
         
     | 
| 
       71 
71 
     | 
    
         
             
              end
         
     | 
| 
      
 72 
     | 
    
         
            +
              snippet += "\ndo_not_validate_attachment_file_type :#{name}\n"
         
     | 
| 
       72 
73 
     | 
    
         
             
              in_current_dir do
         
     | 
| 
       73 
74 
     | 
    
         
             
                transform_file("app/models/user.rb") do |content|
         
     | 
| 
       74 
75 
     | 
    
         
             
                  content.sub(/end\Z/, "#{snippet}\nend")
         
     | 
    
        data/lib/paperclip.rb
    CHANGED
    
    | 
         @@ -43,6 +43,7 @@ require 'paperclip/attachment' 
     | 
|
| 
       43 
43 
     | 
    
         
             
            require 'paperclip/storage'
         
     | 
| 
       44 
44 
     | 
    
         
             
            require 'paperclip/callbacks'
         
     | 
| 
       45 
45 
     | 
    
         
             
            require 'paperclip/file_command_content_type_detector'
         
     | 
| 
      
 46 
     | 
    
         
            +
            require 'paperclip/media_type_spoof_detector'
         
     | 
| 
       46 
47 
     | 
    
         
             
            require 'paperclip/content_type_detector'
         
     | 
| 
       47 
48 
     | 
    
         
             
            require 'paperclip/glue'
         
     | 
| 
       48 
49 
     | 
    
         
             
            require 'paperclip/errors'
         
     | 
    
        data/lib/paperclip/attachment.rb
    CHANGED
    
    | 
         @@ -1,6 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # encoding: utf-8
         
     | 
| 
       2 
2 
     | 
    
         
             
            require 'uri'
         
     | 
| 
       3 
3 
     | 
    
         
             
            require 'paperclip/url_generator'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'active_support/deprecation'
         
     | 
| 
       4 
5 
     | 
    
         | 
| 
       5 
6 
     | 
    
         
             
            module Paperclip
         
     | 
| 
       6 
7 
     | 
    
         
             
              # The Attachment class manages the files for a given attachment. It saves
         
     | 
| 
         @@ -93,6 +94,7 @@ module Paperclip 
     | 
|
| 
       93 
94 
     | 
    
         
             
                #   new_user.avatar = old_user.avatar
         
     | 
| 
       94 
95 
     | 
    
         
             
                def assign uploaded_file
         
     | 
| 
       95 
96 
     | 
    
         
             
                  ensure_required_accessors!
         
     | 
| 
      
 97 
     | 
    
         
            +
                  ensure_required_validations!
         
     | 
| 
       96 
98 
     | 
    
         
             
                  file = Paperclip.io_adapters.for(uploaded_file)
         
     | 
| 
       97 
99 
     | 
    
         | 
| 
       98 
100 
     | 
    
         
             
                  return nil if not file.assignment?
         
     | 
| 
         @@ -166,6 +168,18 @@ module Paperclip 
     | 
|
| 
       166 
168 
     | 
    
         
             
                  path.respond_to?(:unescape) ? path.unescape : path
         
     | 
| 
       167 
169 
     | 
    
         
             
                end
         
     | 
| 
       168 
170 
     | 
    
         | 
| 
      
 171 
     | 
    
         
            +
                # :nodoc:
         
     | 
| 
      
 172 
     | 
    
         
            +
                def staged_path(style_name = default_style)
         
     | 
| 
      
 173 
     | 
    
         
            +
                  if staged?
         
     | 
| 
      
 174 
     | 
    
         
            +
                    @queued_for_write[style_name].path
         
     | 
| 
      
 175 
     | 
    
         
            +
                  end
         
     | 
| 
      
 176 
     | 
    
         
            +
                end
         
     | 
| 
      
 177 
     | 
    
         
            +
             
     | 
| 
      
 178 
     | 
    
         
            +
                # :nodoc:
         
     | 
| 
      
 179 
     | 
    
         
            +
                def staged?
         
     | 
| 
      
 180 
     | 
    
         
            +
                  ! @queued_for_write.empty?
         
     | 
| 
      
 181 
     | 
    
         
            +
                end
         
     | 
| 
      
 182 
     | 
    
         
            +
             
     | 
| 
       169 
183 
     | 
    
         
             
                # Alias to +url+
         
     | 
| 
       170 
184 
     | 
    
         
             
                def to_s style_name = default_style
         
     | 
| 
       171 
185 
     | 
    
         
             
                  url(style_name)
         
     | 
| 
         @@ -373,6 +387,16 @@ module Paperclip 
     | 
|
| 
       373 
387 
     | 
    
         
             
                  @options[:path].respond_to?(:call) ? @options[:path].call(self) : @options[:path]
         
     | 
| 
       374 
388 
     | 
    
         
             
                end
         
     | 
| 
       375 
389 
     | 
    
         | 
| 
      
 390 
     | 
    
         
            +
                def active_validator_classes
         
     | 
| 
      
 391 
     | 
    
         
            +
                  @instance.class.validators.map(&:class)
         
     | 
| 
      
 392 
     | 
    
         
            +
                end
         
     | 
| 
      
 393 
     | 
    
         
            +
             
     | 
| 
      
 394 
     | 
    
         
            +
                def ensure_required_validations!
         
     | 
| 
      
 395 
     | 
    
         
            +
                  if (active_validator_classes & Paperclip::REQUIRED_VALIDATORS).empty?
         
     | 
| 
      
 396 
     | 
    
         
            +
                    raise Paperclip::Errors::MissingRequiredValidatorError
         
     | 
| 
      
 397 
     | 
    
         
            +
                  end
         
     | 
| 
      
 398 
     | 
    
         
            +
                end
         
     | 
| 
      
 399 
     | 
    
         
            +
             
     | 
| 
       376 
400 
     | 
    
         
             
                def ensure_required_accessors! #:nodoc:
         
     | 
| 
       377 
401 
     | 
    
         
             
                  %w(file_name).each do |field|
         
     | 
| 
       378 
402 
     | 
    
         
             
                    unless @instance.respond_to?("#{name}_#{field}") && @instance.respond_to?("#{name}_#{field}=")
         
     | 
| 
         @@ -482,7 +506,7 @@ module Paperclip 
     | 
|
| 
       482 
506 
     | 
    
         
             
                  end
         
     | 
| 
       483 
507 
     | 
    
         
             
                end
         
     | 
| 
       484 
508 
     | 
    
         | 
| 
       485 
     | 
    
         
            -
                # called by storage after the writes are flushed and before @ 
     | 
| 
      
 509 
     | 
    
         
            +
                # called by storage after the writes are flushed and before @queued_for_write is cleared
         
     | 
| 
       486 
510 
     | 
    
         
             
                def after_flush_writes
         
     | 
| 
       487 
511 
     | 
    
         
             
                  @queued_for_write.each do |style, file|
         
     | 
| 
       488 
512 
     | 
    
         
             
                    file.close unless file.closed?
         
     | 
    
        data/lib/paperclip/callbacks.rb
    CHANGED
    
    | 
         @@ -7,7 +7,7 @@ module Paperclip 
     | 
|
| 
       7 
7 
     | 
    
         | 
| 
       8 
8 
     | 
    
         
             
                module Defining
         
     | 
| 
       9 
9 
     | 
    
         
             
                  def define_paperclip_callbacks(*callbacks)
         
     | 
| 
       10 
     | 
    
         
            -
                    define_callbacks 
     | 
| 
      
 10 
     | 
    
         
            +
                    define_callbacks(*[callbacks, {:terminator => "result == false"}].flatten)
         
     | 
| 
       11 
11 
     | 
    
         
             
                    callbacks.each do |callback|
         
     | 
| 
       12 
12 
     | 
    
         
             
                      eval <<-end_callbacks
         
     | 
| 
       13 
13 
     | 
    
         
             
                        def before_#{callback}(*args, &blk)
         
     | 
| 
         @@ -32,10 +32,6 @@ module Paperclip 
     | 
|
| 
       32 
32 
     | 
    
         
             
                    EMPTY_TYPE
         
     | 
| 
       33 
33 
     | 
    
         
             
                  elsif calculated_type_matches.any?
         
     | 
| 
       34 
34 
     | 
    
         
             
                    calculated_type_matches.first
         
     | 
| 
       35 
     | 
    
         
            -
                  elsif official_type_matches.any?
         
     | 
| 
       36 
     | 
    
         
            -
                    official_type_matches.first
         
     | 
| 
       37 
     | 
    
         
            -
                  elsif unofficial_type_matches.any?
         
     | 
| 
       38 
     | 
    
         
            -
                    unofficial_type_matches.first
         
     | 
| 
       39 
35 
     | 
    
         
             
                  else
         
     | 
| 
       40 
36 
     | 
    
         
             
                    type_from_file_command || SENSIBLE_DEFAULT
         
     | 
| 
       41 
37 
     | 
    
         
             
                  end.to_s
         
     | 
| 
         @@ -46,7 +42,7 @@ module Paperclip 
     | 
|
| 
       46 
42 
     | 
    
         
             
                def empty_file?
         
     | 
| 
       47 
43 
     | 
    
         
             
                  File.exists?(@filename) && File.size(@filename) == 0
         
     | 
| 
       48 
44 
     | 
    
         
             
                end
         
     | 
| 
       49 
     | 
    
         
            -
             
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
       50 
46 
     | 
    
         
             
                alias :empty? :empty_file?
         
     | 
| 
       51 
47 
     | 
    
         | 
| 
       52 
48 
     | 
    
         
             
                def blank_name?
         
     | 
| 
         @@ -61,14 +57,6 @@ module Paperclip 
     | 
|
| 
       61 
57 
     | 
    
         
             
                  possible_types.select{|content_type| content_type == type_from_file_command }
         
     | 
| 
       62 
58 
     | 
    
         
             
                end
         
     | 
| 
       63 
59 
     | 
    
         | 
| 
       64 
     | 
    
         
            -
                def official_type_matches
         
     | 
| 
       65 
     | 
    
         
            -
                  possible_types.reject{|content_type| content_type.match(/\/x-/) }
         
     | 
| 
       66 
     | 
    
         
            -
                end
         
     | 
| 
       67 
     | 
    
         
            -
             
     | 
| 
       68 
     | 
    
         
            -
                def unofficial_type_matches
         
     | 
| 
       69 
     | 
    
         
            -
                  possible_types.select{|content_type| content_type.match(/\/x-/) }
         
     | 
| 
       70 
     | 
    
         
            -
                end
         
     | 
| 
       71 
     | 
    
         
            -
             
     | 
| 
       72 
60 
     | 
    
         
             
                def type_from_file_command
         
     | 
| 
       73 
61 
     | 
    
         
             
                  @type_from_file_command ||= FileCommandContentTypeDetector.new(@filename).detect
         
     | 
| 
       74 
62 
     | 
    
         
             
                end
         
     | 
    
        data/lib/paperclip/errors.rb
    CHANGED
    
    | 
         @@ -13,6 +13,11 @@ module Paperclip 
     | 
|
| 
       13 
13 
     | 
    
         
             
                class CommandNotFoundError < Paperclip::Error
         
     | 
| 
       14 
14 
     | 
    
         
             
                end
         
     | 
| 
       15 
15 
     | 
    
         | 
| 
      
 16 
     | 
    
         
            +
                # Attachments require a content_type or file_name validator,
         
     | 
| 
      
 17 
     | 
    
         
            +
                # or to have explicitly opted out of them.
         
     | 
| 
      
 18 
     | 
    
         
            +
                class MissingRequiredValidatorError < Paperclip::Error
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
       16 
21 
     | 
    
         
             
                # Will be thrown when ImageMagic cannot determine the uploaded file's
         
     | 
| 
       17 
22 
     | 
    
         
             
                # metadata, usually this would mean the file is not an image.
         
     | 
| 
       18 
23 
     | 
    
         
             
                class NotIdentifiedByImageMagickError < Paperclip::Error
         
     | 
| 
         @@ -18,6 +18,7 @@ module Paperclip 
     | 
|
| 
       18 
18 
     | 
    
         
             
                  register_new_attachment
         
     | 
| 
       19 
19 
     | 
    
         
             
                  add_active_record_callbacks
         
     | 
| 
       20 
20 
     | 
    
         
             
                  add_paperclip_callbacks
         
     | 
| 
      
 21 
     | 
    
         
            +
                  add_required_validations
         
     | 
| 
       21 
22 
     | 
    
         
             
                end
         
     | 
| 
       22 
23 
     | 
    
         | 
| 
       23 
24 
     | 
    
         
             
                private
         
     | 
| 
         @@ -77,6 +78,10 @@ module Paperclip 
     | 
|
| 
       77 
78 
     | 
    
         
             
                  Paperclip::AttachmentRegistry.register(@klass, @name, @options)
         
     | 
| 
       78 
79 
     | 
    
         
             
                end
         
     | 
| 
       79 
80 
     | 
    
         | 
| 
      
 81 
     | 
    
         
            +
                def add_required_validations
         
     | 
| 
      
 82 
     | 
    
         
            +
                  @klass.validates_media_type_spoof_detection @name
         
     | 
| 
      
 83 
     | 
    
         
            +
                end
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
       80 
85 
     | 
    
         
             
                def add_active_record_callbacks
         
     | 
| 
       81 
86 
     | 
    
         
             
                  name = @name
         
     | 
| 
       82 
87 
     | 
    
         
             
                  @klass.send(:after_save) { send(name).send(:save) }
         
     | 
| 
         @@ -20,11 +20,11 @@ module Paperclip 
     | 
|
| 
       20 
20 
     | 
    
         
             
                  @size = @tempfile.size || @target.size
         
     | 
| 
       21 
21 
     | 
    
         
             
                end
         
     | 
| 
       22 
22 
     | 
    
         | 
| 
       23 
     | 
    
         
            -
                def copy_to_tempfile( 
     | 
| 
       24 
     | 
    
         
            -
                  if  
     | 
| 
       25 
     | 
    
         
            -
                     
     | 
| 
      
 23 
     | 
    
         
            +
                def copy_to_tempfile(source)
         
     | 
| 
      
 24 
     | 
    
         
            +
                  if source.staged?
         
     | 
| 
      
 25 
     | 
    
         
            +
                    FileUtils.cp(source.staged_path(@style), destination.path)
         
     | 
| 
       26 
26 
     | 
    
         
             
                  else
         
     | 
| 
       27 
     | 
    
         
            -
                     
     | 
| 
      
 27 
     | 
    
         
            +
                    source.copy_to_local_file(@style, destination.path)
         
     | 
| 
       28 
28 
     | 
    
         
             
                  end
         
     | 
| 
       29 
29 
     | 
    
         
             
                  destination
         
     | 
| 
       30 
30 
     | 
    
         
             
                end
         
     | 
| 
         @@ -4,19 +4,14 @@ module Paperclip 
     | 
|
| 
       4 
4 
     | 
    
         
             
                REGEXP = /\Adata:([-\w]+\/[-\w\+]+);base64,(.*)/m
         
     | 
| 
       5 
5 
     | 
    
         | 
| 
       6 
6 
     | 
    
         
             
                def initialize(target_uri)
         
     | 
| 
       7 
     | 
    
         
            -
                   
     | 
| 
       8 
     | 
    
         
            -
                  cache_current_values
         
     | 
| 
       9 
     | 
    
         
            -
                  @tempfile = copy_to_tempfile
         
     | 
| 
      
 7 
     | 
    
         
            +
                  super(extract_target(target_uri))
         
     | 
| 
       10 
8 
     | 
    
         
             
                end
         
     | 
| 
       11 
9 
     | 
    
         | 
| 
       12 
10 
     | 
    
         
             
                private
         
     | 
| 
       13 
11 
     | 
    
         | 
| 
       14 
     | 
    
         
            -
                def  
     | 
| 
       15 
     | 
    
         
            -
                   
     | 
| 
       16 
     | 
    
         
            -
                   
     | 
| 
       17 
     | 
    
         
            -
                  @content_type = data_uri_parts[1] || 'text/plain'
         
     | 
| 
       18 
     | 
    
         
            -
                  @target = StringIO.new(Base64.decode64(data_uri_parts[2] || ''))
         
     | 
| 
       19 
     | 
    
         
            -
                  @size = @target.size
         
     | 
| 
      
 12 
     | 
    
         
            +
                def extract_target(uri)
         
     | 
| 
      
 13 
     | 
    
         
            +
                  data_uri_parts = uri.match(REGEXP) || []
         
     | 
| 
      
 14 
     | 
    
         
            +
                  StringIO.new(Base64.decode64(data_uri_parts[2] || ''))
         
     | 
| 
       20 
15 
     | 
    
         
             
                end
         
     | 
| 
       21 
16 
     | 
    
         | 
| 
       22 
17 
     | 
    
         
             
              end
         
     | 
| 
         @@ -2,8 +2,8 @@ module Paperclip 
     | 
|
| 
       2 
2 
     | 
    
         
             
              class StringioAdapter < AbstractAdapter
         
     | 
| 
       3 
3 
     | 
    
         
             
                def initialize(target)
         
     | 
| 
       4 
4 
     | 
    
         
             
                  @target = target
         
     | 
| 
       5 
     | 
    
         
            -
                  cache_current_values
         
     | 
| 
       6 
5 
     | 
    
         
             
                  @tempfile = copy_to_tempfile
         
     | 
| 
      
 6 
     | 
    
         
            +
                  cache_current_values
         
     | 
| 
       7 
7 
     | 
    
         
             
                end
         
     | 
| 
       8 
8 
     | 
    
         | 
| 
       9 
9 
     | 
    
         
             
                attr_writer :content_type
         
     | 
| 
         @@ -11,13 +11,10 @@ module Paperclip 
     | 
|
| 
       11 
11 
     | 
    
         
             
                private
         
     | 
| 
       12 
12 
     | 
    
         | 
| 
       13 
13 
     | 
    
         
             
                def cache_current_values
         
     | 
| 
       14 
     | 
    
         
            -
                  @ 
     | 
| 
       15 
     | 
    
         
            -
                  @original_filename  
     | 
| 
       16 
     | 
    
         
            -
                   
     | 
| 
       17 
     | 
    
         
            -
             
     | 
| 
       18 
     | 
    
         
            -
                  @content_type = @target.content_type if @target.respond_to?(:content_type)
         
     | 
| 
       19 
     | 
    
         
            -
                  @content_type ||= "text/plain"
         
     | 
| 
       20 
     | 
    
         
            -
             
     | 
| 
      
 14 
     | 
    
         
            +
                  @content_type = ContentTypeDetector.new(@tempfile.path).detect
         
     | 
| 
      
 15 
     | 
    
         
            +
                  original_filename = @target.original_filename if @target.respond_to?(:original_filename)
         
     | 
| 
      
 16 
     | 
    
         
            +
                  original_filename ||= "data.#{extension_for(@content_type)}"
         
     | 
| 
      
 17 
     | 
    
         
            +
                  self.original_filename = original_filename.strip
         
     | 
| 
       21 
18 
     | 
    
         
             
                  @size = @target.size
         
     | 
| 
       22 
19 
     | 
    
         
             
                end
         
     | 
| 
       23 
20 
     | 
    
         | 
| 
         @@ -29,6 +26,11 @@ module Paperclip 
     | 
|
| 
       29 
26 
     | 
    
         
             
                  destination
         
     | 
| 
       30 
27 
     | 
    
         
             
                end
         
     | 
| 
       31 
28 
     | 
    
         | 
| 
      
 29 
     | 
    
         
            +
                def extension_for(content_type)
         
     | 
| 
      
 30 
     | 
    
         
            +
                  type = MIME::Types[content_type].first
         
     | 
| 
      
 31 
     | 
    
         
            +
                  type && type.extensions.first
         
     | 
| 
      
 32 
     | 
    
         
            +
                end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
       32 
34 
     | 
    
         
             
              end
         
     | 
| 
       33 
35 
     | 
    
         
             
            end
         
     | 
| 
       34 
36 
     | 
    
         | 
| 
         @@ -0,0 +1,36 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Paperclip
         
     | 
| 
      
 2 
     | 
    
         
            +
              class MediaTypeSpoofDetector
         
     | 
| 
      
 3 
     | 
    
         
            +
                def self.using(file, name)
         
     | 
| 
      
 4 
     | 
    
         
            +
                  new(file, name)
         
     | 
| 
      
 5 
     | 
    
         
            +
                end
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                def initialize(file, name)
         
     | 
| 
      
 8 
     | 
    
         
            +
                  @file = file
         
     | 
| 
      
 9 
     | 
    
         
            +
                  @name = name
         
     | 
| 
      
 10 
     | 
    
         
            +
                end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                def spoofed?
         
     | 
| 
      
 13 
     | 
    
         
            +
                  if ! @name.blank?
         
     | 
| 
      
 14 
     | 
    
         
            +
                    ! supplied_file_media_type.include?(calculated_media_type)
         
     | 
| 
      
 15 
     | 
    
         
            +
                  end
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                private
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                def supplied_file_media_type
         
     | 
| 
      
 21 
     | 
    
         
            +
                  MIME::Types.type_for(@name).collect(&:media_type)
         
     | 
| 
      
 22 
     | 
    
         
            +
                end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                def calculated_media_type
         
     | 
| 
      
 25 
     | 
    
         
            +
                  type_from_file_command.split("/").first
         
     | 
| 
      
 26 
     | 
    
         
            +
                end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                def type_from_file_command
         
     | 
| 
      
 29 
     | 
    
         
            +
                  begin
         
     | 
| 
      
 30 
     | 
    
         
            +
                    Paperclip.run("file", "-b --mime-type :file", :file => @file.path)
         
     | 
| 
      
 31 
     | 
    
         
            +
                  rescue Cocaine::CommandLineError
         
     | 
| 
      
 32 
     | 
    
         
            +
                    ""
         
     | 
| 
      
 33 
     | 
    
         
            +
                  end
         
     | 
| 
      
 34 
     | 
    
         
            +
                end
         
     | 
| 
      
 35 
     | 
    
         
            +
              end
         
     | 
| 
      
 36 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            module Paperclip
         
     | 
| 
       2 
2 
     | 
    
         
             
              class TempfileFactory
         
     | 
| 
       3 
3 
     | 
    
         | 
| 
       4 
     | 
    
         
            -
                def generate(name)
         
     | 
| 
      
 4 
     | 
    
         
            +
                def generate(name = random_name)
         
     | 
| 
       5 
5 
     | 
    
         
             
                  @name = name
         
     | 
| 
       6 
6 
     | 
    
         
             
                  file = Tempfile.new([basename, extension])
         
     | 
| 
       7 
7 
     | 
    
         
             
                  file.binmode
         
     | 
| 
         @@ -15,5 +15,9 @@ module Paperclip 
     | 
|
| 
       15 
15 
     | 
    
         
             
                def basename
         
     | 
| 
       16 
16 
     | 
    
         
             
                  Digest::MD5.hexdigest(File.basename(@name, extension))
         
     | 
| 
       17 
17 
     | 
    
         
             
                end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                def random_name
         
     | 
| 
      
 20 
     | 
    
         
            +
                  SecureRandom.uuid
         
     | 
| 
      
 21 
     | 
    
         
            +
                end
         
     | 
| 
       18 
22 
     | 
    
         
             
              end
         
     | 
| 
       19 
23 
     | 
    
         
             
            end
         
     |