]> git.mxchange.org Git - friendica.git/blobdiff - src/Model/GServer.php
Don't perform a delivery to failing servers
[friendica.git] / src / Model / GServer.php
index 1986e478317296c35813791797750a9b996fcc85..f68851af6826b945cdc25f392a7381a5636c7db8 100644 (file)
@@ -70,6 +70,7 @@ class GServer
        const DETECT_UNSPECIFIC = [self::DETECT_MANUAL, self::DETECT_HEADER, self::DETECT_BODY, self::DETECT_HOST_META, self::DETECT_CONTACTS, self::DETECT_AP_ACTOR];
 
        // Implementation specific endpoints
+       // @todo Possibly add Lemmy detection via the endpoint /api/v3/site
        const DETECT_FRIENDIKA = 10;
        const DETECT_FRIENDICA = 11;
        const DETECT_STATUSNET = 12;
@@ -93,6 +94,7 @@ class GServer
         *
         * @param string $url
         * @param boolean $only_nodeinfo
+        *
         * @return void
         */
        public static function add(string $url, bool $only_nodeinfo = false)
@@ -101,7 +103,7 @@ class GServer
                        return;
                }
 
-               Worker::add(PRIORITY_LOW, 'UpdateGServer', $url, $only_nodeinfo);
+               Worker::add(Worker::PRIORITY_LOW, 'UpdateGServer', $url, $only_nodeinfo);
        }
 
        /**
@@ -109,16 +111,17 @@ class GServer
         *
         * @param string $url
         * @param boolean $no_check Don't check if the server hadn't been found
+        *
         * @return int|null gserver id or NULL on empty URL or failed check
         */
-       public static function getID(string $url, bool $no_check = false)
+       public static function getID(string $url, bool $no_check = false): ?int
        {
+               $url = self::cleanURL($url);
+
                if (empty($url)) {
                        return null;
                }
 
-               $url = self::cleanURL($url);
-
                $gserver = DBA::selectFirst('gserver', ['id'], ['nurl' => Strings::normaliseLink($url)]);
                if (DBA::isResult($gserver)) {
                        Logger::debug('Got ID for URL', ['id' => $gserver['id'], 'url' => $url, 'callstack' => System::callstack(20)]);
@@ -138,7 +141,9 @@ class GServer
         * The pattern is a simple fnmatch() pattern with ? for single wildcard and * for multiple wildcard
         *
         * @param string $pattern
+        *
         * @return array
+        *
         * @throws Exception
         */
        public static function listByDomainPattern(string $pattern): array
@@ -172,9 +177,13 @@ class GServer
        public static function reachable(string $profile, string $server = '', string $network = '', bool $force = false): bool
        {
                if ($server == '') {
-                       $contact = Contact::getByURL($profile, null, ['baseurl']);
+                       $contact = Contact::getByURL($profile, null, ['baseurl', 'network']);
                        if (!empty($contact['baseurl'])) {
                                $server = $contact['baseurl'];
+                       } elseif ($contact['network'] == Protocol::DIASPORA) {
+                               $parts = parse_url($profile);
+                               unset($parts['path']);
+                               $server =  (string)Uri::fromParts($parts);
                        }
                }
 
@@ -185,7 +194,19 @@ class GServer
                return self::check($server, $network, $force);
        }
 
-       public static function getNextUpdateDate(bool $success, string $created = '', string $last_contact = '', bool $undetected = false)
+       /**
+        * Calculate the next update day
+        *
+        * @param bool $success
+        * @param string $created
+        * @param string $last_contact
+        * @param bool $undetected
+        *
+        * @return string
+        *
+        * @throws Exception
+        */
+       public static function getNextUpdateDate(bool $success, string $created = '', string $last_contact = '', bool $undetected = false): string
        {
                // On successful contact process check again next week when it is a detected system.
                // When we haven't detected the system, it could be a static website or a really old system.
@@ -288,7 +309,7 @@ class GServer
                        Logger::info('Set failed status for existing server', ['url' => $url]);
                        return;
                }
-               DBA::insert('gserver', ['url' => $url, 'nurl' => Strings::normaliseLink($url),
+               self::insert(['url' => $url, 'nurl' => Strings::normaliseLink($url),
                        'network' => Protocol::PHANTOM, 'created' => DateTimeFormat::utcNow(),
                        'failed' => true, 'last_failure' => DateTimeFormat::utcNow()]);
                Logger::info('Set failed status for new server', ['url' => $url]);
@@ -297,20 +318,20 @@ class GServer
        /**
         * Remove unwanted content from the given URL
         *
-        * @param string $url
+        * @param string $dirtyUrl
+        *
         * @return string cleaned URL
+        * @throws Exception
         */
-       public static function cleanURL(string $url): string
+       public static function cleanURL(string $dirtyUrl): string
        {
-               $url = trim($url, '/');
-               $url = str_replace('/index.php', '', $url);
-
-               $urlparts = parse_url($url);
-               unset($urlparts['user']);
-               unset($urlparts['pass']);
-               unset($urlparts['query']);
-               unset($urlparts['fragment']);
-               return (string)Uri::fromParts($urlparts);
+               try {
+                       $url = str_replace('/index.php', '', trim($dirtyUrl, '/'));
+                       return (string)(new Uri($url))->withUserInfo('')->withQuery('')->withFragment('');
+               } catch (\Throwable $e) {
+                       Logger::warning('Invalid URL', ['dirtyUrl' => $dirtyUrl, 'url' => $url]);
+                       return '';
+               }
        }
 
        /**
@@ -377,7 +398,7 @@ class GServer
                                if (!self::getID($valid_url, true)) {
                                        self::detect($valid_url, $network, $only_nodeinfo);
                                }
-                               return false;   
+                               return false;
                        }
                        Logger::debug('Found redirect, but ignore it.', ['old' => $url, 'new' => $valid_url]);
                }
@@ -546,8 +567,13 @@ class GServer
 
                $serverdata['registered-users'] = $serverdata['registered-users'] ?? 0;
 
+               // Numbers above a reasonable value (10 millions) are ignored
+               if ($serverdata['registered-users'] > 10000000) {
+                       $serverdata['registered-users'] = 0;
+               }
+
                // On an active server there has to be at least a single user
-               if (!in_array($serverdata['network'], [Protocol::PHANTOM, Protocol::FEED]) && ($serverdata['registered-users'] == 0)) {
+               if (!in_array($serverdata['network'], [Protocol::PHANTOM, Protocol::FEED]) && ($serverdata['registered-users'] <= 0)) {
                        $serverdata['registered-users'] = 1;
                } elseif (in_array($serverdata['network'], [Protocol::PHANTOM, Protocol::FEED])) {
                        $serverdata['registered-users'] = 0;
@@ -561,7 +587,7 @@ class GServer
                $gserver = DBA::selectFirst('gserver', ['network'], ['nurl' => Strings::normaliseLink($url)]);
                if (!DBA::isResult($gserver)) {
                        $serverdata['created'] = DateTimeFormat::utcNow();
-                       $ret = DBA::insert('gserver', $serverdata);
+                       $ret = self::insert($serverdata);
                        $id = DBA::lastInsertId();
                } else {
                        $ret = self::update($serverdata, ['nurl' => $serverdata['nurl']]);
@@ -614,7 +640,9 @@ class GServer
         * Fetch relay data from a given server url
         *
         * @param string $server_url address of the server
+        *
         * @return void
+        *
         * @throws \Friendica\Network\HTTPException\InternalServerErrorException
         */
        private static function discoverRelay(string $server_url)
@@ -711,9 +739,10 @@ class GServer
         * Fetch server data from '/statistics.json' on the given server
         *
         * @param string $url URL of the given server
+        *
         * @return array server data
         */
-       private static function fetchStatistics(string $url, array $serverdata)
+       private static function fetchStatistics(string $url, array $serverdata): array
        {
                $curlResult = DI::httpClient()->get($url . '/statistics.json', HttpClientAccept::JSON);
                if (!$curlResult->isSuccess()) {
@@ -803,6 +832,7 @@ class GServer
         * @param ICanHandleHttpResponses $httpResult
         *
         * @return array Server data
+        *
         * @throws \Friendica\Network\HTTPException\InternalServerErrorException
         */
        private static function fetchNodeinfo(string $url, ICanHandleHttpResponses $httpResult): array
@@ -853,7 +883,9 @@ class GServer
         * Parses Nodeinfo 1
         *
         * @param string $nodeinfo_url address of the nodeinfo path
+        *
         * @return array Server data
+        *
         * @throws \Friendica\Network\HTTPException\InternalServerErrorException
         */
        private static function parseNodeinfo1(string $nodeinfo_url): array
@@ -949,8 +981,11 @@ class GServer
         * Parses Nodeinfo 2
         *
         * @see https://git.feneas.org/jaywink/nodeinfo2
+        *
         * @param string $nodeinfo_url address of the nodeinfo path
+        *
         * @return array Server data
+        *
         * @throws \Friendica\Network\HTTPException\InternalServerErrorException
         */
        private static function parseNodeinfo2(string $nodeinfo_url): array
@@ -1055,11 +1090,14 @@ class GServer
         * Parses NodeInfo2 protocol 1.0
         *
         * @see https://github.com/jaywink/nodeinfo2/blob/master/PROTOCOL.md
+        *
         * @param string $nodeinfo_url address of the nodeinfo path
+        *
         * @return array Server data
+        *
         * @throws \Friendica\Network\HTTPException\InternalServerErrorException
         */
-       private static function parseNodeinfo210(ICanHandleHttpResponses $httpResult)
+       private static function parseNodeinfo210(ICanHandleHttpResponses $httpResult): array
        {
                if (!$httpResult->isSuccess()) {
                        return [];
@@ -1154,6 +1192,7 @@ class GServer
         *
         * @param string $url        URL of the given server
         * @param array  $serverdata array with server data
+        *
         * @return array server data
         */
        private static function fetchSiteinfo(string $url, array $serverdata): array
@@ -1174,7 +1213,7 @@ class GServer
 
                if (!empty($data['url'])) {
                        $serverdata['platform'] = strtolower($data['platform']);
-                       $serverdata['version'] = $data['version'];
+                       $serverdata['version'] = $data['version'] ?? 'N/A';
                }
 
                if (!empty($data['plugins'])) {
@@ -1233,7 +1272,17 @@ class GServer
                return $serverdata;
        }
 
-       private static function fetchDataFromSystemActor(array $data, array $serverdata)
+       /**
+        * 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' => ''];
@@ -1274,17 +1323,18 @@ class GServer
         * 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): bool
        {
                $xrd_timeout = DI::config()->get('system', 'xrd_timeout');
-               $curlResult = DI::httpClient()->get($url . '/.well-known/host-meta', HttpClientAccept::XRD_XML, [HttpClientOptions::TIMEOUT => $xrd_timeout]);
+               $curlResult = DI::httpClient()->get($url . Probe::HOST_META, HttpClientAccept::XRD_XML, [HttpClientOptions::TIMEOUT => $xrd_timeout]);
                if (!$curlResult->isSuccess()) {
                        return false;
                }
 
-               $xrd = XML::parseString($curlResult->getBody());
+               $xrd = XML::parseString($curlResult->getBody(), true);
                if (!is_object($xrd)) {
                        return false;
                }
@@ -1319,6 +1369,7 @@ class GServer
         *
         * @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): array
@@ -1370,6 +1421,7 @@ class GServer
         *
         * @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): array
@@ -1401,6 +1453,7 @@ class GServer
         *
         * @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): array
@@ -1478,7 +1531,7 @@ class GServer
         *
         * @return array server data
         */
-       private static function detectNextcloud(string $url, array $serverdata, bool $validHostMeta)
+       private static function detectNextcloud(string $url, array $serverdata, bool $validHostMeta): array
        {
                $curlResult = DI::httpClient()->get($url . '/status.php', HttpClientAccept::JSON);
                if (!$curlResult->isSuccess() || ($curlResult->getBody() == '')) {
@@ -1511,6 +1564,7 @@ class GServer
         *
         * @param string $url        URL of the given server
         * @param array  $serverdata array with server data
+        *
         * @return array server data
         */
        private static function fetchWeeklyUsage(string $url, array $serverdata): array
@@ -1550,6 +1604,7 @@ class GServer
         *
         * @param string $url        URL of the given server
         * @param array  $serverdata array with server data
+        *
         * @return array server data
         */
        private static function detectMastodonAlikes(string $url, array $serverdata): array
@@ -1621,6 +1676,7 @@ class GServer
         *
         * @param string $url        URL of the given server
         * @param array  $serverdata array with server data
+        *
         * @return array server data
         */
        private static function detectHubzilla(string $url, array $serverdata): array
@@ -1697,6 +1753,7 @@ class GServer
         * Converts input value to a boolean value
         *
         * @param string|integer $val
+        *
         * @return boolean
         */
        private static function toBoolean($val): bool
@@ -1715,6 +1772,7 @@ class GServer
         *
         * @param string $url        URL of the given server
         * @param array  $serverdata array with server data
+        *
         * @return array server data
         */
        private static function detectGNUSocial(string $url, array $serverdata): array
@@ -1769,6 +1827,7 @@ class GServer
         *
         * @param string $url        URL of the given server
         * @param array  $serverdata array with server data
+        *
         * @return array server data
         */
        private static function detectFriendica(string $url, array $serverdata): array
@@ -1846,16 +1905,16 @@ class GServer
         *
         * @return array server data
         */
-        private static function analyseRootBody($curlResult, array $serverdata): array
+       private static function analyseRootBody($curlResult, array $serverdata): array
        {
                if (empty($curlResult->getBody())) {
                        return $serverdata;
                }
 
-               if (file_exists(__DIR__ . '/../../static/generator.config.php')) {
-                       require __DIR__ . '/../../static/generator.config.php';
+               if (file_exists(__DIR__ . '/../../static/platforms.config.php')) {
+                       require __DIR__ . '/../../static/platforms.config.php';
                } else {
-                       throw new HTTPException\InternalServerErrorException('Invalid generator file');
+                       throw new HTTPException\InternalServerErrorException('Invalid platform file');
                }
 
                $platforms = array_merge($ap_platforms, $dfrn_platforms, $zap_platforms, $platforms);
@@ -2053,10 +2112,10 @@ class GServer
 
                while ($gserver = DBA::fetch($gservers)) {
                        Logger::info('Update peer list', ['server' => $gserver['url'], 'id' => $gserver['id']]);
-                       Worker::add(PRIORITY_LOW, 'UpdateServerPeers', $gserver['url']);
+                       Worker::add(Worker::PRIORITY_LOW, 'UpdateServerPeers', $gserver['url']);
 
                        Logger::info('Update directory', ['server' => $gserver['url'], 'id' => $gserver['id']]);
-                       Worker::add(PRIORITY_LOW, 'UpdateServerDirectory', $gserver);
+                       Worker::add(Worker::PRIORITY_LOW, 'UpdateServerDirectory', $gserver);
 
                        $fields = ['last_poco_query' => DateTimeFormat::utcNow()];
                        self::update($fields, ['nurl' => $gserver['nurl']]);
@@ -2109,9 +2168,11 @@ class GServer
                        if ($curlResult->isSuccess()) {
                                $servers = json_decode($curlResult->getBody(), true);
 
-                               foreach ($servers['instances'] as $server) {
-                                       $url = (is_null($server['https_score']) ? 'http' : 'https') . '://' . $server['name'];
-                                       self::add($url);
+                               if (!empty($servers['instances'])) {
+                                       foreach ($servers['instances'] as $server) {
+                                               $url = (is_null($server['https_score']) ? 'http' : 'https') . '://' . $server['name'];
+                                               self::add($url);
+                                       }
                                }
                        }
                }
@@ -2124,7 +2185,7 @@ class GServer
         *
         * @param int $gsid     Server id
         * @param int $protocol Protocol id
-        * @return void
+        *
         * @throws Exception
         */
        public static function setProtocol(int $gsid, int $protocol)
@@ -2184,7 +2245,9 @@ class GServer
         * Fetch the protocol of the given server
         *
         * @param int $gsid Server id
+        *
         * @return ?int One of Post\DeliveryData protocol constants or null if unknown or gserver is missing
+        *
         * @throws Exception
         */
        public static function getProtocol(int $gsid): ?int
@@ -2202,11 +2265,14 @@ class GServer
        }
 
        /**
+        * Update rows in the gserver table.
         * Enforces gserver table field maximum sizes to avoid "Data too long" database errors
         *
         * @param array $fields
         * @param array $condition
+        *
         * @return bool
+        *
         * @throws Exception
         */
        public static function update(array $fields, array $condition): bool
@@ -2215,4 +2281,22 @@ class GServer
 
                return DBA::update('gserver', $fields, $condition);
        }
+
+       /**
+        * Insert a row into the gserver table.
+        * Enforces gserver table field maximum sizes to avoid "Data too long" database errors
+        *
+        * @param array $fields
+        * @param int   $duplicate_mode What to do on a duplicated entry
+        *
+        * @return bool
+        *
+        * @throws Exception
+        */
+       public static function insert(array $fields, int $duplicate_mode = Database::INSERT_DEFAULT): bool
+       {
+               $fields = DI::dbaDefinition()->truncateFieldsForTable('gserver', $fields);
+
+               return DBA::insert('gserver', $fields, $duplicate_mode);
+       }
 }