From 27deb4d188d2a250c15e5df21c849ce114089105 Mon Sep 17 00:00:00 2001
From: Michael <heluecht@pirati.ca>
Date: Mon, 13 Jul 2020 09:45:45 +0000
Subject: [PATCH] Module classes splitted

---
 src/Model/Nodeinfo.php     | 106 ++++++++++++
 src/Module/NodeInfo.php    | 322 -------------------------------------
 src/Module/NodeInfo110.php |  92 +++++++++++
 src/Module/NodeInfo120.php |  86 ++++++++++
 src/Module/NodeInfo210.php |  84 ++++++++++
 static/routes.config.php   |   5 +-
 6 files changed, 371 insertions(+), 324 deletions(-)
 delete mode 100644 src/Module/NodeInfo.php
 create mode 100644 src/Module/NodeInfo110.php
 create mode 100644 src/Module/NodeInfo120.php
 create mode 100644 src/Module/NodeInfo210.php

diff --git a/src/Model/Nodeinfo.php b/src/Model/Nodeinfo.php
index 7ccfefd007..44181ac941 100644
--- a/src/Model/Nodeinfo.php
+++ b/src/Model/Nodeinfo.php
@@ -24,6 +24,7 @@ namespace Friendica\Model;
 use Friendica\Core\Addon;
 use Friendica\Database\DBA;
 use Friendica\DI;
+use stdClass;
 
 /**
  * Model interaction for the nodeinfo
@@ -70,4 +71,109 @@ class Nodeinfo
 		}
 		DBA::close($items);
 	}
+
+	/**
+	 * Return the supported services
+	 *
+	 * @return Object with supported services
+	*/
+	public static function getUsage(bool $version2 = false)
+	{
+		$config = DI::config();
+
+		$usage = new stdClass();
+
+		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'));
+
+			if ($version2) {
+				$usage->users['activeWeek'] = intval($config->get('nodeinfo', 'active_users_weekly'));
+			}
+		}
+
+		return $usage;
+	}
+
+	/**
+	 * Return the supported services
+	 *
+	 * @return array with supported services
+	*/
+	public static function getServices()
+	{
+		$services = [
+			'inbound'  => [],
+			'outbound' => [],
+		];
+
+		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';
+		}
+
+		$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;
+	}
+
+	public static function getOrganization($config)
+	{
+		$organization = ['name' => null, 'contact' => null, 'account' => null];
+
+		if (!empty($config->get('config', 'admin_email'))) {
+			$adminList = explode(',', str_replace(' ', '', $config->get('config', 'admin_email')));
+			$organization['contact'] = $adminList[0];
+			$administrator = User::getByEmail($adminList[0], ['username', 'nickname']);
+			if (!empty($administrator)) {
+				$organization['name'] = $administrator['username'];
+				$organization['account'] = DI::baseUrl()->get() . '/profile/' . $administrator['nickname'];
+			}
+		}
+
+		return $organization;
+	}
 }
diff --git a/src/Module/NodeInfo.php b/src/Module/NodeInfo.php
deleted file mode 100644
index eca0ae3e34..0000000000
--- a/src/Module/NodeInfo.php
+++ /dev/null
@@ -1,322 +0,0 @@
-<?php
-/**
- * @copyright Copyright (C) 2020, Friendica
- *
- * @license GNU AGPL version 3 or any later version
- *
- * This program 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.
- *
- * This program 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 this program.  If not, see <https://www.gnu.org/licenses/>.
- *
- */
-
-namespace Friendica\Module;
-
-use Friendica\BaseModule;
-use Friendica\Core\Addon;
-use Friendica\DI;
-use Friendica\Model\User;
-use stdClass;
-
-/**
- * Standardized way of exposing metadata about a server running one of the distributed social networks.
- * @see https://github.com/jhass/nodeinfo/blob/master/PROTOCOL.md
- */
-class NodeInfo extends BaseModule
-{
-	public static function rawContent(array $parameters = [])
-	{
-		if (empty($parameters['version'])) {
-			self::printNodeInfo2();
-		} elseif ($parameters['version'] == '1.0') {
-			self::printNodeInfo1();
-		} elseif ($parameters['version'] == '2.0') {
-			self::printNodeInfo20();
-		} else {
-			throw new \Friendica\Network\HTTPException\NotFoundException();
-		}
-	}
-
-	/**
-	 * Return the supported services
-	 *
-	 * @return Object with supported services
-	*/
-	private static function getUsage(bool $version2 = false)
-	{
-		$config = DI::config();
-
-		$usage = new stdClass();
-
-		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'));
-
-			if ($version2) {
-				$usage->users['activeWeek'] = intval($config->get('nodeinfo', 'active_users_weekly'));
-			}
-		}
-
-		return $usage;
-	}
-
-	/**
-	 * Return the supported services
-	 *
-	 * @return array with supported services
-	*/
-	private static function getServices()
-	{
-		$services = [
-			'inbound'  => [],
-			'outbound' => [],
-		];
-
-		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';
-		}
-
-		$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
-	 */
-	private static function printNodeInfo1()
-	{
-		$config = DI::config();
-
-		$nodeinfo = [
-			'version'           => '1.0',
-			'software'          => [
-				'name'    => 'friendica',
-				'version' => FRIENDICA_VERSION . '-' . DB_UPDATE_VERSION,
-			],
-			'protocols'         => [
-				'inbound'  => [
-					'friendica'
-				],
-				'outbound' => [
-					'friendica'
-				],
-			],
-			'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']['inbound'][] = 'diaspora';
-			$nodeinfo['protocols']['outbound'][] = 'diaspora';
-		}
-
-		if (empty($config->get('system', 'ostatus_disabled'))) {
-			$nodeinfo['protocols']['inbound'][] = 'gnusocial';
-			$nodeinfo['protocols']['outbound'][] = 'gnusocial';
-		}
-
-		$nodeinfo['usage'] = self::getUsage();
-
-		$nodeinfo['services'] = self::getServices();
-
-		$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'];
-
-		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.0
-	 */
-	private static function printNodeInfo20()
-	{
-		$config = DI::config();
-
-		$imap = (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();
-
-		$nodeinfo['services'] = self::getServices();
-
-		if (Addon::isEnabled('twitter')) {
-			$nodeinfo['services']['inbound'][] = 'twitter';
-		}
-
-		$nodeinfo['services']['inbound'][]  = 'atom1.0';
-		$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');
-		echo json_encode($nodeinfo, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
-		exit;
-	}
-
-	/**
-	 * Print the nodeinfo version 2
-	 */
-	private static function printNodeInfo2()
-	{
-		$config = DI::config();
-
-		$imap = (function_exists('imap_open') && !$config->get('system', 'imap_disabled') && !$config->get('system', 'dfrn_only'));
-
-		$nodeinfo = [
-			'version'           => '1.0',
-			'server'          => [
-				'baseUrl'  => DI::baseUrl()->get(),
-				'name'     => $config->get('config', 'sitename'),
-				'software' => 'friendica',
-				'version'  => FRIENDICA_VERSION . '-' . DB_UPDATE_VERSION,
-			],
-			'organization'      => self::getOrganization($config),
-			'protocols'         => ['dfrn', 'activitypub'],
-			'services'          => [],
-			'openRegistrations' => intval($config->get('config', 'register_policy')) !== Register::CLOSED,
-			'usage'             => [],
-		];
-
-		if (!empty($config->get('system', 'diaspora_enabled'))) {
-			$nodeinfo['protocols'][] = 'diaspora';
-		}
-
-		if (empty($config->get('system', 'ostatus_disabled'))) {
-			$nodeinfo['protocols'][] = 'ostatus';
-		}
-
-		$nodeinfo['usage'] = self::getUsage(true);
-
-		$nodeinfo['services'] = self::getServices();
-
-		if (Addon::isEnabled('twitter')) {
-			$nodeinfo['services']['inbound'][] = 'twitter';
-		}
-
-		$nodeinfo['services']['inbound'][]  = 'atom1.0';
-		$nodeinfo['services']['inbound'][]  = 'rss2.0';
-		$nodeinfo['services']['outbound'][] = 'atom1.0';
-
-		if ($imap) {
-			$nodeinfo['services']['inbound'][] = 'imap';
-		}
-
-		header('Content-type: application/json; charset=utf-8');
-		echo json_encode($nodeinfo, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
-		exit;
-	}
-
-	private static function getOrganization($config)
-	{
-		$organization = ['name' => null, 'contact' => null, 'account' => null];
-
-		if (!empty($config->get('config', 'admin_email'))) {
-			$adminList = explode(',', str_replace(' ', '', $config->get('config', 'admin_email')));
-			$organization['contact'] = $adminList[0];
-			$administrator = User::getByEmail($adminList[0], ['username', 'nickname']);
-			if (!empty($administrator)) {
-				$organization['name'] = $administrator['username'];
-				$organization['account'] = DI::baseUrl()->get() . '/profile/' . $administrator['nickname'];
-			}
-		}
-
-		return $organization;
-	}
-}
diff --git a/src/Module/NodeInfo110.php b/src/Module/NodeInfo110.php
new file mode 100644
index 0000000000..954f362190
--- /dev/null
+++ b/src/Module/NodeInfo110.php
@@ -0,0 +1,92 @@
+<?php
+/**
+ * @copyright Copyright (C) 2020, Friendica
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program 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.
+ *
+ * This program 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 this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Friendica\Module;
+
+use Friendica\BaseModule;
+use Friendica\Core\Addon;
+use Friendica\DI;
+use Friendica\Model\Nodeinfo;
+
+/**
+ * Version 1.0 of Nodeinfo, a standardized way of exposing metadata about a server running one of the distributed social networks.
+ * @see https://github.com/jhass/nodeinfo/blob/master/PROTOCOL.md
+ */
+class NodeInfo110 extends BaseModule
+{
+	public static function rawContent(array $parameters = [])
+	{
+		$config = DI::config();
+
+		$nodeinfo = [
+			'version'           => '1.0',
+			'software'          => [
+				'name'    => 'friendica',
+				'version' => FRIENDICA_VERSION . '-' . DB_UPDATE_VERSION,
+			],
+			'protocols'         => [
+				'inbound'  => [
+					'friendica'
+				],
+				'outbound' => [
+					'friendica'
+				],
+			],
+			'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']['inbound'][] = 'diaspora';
+			$nodeinfo['protocols']['outbound'][] = 'diaspora';
+		}
+
+		if (empty($config->get('system', 'ostatus_disabled'))) {
+			$nodeinfo['protocols']['inbound'][] = 'gnusocial';
+			$nodeinfo['protocols']['outbound'][] = 'gnusocial';
+		}
+
+		$nodeinfo['usage'] = Nodeinfo::getUsage();
+
+		$nodeinfo['services'] = Nodeinfo::getServices();
+
+		$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'];
+
+		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;
+	}
+}
diff --git a/src/Module/NodeInfo120.php b/src/Module/NodeInfo120.php
new file mode 100644
index 0000000000..330c5e10d5
--- /dev/null
+++ b/src/Module/NodeInfo120.php
@@ -0,0 +1,86 @@
+<?php
+/**
+ * @copyright Copyright (C) 2020, Friendica
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program 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.
+ *
+ * This program 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 this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Friendica\Module;
+
+use Friendica\BaseModule;
+use Friendica\Core\Addon;
+use Friendica\DI;
+use Friendica\Model\Nodeinfo;
+
+/**
+ * Version 2.0 of Nodeinfo, a standardized way of exposing metadata about a server running one of the distributed social networks.
+ * @see https://github.com/jhass/nodeinfo/blob/master/PROTOCOL.md
+ */
+class NodeInfo120 extends BaseModule
+{
+	public static function rawContent(array $parameters = [])
+	{
+		$config = DI::config();
+
+		$imap = (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'] = Nodeinfo::getUsage();
+
+		$nodeinfo['services'] = Nodeinfo::getServices();
+
+		if (Addon::isEnabled('twitter')) {
+			$nodeinfo['services']['inbound'][] = 'twitter';
+		}
+
+		$nodeinfo['services']['inbound'][]  = 'atom1.0';
+		$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');
+		echo json_encode($nodeinfo, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
+		exit;
+	}
+}
diff --git a/src/Module/NodeInfo210.php b/src/Module/NodeInfo210.php
new file mode 100644
index 0000000000..63a1749832
--- /dev/null
+++ b/src/Module/NodeInfo210.php
@@ -0,0 +1,84 @@
+<?php
+/**
+ * @copyright Copyright (C) 2020, Friendica
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program 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.
+ *
+ * This program 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 this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace Friendica\Module;
+
+use Friendica\BaseModule;
+use Friendica\Core\Addon;
+use Friendica\DI;
+use Friendica\Model\Nodeinfo;
+
+/**
+ * Version 1.0 of Nodeinfo 2, a sStandardized way of exposing metadata about a server running one of the distributed social networks.
+ * @see https://github.com/jhass/nodeinfo/blob/master/PROTOCOL.md
+ */
+class NodeInfo210 extends BaseModule
+{
+	public static function rawContent(array $parameters = [])
+	{
+		$config = DI::config();
+
+		$imap = (function_exists('imap_open') && !$config->get('system', 'imap_disabled') && !$config->get('system', 'dfrn_only'));
+
+		$nodeinfo = [
+			'version'           => '1.0',
+			'server'          => [
+				'baseUrl'  => DI::baseUrl()->get(),
+				'name'     => $config->get('config', 'sitename'),
+				'software' => 'friendica',
+				'version'  => FRIENDICA_VERSION . '-' . DB_UPDATE_VERSION,
+			],
+			'organization'      => Nodeinfo::getOrganization($config),
+			'protocols'         => ['dfrn', 'activitypub'],
+			'services'          => [],
+			'openRegistrations' => intval($config->get('config', 'register_policy')) !== Register::CLOSED,
+			'usage'             => [],
+		];
+
+		if (!empty($config->get('system', 'diaspora_enabled'))) {
+			$nodeinfo['protocols'][] = 'diaspora';
+		}
+
+		if (empty($config->get('system', 'ostatus_disabled'))) {
+			$nodeinfo['protocols'][] = 'ostatus';
+		}
+
+		$nodeinfo['usage'] = Nodeinfo::getUsage(true);
+
+		$nodeinfo['services'] = Nodeinfo::getServices();
+
+		if (Addon::isEnabled('twitter')) {
+			$nodeinfo['services']['inbound'][] = 'twitter';
+		}
+
+		$nodeinfo['services']['inbound'][]  = 'atom1.0';
+		$nodeinfo['services']['inbound'][]  = 'rss2.0';
+		$nodeinfo['services']['outbound'][] = 'atom1.0';
+
+		if ($imap) {
+			$nodeinfo['services']['inbound'][] = 'imap';
+		}
+
+		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 cb569e01a0..ac78826939 100644
--- a/static/routes.config.php
+++ b/static/routes.config.php
@@ -37,7 +37,7 @@ return [
 		'/host-meta'      => [Module\WellKnown\HostMeta::class,     [R::GET]],
 		'/nodeinfo'       => [Module\WellKnown\NodeInfo::class,     [R::GET]],
 		'/webfinger'      => [Module\Xrd::class,                    [R::GET]],
-		'/x-nodeinfo2'    => [Module\NodeInfo::class,               [R::GET]],
+		'/x-nodeinfo2'    => [Module\NodeInfo210::class,            [R::GET]],
 		'/x-social-relay' => [Module\WellKnown\XSocialRelay::class, [R::GET]],
 	],
 
@@ -200,7 +200,8 @@ return [
 	'/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]],
+	'/nodeinfo/1.0'       => [Module\NodeInfo110::class,     [R::GET]],
+	'/nodeinfo/2.0'       => [Module\NodeInfo120::class,     [R::GET]],
 	'/nogroup'            => [Module\Group::class,           [R::GET]],
 
 	'/noscrape' => [
-- 
2.39.5