lib/carrierwave/uploader/download.rb in carrierwave-1.3.4 vs lib/carrierwave/uploader/download.rb in carrierwave-2.0.0.rc

- old
+ new

@@ -1,145 +1,24 @@ -require 'open-uri' -require 'ssrf_filter' - module CarrierWave module Uploader module Download extend ActiveSupport::Concern include CarrierWave::Uploader::Callbacks include CarrierWave::Uploader::Configuration include CarrierWave::Uploader::Cache - class RemoteFile - attr_reader :uri - - def initialize(uri, remote_headers = {}, skip_ssrf_protection: false) - @uri = uri - @remote_headers = remote_headers.reverse_merge('User-Agent' => "CarrierWave/#{CarrierWave::VERSION}") - @file, @content_type, @headers = nil - @skip_ssrf_protection = skip_ssrf_protection - end - - def original_filename - filename = filename_from_header || filename_from_uri - mime_type = MIME::Types[content_type].first - unless File.extname(filename).present? || mime_type.blank? - filename = "#{filename}.#{mime_type.extensions.first}" - end - filename - end - - def respond_to?(*args) - super or file.respond_to?(*args) - end - - def http? - @uri.scheme =~ /^https?$/ - end - - def content_type - @content_type || 'application/octet-stream' - end - - def headers - @headers || {} - end - - private - - def file - if @file.blank? - if @skip_ssrf_protection - @file = (URI.respond_to?(:open) ? URI : Kernel).open(@uri.to_s, @remote_headers) - @file = @file.is_a?(String) ? StringIO.new(@file) : @file - @content_type = @file.content_type - @headers = @file.meta - @uri = @file.base_uri - else - request = nil - response = SsrfFilter.get(@uri, headers: @remote_headers) do |req| - request = req - end - response.value - @file = StringIO.new(response.body) - @content_type = response.content_type - @headers = response - @uri = request.uri - end - end - @file - - rescue StandardError => e - raise CarrierWave::DownloadError, "could not download file: #{e.message}" - end - - def filename_from_header - if headers['content-disposition'] - match = headers['content-disposition'].match(/filename="?([^"]+)/) - return match[1] unless match.nil? || match[1].empty? - end - end - - def filename_from_uri - URI::DEFAULT_PARSER.unescape(File.basename(@uri.path)) - end - - def method_missing(*args, &block) - file.send(*args, &block) - end - end - ## - # Caches the file by downloading it from the given URL. + # Caches the file by downloading it from the given URL, using downloader. # # === Parameters # # [url (String)] The URL where the remote file is stored # [remote_headers (Hash)] Request headers # def download!(uri, remote_headers = {}) - processed_uri = process_uri(uri) - file = RemoteFile.new(processed_uri, remote_headers, skip_ssrf_protection: skip_ssrf_protection?(processed_uri)) - raise CarrierWave::DownloadError, "trying to download a file which is not served over HTTP" unless file.http? + file = downloader.new(self).download(uri, remote_headers) cache!(file) - end - - ## - # Processes the given URL by parsing and escaping it. Public to allow overriding. - # - # === Parameters - # - # [url (String)] The URL where the remote file is stored - # - def process_uri(uri) - URI.parse(uri) - rescue URI::InvalidURIError - uri_parts = uri.split('?') - # regexp from Ruby's URI::Parser#regexp[:UNSAFE], with [] specifically removed - encoded_uri = URI::DEFAULT_PARSER.escape(uri_parts.shift, /[^\-_.!~*'()a-zA-Z\d;\/?:@&=+$,]/) - encoded_uri << '?' << URI::DEFAULT_PARSER.escape(uri_parts.join('?')) if uri_parts.any? - URI.parse(encoded_uri) rescue raise CarrierWave::DownloadError, "couldn't parse URL" - end - - ## - # If this returns true, SSRF protection will be bypassed. - # You can override this if you want to allow accessing specific local URIs that are not SSRF exploitable. - # - # === Parameters - # - # [uri (URI)] The URI where the remote file is stored - # - # === Examples - # - # class MyUploader < CarrierWave::Uploader::Base - # def skip_ssrf_protection?(uri) - # uri.hostname == 'localhost' && uri.port == 80 - # end - # end - # - def skip_ssrf_protection?(uri) - false end end # Download end # Uploader end # CarrierWave