From 2f284f427427302df39b53beb0a6a7d8f3df82b2 Mon Sep 17 00:00:00 2001 From: Diogo Cordeiro Date: Sun, 5 Jul 2020 02:25:51 +0100 Subject: [PATCH] [ActivityPub][INBOX][Delete] Fix misconceptions References: - https://socialhub.activitypub.rocks/t/the-delete-activity-and-its-misconceptions/137 - https://socialhub.activitypub.rocks/t/the-update-activity-more-than-caching/260 --- .../classes/Activitypub_profile.php | 14 ++- plugins/ActivityPub/lib/explorer.php | 8 +- plugins/ActivityPub/lib/inbox_handler.php | 115 ++++++++++-------- .../scripts/update_activitypub_profiles.php | 6 +- 4 files changed, 85 insertions(+), 58 deletions(-) diff --git a/plugins/ActivityPub/classes/Activitypub_profile.php b/plugins/ActivityPub/classes/Activitypub_profile.php index 8f0d79021e..9309453681 100644 --- a/plugins/ActivityPub/classes/Activitypub_profile.php +++ b/plugins/ActivityPub/classes/Activitypub_profile.php @@ -189,11 +189,11 @@ class Activitypub_profile extends Managed_DataObject /** * Fetch the locally stored profile for this Activitypub_profile * - * @return get_called_class + * @return Profile * @throws NoProfileException if it was not found * @author Diogo Cordeiro */ - public function local_profile() + public function local_profile(): Profile { $profile = Profile::getKV('id', $this->profile_id); if (!$profile instanceof Profile) { @@ -481,16 +481,22 @@ class Activitypub_profile extends Managed_DataObject /** * Update remote user profile in local instance - * Depends on do_update * * @param Activitypub_profile $aprofile - * @param array $res remote response + * @param array|false $res remote response, if array it updates, if false it deletes * @return Profile remote Profile object * @throws NoProfileException * @author Diogo Cordeiro */ public static function update_profile($aprofile, $res) { + if ($res === false) { + $profile = $aprofile->local_profile(); + $id = $profile->getID(); + $profile->delete(); + throw new NoProfileException($id, "410 Gone"); + } + // ActivityPub Profile $aprofile->uri = $res['id']; $aprofile->nickname = $res['preferredUsername']; diff --git a/plugins/ActivityPub/lib/explorer.php b/plugins/ActivityPub/lib/explorer.php index 916c54cddb..c8b9e81f0d 100644 --- a/plugins/ActivityPub/lib/explorer.php +++ b/plugins/ActivityPub/lib/explorer.php @@ -431,14 +431,18 @@ class Activitypub_explorer * profile updating and shall not be used for anything else) * * @param string $url User's url - * @return array + * @return array|false If it is able to fetch, false if it's gone * @throws Exception Either network issues or unsupported Activity format * @author Diogo Cordeiro */ - public static function get_remote_user_activity($url) + public static function get_remote_user_activity(string $url) { $client = new HTTPClient(); $response = $client->get($url, ACTIVITYPUB_HTTP_CLIENT_HEADERS); + // If it was deleted + if ($response->getStatus() == 410) { + return false; + } $res = json_decode($response->getBody(), true); if (Activitypub_explorer::validate_remote_response($res)) { common_debug('ActivityPub Explorer: Found a valid remote actor for ' . $url); diff --git a/plugins/ActivityPub/lib/inbox_handler.php b/plugins/ActivityPub/lib/inbox_handler.php index f04c2aec60..a7862666de 100644 --- a/plugins/ActivityPub/lib/inbox_handler.php +++ b/plugins/ActivityPub/lib/inbox_handler.php @@ -232,64 +232,77 @@ class Activitypub_inbox_handler * @throws NoProfileException * @throws Exception * @author Bruno Casteleiro + * @author Diogo Cordeiro */ private function handle_delete() { $object = $this->object; - if (is_array($object)) { - $object = $object['id']; - } - - // profile deletion ? - if ($this->activity['actor'] == $object) { - $aprofile = Activitypub_profile::from_profile($this->actor); - $this->handle_delete_profile($aprofile); - return; - } - - // note deletion ? - try { - $notice = ActivityPubPlugin::grab_notice_from_url($object, false); - if ($notice instanceof Notice) { - $this->handle_delete_note($notice); + if (is_string($object)) { + $client = new HTTPClient(); + $response = $client->get($object, ACTIVITYPUB_HTTP_CLIENT_HEADERS); + $gone = !$response->isOk(); + if (!$gone) { // It's not gone, we're updating it. + $object = json_decode($response->getBody(), true); + switch ($object['type']) { + case 'Person': + try { + // Update profile if we already have a copy of it + $aprofile = Activitypub_profile::fromUri($object['id'], false); + Activitypub_profile::update_profile($aprofile, $object); + } catch (Exception $e) { + // Import profile if we don't + Activitypub_explorer::get_profile_from_url($object['id']); + } + break; + case 'Tombstone': + try { + $notice = ActivityPubPlugin::grab_notice_from_url($object, false); + if ($notice instanceof Notice) { + $notice->delete(); + } + return; + } catch (Exception $e) { + // either already deleted or not an object at all + // nothing to do.. + } + break; + case 'Note': + // XXX: We do not support updating a note's contents so, we'll ignore it for now... + default: + common_log(LOG_INFO, "Ignoring Delete activity, we do not understand for {$object['type']}."); + } } + } else { + // We don't know the type of the deleted object :( + // Nor if it's gone or not. + try { + $aprofile = Activitypub_profile::fromUri($object, false); + $res = Activitypub_explorer::get_remote_user_activity($object); + Activitypub_profile::update_profile($aprofile, $res); + return; + } catch (Exception $e) { + // Means this wasn't a profile + } + + try { + $client = new HTTPClient(); + $response = $client->get($object, ACTIVITYPUB_HTTP_CLIENT_HEADERS); + // If it was deleted + if ($response->getStatus() == 410) { + $notice = ActivityPubPlugin::grab_notice_from_url($object, false); + if ($notice instanceof Notice) { + $notice->delete(); + } + } else { + // We can't update a note's contents so, we'll ignore it for now... + } + return; + } catch (Exception $e) { + // Means we didn't have this note already + } + return; - } catch (Exception $e) { - // either already deleted or not an object at all - // nothing to do.. } - - common_log(LOG_INFO, "Ignoring Delete activity, nothing that we can/need to handle."); - } - - /** - * Handles a Delete-Profile Activity. - * - * Note that the actual ap_profile is deleted during the ProfileDeleteRelated event, - * subscribed by ActivityPubPlugin. - * - * @param Activitypub_profile $aprofile remote user being deleted - * @return void - * @throws NoProfileException - * @author Bruno Casteleiro - */ - private function handle_delete_profile(Activitypub_profile $aprofile): void - { - $profile = $aprofile->local_profile(); - $profile->delete(); - } - - /** - * Handles a Delete-Note Activity. - * - * @param Notice $note remote note being deleted - * @return void - * @throws AuthorizationException - * @author Bruno Casteleiro - */ - private function handle_delete_note(Notice $note): void - { - $note->deleteAs($this->actor); } /** diff --git a/plugins/ActivityPub/scripts/update_activitypub_profiles.php b/plugins/ActivityPub/scripts/update_activitypub_profiles.php index 47bbb1f588..59c83f00d3 100755 --- a/plugins/ActivityPub/scripts/update_activitypub_profiles.php +++ b/plugins/ActivityPub/scripts/update_activitypub_profiles.php @@ -97,7 +97,11 @@ while ($user->fetch()) { $res = Activitypub_explorer::get_remote_user_activity($user->uri); $updated_profile = Activitypub_profile::update_profile($user, $res); if (!$quiet) { - echo "Updated ".$updated_profile->getBestName()."\n"; + echo 'Updated '.$updated_profile->getBestName()."\n"; + } + } catch (NoProfileException $e) { + if (!$quiet) { + echo 'Deleted '.$user->uri."\n"; } } catch (Exception $e) { // let it go