+ /**
+ * Fetches server data via an ActivityPub account with url of that server
+ *
+ * @param string $url URL of the given server
+ * @param array $serverdata array with server data
+ *
+ * @return array server data
+ *
+ * @throws Exception
+ */
+ private static function fetchDataFromSystemActor(array $data, array $serverdata): array
+ {
+ if (empty($data)) {
+ return ['server' => $serverdata, 'actor' => ''];
+ }
+
+ $actor = JsonLD::compact($data, false);
+ if (in_array(JsonLD::fetchElement($actor, '@type'), ActivityPub\Receiver::ACCOUNT_TYPES)) {
+ $serverdata['network'] = Protocol::ACTIVITYPUB;
+ $serverdata['site_name'] = JsonLD::fetchElement($actor, 'as:name', '@value');
+ $serverdata['info'] = JsonLD::fetchElement($actor, 'as:summary', '@value');
+ if (self::isNomad($actor)) {
+ $serverdata['platform'] = self::getNomadName($actor['@id']);
+ $serverdata['version'] = self::getNomadVersion($actor['@id']);
+ $serverdata['detection-method'] = self::DETECT_SYSTEM_ACTOR;
+ } elseif (!empty($actor['as:generator'])) {
+ $generator = explode(' ', JsonLD::fetchElement($actor['as:generator'], 'as:name', '@value'));
+ $serverdata['platform'] = strtolower(array_shift($generator));
+ $serverdata['version'] = self::getNomadVersion($actor['@id']);
+ $serverdata['detection-method'] = self::DETECT_SYSTEM_ACTOR;
+ } else {
+ $serverdata['detection-method'] = self::DETECT_AP_ACTOR;
+ }
+ return ['server' => $serverdata, 'actor' => $actor['@id']];
+ } elseif ((JsonLD::fetchElement($actor, '@type') == 'as:Collection')) {
+ // By now only Ktistec seems to provide collections this way
+ $serverdata['platform'] = 'ktistec';
+ $serverdata['network'] = Protocol::ACTIVITYPUB;
+ $serverdata['detection-method'] = self::DETECT_AP_COLLECTION;
+
+ $actors = JsonLD::fetchElementArray($actor, 'as:items');
+ if (!empty($actors) && !empty($actors[0]['@id'])) {
+ $actor_url = $actor['@id'] . $actors[0]['@id'];
+ } else {
+ $actor_url = '';
+ }
+
+ return ['server' => $serverdata, 'actor' => $actor_url];
+ }
+ return ['server' => $serverdata, 'actor' => ''];
+ }
+
+ /**
+ * Detect if the given actor is a nomad account
+ *
+ * @param array $actor
+ * @return boolean
+ */
+ private static function isNomad(array $actor): bool
+ {
+ $tags = JsonLD::fetchElementArray($actor, 'as:tag');
+ if (empty($tags)) {
+ return false;
+ }
+
+ foreach ($tags as $tag) {
+ if ((($tag['as:name'] ?? '') == 'Protocol') && in_array('nomad', [$tag['sc:value'] ?? '', $tag['as:content'] ?? ''])) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Fetch the name of Nomad implementation
+ *
+ * @param string $url
+ * @return string
+ */
+ private static function getNomadName(string $url): string
+ {
+ $name = 'nomad';
+ $curlResult = DI::httpClient()->get($url . '/manifest', 'application/manifest+json');
+ if (!$curlResult->isSuccess() || ($curlResult->getBodyString() == '')) {
+ return $name;
+ }
+
+ $data = json_decode($curlResult->getBodyString(), true);
+ if (empty($data)) {
+ return $name;
+ }
+
+ return $data['name'] ?? $name;
+ }
+
+ /**
+ * Fetch the version of the Nomad installation
+ *
+ * @param string $url
+ * @return string
+ */
+ private static function getNomadVersion(string $url): string
+ {
+ $curlResult = DI::httpClient()->get($url . '/api/z/1.0/version', HttpClientAccept::JSON);
+ if (!$curlResult->isSuccess() || ($curlResult->getBodyString() == '')) {
+ return '';
+ }
+
+ $data = json_decode($curlResult->getBodyString(), true);
+ if (empty($data)) {
+ return '';
+ }
+ return $data ?? '';
+ }
+