1
0

remotable.rb 2.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485
  1. # frozen_string_literal: true
  2. module Remotable
  3. extend ActiveSupport::Concern
  4. class_methods do
  5. def remotable_attachment(attachment_name, limit, suppress_errors: true)
  6. attribute_name = "#{attachment_name}_remote_url".to_sym
  7. method_name = "#{attribute_name}=".to_sym
  8. alt_method_name = "reset_#{attachment_name}!".to_sym
  9. define_method method_name do |url|
  10. return if url.blank?
  11. begin
  12. parsed_url = Addressable::URI.parse(url).normalize
  13. rescue Addressable::URI::InvalidURIError
  14. return
  15. end
  16. return if !%w(http https).include?(parsed_url.scheme) || parsed_url.host.blank? || (self[attribute_name] == url && send("#{attachment_name}_file_name").present?)
  17. begin
  18. Request.new(:get, url).perform do |response|
  19. raise Mastodon::UnexpectedResponseError, response unless (200...300).cover?(response.code)
  20. content_type = parse_content_type(response.headers.get('content-type').last)
  21. extname = detect_extname_from_content_type(content_type)
  22. if extname.nil?
  23. disposition = response.headers.get('content-disposition').last
  24. matches = disposition&.match(/filename="([^"]*)"/)
  25. filename = matches.nil? ? parsed_url.path.split('/').last : matches[1]
  26. extname = filename.nil? ? '' : File.extname(filename)
  27. end
  28. basename = SecureRandom.hex(8)
  29. send("#{attachment_name}_file_name=", basename + extname)
  30. send("#{attachment_name}=", StringIO.new(response.body_with_limit(limit)))
  31. self[attribute_name] = url if has_attribute?(attribute_name)
  32. end
  33. rescue Mastodon::UnexpectedResponseError, HTTP::TimeoutError, HTTP::ConnectionError, OpenSSL::SSL::SSLError => e
  34. Rails.logger.debug "Error fetching remote #{attachment_name}: #{e}"
  35. raise e unless suppress_errors
  36. rescue Paperclip::Errors::NotIdentifiedByImageMagickError, Addressable::URI::InvalidURIError, Mastodon::HostValidationError, Mastodon::LengthValidationError, Paperclip::Error, Mastodon::DimensionsValidationError => e
  37. Rails.logger.debug "Error fetching remote #{attachment_name}: #{e}"
  38. nil
  39. end
  40. end
  41. define_method alt_method_name do
  42. url = self[attribute_name]
  43. return if url.blank?
  44. self[attribute_name] = ''
  45. send(method_name, url)
  46. end
  47. end
  48. end
  49. private
  50. def detect_extname_from_content_type(content_type)
  51. return if content_type.nil?
  52. type = MIME::Types[content_type].first
  53. return if type.nil?
  54. extname = type.extensions.first
  55. return if extname.nil?
  56. ".#{extname}"
  57. end
  58. def parse_content_type(content_type)
  59. return if content_type.nil?
  60. content_type.split(/\s*;\s*/).first
  61. end
  62. end