From aa491271f75e1e59131b4c1c6927d3f0db2c63f0 Mon Sep 17 00:00:00 2001 From: Diogo Cordeiro Date: Thu, 2 Aug 2018 18:02:28 +0100 Subject: [PATCH] Fixed various bugs, improved and refactored some stuff (remote avatars are working) --- ActivityPubPlugin.php | 26 ++++++----- actions/apactorinbox.php | 72 +++++++++++++---------------- actions/apnotice.php | 0 actions/apsharedinbox.php | 20 +++----- actions/inbox/Accept.php | 12 ++--- actions/inbox/Announce.php | 2 +- actions/inbox/Create.php | 24 +++++----- actions/inbox/Delete.php | 2 +- actions/inbox/Follow.php | 10 ++-- actions/inbox/Like.php | 2 +- actions/inbox/Undo.php | 12 ++--- classes/Activitypub_mention_tag.php | 0 classes/Activitypub_profile.php | 8 ++-- tests/Unit/HTTPSignatureTest.php | 0 utils/explorer.php | 35 ++++++++------ 15 files changed, 109 insertions(+), 116 deletions(-) mode change 100644 => 100755 actions/apnotice.php mode change 100644 => 100755 classes/Activitypub_mention_tag.php mode change 100644 => 100755 tests/Unit/HTTPSignatureTest.php diff --git a/ActivityPubPlugin.php b/ActivityPubPlugin.php index a6cef51..f73b5a2 100755 --- a/ActivityPubPlugin.php +++ b/ActivityPubPlugin.php @@ -39,7 +39,7 @@ require_once __DIR__ . DIRECTORY_SEPARATOR . "utils" . DIRECTORY_SEPARATOR . "ex require_once __DIR__ . DIRECTORY_SEPARATOR . "utils" . DIRECTORY_SEPARATOR . "postman.php"; // So that this isn't hardcoded everywhere -define('ACTIVITYPUB_BASE_INSTANCE_URI', common_root_url().'index.php/user/'); +define('ACTIVITYPUB_BASE_ACTOR_URI', common_root_url().'index.php/user/'); const ACTIVITYPUB_PUBLIC_TO = ['https://www.w3.org/ns/activitystreams#Public', 'Public', 'as:Public' @@ -65,7 +65,7 @@ class ActivityPubPlugin extends Plugin public static function actor_uri($profile) { if ($profile->isLocal()) { - return ACTIVITYPUB_BASE_INSTANCE_URI.$profile->getID(); + return ACTIVITYPUB_BASE_ACTOR_URI.$profile->getID(); } else { return $profile->getUri(); } @@ -254,7 +254,7 @@ class ActivityPubPlugin extends Plugin $out->elementStart('dl', 'entity_tags activitypub_profile'); $out->element('dt', null, _m('ActivityPub')); - $out->element('dd', null, _m('Active')); + $out->element('dd', null, _m('Remote Profile')); $out->elementEnd('dl'); } @@ -513,7 +513,7 @@ class ActivityPubPlugin extends Plugin * @param string $uri in/out * @return mixed hook return code */ - public function onStartGetProfileUri($profile, &$uri) + public function onStartGetProfileUri(Profile $profile, &$uri) { $aprofile = Activitypub_profile::getKV('profile_id', $profile->id); if ($aprofile instanceof Activitypub_profile) { @@ -952,12 +952,18 @@ class ActivityPubReturn * @param array $mimeTypes Supported Types * @return array|null of supported mime types sorted | null if none valid */ - public static function getBestSupportedMimeType($mimeTypes = null) + public static function getBestSupportedMimeType($mimeTypes) { + // XXX: This function needs improvement! if (!isset($_SERVER['HTTP_ACCEPT'])) { return null; } + // This mime type was messing everything, thus the special case + if ($_SERVER['HTTP_ACCEPT'] == 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"') { + return true; + } + // Values will be stored in this array $AcceptTypes = []; @@ -970,7 +976,7 @@ class ActivityPubReturn $q = 1; // check if there is a different quality if (strpos($a, ';q=')) { - // divide "mime/type;q=X" into two parts: "mime/type" i "X" + // divide "mime/type;q=X" into two parts: "mime/type" and "X" list($a, $q) = explode(';q=', $a); } // mime-type $a is accepted with the quality $q @@ -979,12 +985,7 @@ class ActivityPubReturn } arsort($AcceptTypes); - // if no parameter was passed, just return parsed data - if (!$mimeTypes) { - return $AcceptTypes; - } - - $mimeTypes = array_map('strtolower', (array)$mimeTypes); + $mimeTypes = array_map('strtolower', $mimeTypes); // let’s check our supported types: foreach ($AcceptTypes as $mime => $q) { @@ -992,6 +993,7 @@ class ActivityPubReturn return $mime; } } + // no mime-type found return null; } diff --git a/actions/apactorinbox.php b/actions/apactorinbox.php index 315e7af..2d09baf 100755 --- a/actions/apactorinbox.php +++ b/actions/apactorinbox.php @@ -68,62 +68,56 @@ class apActorInboxAction extends ManagedAction common_debug('ActivityPub Inbox: Received a POST request.'); $data = file_get_contents('php://input'); common_debug('ActivityPub Inbox: Request contents: '.$data); - $data = json_decode(file_get_contents('php://input')); + $data = json_decode(file_get_contents('php://input'), true); // Validate data - if (!(isset($data->type))) { + if (!(isset($data['type']))) { ActivityPubReturn::error('Type was not specified.'); } - if (!isset($data->actor)) { + if (!isset($data['actor'])) { ActivityPubReturn::error('Actor was not specified.'); } - if (!isset($data->object)) { + if (!isset($data['object'])) { ActivityPubReturn::error('Object was not specified.'); } // Get valid Actor object try { - $actor_profile = ActivityPub_explorer::get_profile_from_url($data->actor); + $actor_profile = ActivityPub_explorer::get_profile_from_url($data['actor']); } catch (Exception $e) { ActivityPubReturn::error($e->getMessage(), 404); } - // Public To: - $public_to = ['https://www.w3.org/ns/activitystreams#Public', - 'Public', - 'as:Public' - ]; - $to_profiles = [$profile]; // Process request - switch ($data->type) { - case "Create": - require_once __DIR__ . DIRECTORY_SEPARATOR . "inbox" . DIRECTORY_SEPARATOR . "Create.php"; - break; - case "Delete": - require_once __DIR__ . DIRECTORY_SEPARATOR . "inbox" . DIRECTORY_SEPARATOR . "Delete.php"; - break; - case "Follow": - require_once __DIR__ . DIRECTORY_SEPARATOR . "inbox" . DIRECTORY_SEPARATOR . "Follow.php"; - break; - case "Like": - require_once __DIR__ . DIRECTORY_SEPARATOR . "inbox" . DIRECTORY_SEPARATOR . "Like.php"; - break; - case "Undo": - require_once __DIR__ . DIRECTORY_SEPARATOR . "inbox" . DIRECTORY_SEPARATOR . "Undo.php"; - break; - case "Announce": - require_once __DIR__ . DIRECTORY_SEPARATOR . "inbox" . DIRECTORY_SEPARATOR . "Announce.php"; - break; - case "Accept": - require_once __DIR__ . DIRECTORY_SEPARATOR . "inbox" . DIRECTORY_SEPARATOR . "Accept.php"; - break; - case "Reject": - require_once __DIR__ . DIRECTORY_SEPARATOR . "inbox" . DIRECTORY_SEPARATOR . "Reject.php"; - break; - default: - ActivityPubReturn::error("Invalid type value."); - } + switch ($data['type']) { + case "Create": + require_once __DIR__ . DIRECTORY_SEPARATOR . "inbox" . DIRECTORY_SEPARATOR . "Create.php"; + break; + case "Delete": + require_once __DIR__ . DIRECTORY_SEPARATOR . "inbox" . DIRECTORY_SEPARATOR . "Delete.php"; + break; + case "Follow": + require_once __DIR__ . DIRECTORY_SEPARATOR . "inbox" . DIRECTORY_SEPARATOR . "Follow.php"; + break; + case "Like": + require_once __DIR__ . DIRECTORY_SEPARATOR . "inbox" . DIRECTORY_SEPARATOR . "Like.php"; + break; + case "Undo": + require_once __DIR__ . DIRECTORY_SEPARATOR . "inbox" . DIRECTORY_SEPARATOR . "Undo.php"; + break; + case "Announce": + require_once __DIR__ . DIRECTORY_SEPARATOR . "inbox" . DIRECTORY_SEPARATOR . "Announce.php"; + break; + case "Accept": + require_once __DIR__ . DIRECTORY_SEPARATOR . "inbox" . DIRECTORY_SEPARATOR . "Accept.php"; + break; + case "Reject": + require_once __DIR__ . DIRECTORY_SEPARATOR . "inbox" . DIRECTORY_SEPARATOR . "Reject.php"; + break; + default: + ActivityPubReturn::error("Invalid type value."); + } } } diff --git a/actions/apnotice.php b/actions/apnotice.php old mode 100644 new mode 100755 diff --git a/actions/apsharedinbox.php b/actions/apsharedinbox.php index 27a58ae..273b3ae 100755 --- a/actions/apsharedinbox.php +++ b/actions/apsharedinbox.php @@ -58,36 +58,30 @@ class apSharedInboxAction extends ManagedAction common_debug('ActivityPub Shared Inbox: Received a POST request.'); $data = file_get_contents('php://input'); common_debug('ActivityPub Shared Inbox: Request contents: '.$data); - $data = json_decode(file_get_contents('php://input')); + $data = json_decode(file_get_contents('php://input'), true); // Validate data - if (!isset($data->type)) { + if (!isset($data['type'])) { ActivityPubReturn::error('Type was not specified.'); } - if (!isset($data->actor)) { + if (!isset($data['actor'])) { ActivityPubReturn::error('Actor was not specified.'); } - if (!isset($data->object)) { + if (!isset($data['object'])) { ActivityPubReturn::error('Object was not specified.'); } // Get valid Actor object try { - $actor_profile = ActivityPub_explorer::get_profile_from_url($data->actor); + $actor_profile = ActivityPub_explorer::get_profile_from_url($data['actor']); } catch (Exception $e) { ActivityPubReturn::error($e->getMessage(), 404); } - // Public To: - $public_to = ['https://www.w3.org/ns/activitystreams#Public', - 'Public', - 'as:Public' - ]; - - $to_profiles = ['https://www.w3.org/ns/activitystreams#Public']; + $to_profiles = []; // Process request - switch ($data->type) { + switch ($data['type']) { // Data available: // Profile $actor_profile // string|object $data->object diff --git a/actions/inbox/Accept.php b/actions/inbox/Accept.php index 6372305..a7302bb 100755 --- a/actions/inbox/Accept.php +++ b/actions/inbox/Accept.php @@ -30,20 +30,20 @@ if (!defined('GNUSOCIAL')) { } // Validate data -if (!isset($data->type)) { +if (!isset($data['object']['type'])) { ActivityPubReturn::error("Type was not specified."); } -switch ($data->object->type) { -case "Follow": +switch ($data['object']['type']) { + case "Follow": // Validate data - if (!isset($data->object->object)) { + if (!isset($data['object']['object'])) { ActivityPubReturn::error("Object Actor URL was not specified."); } // Get valid Object profile try { $object_profile = new Activitypub_explorer; - $object_profile = $object_profile->lookup($data->object->object)[0]; + $object_profile = $object_profile->lookup($data['object']['object'])[0]; } catch (Exception $e) { ActivityPubReturn::error("Invalid Object Actor URL.", 404); } @@ -52,7 +52,7 @@ case "Follow": $pending_list->remove(); ActivityPubReturn::answer(); // You are now being followed by this person. break; -default: + default: ActivityPubReturn::error("Invalid object type."); break; } diff --git a/actions/inbox/Announce.php b/actions/inbox/Announce.php index 8c8d69a..5596b9a 100755 --- a/actions/inbox/Announce.php +++ b/actions/inbox/Announce.php @@ -31,7 +31,7 @@ if (!defined('GNUSOCIAL')) { try { try { - $object_notice = ActivityPubPlugin::grab_notice_from_url($data->object); + $object_notice = ActivityPubPlugin::grab_notice_from_url($data['object']); } catch (Exception $e) { ActivityPubReturn::error('Invalid Object specified.'); } diff --git a/actions/inbox/Create.php b/actions/inbox/Create.php index 7a7c71a..8741720 100755 --- a/actions/inbox/Create.php +++ b/actions/inbox/Create.php @@ -31,10 +31,10 @@ if (!defined('GNUSOCIAL')) { $valid_object_types = ['Note']; -$res = $data->object; +$res = $data['object']; try { - Activitypub_notice::validate_remote_notice((array) $res); + Activitypub_notice::validate_remote_notice($res); } catch (Exception $e) { common_debug('ActivityPub Inbox Create Note: Invalid note: '.$e->getMessage()); ActivityPubReturn::error($e->getMessage()); @@ -42,23 +42,23 @@ try { $settings = []; -if (isset($res->inReplyTo)) { - $settings['inReplyTo'] = $res->inReplyTo; +if (isset($res['inReplyTo'])) { + $settings['inReplyTo'] = $res['inReplyTo']; } -if (isset($res->latitude)) { - $settings['latitude'] = $res->latitude; +if (isset($res['latitude'])) { + $settings['latitude'] = $res['latitude']; } -if (isset($res->longitude)) { - $settings['longitude'] = $res->longitude; +if (isset($res['longitude'])) { + $settings['longitude'] = $res['longitude']; } try { Activitypub_notice::create_notice( $actor_profile, - $res->id, - $res->url, - $res->content, - $res->cc, + $res['id'], + $res['url'], + $res['content'], + $res['cc'], $settings ); ActivityPubReturn::answer(); diff --git a/actions/inbox/Delete.php b/actions/inbox/Delete.php index 19a7e5e..fb4f9ed 100755 --- a/actions/inbox/Delete.php +++ b/actions/inbox/Delete.php @@ -30,7 +30,7 @@ if (!defined('GNUSOCIAL')) { } try { - $notice = ActivityPubPlugin::grab_notice_from_url($data->object->id); + $notice = ActivityPubPlugin::grab_notice_from_url($data['object']['id']); $notice->deleteAs($actor_profile); ActivityPubReturn::answer(); } catch (Exception $e) { diff --git a/actions/inbox/Follow.php b/actions/inbox/Follow.php index 7fff84f..bb0d0f9 100755 --- a/actions/inbox/Follow.php +++ b/actions/inbox/Follow.php @@ -30,7 +30,7 @@ if (!defined('GNUSOCIAL')) { } // Validate Object -if (!filter_var($data->object, FILTER_VALIDATE_URL)) { +if (!filter_var($data['object'], FILTER_VALIDATE_URL)) { ActivityPubReturn::error('Invalid Object Actor URL.'); } @@ -38,7 +38,7 @@ if (!filter_var($data->object, FILTER_VALIDATE_URL)) { try { if (!isset($profile)) { $object_profile = new Activitypub_explorer; - $object_profile = $object_profile->lookup($data->object)[0]; + $object_profile = $object_profile->lookup($data['object'])[0]; } else { $object_profile = $profile; unset($profile); @@ -52,14 +52,14 @@ $actor_aprofile = Activitypub_profile::from_profile($actor_profile); if (!Subscription::exists($actor_profile, $object_profile)) { Subscription::start($actor_profile, $object_profile); - common_debug('ActivityPubPlugin: Accepted Follow request from '.$data->actor.' to '.$data->object); + common_debug('ActivityPubPlugin: Accepted Follow request from '.$data['actor'].' to '.$data['object']); // Notify remote instance that we have accepted their request - common_debug('ActivityPubPlugin: Notifying remote instance that we have accepted their Follow request request from '.$data->actor.' to '.$data->object); + common_debug('ActivityPubPlugin: Notifying remote instance that we have accepted their Follow request request from '.$data['actor'].' to '.$data['object']); $postman = new Activitypub_postman($actor_profile, [$actor_aprofile]); $postman->follow(); ActivityPubReturn::answer(); } else { - common_debug('ActivityPubPlugin: Received a repeated Follow request from '.$data->actor.' to '.$data->object); + common_debug('ActivityPubPlugin: Received a repeated Follow request from '.$data['actor'].' to '.$data['object']); ActivityPubReturn::error('Already following.', 409); } diff --git a/actions/inbox/Like.php b/actions/inbox/Like.php index 3e4d1e7..551f1d3 100755 --- a/actions/inbox/Like.php +++ b/actions/inbox/Like.php @@ -31,7 +31,7 @@ if (!defined('GNUSOCIAL')) { try { try { - $object_notice = ActivityPubPlugin::grab_notice_from_url($data->object); + $object_notice = ActivityPubPlugin::grab_notice_from_url($data['object']); } catch (Exception $e) { ActivityPubReturn::error('Invalid Object specified.'); } diff --git a/actions/inbox/Undo.php b/actions/inbox/Undo.php index 10e0cc7..8b942d9 100755 --- a/actions/inbox/Undo.php +++ b/actions/inbox/Undo.php @@ -30,18 +30,18 @@ if (!defined('GNUSOCIAL')) { } // Validate data -if (!isset($data->type)) { +if (!isset($data['type'])) { ActivityPubReturn::error('Type was not specified.'); } -switch ($data->object->type) { +switch ($data['object']['type']) { case 'Like': try { // Validate data - if (!isset($data->object->object)) { + if (!isset($data['object']['object'])) { ActivityPubReturn::error('Notice URI was not specified.'); } - Fave::removeEntry($actor_profile, ActivityPubPlugin::grab_notice_from_url($data->object->object)); + Fave::removeEntry($actor_profile, ActivityPubPlugin::grab_notice_from_url($data['object']['object'])); // Notice disfavorited successfully. ActivityPubReturn::answer(); } catch (Exception $e) { @@ -50,13 +50,13 @@ switch ($data->object->type) { break; case 'Follow': // Validate data - if (!isset($data->object->object)) { + if (!isset($data['object']['object'])) { ActivityPubReturn::error('Object Actor URL was not specified.'); } // Get valid Object profile try { $object_profile = new Activitypub_explorer; - $object_profile = $object_profile->lookup($data->object->object)[0]; + $object_profile = $object_profile->lookup($data['object']['object'])[0]; } catch (Exception $e) { ActivityPubReturn::error('Invalid Object Actor URL.', 404); } diff --git a/classes/Activitypub_mention_tag.php b/classes/Activitypub_mention_tag.php old mode 100644 new mode 100755 diff --git a/classes/Activitypub_profile.php b/classes/Activitypub_profile.php index b628562..6a5094e 100755 --- a/classes/Activitypub_profile.php +++ b/classes/Activitypub_profile.php @@ -318,11 +318,9 @@ class Activitypub_profile extends Managed_DataObject */ public static function fromUri($url) { - $explorer = new Activitypub_explorer(); - $profiles_found = $explorer->lookup($url); - if (!empty($profiles_found)) { - return self::from_profile($profiles_found[0]); - } else { + try { + return self::from_profile(Activitypub_explorer::get_profile_from_url($url)); + } catch (Exception $e) { throw new Exception('No valid ActivityPub profile found for given URI.'); } } diff --git a/tests/Unit/HTTPSignatureTest.php b/tests/Unit/HTTPSignatureTest.php old mode 100644 new mode 100755 diff --git a/utils/explorer.php b/utils/explorer.php index f8baa32..802c54a 100755 --- a/utils/explorer.php +++ b/utils/explorer.php @@ -56,13 +56,11 @@ class Activitypub_explorer { $discovery = new Activitypub_explorer; // Get valid Actor object - try { - $actor_profile = $discovery->lookup($url); + $actor_profile = $discovery->lookup($url); + if (!empty($actor_profile)) { return $actor_profile[0]; - } catch (Exception $e) { - throw new Exception('Invalid Actor.'); } - unset($discovery); + throw new Exception('Invalid Actor.'); } /** @@ -168,10 +166,10 @@ class Activitypub_explorer common_debug('ActivityPub Explorer: Unable to find a local Aprofile for '.$uri.' - looking for a Profile instead.'); // Well, maybe it is a pure blood? // Iff, we are in the same instance: - $ACTIVITYPUB_BASE_INSTANCE_URI_length = strlen(ACTIVITYPUB_BASE_INSTANCE_URI); - if (substr($uri, 0, $ACTIVITYPUB_BASE_INSTANCE_URI_length) == ACTIVITYPUB_BASE_INSTANCE_URI) { + $ACTIVITYPUB_BASE_ACTOR_URI_length = strlen(ACTIVITYPUB_BASE_ACTOR_URI); + if (substr($uri, 0, $ACTIVITYPUB_BASE_ACTOR_URI_length) == ACTIVITYPUB_BASE_ACTOR_URI) { try { - $profile = Profile::getByID(intval(substr($uri, $ACTIVITYPUB_BASE_INSTANCE_URI_length))); + $profile = Profile::getByID(intval(substr($uri, $ACTIVITYPUB_BASE_ACTOR_URI_length))); common_debug('ActivityPub Explorer: Found a Profile for '.$uri); // We found something! $this->discovered_actor_profiles[]= $profile; @@ -215,7 +213,7 @@ class Activitypub_explorer $res = $this->temp_res; unset($this->temp_res); } - if (isset($res['type']) && $res['type'] === 'OrderedCollection' && isset ($res['first'])) { // It's a potential collection of actors!!! + if (isset($res['type']) && $res['type'] === 'OrderedCollection' && isset($res['first'])) { // It's a potential collection of actors!!! common_debug('ActivityPub Explorer: Found a collection of actors for '.$url); $this->travel_collection($res['first']); return true; @@ -275,14 +273,17 @@ class Activitypub_explorer * Download and update given avatar image * * @author GNU Social - * @param string $url - * @return Avatar The Avatar we have on disk. (seldom used) + * @param Profile $profile + * @param string $url + * @return Avatar The Avatar we have on disk. * @throws Exception in various failure cases */ - private function _store_avatar($profile, $url) + private function _store_avatar(Profile $profile, $url) { - if (!common_valid_http_url($url)) { + common_debug('ActivityPub Explorer: Started grabbing remote avatar from: '.$url); + if (!filter_var($url, FILTER_VALIDATE_URL)) { // TRANS: Server exception. %s is a URL. + common_debug('ActivityPub Explorer: Failed because it is an invalid url: '.$url); throw new ServerException(sprintf('Invalid avatar URL %s.'), $url); } @@ -293,10 +294,12 @@ class Activitypub_explorer $imgData = HTTPClient::quickGet($url); // Make sure it's at least an image file. ImageFile can do the rest. if (false === getimagesizefromstring($imgData)) { + common_debug('ActivityPub Explorer: Failed because the downloaded avatar: '.$url. 'is not a validad image.'); throw new UnsupportedMediaException('Downloaded avatar was not an image.'); } file_put_contents($temp_filename, $imgData); unset($imgData); // No need to carry this in memory. + common_debug('ActivityPub Explorer: Stored dowloaded avatar in: '.$temp_filename); $id = $profile->getID(); @@ -308,7 +311,9 @@ class Activitypub_explorer common_timestamp() ); rename($temp_filename, Avatar::path($filename)); + common_debug('ActivityPub Explorer: Moved avatar from: '.$temp_filename.' to '.$filename); } catch (Exception $e) { + common_debug('ActivityPub Explorer: Something went wrong while processing the avatar from: '.$url.' details: '.$e->getMessage()); unlink($temp_filename); throw $e; } @@ -326,6 +331,7 @@ class Activitypub_explorer $profile->avatar = $url; $profile->update($orig); + common_debug('ActivityPub Explorer: Seted Avatar from: '.$url.' to profile.'); return Avatar::getUploaded($profile); } @@ -416,8 +422,7 @@ class Activitypub_explorer $response = $client->get($url, $headers); $res = json_decode($response->getBody(), true); - if (!isset($res['orderedItems'])) - { + if (!isset($res['orderedItems'])) { return false; }