From 890bbfa1a78e2af22d2e14b384742782edf9403c Mon Sep 17 00:00:00 2001 From: Michael Date: Wed, 27 Nov 2019 07:25:54 +0000 Subject: [PATCH 1/6] We now support nodeinfo version 2 / basic nodeinfo data is always enabled --- src/Module/NodeInfo.php | 260 +++++++++++++++++++++++++-------------- static/routes.config.php | 24 ++-- 2 files changed, 180 insertions(+), 104 deletions(-) diff --git a/src/Module/NodeInfo.php b/src/Module/NodeInfo.php index 28f23196f9..3d1a0f5666 100644 --- a/src/Module/NodeInfo.php +++ b/src/Module/NodeInfo.php @@ -13,15 +13,6 @@ use Friendica\Core\System; */ class NodeInfo extends BaseModule { - public static function init(array $parameters = []) - { - $config = self::getApp()->getConfig(); - - if (!$config->get('system', 'nodeinfo')) { - throw new \Friendica\Network\HTTPException\NotFoundException(); - } - } - public static function rawContent(array $parameters = []) { $app = self::getApp(); @@ -31,8 +22,12 @@ class NodeInfo extends BaseModule if (($app->argc > 1) && ($app->argv[0] == '.well-known')) { self::printWellKnown($app); // otherwise print the nodeinfo + } elseif ($parameters['version'] == '1.0') { + self::printNodeInfo1($app); + } elseif ($parameters['version'] == '2.0') { + self::printNodeInfo2($app); } else { - self::printNodeInfo($app); + throw new \Friendica\Network\HTTPException\NotFoundException(); } } @@ -45,16 +40,13 @@ class NodeInfo extends BaseModule */ private static function printWellKnown(App $app) { - $config = $app->getConfig(); - - if (!$config->get('system', 'nodeinfo')) { - throw new \Friendica\Network\HTTPException\NotFoundException(); - } - $nodeinfo = [ - 'links' => [[ - 'rel' => 'http://nodeinfo.diaspora.software/ns/schema/1.0', - 'href' => $app->getBaseURL() . '/nodeinfo/1.0']] + 'links' => [ + ['rel' => 'http://nodeinfo.diaspora.software/ns/schema/1.0', + 'href' => $app->getBaseURL() . '/nodeinfo/1.0'], + ['rel' => 'http://nodeinfo.diaspora.software/ns/schema/2.0', + 'href' => $app->getBaseURL() . '/nodeinfo/2.0'], + ] ]; header('Content-type: application/json; charset=utf-8'); @@ -63,34 +55,121 @@ class NodeInfo extends BaseModule } /** - * Print the nodeinfo + * Return the supported services * * @param App $app - */ - private static function printNodeInfo(App $app) + * + * @return array with supported services + */ + private static function getUsage(App $app) { $config = $app->getConfig(); + $usage = []; + + if (!empty($config->get('system', 'nodeinfo'))) { + $usage['users'] = [ + 'total' => intval($config->get('nodeinfo', 'total_users')), + 'activeHalfyear' => intval($config->get('nodeinfo', 'active_users_halfyear')), + 'activeMonth' => intval($config->get('nodeinfo', 'active_users_monthly')) + ]; + $usage['localPosts'] = intval($config->get('nodeinfo', 'local_posts')); + $usage['localComments'] = intval($config->get('nodeinfo', 'local_comments')); + } + + return $usage; + } + + /** + * Return the supported services + * + * @param App $app + * + * @return array with supported services + */ + private static function getServices(App $app) + { + $config = $app->getConfig(); + + $services = [ + 'inbound' => [], + 'outbound' => [], + ]; + $smtp = (function_exists('imap_open') && !$config->get('system', 'imap_disabled') && !$config->get('system', 'dfrn_only')); + if (Addon::isEnabled('blogger')) { + $services['outbound'][] = 'blogger'; + } + if (Addon::isEnabled('dwpost')) { + $services['outbound'][] = 'dreamwidth'; + } + if (Addon::isEnabled('statusnet')) { + $services['inbound'][] = 'gnusocial'; + $services['outbound'][] = 'gnusocial'; + } + if (Addon::isEnabled('ijpost')) { + $services['outbound'][] = 'insanejournal'; + } + if (Addon::isEnabled('libertree')) { + $services['outbound'][] = 'libertree'; + } + if (Addon::isEnabled('buffer')) { + $services['outbound'][] = 'linkedin'; + } + if (Addon::isEnabled('ljpost')) { + $services['outbound'][] = 'livejournal'; + } + if (Addon::isEnabled('buffer')) { + $services['outbound'][] = 'pinterest'; + } + if (Addon::isEnabled('posterous')) { + $services['outbound'][] = 'posterous'; + } + if (Addon::isEnabled('pumpio')) { + $services['inbound'][] = 'pumpio'; + $services['outbound'][] = 'pumpio'; + } + if ($smtp) { + $services['outbound'][] = 'smtp'; + } + if (Addon::isEnabled('tumblr')) { + $services['outbound'][] = 'tumblr'; + } + if (Addon::isEnabled('twitter') || Addon::isEnabled('buffer')) { + $services['outbound'][] = 'twitter'; + } + if (Addon::isEnabled('wppost')) { + $services['outbound'][] = 'wordpress'; + } + + return $services; + } + + /** + * Print the nodeinfo version 1 + * + * @param App $app + */ + private static function printNodeInfo1(App $app) + { + $config = $app->getConfig(); + $nodeinfo = [ - 'version' => 1.0, + 'version' => '1.0', 'software' => [ 'name' => 'friendica', 'version' => FRIENDICA_VERSION . '-' . DB_UPDATE_VERSION, ], 'protocols' => [ 'inbound' => [ - 'friendica', + 'friendica', 'activitypub' ], 'outbound' => [ - 'friendica', + 'friendica', 'activitypub' ], ], - 'services' => [ - 'inbound' => [], - 'outbound' => [], - ], + 'services' => [], 'usage' => [], 'openRegistrations' => intval($config->get('config', 'register_policy')) !== Register::CLOSED, 'metadata' => [ @@ -108,75 +187,72 @@ class NodeInfo extends BaseModule $nodeinfo['protocols']['outbound'][] = 'gnusocial'; } - if (!empty($config->get('system', 'nodeinfo'))) { + $nodeinfo['usage'] = self::getUsage($app); - $nodeinfo['usage']['users'] = [ - 'total' => intval($config->get('nodeinfo', 'total_users')), - 'activeHalfyear' => intval($config->get('nodeinfo', 'active_users_halfyear')), - 'activeMonth' => intval($config->get('nodeinfo', 'active_users_monthly')) - ]; - $nodeinfo['usage']['localPosts'] = intval($config->get('nodeinfo', 'local_posts')); - $nodeinfo['usage']['localComments'] = intval($config->get('nodeinfo', 'local_comments')); + $nodeinfo['services'] = self::getServices($app); - if (Addon::isEnabled('blogger')) { - $nodeinfo['services']['outbound'][] = 'blogger'; - } - if (Addon::isEnabled('dwpost')) { - $nodeinfo['services']['outbound'][] = 'dreamwidth'; - } - if (Addon::isEnabled('statusnet')) { - $nodeinfo['services']['inbound'][] = 'gnusocial'; - $nodeinfo['services']['outbound'][] = 'gnusocial'; - } - if (Addon::isEnabled('ijpost')) { - $nodeinfo['services']['outbound'][] = 'insanejournal'; - } - if (Addon::isEnabled('libertree')) { - $nodeinfo['services']['outbound'][] = 'libertree'; - } - if (Addon::isEnabled('buffer')) { - $nodeinfo['services']['outbound'][] = 'linkedin'; - } - if (Addon::isEnabled('ljpost')) { - $nodeinfo['services']['outbound'][] = 'livejournal'; - } - if (Addon::isEnabled('buffer')) { - $nodeinfo['services']['outbound'][] = 'pinterest'; - } - if (Addon::isEnabled('posterous')) { - $nodeinfo['services']['outbound'][] = 'posterous'; - } - if (Addon::isEnabled('pumpio')) { - $nodeinfo['services']['inbound'][] = 'pumpio'; - $nodeinfo['services']['outbound'][] = 'pumpio'; - } + $nodeinfo['metadata']['protocols'] = $nodeinfo['protocols']; + $nodeinfo['metadata']['protocols']['outbound'][] = 'atom1.0'; + $nodeinfo['metadata']['protocols']['inbound'][] = 'atom1.0'; + $nodeinfo['metadata']['protocols']['inbound'][] = 'rss2.0'; - if ($smtp) { - $nodeinfo['services']['outbound'][] = 'smtp'; - } - if (Addon::isEnabled('tumblr')) { - $nodeinfo['services']['outbound'][] = 'tumblr'; - } - if (Addon::isEnabled('twitter') || Addon::isEnabled('buffer')) { - $nodeinfo['services']['outbound'][] = 'twitter'; - } - if (Addon::isEnabled('wppost')) { - $nodeinfo['services']['outbound'][] = 'wordpress'; - } - $nodeinfo['metadata']['protocols'] = $nodeinfo['protocols']; - $nodeinfo['metadata']['protocols']['outbound'][] = 'atom1.0'; - $nodeinfo['metadata']['protocols']['inbound'][] = 'atom1.0'; - $nodeinfo['metadata']['protocols']['inbound'][] = 'rss2.0'; + $nodeinfo['metadata']['services'] = $nodeinfo['services']; - $nodeinfo['metadata']['services'] = $nodeinfo['services']; - - if (Addon::isEnabled('twitter')) { - $nodeinfo['metadata']['services']['inbound'][] = 'twitter'; - } - - $nodeinfo['metadata']['explicitContent'] = $config->get('system', 'explicit_content', false) == true; + if (Addon::isEnabled('twitter')) { + $nodeinfo['metadata']['services']['inbound'][] = 'twitter'; } + $nodeinfo['metadata']['explicitContent'] = $config->get('system', 'explicit_content', false) == true; + + header('Content-type: application/json; charset=utf-8'); + echo json_encode($nodeinfo, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); + exit; + } + + /** + * Print the nodeinfo version 2 + * + * @param App $app + */ + private static function printNodeInfo2(App $app) + { + $config = $app->getConfig(); + + $smtp = (function_exists('imap_open') && !$config->get('system', 'imap_disabled') && !$config->get('system', 'dfrn_only')); + + $nodeinfo = [ + 'version' => '2.0', + 'software' => [ + 'name' => 'friendica', + 'version' => FRIENDICA_VERSION . '-' . DB_UPDATE_VERSION, + ], + 'protocols' => ['dfrn', 'activitypub'], + 'services' => [], + 'usage' => [], + 'openRegistrations' => intval($config->get('config', 'register_policy')) !== Register::CLOSED, + 'metadata' => [ + 'nodeName' => $config->get('config', 'sitename'), + ], + ]; + + if (!empty($config->get('system', 'diaspora_enabled'))) { + $nodeinfo['protocols'][] = 'diaspora'; + } + + if (empty($config->get('system', 'ostatus_disabled'))) { + $nodeinfo['protocols'][] = 'ostatus'; + } + + $nodeinfo['usage'] = self::getUsage($app); + + $nodeinfo['services'] = self::getServices($app); + + $nodeinfo['services']['inbound'][] = 'atom1.0'; + $nodeinfo['services']['outbound'][] = 'atom1.0'; + $nodeinfo['services']['outbound'][] = 'rss2.0'; + + $nodeinfo['metadata']['explicitContent'] = $config->get('system', 'explicit_content', false) == true; + header('Content-type: application/json; charset=utf-8'); echo json_encode($nodeinfo, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); exit; diff --git a/static/routes.config.php b/static/routes.config.php index 339860afe6..bc98fd5952 100644 --- a/static/routes.config.php +++ b/static/routes.config.php @@ -17,7 +17,7 @@ return [ '/.well-known' => [ '/host-meta' => [Module\WellKnown\HostMeta::class, [R::GET]], - '/nodeinfo[/1.0]' => [Module\NodeInfo::class, [R::GET]], + '/nodeinfo' => [Module\NodeInfo::class, [R::GET]], '/webfinger' => [Module\Xrd::class, [R::GET]], '/x-social-relay' => [Module\WellKnown\XSocialRelay::class, [R::GET]], ], @@ -145,17 +145,17 @@ return [ '/ignore/{id}' => [Module\Item\Ignore::class, [R::GET]], ], - '/like/{item:\d+}' => [Module\Like::class, [R::GET]], - '/localtime' => [Module\Debug\Localtime::class, [R::GET, R::POST]], - '/login' => [Module\Login::class, [R::GET, R::POST]], - '/logout' => [Module\Logout::class, [R::GET, R::POST]], - '/magic' => [Module\Magic::class, [R::GET]], - '/maintenance' => [Module\Maintenance::class, [R::GET]], - '/manifest' => [Module\Manifest::class, [R::GET]], - '/modexp/{nick}' => [Module\PublicRSAKey::class, [R::GET]], - '/newmember' => [Module\Welcome::class, [R::GET]], - '/nodeinfo/1.0' => [Module\NodeInfo::class, [R::GET]], - '/nogroup' => [Module\Group::class, [R::GET]], + '/like/{item:\d+}' => [Module\Like::class, [R::GET]], + '/localtime' => [Module\Debug\Localtime::class, [R::GET, R::POST]], + '/login' => [Module\Login::class, [R::GET, R::POST]], + '/logout' => [Module\Logout::class, [R::GET, R::POST]], + '/magic' => [Module\Magic::class, [R::GET]], + '/maintenance' => [Module\Maintenance::class, [R::GET]], + '/manifest' => [Module\Manifest::class, [R::GET]], + '/modexp/{nick}' => [Module\PublicRSAKey::class, [R::GET]], + '/newmember' => [Module\Welcome::class, [R::GET]], + '/nodeinfo/{version}' => [Module\NodeInfo::class, [R::GET]], + '/nogroup' => [Module\Group::class, [R::GET]], '/notify' => [ '[/]' => [Module\Notifications\Notify::class, [R::GET]], From a26689f69c057cbf29698396c916573302a7f2e7 Mon Sep 17 00:00:00 2001 From: Michael Date: Wed, 27 Nov 2019 07:32:46 +0000 Subject: [PATCH 2/6] remove unused variable --- src/Module/NodeInfo.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Module/NodeInfo.php b/src/Module/NodeInfo.php index 3d1a0f5666..e1b75d666d 100644 --- a/src/Module/NodeInfo.php +++ b/src/Module/NodeInfo.php @@ -218,8 +218,6 @@ class NodeInfo extends BaseModule { $config = $app->getConfig(); - $smtp = (function_exists('imap_open') && !$config->get('system', 'imap_disabled') && !$config->get('system', 'dfrn_only')); - $nodeinfo = [ 'version' => '2.0', 'software' => [ From 054a111f3cc52add28ebc07f5e3ae9e01a41a8e4 Mon Sep 17 00:00:00 2001 From: Michael Date: Wed, 27 Nov 2019 07:36:24 +0000 Subject: [PATCH 3/6] Twitter is also inbound --- src/Module/NodeInfo.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Module/NodeInfo.php b/src/Module/NodeInfo.php index e1b75d666d..eedbe8ee7c 100644 --- a/src/Module/NodeInfo.php +++ b/src/Module/NodeInfo.php @@ -245,6 +245,10 @@ class NodeInfo extends BaseModule $nodeinfo['services'] = self::getServices($app); + if (Addon::isEnabled('twitter')) { + $nodeinfo['services']['inbound'][] = 'twitter'; + } + $nodeinfo['services']['inbound'][] = 'atom1.0'; $nodeinfo['services']['outbound'][] = 'atom1.0'; $nodeinfo['services']['outbound'][] = 'rss2.0'; From ae306ea7dea765ade9f15a672ae63222c3c698fc Mon Sep 17 00:00:00 2001 From: Michael Date: Wed, 27 Nov 2019 07:38:57 +0000 Subject: [PATCH 4/6] rss is inbound --- src/Module/NodeInfo.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Module/NodeInfo.php b/src/Module/NodeInfo.php index eedbe8ee7c..999b3fb8f2 100644 --- a/src/Module/NodeInfo.php +++ b/src/Module/NodeInfo.php @@ -250,8 +250,8 @@ class NodeInfo extends BaseModule } $nodeinfo['services']['inbound'][] = 'atom1.0'; + $nodeinfo['services']['inbound'][] = 'rss2.0'; $nodeinfo['services']['outbound'][] = 'atom1.0'; - $nodeinfo['services']['outbound'][] = 'rss2.0'; $nodeinfo['metadata']['explicitContent'] = $config->get('system', 'explicit_content', false) == true; From cb37588375c6a765b1d289e26a3e38c730d9a282 Mon Sep 17 00:00:00 2001 From: Michael Date: Wed, 27 Nov 2019 07:44:59 +0000 Subject: [PATCH 5/6] We can always send via SMTP, receiving mails is via IMAP --- src/Module/NodeInfo.php | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/src/Module/NodeInfo.php b/src/Module/NodeInfo.php index 999b3fb8f2..a4bc9be2b4 100644 --- a/src/Module/NodeInfo.php +++ b/src/Module/NodeInfo.php @@ -89,15 +89,11 @@ class NodeInfo extends BaseModule */ private static function getServices(App $app) { - $config = $app->getConfig(); - $services = [ 'inbound' => [], 'outbound' => [], ]; - $smtp = (function_exists('imap_open') && !$config->get('system', 'imap_disabled') && !$config->get('system', 'dfrn_only')); - if (Addon::isEnabled('blogger')) { $services['outbound'][] = 'blogger'; } @@ -130,9 +126,9 @@ class NodeInfo extends BaseModule $services['inbound'][] = 'pumpio'; $services['outbound'][] = 'pumpio'; } - if ($smtp) { - $services['outbound'][] = 'smtp'; - } + + $services['outbound'][] = 'smtp'; + if (Addon::isEnabled('tumblr')) { $services['outbound'][] = 'tumblr'; } @@ -218,6 +214,8 @@ class NodeInfo extends BaseModule { $config = $app->getConfig(); + $imap = (function_exists('imap_open') && !$config->get('system', 'imap_disabled') && !$config->get('system', 'dfrn_only')); + $nodeinfo = [ 'version' => '2.0', 'software' => [ @@ -253,6 +251,10 @@ class NodeInfo extends BaseModule $nodeinfo['services']['inbound'][] = 'rss2.0'; $nodeinfo['services']['outbound'][] = 'atom1.0'; + if ($imap) { + $nodeinfo['services']['inbound'][] = 'imap'; + } + $nodeinfo['metadata']['explicitContent'] = $config->get('system', 'explicit_content', false) == true; header('Content-type: application/json; charset=utf-8'); From 0979daddacbcdb043e5968b9a68f6b8ca9375ea9 Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 28 Nov 2019 07:07:34 +0000 Subject: [PATCH 6/6] Modules separated --- src/Module/NodeInfo.php | 30 +-------------------- src/Module/WellKnown/NodeInfo.php | 43 +++++++++++++++++++++++++++++++ static/routes.config.php | 2 +- 3 files changed, 45 insertions(+), 30 deletions(-) create mode 100644 src/Module/WellKnown/NodeInfo.php diff --git a/src/Module/NodeInfo.php b/src/Module/NodeInfo.php index a4bc9be2b4..deec94d93f 100644 --- a/src/Module/NodeInfo.php +++ b/src/Module/NodeInfo.php @@ -17,12 +17,7 @@ class NodeInfo extends BaseModule { $app = self::getApp(); - // @TODO: Replace with parameter from router - // if the first argument is ".well-known", print the well-known text - if (($app->argc > 1) && ($app->argv[0] == '.well-known')) { - self::printWellKnown($app); - // otherwise print the nodeinfo - } elseif ($parameters['version'] == '1.0') { + if ($parameters['version'] == '1.0') { self::printNodeInfo1($app); } elseif ($parameters['version'] == '2.0') { self::printNodeInfo2($app); @@ -31,29 +26,6 @@ class NodeInfo extends BaseModule } } - /** - * Prints the well-known nodeinfo redirect - * - * @param App $app - * - * @throws \Friendica\Network\HTTPException\NotFoundException - */ - private static function printWellKnown(App $app) - { - $nodeinfo = [ - 'links' => [ - ['rel' => 'http://nodeinfo.diaspora.software/ns/schema/1.0', - 'href' => $app->getBaseURL() . '/nodeinfo/1.0'], - ['rel' => 'http://nodeinfo.diaspora.software/ns/schema/2.0', - 'href' => $app->getBaseURL() . '/nodeinfo/2.0'], - ] - ]; - - header('Content-type: application/json; charset=utf-8'); - echo json_encode($nodeinfo, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); - exit; - } - /** * Return the supported services * diff --git a/src/Module/WellKnown/NodeInfo.php b/src/Module/WellKnown/NodeInfo.php new file mode 100644 index 0000000000..7d87252edd --- /dev/null +++ b/src/Module/WellKnown/NodeInfo.php @@ -0,0 +1,43 @@ + [ + ['rel' => 'http://nodeinfo.diaspora.software/ns/schema/1.0', + 'href' => $app->getBaseURL() . '/nodeinfo/1.0'], + ['rel' => 'http://nodeinfo.diaspora.software/ns/schema/2.0', + 'href' => $app->getBaseURL() . '/nodeinfo/2.0'], + ] + ]; + + header('Content-type: application/json; charset=utf-8'); + echo json_encode($nodeinfo, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); + exit; + } +} diff --git a/static/routes.config.php b/static/routes.config.php index bc98fd5952..f5b44aec22 100644 --- a/static/routes.config.php +++ b/static/routes.config.php @@ -17,7 +17,7 @@ return [ '/.well-known' => [ '/host-meta' => [Module\WellKnown\HostMeta::class, [R::GET]], - '/nodeinfo' => [Module\NodeInfo::class, [R::GET]], + '/nodeinfo' => [Module\WellKnown\NodeInfo::class, [R::GET]], '/webfinger' => [Module\Xrd::class, [R::GET]], '/x-social-relay' => [Module\WellKnown\XSocialRelay::class, [R::GET]], ],