resolve_url_service.rb 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. # frozen_string_literal: true
  2. class ResolveURLService < BaseService
  3. include JsonLdHelper
  4. include Authorization
  5. USERNAME_STATUS_RE = %r{/@(?<username>#{Account::USERNAME_RE})/(?<status_id>[0-9]+)\Z}
  6. def call(url, on_behalf_of: nil)
  7. @url = url
  8. @on_behalf_of = on_behalf_of
  9. if local_url?
  10. process_local_url
  11. elsif !fetched_resource.nil?
  12. process_url
  13. else
  14. process_url_from_db
  15. end
  16. end
  17. private
  18. def process_url
  19. if equals_or_includes_any?(type, ActivityPub::FetchRemoteActorService::SUPPORTED_TYPES)
  20. ActivityPub::FetchRemoteActorService.new.call(resource_url, prefetched_body: body)
  21. elsif equals_or_includes_any?(type, ActivityPub::Activity::Create::SUPPORTED_TYPES + ActivityPub::Activity::Create::CONVERTED_TYPES)
  22. status = FetchRemoteStatusService.new.call(resource_url, prefetched_body: body)
  23. authorize_with @on_behalf_of, status, :show? unless status.nil?
  24. status
  25. end
  26. end
  27. def process_url_from_db
  28. if [500, 502, 503, 504, nil].include?(fetch_resource_service.response_code)
  29. account = Account.find_by(uri: @url)
  30. return account unless account.nil?
  31. end
  32. return unless @on_behalf_of.present? && [401, 403, 404].include?(fetch_resource_service.response_code)
  33. # It may happen that the resource is a private toot, and thus not fetchable,
  34. # but we can return the toot if we already know about it.
  35. scope = Status.where(uri: @url)
  36. # We don't have an index on `url`, so try guessing the `uri` from `url`
  37. parsed_url = Addressable::URI.parse(@url)
  38. parsed_url.path.match(USERNAME_STATUS_RE) do |matched|
  39. parsed_url.path = "/users/#{matched[:username]}/statuses/#{matched[:status_id]}"
  40. scope = scope.or(Status.where(uri: parsed_url.to_s, url: @url))
  41. end
  42. status = scope.first
  43. authorize_with @on_behalf_of, status, :show? unless status.nil?
  44. status
  45. rescue Mastodon::NotPermittedError
  46. nil
  47. end
  48. def fetched_resource
  49. @fetched_resource ||= fetch_resource_service.call(@url)
  50. end
  51. def fetch_resource_service
  52. @_fetch_resource_service ||= FetchResourceService.new
  53. end
  54. def resource_url
  55. fetched_resource.first
  56. end
  57. def body
  58. fetched_resource.second[:prefetched_body]
  59. end
  60. def type
  61. json_data['type']
  62. end
  63. def json_data
  64. @json_data ||= body_to_json(body)
  65. end
  66. def local_url?
  67. TagManager.instance.local_url?(@url)
  68. end
  69. def process_local_url
  70. recognized_params = Rails.application.routes.recognize_path(@url)
  71. return unless recognized_params[:action] == 'show'
  72. if recognized_params[:controller] == 'statuses'
  73. status = Status.find_by(id: recognized_params[:id])
  74. check_local_status(status)
  75. elsif recognized_params[:controller] == 'accounts'
  76. Account.find_local(recognized_params[:username])
  77. end
  78. end
  79. def check_local_status(status)
  80. return if status.nil?
  81. authorize_with @on_behalf_of, status, :show?
  82. status
  83. rescue Mastodon::NotPermittedError
  84. nil
  85. end
  86. end