+ /**
+ * Checks if the server contains a valid host meta file
+ *
+ * @param string $url URL of the given server
+ *
+ * @return boolean 'true' if the server seems to be vital
+ */
+ private static function validHostMeta(string $url)
+ {
+ $xrd_timeout = DI::config()->get('system', 'xrd_timeout');
+ $curlResult = Network::curl($url . '/.well-known/host-meta', false, ['timeout' => $xrd_timeout]);
+ if (!$curlResult->isSuccess()) {
+ return false;
+ }
+
+ $xrd = XML::parseString($curlResult->getBody(), false);
+ if (!is_object($xrd)) {
+ return false;
+ }
+
+ $elements = XML::elementToArray($xrd);
+ if (empty($elements) || empty($elements['xrd']) || empty($elements['xrd']['link'])) {
+ return false;
+ }
+
+ $valid = false;
+ foreach ($elements['xrd']['link'] as $link) {
+ // When there is more than a single "link" element, the array looks slightly different
+ if (!empty($link['@attributes'])) {
+ $link = $link['@attributes'];
+ }
+
+ if (empty($link['rel']) || empty($link['template'])) {
+ continue;
+ }
+
+ if ($link['rel'] == 'lrdd') {
+ // When the webfinger host is the same like the system host, it should be ok.
+ $valid = (parse_url($url, PHP_URL_HOST) == parse_url($link['template'], PHP_URL_HOST));
+ }
+ }
+
+ return $valid;
+ }
+
+ /**
+ * Detect the network of the given server via their known contacts
+ *
+ * @param string $url URL of the given server
+ * @param array $serverdata array with server data
+ *
+ * @return array server data
+ */
+ private static function detectNetworkViaContacts(string $url, array $serverdata)
+ {
+ $contacts = [];
+
+ $gcontacts = DBA::select('gcontact', ['url', 'nurl'], ['server_url' => [$url, $serverdata['nurl']]]);
+ while ($gcontact = DBA::fetch($gcontacts)) {
+ $contacts[$gcontact['nurl']] = $gcontact['url'];
+ }
+ DBA::close($gcontacts);
+
+ $apcontacts = DBA::select('apcontact', ['url'], ['baseurl' => [$url, $serverdata['nurl']]]);
+ while ($gcontact = DBA::fetch($gcontacts)) {
+ $contacts[Strings::normaliseLink($apcontact['url'])] = $apcontact['url'];
+ }
+ DBA::close($apcontacts);
+
+ $pcontacts = DBA::select('contact', ['url', 'nurl'], ['uid' => 0, 'baseurl' => [$url, $serverdata['nurl']]]);
+ while ($gcontact = DBA::fetch($gcontacts)) {
+ $contacts[$pcontact['nurl']] = $pcontact['url'];
+ }
+ DBA::close($pcontacts);
+
+ if (empty($contacts)) {
+ return $serverdata;
+ }
+
+ foreach ($contacts as $contact) {
+ $probed = Probe::uri($contact);
+ if (in_array($probed['network'], Protocol::FEDERATED)) {
+ $serverdata['network'] = $probed['network'];
+ break;
+ }
+ }
+
+ $serverdata['registered-users'] = max($serverdata['registered-users'], count($contacts));
+
+ return $serverdata;
+ }
+
+ /**
+ * Checks if the given server does have a '/poco' endpoint.
+ * This is used for the 'PortableContact' functionality,
+ * which is used by both Friendica and Hubzilla.
+ *
+ * @param string $url URL of the given server
+ * @param array $serverdata array with server data
+ *
+ * @return array server data
+ */
+ private static function checkPoCo(string $url, array $serverdata)
+ {
+ $serverdata['poco'] = '';
+
+ $curlResult = Network::curl($url. '/poco');
+ if (!$curlResult->isSuccess()) {
+ return $serverdata;
+ }
+
+ $data = json_decode($curlResult->getBody(), true);
+ if (empty($data)) {
+ return $serverdata;
+ }
+
+ if (!empty($data['totalResults'])) {
+ $registeredUsers = $serverdata['registered-users'] ?? 0;
+ $serverdata['registered-users'] = max($data['totalResults'], $registeredUsers);
+ $serverdata['directory-type'] = self::DT_POCO;
+ $serverdata['poco'] = $url . '/poco';
+ }
+
+ return $serverdata;
+ }
+
+ /**
+ * Checks if the given server does have a Mastodon style directory endpoint.
+ *
+ * @param string $url URL of the given server
+ * @param array $serverdata array with server data
+ *
+ * @return array server data
+ */
+ public static function checkMastodonDirectory(string $url, array $serverdata)
+ {
+ $curlResult = Network::curl($url . '/api/v1/directory?limit=1');
+ if (!$curlResult->isSuccess()) {
+ return $serverdata;
+ }
+
+ $data = json_decode($curlResult->getBody(), true);
+ if (empty($data)) {
+ return $serverdata;
+ }
+
+ if (count($data) == 1) {
+ $serverdata['directory-type'] = self::DT_MASTODON;
+ }
+
+ return $serverdata;
+ }
+
+ /**
+ * Detects the version number of a given server when it was a NextCloud installation
+ *
+ * @param string $url URL of the given server
+ * @param array $serverdata array with server data
+ *
+ * @return array server data
+ */
+ private static function detectNextcloud(string $url, array $serverdata)