diff --git a/components/FreeNetwork/Entity/FreenetworkActor.php b/components/FreeNetwork/Entity/FreenetworkActor.php deleted file mode 100644 index 5591e5d19b..0000000000 --- a/components/FreeNetwork/Entity/FreenetworkActor.php +++ /dev/null @@ -1,158 +0,0 @@ -. - -// }}} - -/** - * WebFinger implementation for GNU social - * - * @package GNUsocial - * - * @author Diogo Peralta Cordeiro profile_page; - } - - public function setProfilepage(string $profile_page): void - { - $this->profile_page = $profile_page; - } - - public function getSource(): string - { - return $this->source; - } - - public function setSource(string $source): void - { - $this->source = $source; - } - - public function getActorId(): int - { - return $this->actor_id; - } - - public function setActorId(int $actor_id): void - { - $this->actor_id = $actor_id; - } - - public function getIsLocal(): bool - { - return $this->is_local; - } - - public function setIsLocal(bool $is_local): void - { - $this->is_local = $is_local; - } - - public function getCreated(): DateTimeInterface - { - return $this->created; - } - - public function setCreated(DateTimeInterface $created): void - { - $this->created = $created; - } - - public function getModified(): DateTimeInterface - { - return $this->modified; - } - - public function setModified(DateTimeInterface $modified): void - { - $this->modified = $modified; - } - // @codeCoverageIgnoreEnd - // }}} Autocode - - public static function getOrCreateByRemoteUri($actor_uri): self - { - $fnactor = DB::findBy('freenetwork_actor', ['profile_page' => $actor_uri, 'is_local' => false]); - if ($fnactor === []) { - // TODO grab with webfinger - // If already has for a different protocol and isn't local, update - // else create actor and then fnactor - $fnactor = self::create([ - 'profile_page' => $actor_uri, - 'actor_id' => 1, - 'is_local' => false, - ]); - DB::persist($fnactor); - return $fnactor; - } else { - return $fnactor[0]; - } - } - - public static function schemaDef() - { - return [ - 'name' => 'freenetwork_actor', - 'fields' => [ - 'profile_page' => ['type' => 'text', 'not null' => true], - 'actor_id' => ['type' => 'int', 'not null' => true], - 'is_local' => ['type' => 'bool', 'not null' => true, 'description' => 'whether this was a locally generated or an imported actor'], - 'created' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was created'], - 'modified' => ['type' => 'timestamp', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'], - ], - 'primary key' => ['profile_page'], - 'indexes' => [ - 'freenetwork_profile_page_idx' => ['actor_id'], - ], - 'foreign keys' => [ - 'freenetwork_actor_actor_id_fkey' => ['actor', ['actor_id' => 'id']], - ], - ]; - } -} diff --git a/components/FreeNetwork/FreeNetwork.php b/components/FreeNetwork/FreeNetwork.php index a58c4383a4..45c119ab2a 100644 --- a/components/FreeNetwork/FreeNetwork.php +++ b/components/FreeNetwork/FreeNetwork.php @@ -21,14 +21,12 @@ declare(strict_types = 1); namespace Component\FreeNetwork; -use App\Core\DB\DB; use App\Core\Event; -use App\Core\Router\Router; -use Component\FreeNetwork\Entity\FreenetworkActor; use function App\Core\I18n\_m; use App\Core\Log; use App\Core\Modules\Component; use App\Core\Router\RouteLoader; +use App\Core\Router\Router; use App\Entity\Actor; use App\Entity\LocalUser; use App\Entity\Note; @@ -43,7 +41,7 @@ use Component\FreeNetwork\Controller\Webfinger; use Component\FreeNetwork\Util\Discovery; use Component\FreeNetwork\Util\WebfingerResource; use Component\FreeNetwork\Util\WebfingerResource\WebfingerResourceActor; -use Doctrine\ORM\NoResultException; +use Component\FreeNetwork\Util\WebfingerResource\WebfingerResourceNote; use Exception; use Plugin\ActivityPub\Entity\ActivitypubActivity; use Plugin\ActivityPub\Util\Response\TypeResponse; @@ -62,82 +60,46 @@ use XML_XRD_Element_Link; */ class FreeNetwork extends Component { - const PLUGIN_VERSION = '0.1.0'; + public const PLUGIN_VERSION = '0.1.0'; - const OAUTH_ACCESS_TOKEN_REL = 'http://apinamespace.org/oauth/access_token'; - const OAUTH_REQUEST_TOKEN_REL = 'http://apinamespace.org/oauth/request_token'; - const OAUTH_AUTHORIZE_REL = 'http://apinamespace.org/oauth/authorize'; + public const OAUTH_ACCESS_TOKEN_REL = 'http://apinamespace.org/oauth/access_token'; + public const OAUTH_REQUEST_TOKEN_REL = 'http://apinamespace.org/oauth/request_token'; + public const OAUTH_AUTHORIZE_REL = 'http://apinamespace.org/oauth/authorize'; public function onAddRoute(RouteLoader $m): bool { $m->connect('freenetwork_hostmeta', '.well-known/host-meta', [HostMeta::class, 'handle']); - $m->connect('freenetwork_hostmeta_format', '.well-known/host-meta.:format', + $m->connect( + 'freenetwork_hostmeta_format', + '.well-known/host-meta.:format', [HostMeta::class, 'handle'], - ['format' => '(xml|json)']); + ['format' => '(xml|json)'], + ); // the resource GET parameter can be anywhere, so don't mention it here $m->connect('freenetwork_webfinger', '.well-known/webfinger', [Webfinger::class, 'handle']); - $m->connect('freenetwork_webfinger_format', '.well-known/webfinger.:format', + $m->connect( + 'freenetwork_webfinger_format', + '.well-known/webfinger.:format', [Webfinger::class, 'handle'], - ['format' => '(xml|json)']); + ['format' => '(xml|json)'], + ); $m->connect('freenetwork_ownerxrd', 'main/ownerxrd', [OwnerXrd::class, 'handle']); return Event::next; } - public function onLoginAction($action, &$login) - { - switch ($action) { - case 'hostmeta': - case 'webfinger': - $login = true; - return false; - } - - return true; - } - - /** - * @param Actor $actor - * @param LocalUser $user - * @return bool - */ -// public function onSuccessfulLocalUserRegistration(Actor $actor, LocalUser $user): bool -// { -// //$profile_page = Router::url(id: 'actor_view_nickname', args: ['nickname' => $actor->getNickname()], type: Router::ABSOLUTE_URL); -// $profile_page = $actor->getUrl(Router::ABSOLUTE_URL); -// $actor_uri = $actor->getUri(Router::ABSOLUTE_URL); -// Event::handle('FreeNetworkSaveProfilePage', [$source = 'user', $actor_id = $actor->getId(), &$profile_page, &$actor_uri]); -// $fnactorpp = FreenetworkActor::create([ -// 'actor_uri' => $profile_page, -// 'source' => $source, -// 'actor_id' => $actor_id, -// 'is_local' => true, -// ]); -// DB::persist($fnactorpp); -// if ($profile_page !== $actor_uri) { -// $fnactoruri = FreenetworkActor::create([ -// 'actor_uri' => $actor_uri, -// 'source' => $source, -// 'actor_id' => $actor_id, -// 'is_local' => true, -// ]); -// DB::persist($fnactoruri); -// } -// return Event::next; -// } - - public function onStartGetProfileAcctUri(Actor $profile, &$acct) + public function onStartGetProfileAcctUri(Actor $profile, &$acct): bool { $wfr = new WebFingerResourceActor($profile); try { $acct = $wfr->reconstructAcct(); - } catch (Exception $e) { - return true; + } catch (Exception) { + return Event::next; } - return false; + return Event::stop; } - public function onEndGetWebFingerResource($resource, WebfingerResource &$target = null, array $args = []) + public function onEndGetWebFingerResource($resource, ?WebfingerResource &$target = null, array $args = []) { // * Either we didn't find the profile, then we want to make // the $profile variable null for clarity. @@ -147,57 +109,41 @@ class FreeNetwork extends Component // our search here by discarding the remote profile. $profile = null; if (Discovery::isAcct($resource)) { - $parts = explode('@', substr(urldecode($resource), 5)); // 5 is strlen of 'acct:' - if (count($parts) == 2) { - list($nick, $domain) = $parts; - if ($domain !== $_ENV['SOCIAL_DOMAIN']) {// XXX: Common::config('site', 'server')) { + $parts = explode('@', mb_substr(urldecode($resource), 5)); // 5 is strlen of 'acct:' + if (\count($parts) == 2) { + [$nick, $domain] = $parts; + if ($domain !== $_ENV['SOCIAL_DOMAIN']) { throw new ServerException(_m('Remote profiles not supported via WebFinger yet.')); } - $nick = Nickname::normalize(nickname: $nick, check_already_used: false, check_is_allowed: false); + $nick = Nickname::normalize(nickname: $nick, check_already_used: false, check_is_allowed: false); $freenetwork_actor = LocalUser::getWithPK(['nickname' => $nick]); if (!($freenetwork_actor instanceof LocalUser)) { throw new NoSuchActorException($nick); } $profile = $freenetwork_actor->getActor(); } - } elseif (!filter_var($resource, FILTER_VALIDATE_URL)) { - // Try the User URI lookup! - try { - $resource_parts = parse_url($resource); - if ($resource_parts['host'] === $_ENV['SOCIAL_DOMAIN']) { - $str = parse_url($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) { - $profile = LocalUser::getWithPK(['nickname' => $matches[1]])->getActor(); - } else if (preg_match_all($reuri, $str, $matches, PREG_SET_ORDER, 0) === 1) { - $profile = Actor::getById($matches[1]); - } - } else { - throw new NoResultException(); - } - } catch (NoResultException $e) { - // not a User, maybe a Note? we'll try that further down... - } } else { - // this means $resource is a common_valid_http_url (or https) - // First build up a set of alternative resource URLs that we can use. try { - Log::debug(__METHOD__ . ': Finding User URI for WebFinger lookup on resource==' . $resource); - $freenetwork_actor = FreenetworkActor::getWithPK(['profile_page' => $resource]); - if ($freenetwork_actor !== null) { - $profile = Actor::getById($freenetwork_actor->getActorId()); + if (filter_var($resource, \FILTER_VALIDATE_URL) !== false) { + // This means $resource is a valid url + $resource_parts = parse_url($resource); + // TODO: Use URLMatcher + if ($resource_parts['host'] === $_ENV['SOCIAL_DOMAIN']) { // XXX: Common::config('site', 'server')) { + $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) { + $profile = LocalUser::getWithPK(['nickname' => $matches[0][1]])->getActor(); + } elseif (preg_match_all($reuri, $str, $matches, \PREG_SET_ORDER, 0) === 1) { + $profile = Actor::getById((int) $matches[0][1]); + } + } } - unset($freenetwork_actor); - } catch (Exception $e) { - // Most likely a UserNoProfileException, if it ever happens - // and then we need to do some debugging and perhaps fixes. - Log::error(get_class($e) . ': ' . $e->getMessage()); - throw $e; - } + } catch (NoSuchActorException $e) { + // not a User, maybe a Note? we'll try that further down... // try { // Log::debug(__METHOD__ . ': Finding User_group URI for WebFinger lookup on resource==' . $resource); @@ -212,44 +158,46 @@ class FreeNetwork extends Component // Log::error(get_class($e) . ': ' . $e->getMessage()); // throw $e; // } + } } if ($profile instanceof Actor) { Log::debug(__METHOD__ . ': Found Profile with ID==' . $profile->getID() . ' for resource==' . $resource); $target = new WebfingerResourceActor($profile); - return false; // We got our target, stop handler execution + return Event::stop; // We got our target, stop handler execution } $APNote = ActivitypubActivity::getWithPK(['object_uri' => $resource]); if ($APNote instanceof ActivitypubActivity) { - $target = new WebfingerResource\WebfingerResourceNote(Note::getWithPK(['id' => $APNote->getObjectId()])); - return false; + $target = new WebfingerResourceNote(Note::getWithPK(['id' => $APNote->getObjectId()])); + return Event::stop; // We got our target, stop handler execution } - return true; + return Event::next; } - public function onStartHostMetaLinks(array &$links) + public function onStartHostMetaLinks(array &$links): bool { foreach (Discovery::supportedMimeTypes() as $type) { - $links[] = new XML_XRD_Element_Link(Discovery::LRDD_REL, + $links[] = new XML_XRD_Element_Link( + Discovery::LRDD_REL, Router::url(id: 'freenetwork_webfinger', args: [], type: Router::ABSOLUTE_URL) . '?resource={uri}', $type, - isTemplate: true); + isTemplate: true, + ); } // TODO OAuth connections //$links[] = new XML_XRD_Element_link(self::OAUTH_ACCESS_TOKEN_REL, common_local_url('ApiOAuthAccessToken')); //$links[] = new XML_XRD_Element_link(self::OAUTH_REQUEST_TOKEN_REL, common_local_url('ApiOAuthRequestToken')); //$links[] = new XML_XRD_Element_link(self::OAUTH_AUTHORIZE_REL, common_local_url('ApiOAuthAuthorize')); + return Event::next; } /** * Add a link header for LRDD Discovery - * - * @param mixed $action */ - public function onStartShowHTML($action) + public function onStartShowHTML($action): bool { if ($action instanceof ShowstreamAction) { $resource = $action->getTarget()->getUri(); @@ -259,18 +207,21 @@ class FreeNetwork extends Component header('Link: <' . $url . '>; rel="' . Discovery::LRDD_REL . '"; type="' . $type . '"', false); } } + return Event::next; } - public function onStartDiscoveryMethodRegistration(Discovery $disco) + public function onStartDiscoveryMethodRegistration(Discovery $disco): bool { - $disco->registerMethod('LRDDMethod_WebFinger'); + $disco->registerMethod('\Component\FreeNetwork\Util\LrddMethod\LrddMethodWebfinger'); + return Event::next; } - public function onEndDiscoveryMethodRegistration(Discovery $disco) + public function onEndDiscoveryMethodRegistration(Discovery $disco): bool { - $disco->registerMethod('LRDDMethod_HostMeta'); - $disco->registerMethod('LRDDMethod_LinkHeader'); - $disco->registerMethod('LRDDMethod_LinkHTML'); + $disco->registerMethod('\Component\FreeNetwork\Util\LrddMethod\LrddMethodHostMeta'); + $disco->registerMethod('\Component\FreeNetwork\Util\LrddMethod\LrddMethodLinkHeader'); + $disco->registerMethod('\Component\FreeNetwork\Util\LrddMethod\LrddMethodLinkHtml'); + return Event::next; } /** @@ -279,7 +230,7 @@ class FreeNetwork extends Component */ public function onControllerResponseInFormat(string $route, array $accept_header, array $vars, ?TypeResponse &$response = null): bool { - if (!in_array($route, ['freenetwork_hostmeta', 'freenetwork_hostmeta_format', 'freenetwork_webfinger', 'freenetwork_webfinger_format', 'freenetwork_ownerxrd'])) { + if (!\in_array($route, ['freenetwork_hostmeta', 'freenetwork_hostmeta_format', 'freenetwork_webfinger', 'freenetwork_webfinger_format', 'freenetwork_ownerxrd'])) { return Event::next; } @@ -293,7 +244,7 @@ class FreeNetwork extends Component * -- RFC 7033 (WebFinger) * http://tools.ietf.org/html/rfc7033 */ - $mimeType = count($mimeType) !== 0 ? array_pop($mimeType) : $vars['default_mimetype']; + $mimeType = \count($mimeType) !== 0 ? array_pop($mimeType) : $vars['default_mimetype']; $headers = []; @@ -310,32 +261,6 @@ class FreeNetwork extends Component return Event::stop; } - /** - * Fetch all the aliases of some remote profile - * - * @param string $uri profile's URI - * - * @throws Exception (If the Discovery's HTTP requests fail) - * - * @return null|array aliases - * - * @author Bruno Casteleiro - */ - public static function grab_profile_aliases(string $uri): ?array - { - $disco = new Discovery(); - $xrd = $disco->lookup($uri); - - $all_ids = array_merge([$xrd->subject], $xrd->aliases); - - if (!in_array($uri, $all_ids)) { - Log::info('The original URI was not listed itself when doing discovery on it!'); - return null; - } - - return $all_ids; - } - public function onPluginVersion(array &$versions): bool { $versions[] = [ diff --git a/components/FreeNetwork/Util/Discovery.php b/components/FreeNetwork/Util/Discovery.php index 9907eb8260..a61b9de824 100644 --- a/components/FreeNetwork/Util/Discovery.php +++ b/components/FreeNetwork/Util/Discovery.php @@ -1,4 +1,6 @@ self::JRD_MIMETYPE, @@ -83,10 +86,8 @@ class Discovery * Register a discovery class * * @param string $class Class name - * - * @return void */ - public function registerMethod($class) + public function registerMethod($class): void { $this->methods[] = $class; } @@ -98,9 +99,9 @@ class Discovery * * @return XML_XRD object for the resource descriptor of the id */ - public function lookup($id) + public function lookup(string $id): XML_XRD { - // Normalize the incoming $id to make sure we have a uri + // Normalize the incoming $id to make sure we have an uri $uri = self::normalize($id); Log::debug(sprintf('Performing discovery for "%s" (normalized "%s")', $id, $uri)); @@ -123,18 +124,17 @@ class Discovery throw new Exception('No resource descriptor URI in link.'); } - $client = new HTTPClient(); $headers = []; - if (!is_null($link->type)) { - $headers[] = "Accept: {$link->type}"; + if (!\is_null($link->type)) { + $headers['Accept'] = $link->type; } - $response = $client->get($xrd_uri, $headers); - if ($response->getStatus() != 200) { + $response = HTTPClient::get($xrd_uri, ['headers' => $headers]); + if ($response->getStatusCode() !== 200) { throw new Exception('Unexpected HTTP status code.'); } - switch (common_bare_mime($response->getHeader('content-type'))) { + switch (GSFile::mimetypeBare($response->getHeaders()['content-type'][0])) { case self::JRD_MIMETYPE_OLD: case self::JRD_MIMETYPE: $type = 'json'; @@ -144,24 +144,24 @@ class Discovery break; default: // fall back to letting XML_XRD auto-detect - Log::debug('No recognized content-type header for resource descriptor body on ' . _ve($xrd_uri)); + Log::debug('No recognized content-type header for resource descriptor body on ' . $xrd_uri); $type = null; } - $xrd->loadString($response->getBody(), $type); + $xrd->loadString($response->getContent(), $type); return $xrd; } catch (ClientException $e) { if ($e->getCode() === 403) { - Log::info(sprintf('%s: Aborting discovery on URL %s: %s', _ve($class), _ve($uri), _ve($e->getMessage()))); + Log::info(sprintf('%s: Aborting discovery on URL %s: %s', $class, $uri, $e->getMessage())); break; } } catch (Exception $e) { - Log::info(sprintf('%s: Failed for %s: %s', _ve($class), _ve($uri), _ve($e->getMessage()))); + Log::info(sprintf('%s: Failed for %s: %s', $class, $uri, $e->getMessage())); continue; } } // TRANS: Exception. %s is an ID. - throw new Exception(sprintf(_('Unable to find services for %s.'), $id)); + throw new Exception(sprintf(_m('Unable to find services for %s.'), $id)); } /** @@ -170,9 +170,9 @@ class Discovery * @param array $links Links to check (as instances of XML_XRD_Element_Link) * @param string $service Service to find * - * @return array $link assoc array representing the link + * @return XML_XRD_Element_Link $link */ - public static function getService(array $links, $service) + public static function getService(array $links, $service): XML_XRD_Element_Link { foreach ($links as $link) { if ($link->rel === $service) { @@ -187,17 +187,12 @@ class Discovery /** * Given a "user id" make sure it's normalized to an acct: uri * - * @param string $user_id User ID to normalize - * @param mixed $uri + * @param string $uri User ID to normalize * * @return string normalized acct: URI */ - public static function normalize($uri) + public static function normalize(string $uri): string { - if (is_null($uri) || $uri === '') { - throw new Exception(_m('No resource given.')); - } - $parts = parse_url($uri); // If we don't have a scheme, but the path implies user@host, // though this is far from a perfect matching procedure... @@ -209,7 +204,7 @@ class Discovery return $uri; } - public static function isAcct($uri) + public static function isAcct($uri): bool { return mb_strtolower(mb_substr($uri, 0, 5)) == 'acct:'; } @@ -224,10 +219,8 @@ class Discovery * * @return string replaced values */ - public static function applyTemplate($template, $uri) + public static function applyTemplate($template, $uri): string { - $template = str_replace('{uri}', urlencode($uri), $template); - - return $template; + return str_replace('{uri}', urlencode($uri), $template); } } diff --git a/components/FreeNetwork/Util/LrddMethod.php b/components/FreeNetwork/Util/LrddMethod.php index be756b7800..9d04ee5a13 100644 --- a/components/FreeNetwork/Util/LrddMethod.php +++ b/components/FreeNetwork/Util/LrddMethod.php @@ -1,9 +1,13 @@ get($url); - break; - case HTTPClient::METHOD_HEAD: - $response = $client->head($url); - break; - default: - throw new Exception('Bad HTTP method.'); - } - - if ($response->getStatus() != 200) { + if ($response->getStatusCode() !== 200) { throw new Exception('Unexpected HTTP status code.'); } diff --git a/components/FreeNetwork/Util/LrddMethod/LrddMethodHostMeta.php b/components/FreeNetwork/Util/LrddMethod/LrddMethodHostMeta.php index 7e502e8dff..e50b49fe3f 100644 --- a/components/FreeNetwork/Util/LrddMethod/LrddMethodHostMeta.php +++ b/components/FreeNetwork/Util/LrddMethod/LrddMethodHostMeta.php @@ -17,6 +17,7 @@ namespace Component\FreeNetwork\Util\LrddMethod; // You should have received a copy of the GNU Affero General Public License // along with GNU social. If not, see . use App\Core\Log; +use Component\FreeNetwork\Util\Discovery; use Component\FreeNetwork\Util\LrddMethod; use Exception; @@ -72,7 +73,7 @@ class LrddMethodHostMeta extends LRDDMethod try { $response = self::fetchUrl($url); - $this->xrd->loadString($response->getBody()); + $this->xrd->loadString($response->getContent()); } catch (Exception $e) { Log::debug('LRDD could not load resource descriptor: ' . $url . ' (' . $e->getMessage() . ')'); continue; diff --git a/components/FreeNetwork/Util/LrddMethod/LrddMethodLinkHeader.php b/components/FreeNetwork/Util/LrddMethod/LrddMethodLinkHeader.php index 35b04216a4..634bb157e5 100644 --- a/components/FreeNetwork/Util/LrddMethod/LrddMethodLinkHeader.php +++ b/components/FreeNetwork/Util/LrddMethod/LrddMethodLinkHeader.php @@ -1,8 +1,9 @@ getHeader('Link'); + $link_header = $response->getHeaders()['link'][0]; if (empty($link_header)) { throw new Exception('No Link header found'); } diff --git a/components/FreeNetwork/Util/LrddMethod/LrddMethodLinkHtml.php b/components/FreeNetwork/Util/LrddMethod/LrddMethodLinkHtml.php index a1e76e14e5..8d9f5b1e4f 100644 --- a/components/FreeNetwork/Util/LrddMethod/LrddMethodLinkHtml.php +++ b/components/FreeNetwork/Util/LrddMethod/LrddMethodLinkHtml.php @@ -47,7 +47,7 @@ class LrddMethodLinkHtml extends LRDDMethod { $response = self::fetchUrl($uri); - return self::parse($response->getBody()); + return self::parse($response->getContent()); } /** diff --git a/social.yaml b/social.yaml index f1b0f90e7a..c813a32587 100644 --- a/social.yaml +++ b/social.yaml @@ -265,7 +265,7 @@ parameters: proxy_auth_scheme: discovery: - CORS: false + cors: false streams: notes_per_page: 32