| 
									
										
										
										
											2021-08-24 20:29:26 +01:00
										 |  |  | <?php | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-24 01:58:41 +00:00
										 |  |  | declare(strict_types = 1); | 
					
						
							| 
									
										
										
										
											2021-10-10 09:26:18 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-04 04:07:08 +00:00
										 |  |  | // {{{ License
 | 
					
						
							|  |  |  | // This file is part of GNU social - https://www.gnu.org/software/social
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // GNU social is free software: you can redistribute it and/or modify
 | 
					
						
							|  |  |  | // it under the terms of the GNU Affero General Public License as published by
 | 
					
						
							|  |  |  | // the Free Software Foundation, either version 3 of the License, or
 | 
					
						
							|  |  |  | // (at your option) any later version.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // GNU social is distributed in the hope that it will be useful,
 | 
					
						
							|  |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					
						
							|  |  |  | // GNU Affero General Public License for more details.
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | // You should have received a copy of the GNU Affero General Public License
 | 
					
						
							|  |  |  | // along with GNU social.  If not, see <http://www.gnu.org/licenses/>.
 | 
					
						
							|  |  |  | // }}}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * ActivityPub implementation for GNU social | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @package   GNUsocial | 
					
						
							|  |  |  |  * @category  ActivityPub | 
					
						
							| 
									
										
										
										
											2021-12-24 01:58:41 +00:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2021-12-04 04:07:08 +00:00
										 |  |  |  * @author    Diogo Peralta Cordeiro <@diogo.site> | 
					
						
							|  |  |  |  * @copyright 2018-2019, 2021 Free Software Foundation, Inc http://www.fsf.org | 
					
						
							|  |  |  |  * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-24 20:29:26 +01:00
										 |  |  | namespace Plugin\ActivityPub; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-11 11:39:25 +00:00
										 |  |  | use ActivityPhp\Type; | 
					
						
							| 
									
										
										
										
											2021-12-08 22:24:52 +00:00
										 |  |  | use App\Core\DB\DB; | 
					
						
							| 
									
										
										
										
											2021-08-24 20:29:26 +01:00
										 |  |  | use App\Core\Event; | 
					
						
							| 
									
										
										
										
											2021-11-29 23:58:42 +00:00
										 |  |  | use App\Core\HTTPClient; | 
					
						
							| 
									
										
										
										
											2021-11-01 12:16:46 +00:00
										 |  |  | use App\Core\Log; | 
					
						
							| 
									
										
										
										
											2021-08-24 20:29:26 +01:00
										 |  |  | use App\Core\Modules\Plugin; | 
					
						
							|  |  |  | use App\Core\Router\RouteLoader; | 
					
						
							| 
									
										
										
										
											2021-10-04 17:00:58 +01:00
										 |  |  | use App\Core\Router\Router; | 
					
						
							| 
									
										
										
										
											2021-11-29 23:58:42 +00:00
										 |  |  | use App\Entity\Activity; | 
					
						
							| 
									
										
										
										
											2021-10-18 13:22:02 +01:00
										 |  |  | use App\Entity\Actor; | 
					
						
							| 
									
										
										
										
											2021-10-27 04:14:01 +01:00
										 |  |  | use App\Entity\LocalUser; | 
					
						
							| 
									
										
										
										
											2021-12-08 22:24:52 +00:00
										 |  |  | use App\Entity\Note; | 
					
						
							| 
									
										
										
										
											2021-11-01 12:16:46 +00:00
										 |  |  | use App\Util\Common; | 
					
						
							| 
									
										
										
										
											2022-01-02 20:37:15 +00:00
										 |  |  | use App\Util\Exception\BugFoundException; | 
					
						
							| 
									
										
										
										
											2021-10-27 04:14:01 +01:00
										 |  |  | use App\Util\Exception\NoSuchActorException; | 
					
						
							|  |  |  | use App\Util\Nickname; | 
					
						
							| 
									
										
										
										
											2022-01-11 20:28:15 +00:00
										 |  |  | use Component\Collection\Util\Controller\OrderedCollection; | 
					
						
							| 
									
										
										
										
											2021-12-02 04:25:58 +00:00
										 |  |  | use Component\FreeNetwork\Entity\FreeNetworkActorProtocol; | 
					
						
							|  |  |  | use Component\FreeNetwork\Util\Discovery; | 
					
						
							| 
									
										
										
										
											2021-08-24 20:29:26 +01:00
										 |  |  | use Exception; | 
					
						
							| 
									
										
										
										
											2021-12-24 01:58:41 +00:00
										 |  |  | use InvalidArgumentException; | 
					
						
							|  |  |  | use const PHP_URL_HOST; | 
					
						
							| 
									
										
										
										
											2021-08-24 20:29:26 +01:00
										 |  |  | use Plugin\ActivityPub\Controller\Inbox; | 
					
						
							| 
									
										
										
										
											2022-01-11 20:28:15 +00:00
										 |  |  | use Plugin\ActivityPub\Controller\Outbox; | 
					
						
							| 
									
										
										
										
											2021-12-08 22:24:52 +00:00
										 |  |  | use Plugin\ActivityPub\Entity\ActivitypubActivity; | 
					
						
							| 
									
										
										
										
											2021-10-27 04:14:01 +01:00
										 |  |  | use Plugin\ActivityPub\Entity\ActivitypubActor; | 
					
						
							| 
									
										
										
										
											2021-12-08 22:24:52 +00:00
										 |  |  | use Plugin\ActivityPub\Entity\ActivitypubObject; | 
					
						
							| 
									
										
										
										
											2021-11-30 16:47:31 +00:00
										 |  |  | use Plugin\ActivityPub\Util\HTTPSignature; | 
					
						
							| 
									
										
										
										
											2021-12-04 04:07:08 +00:00
										 |  |  | use Plugin\ActivityPub\Util\Model; | 
					
						
							| 
									
										
										
										
											2022-01-11 20:28:15 +00:00
										 |  |  | use Plugin\ActivityPub\Util\OrderedCollectionController; | 
					
						
							| 
									
										
										
										
											2022-02-23 17:39:11 +00:00
										 |  |  | use Plugin\ActivityPub\Util\Response\ActivityResponse; | 
					
						
							| 
									
										
										
										
											2021-10-04 17:00:58 +01:00
										 |  |  | use Plugin\ActivityPub\Util\Response\ActorResponse; | 
					
						
							|  |  |  | use Plugin\ActivityPub\Util\Response\NoteResponse; | 
					
						
							| 
									
										
										
										
											2021-12-04 04:07:08 +00:00
										 |  |  | use Plugin\ActivityPub\Util\TypeResponse; | 
					
						
							|  |  |  | use Plugin\ActivityPub\Util\Validator\contentLangModelValidator; | 
					
						
							|  |  |  | use Plugin\ActivityPub\Util\Validator\manuallyApprovesFollowersModelValidator; | 
					
						
							| 
									
										
										
										
											2022-02-23 17:39:11 +00:00
										 |  |  | use Symfony\Component\HttpFoundation\JsonResponse; | 
					
						
							| 
									
										
										
										
											2021-12-04 04:07:08 +00:00
										 |  |  | use Symfony\Contracts\HttpClient\Exception\ClientExceptionInterface; | 
					
						
							|  |  |  | use Symfony\Contracts\HttpClient\Exception\RedirectionExceptionInterface; | 
					
						
							|  |  |  | use Symfony\Contracts\HttpClient\Exception\ServerExceptionInterface; | 
					
						
							|  |  |  | use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface; | 
					
						
							| 
									
										
										
										
											2021-11-29 23:58:42 +00:00
										 |  |  | use Symfony\Contracts\HttpClient\ResponseInterface; | 
					
						
							| 
									
										
										
										
											2021-10-18 13:22:02 +01:00
										 |  |  | use XML_XRD; | 
					
						
							|  |  |  | use XML_XRD_Element_Link; | 
					
						
							| 
									
										
										
										
											2021-08-24 20:29:26 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-04 04:07:08 +00:00
										 |  |  | /** | 
					
						
							|  |  |  |  * Adds ActivityPub support to GNU social when enabled | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * @copyright 2018-2019, 2021 Free Software Foundation, Inc http://www.fsf.org | 
					
						
							|  |  |  |  * @license   https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2021-08-24 20:29:26 +01:00
										 |  |  | class ActivityPub extends Plugin | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2021-10-27 04:14:01 +01:00
										 |  |  |     // ActivityStreams 2.0 Accept Headers
 | 
					
						
							| 
									
										
										
										
											2021-10-18 13:22:02 +01:00
										 |  |  |     public static array $accept_headers = [ | 
					
						
							|  |  |  |         'application/ld+json; profile="https://www.w3.org/ns/activitystreams"', | 
					
						
							|  |  |  |         'application/activity+json', | 
					
						
							|  |  |  |         'application/json', | 
					
						
							|  |  |  |         'application/ld+json', | 
					
						
							|  |  |  |     ]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-27 04:14:01 +01:00
										 |  |  |     // So that this isn't hardcoded everywhere
 | 
					
						
							| 
									
										
										
										
											2021-11-27 15:06:46 +00:00
										 |  |  |     public const PUBLIC_TO = [ | 
					
						
							|  |  |  |         'https://www.w3.org/ns/activitystreams#Public', | 
					
						
							| 
									
										
										
										
											2021-10-27 04:14:01 +01:00
										 |  |  |         'Public', | 
					
						
							|  |  |  |         'as:Public', | 
					
						
							|  |  |  |     ]; | 
					
						
							|  |  |  |     public const HTTP_CLIENT_HEADERS = [ | 
					
						
							| 
									
										
										
										
											2021-12-24 01:58:41 +00:00
										 |  |  |         'Accept'     => 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"', | 
					
						
							| 
									
										
										
										
											2021-10-27 04:14:01 +01:00
										 |  |  |         'User-Agent' => 'GNUsocialBot ' . GNUSOCIAL_VERSION . ' - ' . GNUSOCIAL_PROJECT_URL, | 
					
						
							|  |  |  |     ]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-24 20:29:26 +01:00
										 |  |  |     public function version(): string | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return '3.0.0'; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-23 17:39:11 +00:00
										 |  |  |     public static array $activity_streams_two_context = [ | 
					
						
							|  |  |  |         'https://www.w3.org/ns/activitystreams', | 
					
						
							|  |  |  |         'https://w3id.org/security/v1', | 
					
						
							|  |  |  |         ['gs'          => 'https://www.gnu.org/software/social/ns#'], | 
					
						
							|  |  |  |         ['litepub'     => 'http://litepub.social/ns#'], | 
					
						
							|  |  |  |         ['chatMessage' => 'litepub:chatMessage'], | 
					
						
							|  |  |  |         [ | 
					
						
							|  |  |  |             'inConversation' => [ | 
					
						
							|  |  |  |                 '@id'   => 'gs:inConversation', | 
					
						
							|  |  |  |                 '@type' => '@id', | 
					
						
							|  |  |  |             ], | 
					
						
							|  |  |  |         ], | 
					
						
							|  |  |  |     ]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     public function onInitializePlugin(): bool | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         Event::handle('ActivityStreamsTwoContext', [&self::$activity_streams_two_context]); | 
					
						
							|  |  |  |         self::$activity_streams_two_context = array_unique(self::$activity_streams_two_context, \SORT_REGULAR); | 
					
						
							|  |  |  |         return Event::next; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-24 20:29:26 +01:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * This code executes when GNU social creates the page routing, and we hook | 
					
						
							| 
									
										
										
										
											2021-10-04 17:00:58 +01:00
										 |  |  |      * on this event to add our Inbox and Outbox handler for ActivityPub. | 
					
						
							| 
									
										
										
										
											2021-08-24 20:29:26 +01:00
										 |  |  |      * | 
					
						
							| 
									
										
										
										
											2021-10-10 09:26:18 +01:00
										 |  |  |      * @param RouteLoader $r the router that was initialized | 
					
						
							| 
									
										
										
										
											2021-08-24 20:29:26 +01:00
										 |  |  |      */ | 
					
						
							|  |  |  |     public function onAddRoute(RouteLoader $r): bool | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2021-09-16 17:04:05 +01:00
										 |  |  |         $r->connect( | 
					
						
							| 
									
										
										
										
											2021-10-19 23:46:01 +01:00
										 |  |  |             'activitypub_inbox', | 
					
						
							|  |  |  |             '/inbox.json', | 
					
						
							| 
									
										
										
										
											2022-02-20 05:03:05 +00:00
										 |  |  |             Inbox::class, | 
					
						
							| 
									
										
										
										
											2021-12-12 06:40:13 +00:00
										 |  |  |             options: ['format' => self::$accept_headers[0]], | 
					
						
							| 
									
										
										
										
											2021-09-16 17:04:05 +01:00
										 |  |  |         ); | 
					
						
							|  |  |  |         $r->connect( | 
					
						
							| 
									
										
										
										
											2021-10-19 23:46:01 +01:00
										 |  |  |             'activitypub_actor_inbox', | 
					
						
							|  |  |  |             '/actor/{gsactor_id<\d+>}/inbox.json', | 
					
						
							| 
									
										
										
										
											2021-09-16 17:04:05 +01:00
										 |  |  |             [Inbox::class, 'handle'], | 
					
						
							| 
									
										
										
										
											2021-12-12 06:40:13 +00:00
										 |  |  |             options: ['format' => self::$accept_headers[0]], | 
					
						
							| 
									
										
										
										
											2021-09-16 17:04:05 +01:00
										 |  |  |         ); | 
					
						
							| 
									
										
										
										
											2021-08-24 20:29:26 +01:00
										 |  |  |         $r->connect( | 
					
						
							| 
									
										
										
										
											2021-10-19 23:46:01 +01:00
										 |  |  |             'activitypub_actor_outbox', | 
					
						
							|  |  |  |             '/actor/{gsactor_id<\d+>}/outbox.json', | 
					
						
							| 
									
										
										
										
											2022-01-11 20:28:15 +00:00
										 |  |  |             [Outbox::class, 'viewOutboxByActorId'], | 
					
						
							| 
									
										
										
										
											2021-10-27 04:14:01 +01:00
										 |  |  |             options: ['accept' => self::$accept_headers, 'format' => self::$accept_headers[0]], | 
					
						
							| 
									
										
										
										
											2021-08-24 20:29:26 +01:00
										 |  |  |         ); | 
					
						
							|  |  |  |         return Event::next; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-04 04:07:08 +00:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Fill Actor->getUrl() calls with correct URL coming from ActivityPub | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2021-12-12 06:40:13 +00:00
										 |  |  |     public function onStartGetActorUri(Actor $actor, int $type, ?string &$url): bool | 
					
						
							| 
									
										
										
										
											2021-11-27 15:06:46 +00:00
										 |  |  |     { | 
					
						
							|  |  |  |         if ( | 
					
						
							|  |  |  |             // Is remote?
 | 
					
						
							|  |  |  |             !$actor->getIsLocal() | 
					
						
							|  |  |  |             // Is in ActivityPub?
 | 
					
						
							| 
									
										
										
										
											2022-02-18 17:48:06 +00:00
										 |  |  |             && !\is_null($ap_actor = DB::findOneBy(ActivitypubActor::class, ['actor_id' => $actor->getId()], return_null: true)) | 
					
						
							| 
									
										
										
										
											2021-11-27 15:06:46 +00:00
										 |  |  |             // We can only provide a full URL (anything else wouldn't make sense)
 | 
					
						
							|  |  |  |             && $type === Router::ABSOLUTE_URL | 
					
						
							|  |  |  |         ) { | 
					
						
							| 
									
										
										
										
											2021-11-29 23:58:42 +00:00
										 |  |  |             $url = $ap_actor->getUri(); | 
					
						
							| 
									
										
										
										
											2021-11-27 15:06:46 +00:00
										 |  |  |             return Event::stop; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return Event::next; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-28 15:02:03 +00:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Fill Actor->canAdmin() for Actors that came from ActivityPub | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     public function onFreeNetworkActorCanAdmin(Actor $actor, Actor $other, bool &$canAdmin): bool | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         // Are both in AP?
 | 
					
						
							|  |  |  |         if ( | 
					
						
							| 
									
										
										
										
											2022-02-18 17:48:06 +00:00
										 |  |  |             !\is_null($ap_actor = DB::findOneBy(ActivitypubActor::class, ['actor_id' => $actor->getId()], return_null: true)) | 
					
						
							|  |  |  |             && !\is_null($ap_other = DB::findOneBy(ActivitypubActor::class, ['actor_id' => $other->getId()], return_null: true)) | 
					
						
							| 
									
										
										
										
											2021-12-28 15:02:03 +00:00
										 |  |  |         ) { | 
					
						
							|  |  |  |             // Are they both in the same server?
 | 
					
						
							|  |  |  |             $canAdmin = parse_url($ap_actor->getUri(), PHP_URL_HOST) === parse_url($ap_other->getUri(), PHP_URL_HOST); | 
					
						
							|  |  |  |             return Event::stop; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return Event::next; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-04 04:07:08 +00:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Overload core endpoints to make resources available in ActivityStreams 2.0 | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @throws Exception | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     public function onControllerResponseInFormat(string $route, array $accept_header, array $vars, ?TypeResponse &$response = null): bool | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2021-12-24 01:58:41 +00:00
										 |  |  |         if (\count(array_intersect(self::$accept_headers, $accept_header)) === 0) { | 
					
						
							| 
									
										
										
										
											2021-12-04 04:07:08 +00:00
										 |  |  |             return Event::next; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         switch ($route) { | 
					
						
							| 
									
										
										
										
											2022-02-16 18:45:30 +00:00
										 |  |  |             case 'actor_view_id': | 
					
						
							| 
									
										
										
										
											2022-02-10 16:02:51 +00:00
										 |  |  |             case 'person_actor_view_id': | 
					
						
							|  |  |  |             case 'person_actor_view_nickname': | 
					
						
							|  |  |  |             case 'group_actor_view_id': | 
					
						
							|  |  |  |             case 'group_actor_view_nickname': | 
					
						
							|  |  |  |             case 'bot_actor_view_id': | 
					
						
							|  |  |  |             case 'bot_actor_view_nickname': | 
					
						
							| 
									
										
										
										
											2021-12-04 04:07:08 +00:00
										 |  |  |                 $response = ActorResponse::handle($vars['actor']); | 
					
						
							| 
									
										
										
										
											2022-01-11 20:28:15 +00:00
										 |  |  |                 break; | 
					
						
							| 
									
										
										
										
											2022-02-11 00:22:22 +00:00
										 |  |  |             case 'activity_view': | 
					
						
							|  |  |  |                 $response = ActivityResponse::handle($vars['activity']); | 
					
						
							|  |  |  |                 break; | 
					
						
							| 
									
										
										
										
											2021-12-04 04:07:08 +00:00
										 |  |  |             case 'note_view': | 
					
						
							|  |  |  |                 $response = NoteResponse::handle($vars['note']); | 
					
						
							| 
									
										
										
										
											2022-01-11 20:28:15 +00:00
										 |  |  |                 break; | 
					
						
							|  |  |  |             case 'activitypub_actor_outbox': | 
					
						
							|  |  |  |                 $response = new TypeResponse($vars['type']); | 
					
						
							|  |  |  |                 break; | 
					
						
							| 
									
										
										
										
											2021-12-04 04:07:08 +00:00
										 |  |  |             default: | 
					
						
							| 
									
										
										
										
											2022-01-11 20:28:15 +00:00
										 |  |  |                 if (Event::handle('ActivityPubActivityStreamsTwoResponse', [$route, $vars, &$response]) !== Event::stop) { | 
					
						
							|  |  |  |                     if (is_subclass_of($vars['controller'][0], OrderedCollection::class)) { | 
					
						
							|  |  |  |                         $response = new TypeResponse(OrderedCollectionController::fromControllerVars($vars)['type']); | 
					
						
							| 
									
										
										
										
											2022-02-11 00:22:22 +00:00
										 |  |  |                     } else { | 
					
						
							|  |  |  |                         $response = new JsonResponse(['error' => 'Unknown Object cannot be represented.']); | 
					
						
							| 
									
										
										
										
											2022-01-11 20:28:15 +00:00
										 |  |  |                     } | 
					
						
							| 
									
										
										
										
											2021-12-04 04:07:08 +00:00
										 |  |  |                 } | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2022-01-11 20:28:15 +00:00
										 |  |  |         return Event::stop; | 
					
						
							| 
									
										
										
										
											2021-12-04 04:07:08 +00:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Add ActivityStreams 2 Extensions | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     public function onActivityPubValidateActivityStreamsTwoData(string $type_name, array &$validators): bool | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         switch ($type_name) { | 
					
						
							|  |  |  |             case 'Person': | 
					
						
							|  |  |  |                 $validators['manuallyApprovesFollowers'] = manuallyApprovesFollowersModelValidator::class; | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  |             case 'Note': | 
					
						
							|  |  |  |                 $validators['contentLang'] = contentLangModelValidator::class; | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return Event::next; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // FreeNetworkComponent Events
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Let FreeNetwork Component know we exist and which class to use to call the freeNetworkDistribute method | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2021-12-01 20:53:32 +00:00
										 |  |  |     public function onAddFreeNetworkProtocol(array &$protocols): bool | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2021-11-29 23:58:42 +00:00
										 |  |  |         $protocols[] = '\Plugin\ActivityPub\ActivityPub'; | 
					
						
							|  |  |  |         return Event::next; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-21 04:53:12 +00:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * The FreeNetwork component will call this function to pull ActivityPub objects by URI | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param string $uri Query | 
					
						
							| 
									
										
										
										
											2022-02-23 17:39:11 +00:00
										 |  |  |      * | 
					
						
							| 
									
										
										
										
											2022-02-21 04:53:12 +00:00
										 |  |  |      * @return bool true if imported, false otherwise | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     public static function freeNetworkGrabRemote(string $uri): bool | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if (Common::isValidHttpUrl($uri)) { | 
					
						
							|  |  |  |             try { | 
					
						
							|  |  |  |                 $object = self::getObjectByUri($uri); | 
					
						
							|  |  |  |                 if (!\is_null($object)) { | 
					
						
							|  |  |  |                     if ($object instanceof Type\AbstractObject) { | 
					
						
							| 
									
										
										
										
											2022-02-23 17:39:11 +00:00
										 |  |  |                         if (\in_array($object->get('type'), array_keys(Model\Actor::$_as2_actor_type_to_gs_actor_type))) { | 
					
						
							|  |  |  |                             DB::wrapInTransaction(fn () => Model\Actor::fromJson($object)); | 
					
						
							| 
									
										
										
										
											2022-02-21 04:53:12 +00:00
										 |  |  |                         } else { | 
					
						
							| 
									
										
										
										
											2022-02-23 17:39:11 +00:00
										 |  |  |                             DB::wrapInTransaction(fn () => Model\Activity::fromJson($object)); | 
					
						
							| 
									
										
										
										
											2022-02-21 04:53:12 +00:00
										 |  |  |                         } | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                     return true; | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2022-02-23 17:39:11 +00:00
										 |  |  |             } catch (Exception|Throwable) { | 
					
						
							| 
									
										
										
										
											2022-02-21 04:53:12 +00:00
										 |  |  |                 // May be invalid input, we can safely ignore in this case
 | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-04 04:07:08 +00:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * The FreeNetwork component will call this function to distribute this instance's activities | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @throws ClientExceptionInterface | 
					
						
							|  |  |  |      * @throws RedirectionExceptionInterface | 
					
						
							|  |  |  |      * @throws ServerExceptionInterface | 
					
						
							|  |  |  |      * @throws TransportExceptionInterface | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2021-12-02 04:25:58 +00:00
										 |  |  |     public static function freeNetworkDistribute(Actor $sender, Activity $activity, array $targets, ?string $reason = null, array &$delivered = []): bool | 
					
						
							| 
									
										
										
										
											2021-11-27 15:06:46 +00:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2021-11-29 23:58:42 +00:00
										 |  |  |         $to_addr = []; | 
					
						
							| 
									
										
										
										
											2021-12-02 04:25:58 +00:00
										 |  |  |         foreach ($targets as $actor) { | 
					
						
							|  |  |  |             if (FreeNetworkActorProtocol::canIActor('activitypub', $actor->getId())) { | 
					
						
							| 
									
										
										
										
											2022-02-18 17:48:06 +00:00
										 |  |  |                 if (\is_null($ap_target = DB::findOneBy(ActivitypubActor::class, ['actor_id' => $actor->getId()], return_null: true))) { | 
					
						
							| 
									
										
										
										
											2021-12-02 04:25:58 +00:00
										 |  |  |                     continue; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 $to_addr[$ap_target->getInboxSharedUri() ?? $ap_target->getInboxUri()][] = $actor; | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 return Event::next; | 
					
						
							| 
									
										
										
										
											2021-11-29 23:58:42 +00:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $errors = []; | 
					
						
							| 
									
										
										
										
											2021-12-02 04:25:58 +00:00
										 |  |  |         //$to_failed = [];
 | 
					
						
							| 
									
										
										
										
											2021-11-29 23:58:42 +00:00
										 |  |  |         foreach ($to_addr as $inbox => $dummy) { | 
					
						
							|  |  |  |             try { | 
					
						
							| 
									
										
										
										
											2022-02-11 11:39:25 +00:00
										 |  |  |                 $data = Model::toJson($activity); | 
					
						
							|  |  |  |                 if ($sender->isGroup()) { | 
					
						
							|  |  |  |                     // When the sender is a group, we have to wrap it in an Announce activity
 | 
					
						
							|  |  |  |                     $data = Type::create('Announce', ['object' => $data])->toJson(); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 $res = self::postman($sender, $data, $inbox); | 
					
						
							| 
									
										
										
										
											2021-11-29 23:58:42 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-02 04:25:58 +00:00
										 |  |  |                 // accumulate errors for later use, if needed
 | 
					
						
							| 
									
										
										
										
											2021-11-29 23:58:42 +00:00
										 |  |  |                 $status_code = $res->getStatusCode(); | 
					
						
							|  |  |  |                 if (!($status_code === 200 || $status_code === 202 || $status_code === 409)) { | 
					
						
							|  |  |  |                     $res_body = json_decode($res->getContent(), true); | 
					
						
							| 
									
										
										
										
											2021-12-04 04:07:08 +00:00
										 |  |  |                     $errors[] = $res_body['error'] ?? 'An unknown error occurred.'; | 
					
						
							| 
									
										
										
										
											2021-12-24 01:58:41 +00:00
										 |  |  |                 //$to_failed[$inbox] = $activity;
 | 
					
						
							| 
									
										
										
										
											2021-12-02 04:25:58 +00:00
										 |  |  |                 } else { | 
					
						
							|  |  |  |                     array_push($delivered, ...$dummy); | 
					
						
							|  |  |  |                     foreach ($dummy as $actor) { | 
					
						
							|  |  |  |                         FreeNetworkActorProtocol::protocolSucceeded( | 
					
						
							|  |  |  |                             'activitypub', | 
					
						
							|  |  |  |                             $actor, | 
					
						
							| 
									
										
										
										
											2021-12-04 04:07:08 +00:00
										 |  |  |                             Discovery::normalize($actor->getNickname() . '@' . parse_url($inbox, PHP_URL_HOST)), | 
					
						
							| 
									
										
										
										
											2021-12-02 04:25:58 +00:00
										 |  |  |                         ); | 
					
						
							|  |  |  |                     } | 
					
						
							| 
									
										
										
										
											2021-11-29 23:58:42 +00:00
										 |  |  |                 } | 
					
						
							|  |  |  |             } catch (Exception $e) { | 
					
						
							| 
									
										
										
										
											2021-12-28 16:44:38 +00:00
										 |  |  |                 Log::error('ActivityPub @ freeNetworkDistribute: ' . $e->getMessage(), [$e]); | 
					
						
							| 
									
										
										
										
											2021-12-02 04:25:58 +00:00
										 |  |  |                 //$to_failed[$inbox] = $activity;
 | 
					
						
							| 
									
										
										
										
											2021-11-29 23:58:42 +00:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (!empty($errors)) { | 
					
						
							| 
									
										
										
										
											2021-12-28 16:44:38 +00:00
										 |  |  |             Log::error(sizeof($errors) . ' instance/s failed to handle our activity!'); | 
					
						
							| 
									
										
										
										
											2021-11-29 23:58:42 +00:00
										 |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							| 
									
										
										
										
											2021-12-04 04:07:08 +00:00
										 |  |  |      * Internal tool to sign and send activities out | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @throws Exception | 
					
						
							| 
									
										
										
										
											2021-11-29 23:58:42 +00:00
										 |  |  |      */ | 
					
						
							| 
									
										
										
										
											2021-12-04 04:07:08 +00:00
										 |  |  |     private static function postman(Actor $sender, string $json_activity, string $inbox, string $method = 'post'): ResponseInterface | 
					
						
							| 
									
										
										
										
											2021-11-29 23:58:42 +00:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2021-12-04 04:07:08 +00:00
										 |  |  |         Log::debug('ActivityPub Postman: Delivering ' . $json_activity . ' to ' . $inbox); | 
					
						
							| 
									
										
										
										
											2021-11-29 23:58:42 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-04 04:07:08 +00:00
										 |  |  |         $headers = HTTPSignature::sign($sender, $inbox, $json_activity); | 
					
						
							| 
									
										
										
										
											2021-11-30 16:47:31 +00:00
										 |  |  |         Log::debug('ActivityPub Postman: Delivery headers were: ' . print_r($headers, true)); | 
					
						
							| 
									
										
										
										
											2021-11-29 23:58:42 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-04 04:07:08 +00:00
										 |  |  |         $response = HTTPClient::$method($inbox, ['headers' => $headers, 'body' => $json_activity]); | 
					
						
							| 
									
										
										
										
											2021-12-01 20:53:32 +00:00
										 |  |  |         Log::debug('ActivityPub Postman: Delivery result with status code ' . $response->getStatusCode() . ': ' . $response->getContent()); | 
					
						
							| 
									
										
										
										
											2021-11-29 23:58:42 +00:00
										 |  |  |         return $response; | 
					
						
							| 
									
										
										
										
											2021-11-27 15:06:46 +00:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-04 04:07:08 +00:00
										 |  |  |     // WebFinger Events
 | 
					
						
							| 
									
										
										
										
											2021-11-01 12:16:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-10-18 13:22:02 +01:00
										 |  |  |     /** | 
					
						
							| 
									
										
										
										
											2021-12-04 04:07:08 +00:00
										 |  |  |      * Add activity+json mimetype to WebFinger | 
					
						
							| 
									
										
										
										
											2021-10-18 13:22:02 +01:00
										 |  |  |      */ | 
					
						
							| 
									
										
										
										
											2021-10-27 04:14:01 +01:00
										 |  |  |     public function onEndWebFingerProfileLinks(XML_XRD $xrd, Actor $object): bool | 
					
						
							| 
									
										
										
										
											2021-10-04 17:00:58 +01:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2021-10-18 13:22:02 +01:00
										 |  |  |         if ($object->isPerson()) { | 
					
						
							|  |  |  |             $link = new XML_XRD_Element_Link( | 
					
						
							| 
									
										
										
										
											2021-10-27 04:14:01 +01:00
										 |  |  |                 rel: 'self', | 
					
						
							|  |  |  |                 href: $object->getUri(Router::ABSOLUTE_URL),//Router::url('actor_view_id', ['id' => $object->getId()], Router::ABSOLUTE_URL),
 | 
					
						
							|  |  |  |                 type: 'application/activity+json', | 
					
						
							| 
									
										
										
										
											2021-10-18 13:22:02 +01:00
										 |  |  |             ); | 
					
						
							|  |  |  |             $xrd->links[] = clone $link; | 
					
						
							| 
									
										
										
										
											2021-10-04 17:00:58 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-10-27 04:14:01 +01:00
										 |  |  |         return Event::next; | 
					
						
							| 
									
										
										
										
											2021-10-18 13:22:02 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-04 04:07:08 +00:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * When FreeNetwork component asks us to help with identifying Actors from XRDs | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2021-12-02 04:25:58 +00:00
										 |  |  |     public function onFreeNetworkFoundXrd(XML_XRD $xrd, ?Actor &$actor = null): bool | 
					
						
							| 
									
										
										
										
											2021-11-01 12:16:46 +00:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2021-12-02 04:25:58 +00:00
										 |  |  |         $addr = null; | 
					
						
							|  |  |  |         foreach ($xrd->aliases as $alias) { | 
					
						
							|  |  |  |             if (Discovery::isAcct($alias)) { | 
					
						
							|  |  |  |                 $addr = Discovery::normalize($alias); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-12-24 01:58:41 +00:00
										 |  |  |         if (\is_null($addr)) { | 
					
						
							| 
									
										
										
										
											2021-12-02 04:25:58 +00:00
										 |  |  |             return Event::next; | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             if (!FreeNetworkActorProtocol::canIAddr('activitypub', $addr)) { | 
					
						
							|  |  |  |                 return Event::next; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-12-02 03:34:31 +00:00
										 |  |  |         try { | 
					
						
							| 
									
										
										
										
											2021-12-02 04:25:58 +00:00
										 |  |  |             $ap_actor = ActivitypubActor::fromXrd($addr, $xrd); | 
					
						
							| 
									
										
										
										
											2021-12-24 01:58:41 +00:00
										 |  |  |             $actor    = Actor::getById($ap_actor->getActorId()); | 
					
						
							| 
									
										
										
										
											2021-12-02 04:25:58 +00:00
										 |  |  |             FreeNetworkActorProtocol::protocolSucceeded('activitypub', $actor, $addr); | 
					
						
							| 
									
										
										
										
											2021-12-02 03:34:31 +00:00
										 |  |  |             return Event::stop; | 
					
						
							|  |  |  |         } catch (Exception $e) { | 
					
						
							| 
									
										
										
										
											2021-12-04 04:07:08 +00:00
										 |  |  |             Log::error('ActivityPub Actor from URL Mention check failed: ' . $e->getMessage()); | 
					
						
							| 
									
										
										
										
											2021-12-02 03:34:31 +00:00
										 |  |  |             return Event::next; | 
					
						
							| 
									
										
										
										
											2021-11-01 12:16:46 +00:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-10-04 17:00:58 +01:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-11-01 12:16:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-04 04:07:08 +00:00
										 |  |  |     // Discovery Events
 | 
					
						
							| 
									
										
										
										
											2021-11-01 12:16:46 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							| 
									
										
										
										
											2021-12-04 04:07:08 +00:00
										 |  |  |      * When FreeNetwork component asks us to help with identifying Actors from URIs | 
					
						
							| 
									
										
										
										
											2021-11-01 12:16:46 +00:00
										 |  |  |      */ | 
					
						
							| 
									
										
										
										
											2021-12-04 04:07:08 +00:00
										 |  |  |     public function onFreeNetworkFindMentions(string $target, ?Actor &$actor = null): bool | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         try { | 
					
						
							|  |  |  |             if (FreeNetworkActorProtocol::canIAddr('activitypub', $addr = Discovery::normalize($target))) { | 
					
						
							| 
									
										
										
										
											2022-02-23 17:39:11 +00:00
										 |  |  |                 $ap_actor = DB::wrapInTransaction(fn () => ActivitypubActor::getByAddr($addr)); | 
					
						
							| 
									
										
										
										
											2021-12-24 01:58:41 +00:00
										 |  |  |                 $actor    = Actor::getById($ap_actor->getActorId()); | 
					
						
							| 
									
										
										
										
											2021-12-04 04:07:08 +00:00
										 |  |  |                 FreeNetworkActorProtocol::protocolSucceeded('activitypub', $actor->getId(), $addr); | 
					
						
							|  |  |  |                 return Event::stop; | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 return Event::next; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } catch (Exception $e) { | 
					
						
							| 
									
										
										
										
											2022-02-20 13:59:06 +00:00
										 |  |  |             Log::error('ActivityPub WebFinger Mention check failed.', [$e]); | 
					
						
							| 
									
										
										
										
											2021-12-04 04:07:08 +00:00
										 |  |  |             return Event::next; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-11-01 12:16:46 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-12 06:40:13 +00:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * @return string got from URI | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     public static function getUriByObject(mixed $object): string | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2022-01-02 20:37:15 +00:00
										 |  |  |         switch ($object::class) { | 
					
						
							|  |  |  |             case Note::class: | 
					
						
							|  |  |  |                 if ($object->getIsLocal()) { | 
					
						
							|  |  |  |                     return $object->getUrl(); | 
					
						
							|  |  |  |                 } else { | 
					
						
							|  |  |  |                     // Try known remote objects
 | 
					
						
							| 
									
										
										
										
											2022-02-18 17:48:06 +00:00
										 |  |  |                     $known_object = DB::findOneBy(ActivitypubObject::class, ['object_type' => 'note', 'object_id' => $object->getId()], return_null: true); | 
					
						
							| 
									
										
										
										
											2022-01-02 20:37:15 +00:00
										 |  |  |                     if ($known_object instanceof ActivitypubObject) { | 
					
						
							|  |  |  |                         return $known_object->getObjectUri(); | 
					
						
							|  |  |  |                     } else { | 
					
						
							|  |  |  |                         throw new BugFoundException('ActivityPub cannot generate an URI for a stored note.', [$object, $known_object]); | 
					
						
							|  |  |  |                     } | 
					
						
							| 
									
										
										
										
											2021-12-12 06:40:13 +00:00
										 |  |  |                 } | 
					
						
							| 
									
										
										
										
											2022-01-02 20:37:15 +00:00
										 |  |  |                 break; | 
					
						
							|  |  |  |             case Actor::class: | 
					
						
							|  |  |  |                 return $object->getUri(); | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  |             case Activity::class: | 
					
						
							|  |  |  |                 // Try known remote activities
 | 
					
						
							| 
									
										
										
										
											2022-02-18 17:48:06 +00:00
										 |  |  |                 $known_activity = DB::findOneBy(ActivitypubActivity::class, ['activity_id' => $object->getId()], return_null: true); | 
					
						
							|  |  |  |                 if (!\is_null($known_activity)) { | 
					
						
							| 
									
										
										
										
											2022-01-02 20:37:15 +00:00
										 |  |  |                     return $known_activity->getActivityUri(); | 
					
						
							|  |  |  |                 } else { | 
					
						
							|  |  |  |                     return Router::url('activity_view', ['id' => $object->getId()], Router::ABSOLUTE_URL); | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  |             default: | 
					
						
							|  |  |  |                 throw new InvalidArgumentException('ActivityPub::getUriByObject found a limitation with: ' . var_export($object, true)); | 
					
						
							| 
									
										
										
										
											2021-12-24 01:58:41 +00:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-12-12 06:40:13 +00:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-08 22:24:52 +00:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Get a Note from ActivityPub URI, if it doesn't exist, attempt to fetch it | 
					
						
							|  |  |  |      * This should only be necessary internally. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @throws ClientExceptionInterface | 
					
						
							|  |  |  |      * @throws RedirectionExceptionInterface | 
					
						
							|  |  |  |      * @throws ServerExceptionInterface | 
					
						
							|  |  |  |      * @throws TransportExceptionInterface | 
					
						
							| 
									
										
										
										
											2021-12-24 01:58:41 +00:00
										 |  |  |      * | 
					
						
							| 
									
										
										
										
											2022-02-23 17:39:11 +00:00
										 |  |  |      * @return null|Actor|mixed|Note got from URI | 
					
						
							| 
									
										
										
										
											2021-12-08 22:24:52 +00:00
										 |  |  |      */ | 
					
						
							|  |  |  |     public static function getObjectByUri(string $resource, bool $try_online = true) | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2022-01-02 20:37:15 +00:00
										 |  |  |         // Try known object
 | 
					
						
							| 
									
										
										
										
											2022-02-18 17:48:06 +00:00
										 |  |  |         $known_object = DB::findOneBy(ActivitypubObject::class, ['object_uri' => $resource], return_null: true); | 
					
						
							|  |  |  |         if (!\is_null($known_object)) { | 
					
						
							| 
									
										
										
										
											2021-12-08 22:24:52 +00:00
										 |  |  |             return $known_object->getObject(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-02 20:37:15 +00:00
										 |  |  |         // Try known activity
 | 
					
						
							| 
									
										
										
										
											2022-02-18 17:48:06 +00:00
										 |  |  |         $known_activity = DB::findOneBy(ActivitypubActivity::class, ['activity_uri' => $resource], return_null: true); | 
					
						
							|  |  |  |         if (!\is_null($known_activity)) { | 
					
						
							| 
									
										
										
										
											2021-12-08 22:24:52 +00:00
										 |  |  |             return $known_activity->getActivity(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-02 20:37:15 +00:00
										 |  |  |         // Try local Note
 | 
					
						
							| 
									
										
										
										
											2021-12-08 22:24:52 +00:00
										 |  |  |         if (Common::isValidHttpUrl($resource)) { | 
					
						
							|  |  |  |             $resource_parts = parse_url($resource); | 
					
						
							|  |  |  |             // TODO: Use URLMatcher
 | 
					
						
							| 
									
										
										
										
											2022-02-11 00:17:20 +00:00
										 |  |  |             if ($resource_parts['host'] === Common::config('site', 'server')) { | 
					
						
							| 
									
										
										
										
											2022-01-02 20:37:15 +00:00
										 |  |  |                 $local_note = DB::findOneBy('note', ['url' => $resource], return_null: true); | 
					
						
							| 
									
										
										
										
											2022-02-23 17:39:11 +00:00
										 |  |  |                 if (!\is_null($local_note)) { | 
					
						
							| 
									
										
										
										
											2021-12-08 22:24:52 +00:00
										 |  |  |                     return $local_note; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-02 20:37:15 +00:00
										 |  |  |         // Try Actor
 | 
					
						
							|  |  |  |         try { | 
					
						
							|  |  |  |             return self::getActorByUri($resource, try_online: false); | 
					
						
							|  |  |  |         } catch (Exception) { | 
					
						
							|  |  |  |             // Ignore, this is brute forcing, it's okay not to find
 | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-08 22:24:52 +00:00
										 |  |  |         // Try remote
 | 
					
						
							|  |  |  |         if (!$try_online) { | 
					
						
							| 
									
										
										
										
											2022-02-23 17:39:11 +00:00
										 |  |  |             return; | 
					
						
							| 
									
										
										
										
											2021-12-08 22:24:52 +00:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-24 01:58:41 +00:00
										 |  |  |         $response = HTTPClient::get($resource, ['headers' => self::HTTP_CLIENT_HEADERS]); | 
					
						
							| 
									
										
										
										
											2021-12-08 22:24:52 +00:00
										 |  |  |         // If it was deleted
 | 
					
						
							|  |  |  |         if ($response->getStatusCode() == 410) { | 
					
						
							|  |  |  |             //$obj = Type::create('Tombstone', ['id' => $resource]);
 | 
					
						
							| 
									
										
										
										
											2022-02-23 17:39:11 +00:00
										 |  |  |             return; | 
					
						
							| 
									
										
										
										
											2021-12-08 22:24:52 +00:00
										 |  |  |         } elseif (!HTTPClient::statusCodeIsOkay($response)) { // If it is unavailable
 | 
					
						
							|  |  |  |             throw new Exception('Non Ok Status Code for given Object id.'); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             return Model::jsonToType($response->getContent()); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-01 12:16:46 +00:00
										 |  |  |     /** | 
					
						
							| 
									
										
										
										
											2021-12-04 04:07:08 +00:00
										 |  |  |      * Get an Actor from ActivityPub URI, if it doesn't exist, attempt to fetch it | 
					
						
							|  |  |  |      * This should only be necessary internally. | 
					
						
							| 
									
										
										
										
											2021-11-01 12:16:46 +00:00
										 |  |  |      * | 
					
						
							| 
									
										
										
										
											2021-12-04 04:07:08 +00:00
										 |  |  |      * @throws NoSuchActorException | 
					
						
							| 
									
										
										
										
											2021-12-24 01:58:41 +00:00
										 |  |  |      * | 
					
						
							|  |  |  |      * @return Actor got from URI | 
					
						
							| 
									
										
										
										
											2021-11-01 12:16:46 +00:00
										 |  |  |      */ | 
					
						
							| 
									
										
										
										
											2022-01-02 20:37:15 +00:00
										 |  |  |     public static function getActorByUri(string $resource, bool $try_online = true): Actor | 
					
						
							| 
									
										
										
										
											2021-12-04 04:07:08 +00:00
										 |  |  |     { | 
					
						
							|  |  |  |         // Try local
 | 
					
						
							|  |  |  |         if (Common::isValidHttpUrl($resource)) { | 
					
						
							|  |  |  |             // This means $resource is a valid url
 | 
					
						
							|  |  |  |             $resource_parts = parse_url($resource); | 
					
						
							|  |  |  |             // TODO: Use URLMatcher
 | 
					
						
							| 
									
										
										
										
											2022-02-11 00:17:20 +00:00
										 |  |  |             if ($resource_parts['host'] === Common::config('site', 'server')) { | 
					
						
							| 
									
										
										
										
											2021-12-04 04:07:08 +00:00
										 |  |  |                 $str = $resource_parts['path']; | 
					
						
							|  |  |  |                 // actor_view_nickname
 | 
					
						
							|  |  |  |                 $renick = '/\/@(' . Nickname::DISPLAY_FMT . ')\/?/m'; | 
					
						
							|  |  |  |                 // actor_view_id
 | 
					
						
							|  |  |  |                 $reuri = '/\/actor\/(\d+)\/?/m'; | 
					
						
							|  |  |  |                 if (preg_match_all($renick, $str, $matches, PREG_SET_ORDER, 0) === 1) { | 
					
						
							| 
									
										
										
										
											2022-02-18 17:48:06 +00:00
										 |  |  |                     return DB::findOneBy(LocalUser::class, ['nickname' => $matches[0][1]])->getActor(); | 
					
						
							| 
									
										
										
										
											2021-12-04 04:07:08 +00:00
										 |  |  |                 } elseif (preg_match_all($reuri, $str, $matches, PREG_SET_ORDER, 0) === 1) { | 
					
						
							| 
									
										
										
										
											2021-12-24 01:58:41 +00:00
										 |  |  |                     return Actor::getById((int) $matches[0][1]); | 
					
						
							| 
									
										
										
										
											2021-12-04 04:07:08 +00:00
										 |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2022-01-03 00:57:37 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         // Try known remote
 | 
					
						
							|  |  |  |         $aprofile = DB::findOneBy(ActivitypubActor::class, ['uri' => $resource], return_null: true); | 
					
						
							|  |  |  |         if (!\is_null($aprofile)) { | 
					
						
							|  |  |  |             return Actor::getById($aprofile->getActorId()); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-04 04:07:08 +00:00
										 |  |  |         // Try remote
 | 
					
						
							| 
									
										
										
										
											2022-01-02 20:37:15 +00:00
										 |  |  |         if ($try_online) { | 
					
						
							|  |  |  |             $aprofile = ActivitypubActor::getByAddr($resource); | 
					
						
							|  |  |  |             if ($aprofile instanceof ActivitypubActor) { | 
					
						
							|  |  |  |                 return Actor::getById($aprofile->getActorId()); | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2021-12-04 04:07:08 +00:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2022-01-02 20:37:15 +00:00
										 |  |  |         throw new NoSuchActorException("From URI: {$resource}"); | 
					
						
							| 
									
										
										
										
											2021-12-04 04:07:08 +00:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-09-06 23:47:28 +01:00
										 |  |  | } |