about summary refs log tree commit diff
path: root/app/services
diff options
context:
space:
mode:
Diffstat (limited to 'app/services')
-rw-r--r--app/services/activitypub/fetch_remote_account_service.rb7
-rw-r--r--app/services/activitypub/prepare_followers_synchronization_service.rb13
-rw-r--r--app/services/activitypub/synchronize_followers_service.rb74
-rw-r--r--app/services/app_sign_up_service.rb4
-rw-r--r--app/services/mute_service.rb6
-rw-r--r--app/services/process_mentions_service.rb2
-rw-r--r--app/services/resolve_account_service.rb9
7 files changed, 101 insertions, 14 deletions
diff --git a/app/services/activitypub/fetch_remote_account_service.rb b/app/services/activitypub/fetch_remote_account_service.rb
index 83fbf6d07..e5bd0c47c 100644
--- a/app/services/activitypub/fetch_remote_account_service.rb
+++ b/app/services/activitypub/fetch_remote_account_service.rb
@@ -39,17 +39,16 @@ class ActivityPub::FetchRemoteAccountService < BaseService
     webfinger                            = webfinger!("acct:#{@username}@#{@domain}")
     confirmed_username, confirmed_domain = split_acct(webfinger.subject)
 
-    return webfinger.link('self')&.href == @uri if @username.casecmp(confirmed_username).zero? && @domain.casecmp(confirmed_domain).zero?
+    return webfinger.link('self', 'href') == @uri if @username.casecmp(confirmed_username).zero? && @domain.casecmp(confirmed_domain).zero?
 
     webfinger                            = webfinger!("acct:#{confirmed_username}@#{confirmed_domain}")
     @username, @domain                   = split_acct(webfinger.subject)
-    self_reference                       = webfinger.link('self')
 
     return false unless @username.casecmp(confirmed_username).zero? && @domain.casecmp(confirmed_domain).zero?
-    return false if self_reference&.href != @uri
+    return false if webfinger.link('self', 'href') != @uri
 
     true
-  rescue Goldfinger::Error
+  rescue Webfinger::Error
     false
   end
 
diff --git a/app/services/activitypub/prepare_followers_synchronization_service.rb b/app/services/activitypub/prepare_followers_synchronization_service.rb
new file mode 100644
index 000000000..2d22ed701
--- /dev/null
+++ b/app/services/activitypub/prepare_followers_synchronization_service.rb
@@ -0,0 +1,13 @@
+# frozen_string_literal: true
+
+class ActivityPub::PrepareFollowersSynchronizationService < BaseService
+  include JsonLdHelper
+
+  def call(account, params)
+    @account = account
+
+    return if params['collectionId'] != @account.followers_url || invalid_origin?(params['url']) || @account.local_followers_hash == params['digest']
+
+    ActivityPub::FollowersSynchronizationWorker.perform_async(@account.id, params['url'])
+  end
+end
diff --git a/app/services/activitypub/synchronize_followers_service.rb b/app/services/activitypub/synchronize_followers_service.rb
new file mode 100644
index 000000000..d83fcf55e
--- /dev/null
+++ b/app/services/activitypub/synchronize_followers_service.rb
@@ -0,0 +1,74 @@
+# frozen_string_literal: true
+
+class ActivityPub::SynchronizeFollowersService < BaseService
+  include JsonLdHelper
+  include Payloadable
+
+  def call(account, partial_collection_url)
+    @account = account
+
+    items = collection_items(partial_collection_url)
+    return if items.nil?
+
+    # There could be unresolved accounts (hence the call to .compact) but this
+    # should never happen in practice, since in almost all cases we keep an
+    # Account record, and should we not do that, we should have sent a Delete.
+    # In any case there is not much we can do if that occurs.
+    @expected_followers = items.map { |uri| ActivityPub::TagManager.instance.uri_to_resource(uri, Account) }.compact
+
+    remove_unexpected_local_followers!
+    handle_unexpected_outgoing_follows!
+  end
+
+  private
+
+  def remove_unexpected_local_followers!
+    @account.followers.local.where.not(id: @expected_followers.map(&:id)).each do |unexpected_follower|
+      UnfollowService.new.call(unexpected_follower, @account)
+    end
+  end
+
+  def handle_unexpected_outgoing_follows!
+    @expected_followers.each do |expected_follower|
+      next if expected_follower.following?(@account)
+
+      if expected_follower.requested?(@account)
+        # For some reason the follow request went through but we missed it
+        expected_follower.follow_requests.find_by(target_account: @account)&.authorize!
+      else
+        # Since we were not aware of the follow from our side, we do not have an
+        # ID for it that we can include in the Undo activity. For this reason,
+        # the Undo may not work with software that relies exclusively on
+        # matching activity IDs and not the actor and target
+        follow = Follow.new(account: expected_follower, target_account: @account)
+        ActivityPub::DeliveryWorker.perform_async(build_undo_follow_json(follow), follow.account_id, follow.target_account.inbox_url)
+      end
+    end
+  end
+
+  def build_undo_follow_json(follow)
+    Oj.dump(serialize_payload(follow, ActivityPub::UndoFollowSerializer))
+  end
+
+  def collection_items(collection_or_uri)
+    collection = fetch_collection(collection_or_uri)
+    return unless collection.is_a?(Hash)
+
+    collection = fetch_collection(collection['first']) if collection['first'].present?
+    return unless collection.is_a?(Hash)
+
+    case collection['type']
+    when 'Collection', 'CollectionPage'
+      collection['items']
+    when 'OrderedCollection', 'OrderedCollectionPage'
+      collection['orderedItems']
+    end
+  end
+
+  def fetch_collection(collection_or_uri)
+    return collection_or_uri if collection_or_uri.is_a?(Hash)
+    return if invalid_origin?(collection_or_uri)
+
+    fetch_resource_without_id_validation(collection_or_uri, nil, true)
+  end
+end
diff --git a/app/services/app_sign_up_service.rb b/app/services/app_sign_up_service.rb
index c9739c77d..e00694157 100644
--- a/app/services/app_sign_up_service.rb
+++ b/app/services/app_sign_up_service.rb
@@ -1,13 +1,13 @@
 # frozen_string_literal: true
 
 class AppSignUpService < BaseService
-  def call(app, params)
+  def call(app, remote_ip, params)
     return unless allowed_registrations?
 
     user_params           = params.slice(:email, :password, :agreement, :locale)
     account_params        = params.slice(:username)
     invite_request_params = { text: params[:reason] }
-    user                  = User.create!(user_params.merge(created_by_application: app, password_confirmation: user_params[:password], account_attributes: account_params, invite_request_attributes: invite_request_params))
+    user                  = User.create!(user_params.merge(created_by_application: app, sign_up_ip: remote_ip, password_confirmation: user_params[:password], account_attributes: account_params, invite_request_attributes: invite_request_params))
 
     Doorkeeper::AccessToken.create!(application: app,
                                     resource_owner_id: user.id,
diff --git a/app/services/mute_service.rb b/app/services/mute_service.rb
index 676804cb9..9ae9afd62 100644
--- a/app/services/mute_service.rb
+++ b/app/services/mute_service.rb
@@ -1,10 +1,10 @@
 # frozen_string_literal: true
 
 class MuteService < BaseService
-  def call(account, target_account, notifications: nil)
+  def call(account, target_account, notifications: nil, duration: 0)
     return if account.id == target_account.id
 
-    mute = account.mute!(target_account, notifications: notifications)
+    mute = account.mute!(target_account, notifications: notifications, duration: duration)
 
     if mute.hide_notifications?
       BlockWorker.perform_async(account.id, target_account.id)
@@ -12,6 +12,8 @@ class MuteService < BaseService
       MuteWorker.perform_async(account.id, target_account.id)
     end
 
+    DeleteMuteWorker.perform_at(duration.seconds, mute.id) if duration != 0
+
     mute
   end
 end
diff --git a/app/services/process_mentions_service.rb b/app/services/process_mentions_service.rb
index feffb872b..d5ea69da1 100644
--- a/app/services/process_mentions_service.rb
+++ b/app/services/process_mentions_service.rb
@@ -29,7 +29,7 @@ class ProcessMentionsService < BaseService
       if mention_undeliverable?(mentioned_account)
         begin
           mentioned_account = resolve_account_service.call(Regexp.last_match(1))
-        rescue Goldfinger::Error, HTTP::Error, OpenSSL::SSL::SSLError, Mastodon::UnexpectedResponseError
+        rescue Webfinger::Error, HTTP::Error, OpenSSL::SSL::SSLError, Mastodon::UnexpectedResponseError
           mentioned_account = nil
         end
       end
diff --git a/app/services/resolve_account_service.rb b/app/services/resolve_account_service.rb
index ba77552c6..3f7bb7cc5 100644
--- a/app/services/resolve_account_service.rb
+++ b/app/services/resolve_account_service.rb
@@ -26,11 +26,10 @@ class ResolveAccountService < BaseService
 
     @account ||= Account.find_remote(@username, @domain)
 
-    return @account if @account&.local? || !webfinger_update_due?
+    return @account if @account&.local? || @domain.nil? || !webfinger_update_due?
 
     # At this point we are in need of a Webfinger query, which may
     # yield us a different username/domain through a redirect
-
     process_webfinger!(@uri)
 
     # Because the username/domain pair may be different than what
@@ -47,7 +46,7 @@ class ResolveAccountService < BaseService
     # either needs to be created, or updated from fresh data
 
     process_account!
-  rescue Goldfinger::Error, WebfingerRedirectError, Oj::ParseError => e
+  rescue Webfinger::Error, WebfingerRedirectError, Oj::ParseError => e
     Rails.logger.debug "Webfinger query for #{@uri} failed: #{e}"
     nil
   end
@@ -118,11 +117,11 @@ class ResolveAccountService < BaseService
   end
 
   def activitypub_ready?
-    !@webfinger.link('self').nil? && ['application/activity+json', 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'].include?(@webfinger.link('self').type)
+    ['application/activity+json', 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'].include?(@webfinger.link('self', 'type'))
   end
 
   def actor_url
-    @actor_url ||= @webfinger.link('self').href
+    @actor_url ||= @webfinger.link('self', 'href')
   end
 
   def actor_json