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