]> git.mxchange.org Git - friendica.git/blob - src/Model/GServer.php
Additional endpoints added
[friendica.git] / src / Model / GServer.php
1 <?php
2
3 /**
4  * @file src/Model/GServer.php
5  * @brief This file includes the GServer class to handle with servers
6  */
7 namespace Friendica\Model;
8
9 use DOMDocument;
10 use DOMXPath;
11 use Friendica\Core\Config;
12 use Friendica\Core\Protocol;
13 use Friendica\Database\DBA;
14 use Friendica\Module\Register;
15 use Friendica\Util\Network;
16 use Friendica\Util\DateTimeFormat;
17 use Friendica\Util\Strings;
18 use Friendica\Util\XML;
19 use Friendica\Core\Logger;
20
21 /*
22 use Exception;
23 use Friendica\Core\System;
24 use Friendica\Core\Worker;
25 use Friendica\Network\Probe;
26 use Friendica\Protocol\PortableContact;
27 */
28 /**
29  * @brief This class handles GServer related functions
30  */
31 class GServer
32 {
33         /**
34          * Detect server type
35          *
36          * @param string  $url   Server url
37          * @param boolean $force Force update
38          *
39          * @return boolean 'true' if server could be detected
40          */
41         public static function detect($url, $force = false)
42         {
43                 /// @Todo:
44                 // - Update Check
45                 // - poco endpoint
46                 // - Pleroma version number
47
48 //              $gserver = DBA::selectFirst('gserver', [], ['nurl' => Strings::normaliseLink($url)]);
49                 $serverdata = [];
50
51                 // When a nodeinfo is present, we don't need to dig further
52                 $xrd_timeout = Config::get('system', 'xrd_timeout');
53                 $curlResult = Network::curl($url . '/.well-known/nodeinfo', false, ['timeout' => $xrd_timeout]);
54                 if ($curlResult->isTimeout()) {
55                         DBA::update('gserver', ['last_failure' => DateTimeFormat::utcNow()], ['nurl' => Strings::normaliseLink($url)]);
56                         return false;
57                 }
58
59                 $nodeinfo = self::fetchNodeinfo($url, $curlResult);
60
61                 if (empty($nodeinfo)) {
62                         $nodeinfo = self::fetchStatistics($url);
63                 }
64
65                 if (empty($nodeinfo) || ($nodeinfo['network'] == Protocol::DFRN)) {
66                         // Fetch the landing page, possibly it reveals some data
67                         $curlResult = Network::curl($url, false, ['timeout' => $xrd_timeout]);
68                         if ($curlResult->isSuccess()) {
69                                 $serverdata = self::analyseRootHeader($curlResult, $serverdata);
70                                 $serverdata = self::analyseRootBody($curlResult, $serverdata);
71                         }
72
73                         if (!$curlResult->isSuccess() || empty($curlResult->getBody())) {
74                                 DBA::update('gserver', ['last_failure' => DateTimeFormat::utcNow()], ['nurl' => Strings::normaliseLink($url)]);
75                                 return false;
76                         }
77
78                         if (empty($serverdata['network']) || ($serverdata['network'] == Protocol::DFRN)) {
79                                 $serverdata = self::detectFriendica($url, $serverdata);
80                         }
81
82                         if (empty($serverdata['network']) || ($serverdata['network'] == Protocol::ACTIVITYPUB)) {
83                                 $serverdata = self::detectMastodonAlikes($url, $serverdata);
84                         }
85
86                         // the 'siteinfo.json' is some specific endpoint of Hubzilla and Red
87                         if (empty($serverdata['network']) || ($serverdata['network'] == Protocol::ZOT)) {
88                                 $serverdata = self::fetchSiteinfo($url, $serverdata);
89                         }
90
91                         if (empty($serverdata['network'])) {
92                                 $serverdata = self::detectHubzilla($url, $serverdata);
93                         }
94
95                         if (empty($serverdata['network'])) {
96                                 $serverdata = self::detectNextcloud($url, $serverdata);
97                         }
98
99                         if (empty($serverdata['network'])) {
100                                 $serverdata = self::detectGNUSocial($url, $serverdata);
101                         }
102                 } else {
103                         $serverdata = $nodeinfo;
104                 }
105
106                 // We can't detect the network type. Possibly it is some system that we don't know yet
107                 if (empty($serverdata['network'])) {
108                         $serverdata['network'] = Protocol::PHANTOM;
109                 }
110
111                 $serverdata['url'] = $url;
112                 $serverdata['nurl'] = Strings::normaliseLink($url);
113
114                 // When we don't have the registered users, we simply count what we know
115                 if (empty($serverdata['registered-users'])) {
116                         $gcontacts = DBA::count('gcontact', ['server_url' => [$url, $serverdata['nurl']]]);
117                         $apcontacts = DBA::count('apcontact', ['baseurl' => [$url, $serverdata['nurl']]]);
118                         $contacts = DBA::count('contact', ['uid' => 0, 'baseurl' => [$url, $serverdata['nurl']]]);
119                         $serverdata['registered-users'] = max($gcontacts, $apcontacts, $contacts);
120                 }
121
122                 $fields = array_keys($serverdata);
123                 $old_data = DBA::selectFirst('gserver', $fields, ['nurl' => Strings::normaliseLink($url)]);
124                 if (!DBA::isResult($old_data)) {
125 die('Möööp');
126                         $serverdata['created'] = DateTimeFormat::utcNow();
127                         DBA::insert('gserver', $serverdata);
128                 } else {
129                         $serverdata['last_contact'] = DateTimeFormat::utcNow();
130                         DBA::update('gserver', $serverdata, ['nurl' => $serverdata['nurl']], $old_data);
131                 }
132
133                 return $serverdata;
134         }
135
136         private static function fetchStatistics($url)
137         {
138                 $curlResult = Network::curl($url . '/statistics.json');
139                 if (!$curlResult->isSuccess()) {
140                         return [];
141                 }
142
143                 $data = json_decode($curlResult->getBody(), true);
144                 if (empty($data)) {
145                         return [];
146                 }
147
148                 $serverdata = [];
149
150                 if (!empty($data['version'])) {
151                         $serverdata['version'] = $data['version'];
152                         // Version numbers on statistics.json are presented with additional info, e.g.:
153                         // 0.6.3.0-p1702cc1c, 0.6.99.0-p1b9ab160 or 3.4.3-2-1191.
154                         $serverdata['version'] = preg_replace('=(.+)-(.{4,})=ism', '$1', $serverdata['version']);
155                 }
156
157                 if (!empty($data['name'])) {
158                         $serverdata['site_name'] = $data['name'];
159                 }
160
161                 if (!empty($data['network'])) {
162                         $serverdata['platform'] = $data['network'];
163
164                         if ($serverdata['platform'] == 'Diaspora') {
165                                 $serverdata['network'] = Protocol::DIASPORA;
166                         } elseif ($serverdata['platform'] == 'Friendica') {
167                                 $serverdata['network'] = Protocol::DFRN;
168                         } elseif ($serverdata['platform'] == 'redmatrix') {
169                                 $serverdata['network'] = Protocol::ZOT;
170                         } else {
171                                 print_r($serverdata);
172                                 die('aaa');
173                         }
174                 }
175
176
177                 if (!empty($data['registrations_open'])) {
178                         $serverdata['register_policy'] = Register::OPEN;
179                 } else {
180                         $serverdata['register_policy'] = Register::CLOSED;
181                 }
182
183                 return $serverdata;
184         }
185
186         /**
187          * @brief Detect server type by using the nodeinfo data
188          *
189          * @param string $url address of the server
190          * @return array Server data
191          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
192          */
193         private static function fetchNodeinfo($url, $curlResult)
194         {
195                 $nodeinfo = json_decode($curlResult->getBody(), true);
196
197                 if (!is_array($nodeinfo) || !isset($nodeinfo['links'])) {
198                         return [];
199                 }
200
201                 $nodeinfo1_url = '';
202                 $nodeinfo2_url = '';
203
204                 foreach ($nodeinfo['links'] as $link) {
205                         if (!is_array($link) || empty($link['rel']) || empty($link['href'])) {
206                                 Logger::info('Invalid nodeinfo format', ['url' => $url]);
207                                 continue;
208                         }
209                         if ($link['rel'] == 'http://nodeinfo.diaspora.software/ns/schema/1.0') {
210                                 $nodeinfo1_url = $link['href'];
211                         } elseif ($link['rel'] == 'http://nodeinfo.diaspora.software/ns/schema/2.0') {
212                                 $nodeinfo2_url = $link['href'];
213                         }
214                 }
215
216                 if ($nodeinfo1_url . $nodeinfo2_url == '') {
217                         return [];
218                 }
219
220                 $server = [];
221
222                 // When the nodeinfo url isn't on the same host, then there is obviously something wrong
223                 if (!empty($nodeinfo2_url) && (parse_url($url, PHP_URL_HOST) == parse_url($nodeinfo2_url, PHP_URL_HOST))) {
224                         $server = self::parseNodeinfo2($nodeinfo2_url);
225                 }
226
227                 // When the nodeinfo url isn't on the same host, then there is obviously something wrong
228                 if (empty($server) && !empty($nodeinfo1_url) && (parse_url($url, PHP_URL_HOST) == parse_url($nodeinfo1_url, PHP_URL_HOST))) {
229                         $server = self::parseNodeinfo1($nodeinfo1_url);
230                 }
231
232                 return $server;
233         }
234
235         /**
236          * @brief Parses Nodeinfo 1
237          *
238          * @param string $nodeinfo_url address of the nodeinfo path
239          * @return array Server data
240          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
241          */
242         private static function parseNodeinfo1($nodeinfo_url)
243         {
244                 $curlResult = Network::curl($nodeinfo_url);
245
246                 if (!$curlResult->isSuccess()) {
247                         return false;
248                 }
249
250                 $nodeinfo = json_decode($curlResult->getBody(), true);
251
252                 if (!is_array($nodeinfo)) {
253                         return false;
254                 }
255
256                 $server = [];
257
258                 $server['register_policy'] = Register::CLOSED;
259
260                 if (!empty($nodeinfo['openRegistrations'])) {
261                         $server['register_policy'] = Register::OPEN;
262                 }
263
264                 if (is_array($nodeinfo['software'])) {
265                         if (isset($nodeinfo['software']['name'])) {
266                                 $server['platform'] = $nodeinfo['software']['name'];
267                         }
268
269                         if (isset($nodeinfo['software']['version'])) {
270                                 $server['version'] = $nodeinfo['software']['version'];
271                                 // Version numbers on Nodeinfo are presented with additional info, e.g.:
272                                 // 0.6.3.0-p1702cc1c, 0.6.99.0-p1b9ab160 or 3.4.3-2-1191.
273                                 $server['version'] = preg_replace('=(.+)-(.{4,})=ism', '$1', $server['version']);
274                         }
275                 }
276
277                 if (isset($nodeinfo['metadata']['nodeName'])) {
278                         $server['site_name'] = $nodeinfo['metadata']['nodeName'];
279                 }
280
281                 if (!empty($nodeinfo['usage']['users']['total'])) {
282                         $server['registered-users'] = $nodeinfo['usage']['users']['total'];
283                 }
284
285                 if (!empty($nodeinfo['protocols']['inbound']) && is_array($nodeinfo['protocols']['inbound'])) {
286                         $protocols = [];
287                         foreach ($nodeinfo['protocols']['inbound'] as $protocol) {
288                                 $protocols[$protocol] = true;
289                         }
290
291                         if (!empty($protocols['friendica'])) {
292                                 $server['network'] = Protocol::DFRN;
293                         } elseif (!empty($protocols['activitypub'])) {
294                                 $server['network'] = Protocol::ACTIVITYPUB;
295                         } elseif (!empty($protocols['diaspora'])) {
296                                 $server['network'] = Protocol::DIASPORA;
297                         } elseif (!empty($protocols['ostatus'])) {
298                                 $server['network'] = Protocol::OSTATUS;
299                         } elseif (!empty($protocols['gnusocial'])) {
300                                 $server['network'] = Protocol::OSTATUS;
301                         } elseif (!empty($protocols['zot'])) {
302                                 $server['network'] = Protocol::ZOT;
303                         } else {
304                                 print_r($protocols);
305                                 die('Protocol 1');
306                         }
307                 }
308
309                 if (!$server) {
310                         return false;
311                 }
312
313                 return $server;
314         }
315
316         /**
317          * @brief Parses Nodeinfo 2
318          *
319          * @param string $nodeinfo_url address of the nodeinfo path
320          * @return array Server data
321          * @throws \Friendica\Network\HTTPException\InternalServerErrorException
322          */
323         private static function parseNodeinfo2($nodeinfo_url)
324         {
325                 $curlResult = Network::curl($nodeinfo_url);
326                 if (!$curlResult->isSuccess()) {
327                         return false;
328                 }
329
330                 $nodeinfo = json_decode($curlResult->getBody(), true);
331
332                 if (!is_array($nodeinfo)) {
333                         return false;
334                 }
335
336                 $server = [];
337
338                 $server['register_policy'] = Register::CLOSED;
339
340                 if (!empty($nodeinfo['openRegistrations'])) {
341                         $server['register_policy'] = Register::OPEN;
342                 }
343
344                 if (is_array($nodeinfo['software'])) {
345                         if (isset($nodeinfo['software']['name'])) {
346                                 $server['platform'] = $nodeinfo['software']['name'];
347                         }
348
349                         if (isset($nodeinfo['software']['version'])) {
350                                 $server['version'] = $nodeinfo['software']['version'];
351                                 // Version numbers on Nodeinfo are presented with additional info, e.g.:
352                                 // 0.6.3.0-p1702cc1c, 0.6.99.0-p1b9ab160 or 3.4.3-2-1191.
353                                 $server['version'] = preg_replace('=(.+)-(.{4,})=ism', '$1', $server['version']);
354                         }
355                 }
356
357                 if (isset($nodeinfo['metadata']['nodeName'])) {
358                         $server['site_name'] = $nodeinfo['metadata']['nodeName'];
359                 }
360
361                 if (!empty($nodeinfo['usage']['users']['total'])) {
362                         $server['registered-users'] = $nodeinfo['usage']['users']['total'];
363                 }
364
365                 if (!empty($nodeinfo['protocols'])) {
366                         $protocols = [];
367                         foreach ($nodeinfo['protocols'] as $protocol) {
368                                 $protocols[$protocol] = true;
369                         }
370
371                         if (!empty($protocols['friendica'])) {
372                                 $server['network'] = Protocol::DFRN;
373                         } elseif (!empty($protocols['activitypub'])) {
374                                 $server['network'] = Protocol::ACTIVITYPUB;
375                         } elseif (!empty($protocols['diaspora'])) {
376                                 $server['network'] = Protocol::DIASPORA;
377                         } elseif (!empty($protocols['ostatus'])) {
378                                 $server['network'] = Protocol::OSTATUS;
379                         } elseif (!empty($protocols['gnusocial'])) {
380                                 $server['network'] = Protocol::OSTATUS;
381                         } elseif (!empty($protocols['zot'])) {
382                                 $server['network'] = Protocol::ZOT;
383                         } else {
384                                 print_r($protocols);
385                                 die('Protocol 2');
386                         }
387                 }
388
389                 if (empty($server)) {
390                         return false;
391                 }
392
393                 return $server;
394         }
395
396         private static function fetchSiteinfo($url, $serverdata)
397         {
398                 $curlResult = Network::curl($url . '/siteinfo.json');
399                 if (!$curlResult->isSuccess()) {
400                         return $serverdata;
401                 }
402
403                 $data = json_decode($curlResult->getBody(), true);
404                 if (empty($data)) {
405                         return $serverdata;
406                 }
407
408                 if (isset($data['url'])) {
409                         $serverdata['platform'] = $data['platform'];
410                         $serverdata['version'] = $data['version'];
411                 }
412
413                 if (!empty($data['plugins'])) {
414                         if (in_array('pubcrawl', $data['plugins'])) {
415                                 $serverdata['network'] = Protocol::ACTIVITYPUB;
416                         } elseif (in_array('diaspora', $data['plugins'])) {
417                                 $serverdata['network'] = Protocol::DIASPORA;
418                         } elseif (in_array('gnusoc', $data['plugins'])) {
419                                 $serverdata['network'] = Protocol::OSTATUS;
420                         } else {
421                                 $serverdata['network'] = Protocol::ZOT;
422                         }
423                 }
424
425                 if (!empty($data['site_name'])) {
426                         $serverdata['site_name'] = $data['site_name'];
427                 }
428
429                 if (!empty($data['channels_total'])) {
430                         $serverdata['registered-users'] = $data['channels_total'];
431                 }
432
433                 if (!empty($data['register_policy'])) {
434                         switch ($data['register_policy']) {
435                                 case 'REGISTER_OPEN':
436                                         $serverdata['register_policy'] = Register::OPEN;
437                                         break;
438
439                                 case 'REGISTER_APPROVE':
440                                         $serverdata['register_policy'] = Register::APPROVE;
441                                         break;
442
443                                 case 'REGISTER_CLOSED':
444                                 default:
445                                         $serverdata['register_policy'] = Register::CLOSED;
446                                         break;
447                         }
448                 }
449
450                 return $serverdata;
451         }
452
453         private static function detectNextcloud($url, $serverdata)
454         {
455                 $curlResult = Network::curl($url . '/status.php');
456
457                 if (!$curlResult->isSuccess() || ($curlResult->getBody() == '')) {
458                         return $serverdata;
459                 }
460
461                 $data = json_decode($curlResult->getBody(), true);
462                 if (empty($data)) {
463                         return $serverdata;
464                 }
465
466                 if (!empty($data['version'])) {
467                         $serverdata['platform'] = 'nextcloud';
468                         $serverdata['version'] = $data['version'];
469                         $serverdata['network'] = Protocol::ACTIVITYPUB;
470                 }
471
472                 return $serverdata;
473         }
474
475         private static function detectMastodonAlikes($url, $serverdata)
476         {
477                 $curlResult = Network::curl($url . '/api/v1/instance');
478
479                 if (!$curlResult->isSuccess() || ($curlResult->getBody() == '')) {
480                         return $serverdata;
481                 }
482
483                 $data = json_decode($curlResult->getBody(), true);
484                 if (empty($data)) {
485                         return $serverdata;
486                 }
487
488                 if (!empty($data['version'])) {
489                         $serverdata['platform'] = 'mastodon';
490                         $serverdata['version'] = defaults($data, 'version', '');
491                         $serverdata['network'] = Protocol::ACTIVITYPUB;
492                 }
493
494                 if (!empty($data['title'])) {
495                         $serverdata['site_name'] = $data['title'];
496                 }
497
498                 if (!empty($data['description'])) {
499                         $serverdata['info'] = trim($data['description']);
500                 }
501
502                 if (!empty($data['stats']['user_count'])) {
503                         $serverdata['registered-users'] = $data['stats']['user_count'];
504                 }
505
506                 if (!empty($serverdata['version']) && strstr($serverdata['version'], 'Pleroma')) {
507                         $serverdata['platform'] = 'pleroma';
508                         $serverdata['version'] = trim(str_replace('Pleroma', '', $serverdata['version'])); // 2.7.2 (compatible; Pleroma 1.0.0-1225-gf31ad554-develop)
509                 }
510
511                 if (!empty($serverdata['version']) && strstr($serverdata['version'], 'Pixelfed')) {
512                         print_r($serverdata);
513                         die();
514 //                      $serverdata['platform'] = 'pixelfed';
515 //                      $serverdata['version'] = trim(str_replace('Pixelfed', '', $serverdata['version'])); // 2.7.2 (compatible; Pixelfed 0.10.5)
516                 }
517
518                 return $serverdata;
519         }
520
521         private static function detectHubzilla($url, $serverdata)
522         {
523                 $curlResult = Network::curl($url . '/api/statusnet/config.json');
524                 if (!$curlResult->isSuccess() || ($curlResult->getBody() == '')) {
525                         return $serverdata;
526                 }
527
528                 $data = json_decode($curlResult->getBody(), true);
529                 if (empty($data)) {
530                         return $serverdata;
531                 }
532
533                 if (!empty($data['site']['name'])) {
534                         $serverdata['site_name'] = $data['site']['name'];
535                 }
536
537                 if (!empty($data['site']['platform'])) {
538 print_r($data);
539 die('1');
540                         $serverdata['platform'] = $data['site']['platform']['PLATFORM_NAME'];
541                         $serverdata['version'] = $data['site']['platform']['STD_VERSION'];
542                         $serverdata['network'] = Protocol::ZOT;
543                 }
544
545                 if (isset($data['site']['BlaBlaNet'])) {
546 print_r($data);
547 die('2');
548                         $serverdata['platform'] = $data['site']['BlaBlaNet']['PLATFORM_NAME'];
549                         $serverdata['version'] = $data['site']['BlaBlaNet']['STD_VERSION'];
550                         $serverdata['network'] = Protocol::ZOT;
551                 }
552
553                 if (isset($data['site']['hubzilla'])) {
554 print_r($data);
555 die('3');
556                         $serverdata['platform'] = $data['site']['hubzilla']['PLATFORM_NAME'];
557                         $serverdata['version'] = $data['site']['hubzilla']['RED_VERSION'];
558                         $serverdata['network'] = Protocol::ZOT;
559                 }
560
561                 if (isset($data['site']['redmatrix'])) {
562                         if (isset($data['site']['redmatrix']['PLATFORM_NAME'])) {
563                                 $serverdata['platform'] = $data['site']['redmatrix']['PLATFORM_NAME'];
564                         } elseif (isset($data['site']['redmatrix']['RED_PLATFORM'])) {
565                                 $serverdata['platform'] = $data['site']['redmatrix']['RED_PLATFORM'];
566                         }
567
568                         $serverdata['version'] = $data['site']['redmatrix']['RED_VERSION'];
569                         $serverdata['network'] = Protocol::ZOT;
570                 }
571
572                 if (isset($data['site']['friendica'])) {
573 print_r($data);
574 print_r($serverdata);
575 die('5');
576                         $serverdata['platform'] = $data['site']['friendica']['FRIENDICA_PLATFORM'];
577                         $serverdata['version'] = $data['site']['friendica']['FRIENDICA_VERSION'];
578                         $serverdata['network'] = Protocol::DFRN;
579                 }
580
581                 $private = false;
582                 $inviteonly = false;
583                 $closed = false;
584
585                 if (!empty($data['site']['closed'])) {
586                         $closed = self::toBoolean($data['site']['closed']);
587                 }
588
589                 if (!empty($data['site']['private'])) {
590                         $private = self::toBoolean($data['site']['private']);
591                 }
592
593                 if (!empty($data['site']['inviteonly'])) {
594                         $inviteonly = self::toBoolean($data['site']['inviteonly']);
595                 }
596
597                 if (!$closed && !$private and $inviteonly) {
598                         $register_policy = Register::APPROVE;
599                 } elseif (!$closed && !$private) {
600                         $register_policy = Register::OPEN;
601                 } else {
602                         $register_policy = Register::CLOSED;
603                 }
604
605                 return $serverdata;
606         }
607
608         private static function toBoolean($val)
609         {
610                 if (($val == 'true') || ($val == 1)) {
611                         return true;
612                 } elseif (($val == 'false') || ($val == 0)) {
613                         return false;
614                 }
615
616                 return $val;
617         }
618
619         private static function detectGNUSocial($url, $serverdata)
620         {
621                 $curlResult = Network::curl($url . '/api/statusnet/version.json');
622
623                 if ($curlResult->isSuccess() && ($curlResult->getBody() != '{"error":"not implemented"}') &&
624                         ($curlResult->getBody() != '') && (strlen($curlResult->getBody()) < 30)) {
625                         $serverdata['platform'] = 'StatusNet';
626                         // Remove junk that some GNU Social servers return
627                         $serverdata['version'] = str_replace(chr(239).chr(187).chr(191), '', $curlResult->getBody());
628                         $serverdata['version'] = trim($serverdata['version'], '"');
629                         $serverdata['network'] = Protocol::OSTATUS;
630                 }
631
632                 // Test for GNU Social
633                 $curlResult = Network::curl($url . '/api/gnusocial/version.json');
634
635                 if ($curlResult->isSuccess() && ($curlResult->getBody() != '{"error":"not implemented"}') &&
636                         ($curlResult->getBody() != '') && (strlen($curlResult->getBody()) < 30)) {
637                         $serverdata['platform'] = 'GNU Social';
638                         // Remove junk that some GNU Social servers return
639                         $serverdata['version'] = str_replace(chr(239) . chr(187) . chr(191), '', $curlResult->getBody());
640                         $serverdata['version'] = trim($serverdata['version'], '"');
641                         $serverdata['network'] = Protocol::OSTATUS;
642                 }
643
644                 return $serverdata;
645         }
646
647         private static function detectFriendica($url, $serverdata)
648         {
649                 $curlResult = Network::curl($url . '/friendica/json');
650                 if (!$curlResult->isSuccess()) {
651                         $curlResult = Network::curl($url . '/friendika/json');
652                 }
653
654                 if (!$curlResult->isSuccess()) {
655                         return $serverdata;
656                 }
657
658                 $data = json_decode($curlResult->getBody(), true);
659                 if (empty($data) || empty($data['version'])) {
660                         return $serverdata;
661                 }
662
663                 $serverdata['network'] = Protocol::DFRN;
664                 $serverdata['version'] = $data['version'];
665
666                 if (!empty($data['no_scrape_url'])) {
667                         $serverdata['noscrape'] = $data['no_scrape_url'];
668                 }
669
670                 if (!empty($data['site_name'])) {
671                         $serverdata['site_name'] = $data['site_name'];
672                 }
673
674                 if (!empty($data['info'])) {
675                         $serverdata['info'] = trim($data['info']);
676                 }
677
678                 $register_policy = defaults($data, 'register_policy', 'REGISTER_CLOSED');
679                 switch ($register_policy) {
680                         case 'REGISTER_OPEN':
681                                 $serverdata['register_policy'] = Register::OPEN;
682                                 break;
683
684                         case 'REGISTER_APPROVE':
685                                 $serverdata['register_policy'] = Register::APPROVE;
686                                 break;
687
688                         case 'REGISTER_CLOSED':
689                         case 'REGISTER_INVITATION':
690                                 $serverdata['register_policy'] = Register::CLOSED;
691                                 break;
692                         default:
693                                 Logger::info('Register policy is invalid', ['policy' => $register_policy, 'server' => $url]);
694                                 $serverdata['register_policy'] = Register::CLOSED;
695                                 break;
696                 }
697
698                 $serverdata['platform'] = defaults($data, 'platform', '');
699
700                 return $serverdata;
701         }
702
703         private static function analyseRootBody($curlResult, $serverdata)
704         {
705                 $doc = new DOMDocument();
706                 @$doc->loadHTML($curlResult->getBody());
707                 $xpath = new DOMXPath($doc);
708
709                 $title = trim(XML::getFirstNodeValue($xpath, '//head/title/text()'));
710                 if (!empty($title)) {
711                         $serverdata['site_name'] = $title;
712                 }
713
714                 $list = $xpath->query('//meta[@name]');
715
716                 foreach ($list as $node) {
717                         $attr = [];
718                         if ($node->attributes->length) {
719                                 foreach ($node->attributes as $attribute) {
720                                         $attribute->value = trim($attribute->value);
721                                         if (empty($attribute->value)) {
722                                                 continue;
723                                         }
724
725                                         $attr[$attribute->name] = $attribute->value;
726                                 }
727
728                                 if (empty($attr['name']) || empty($attr['content'])) {
729                                         continue;
730                                 }
731                         }
732 //print_r($attr);
733                         if ($attr['name'] == 'description') {
734                                 $serverdata['info'] = $attr['content'];
735                         }
736
737                         if ($attr['name'] == 'application-name') {
738                                 $serverdata['platform'] = $attr['content'];
739                                 if (in_array($attr['content'], ['Misskey', 'Write.as'])) {
740                                         $serverdata['network'] = Protocol::ACTIVITYPUB;
741                                 }
742                         }
743 //
744                         if (($attr['name'] == 'generator') && in_array($attr['content'], ['Write.as'])) {
745 die('as');
746 //                              $serverdata['platform'] = $attr['content'];
747 //                              $serverdata['network'] = Protocol::ACTIVITYPUB;
748                         } elseif ($attr['name'] == 'generator') {
749                                 $serverdata['platform'] = $attr['content'];
750
751                                 $version_part = explode(' ', $attr['content']);
752
753                                 if (count($version_part) == 3) {
754                                         if (($version_part[0] == 'Red') && ($version_part[1] == 'Matrix')) {
755 //                                              $serverdata['platform'] = $version_part[0] . ' ' . $version_part[1];
756 //                                              $serverdata['version'] = $version_part[2];
757 //                                              $serverdata['network'] = Protocol::DIASPORA;
758                                         }
759                                 } elseif (count($version_part) == 2) {
760                                         if (in_array($version_part[0], ['WordPress'])) {
761                                                 $serverdata['platform'] = $version_part[0];
762                                                 $serverdata['version'] = $version_part[1];
763                                                 $serverdata['network'] = Protocol::ACTIVITYPUB;
764                                         }
765                                         if (in_array($version_part[0], ['Friendika', 'Friendica'])) {
766                                                 $serverdata['platform'] = $version_part[0];
767                                                 $serverdata['version'] = $version_part[1];
768                                                 $serverdata['network'] = Protocol::DFRN;
769                                         }
770                                 }
771                         }
772                 }
773
774                 $list = $xpath->query('//meta[@property]');
775
776                 foreach ($list as $node) {
777                         $attr = [];
778                         if ($node->attributes->length) {
779                                 foreach ($node->attributes as $attribute) {
780                                         $attribute->value = trim($attribute->value);
781                                         if (empty($attribute->value)) {
782                                                 continue;
783                                         }
784
785                                         $attr[$attribute->name] = $attribute->value;
786                                 }
787
788                                 if (empty($attr['property']) || empty($attr['content'])) {
789                                         continue;
790                                 }
791                         }
792 //print_r($attr);
793
794                         if ($attr['property'] == 'og:site_name') {
795                                 $serverdata['site_name'] = $attr['content'];
796                         }
797
798                         if ($attr['property'] == 'og:description') {
799                                 $serverdata['info'] = $attr['content'];
800                         }
801
802 //                      if (($attr['property'] == 'og:title') && in_array($attr['content'], ['pixelfed', 'Socialhome'])) {
803 //                      if (($attr['property'] == 'og:title') && in_array($attr['content'], ['Nextcloud'])) {
804 //                              $serverdata['platform'] = $attr['content'];
805 //                              $serverdata['network'] = Protocol::ACTIVITYPUB;
806 //                      }
807
808                         if ($attr['property'] == 'og:platform') {
809                                 $serverdata['platform'] = $attr['content'];
810
811                                 if (in_array($attr['content'], ['PeerTube'])) {
812                                         $serverdata['network'] = Protocol::ACTIVITYPUB;
813                                 }
814                         }
815
816                         if ($attr['property'] == 'generator') {
817                                 $serverdata['platform'] = $attr['content'];
818
819                                 if (in_array($attr['content'], ['hubzilla'])) {
820                                         // We later check which compatible protocol modules are loaded.
821                                         $serverdata['network'] = Protocol::ZOT;
822                                 }
823                         }
824                 }
825
826                 return $serverdata;
827         }
828
829         private static function analyseRootHeader($curlResult, $serverdata)
830         {
831                 if ($curlResult->getHeader('server') == 'Mastodon') {
832                         $serverdata['platform'] = 'mastodon';
833                         $serverdata['network'] = $network = Protocol::ACTIVITYPUB;
834                 } elseif ($curlResult->inHeader('x-diaspora-version')) {
835                         $serverdata['platform'] = 'diaspora';
836                         $serverdata['network'] = $network = Protocol::DIASPORA;
837                         $serverdata['version'] = $curlResult->getHeader('x-diaspora-version');
838
839                 } elseif ($curlResult->inHeader('x-friendica-version')) {
840                         $serverdata['platform'] = 'friendica';
841                         $serverdata['network'] = $network = Protocol::DFRN;
842                         $serverdata['version'] = $curlResult->getHeader('x-friendica-version');
843
844                 } else {
845 //print_r($curlResult->getHeaderArray());
846                 }
847                 return $serverdata;
848         }
849 }