]> git.mxchange.org Git - friendica.git/blobdiff - src/Util/Network.php
Merge pull request #13161 from annando/bluesky-activities
[friendica.git] / src / Util / Network.php
index 2cad22acf83791bd7e2d8dad7a480f10175bdaa1..f7ceab5433b217bbc33c2ccb98770cbaf40020f1 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /**
- * @copyright Copyright (C) 2010-2022, the Friendica project
+ * @copyright Copyright (C) 2010-2023, the Friendica project
  *
  * @license GNU AGPL version 3 or any later version
  *
@@ -25,8 +25,11 @@ use Friendica\Core\Hook;
 use Friendica\Core\Logger;
 use Friendica\DI;
 use Friendica\Model\Contact;
+use Friendica\Network\HTTPClient\Client\HttpClientAccept;
+use Friendica\Network\HTTPClient\Client\HttpClientOptions;
 use Friendica\Network\HTTPException\NotModifiedException;
 use GuzzleHttp\Psr7\Uri;
+use Psr\Http\Message\UriInterface;
 
 class Network
 {
@@ -48,6 +51,7 @@ class Network
         * and check DNS to see if it's real (or check if is a valid IP address)
         *
         * @param string $url The URL to be validated
+        *
         * @return string|boolean The actual working URL, false else
         * @throws \Friendica\Network\HTTPException\InternalServerErrorException
         */
@@ -66,14 +70,39 @@ class Network
                        $url = 'http://' . $url;
                }
 
-               /// @TODO Really suppress function outcomes? Why not find them + debug them?
-               $h = @parse_url($url);
+               $xrd_timeout = DI::config()->get('system', 'xrd_timeout');
+               $host = parse_url($url, PHP_URL_HOST);
 
-               if (!empty($h['host']) && (@dns_get_record($h['host'], DNS_A + DNS_CNAME) || filter_var($h['host'], FILTER_VALIDATE_IP))) {
-                       return $url;
+               if (empty($host) || !(filter_var($host, FILTER_VALIDATE_IP) || @dns_get_record($host . '.', DNS_A + DNS_AAAA))) {
+                       return false;
                }
 
-               return false;
+               if (in_array(parse_url($url, PHP_URL_SCHEME), ['https', 'http'])) {
+                       $options = [HttpClientOptions::VERIFY => true, HttpClientOptions::TIMEOUT => $xrd_timeout];
+                       try {
+                               $curlResult = DI::httpClient()->head($url, $options);
+                       } catch (\Exception $e) {
+                               return false;
+                       }
+
+                       // Workaround for systems that can't handle a HEAD request. Don't retry on timeouts.
+                       if (!$curlResult->isSuccess() && ($curlResult->getReturnCode() >= 400) && !in_array($curlResult->getReturnCode(), [408, 504])) {
+                               try {
+                                       $curlResult = DI::httpClient()->get($url, HttpClientAccept::DEFAULT, $options);
+                               } catch (\Exception $e) {
+                                       return false;
+                               }
+                       }
+
+                       if (!$curlResult->isSuccess()) {
+                               Logger::notice('Url not reachable', ['host' => $host, 'url' => $url]);
+                               return false;
+                       } elseif ($curlResult->isRedirectUrl()) {
+                               $url = $curlResult->getRedirectUrl();
+                       }
+               }
+
+               return $url;
        }
 
        /**
@@ -95,7 +124,7 @@ class Network
                $h = substr($addr, strpos($addr, '@') + 1);
 
                // Concerning the @ see here: https://stackoverflow.com/questions/36280957/dns-get-record-a-temporary-server-error-occurred
-               if ($h && (@dns_get_record($h, DNS_A + DNS_MX) || filter_var($h, FILTER_VALIDATE_IP))) {
+               if ($h && (@dns_get_record($h, DNS_A + DNS_AAAA + DNS_MX) || filter_var($h, FILTER_VALIDATE_IP))) {
                        return true;
                }
                if ($h && @dns_get_record($h, DNS_CNAME + DNS_MX)) {
@@ -157,11 +186,28 @@ class Network
         * @param string $url The url to check the domain from
         *
         * @return boolean
+        *
+        * @deprecated since 2023.03 Use isUriBlocked instead
         */
        public static function isUrlBlocked(string $url): bool
        {
-               $host = @parse_url($url, PHP_URL_HOST);
-               if (!$host) {
+               try {
+                       return self::isUriBlocked(new Uri($url));
+               } catch (\Throwable $e) {
+                       Logger::warning('Invalid URL', ['url' => $url]);
+                       return false;
+               }
+       }
+
+       /**
+        * Checks if the provided URI domain is on the domain blocklist.
+        *
+        * @param UriInterface $uri
+        * @return boolean
+        */
+       public static function isUriBlocked(UriInterface $uri): bool
+       {
+               if (!$uri->getHost()) {
                        return false;
                }
 
@@ -171,7 +217,7 @@ class Network
                }
 
                foreach ($domain_blocklist as $domain_block) {
-                       if (fnmatch(strtolower($domain_block['domain']), strtolower($host))) {
+                       if (fnmatch(strtolower($domain_block['domain']), strtolower($uri->getHost()))) {
                                return true;
                        }
                }
@@ -240,6 +286,7 @@ class Network
         *
         * @param string $domain
         * @param array  $domain_list
+        *
         * @return boolean
         */
        public static function isDomainAllowed(string $domain, array $domain_list): bool
@@ -278,6 +325,7 @@ class Network
         * Remove Google Analytics and other tracking platforms params from URL
         *
         * @param string $url Any user-submitted URL that may contain tracking params
+        *
         * @return string The same URL stripped of tracking parameters
         */
        public static function stripTrackingQueryParams(string $url): string
@@ -309,7 +357,7 @@ class Network
                                                $pair = $param . '=' . str_replace(' ', '+', $value);
                                                $url = str_replace($pair, '', $url);
 
-                                               // Third try: Maybey the url isn't encoded at all
+                                               // Third try: Maybe the url isn't encoded at all
                                                $pair = $param . '=' . $value;
                                                $url = str_replace($pair, '', $url);
 
@@ -331,10 +379,12 @@ class Network
         *
         * @param string $url
         * @param string $basepath
+        *
         * @return string url
         */
-       public static function addBasePath(string $url, string $basepath)
+       public static function addBasePath(string $url, string $basepath): string
        {
+               $url = trim($url);
                if (!empty(parse_url($url, PHP_URL_SCHEME)) || empty(parse_url($basepath, PHP_URL_SCHEME)) || empty($url) || empty(parse_url($url))) {
                        return $url;
                }
@@ -353,6 +403,7 @@ class Network
         *
         * @param string $url1
         * @param string $url2
+        *
         * @return string The matching part or empty string on error
         */
        public static function getUrlMatch(string $url1, string $url2): string
@@ -440,6 +491,7 @@ class Network
         * Glue url parts together
         *
         * @param array $parsed URL parts
+        *
         * @return string|null The glued URL or null on error
         * @deprecated since version 2021.12, use GuzzleHttp\Psr7\Uri::fromParts($parts) instead
         */
@@ -456,21 +508,22 @@ class Network
                $scheme    = $get('scheme');
                $query     = $get('query');
                $fragment  = $get('fragment');
-               $authority = ($userinfo !== null ? $userinfo . '@' : '') . 
+               $authority = ($userinfo !== null ? $userinfo . '@' : '') .
                                                $get('host') .
                                                ($port ? ":$port" : '');
 
-               return  (strlen($scheme) ? $scheme . ':' : '') .
-                       (strlen($authority) ? '//' . $authority : '') .
+               return  (!empty($scheme) ? $scheme . ':' : '') .
+                       (!empty($authority) ? '//' . $authority : '') .
                        $get('path') .
-                       (strlen($query) ? '?' . $query : '') .
-                       (strlen($fragment) ? '#' . $fragment : '');
+                       (!empty($query) ? '?' . $query : '') .
+                       (!empty($fragment) ? '#' . $fragment : '');
        }
 
        /**
         * Convert an URI to an IDN compatible URI
         *
         * @param string $uri
+        *
         * @return string
         */
        public static function convertToIdn(string $uri): string
@@ -478,7 +531,7 @@ class Network
                $parts = parse_url($uri);
                if (!empty($parts['scheme']) && !empty($parts['host'])) {
                        $parts['host'] = idn_to_ascii($parts['host']);
-                       $uri = Uri::fromParts($parts);
+                       $uri = (string)Uri::fromParts($parts);
                } else {
                        $parts = explode('@', $uri);
                        if (count($parts) == 2) {
@@ -495,6 +548,7 @@ class Network
         * Switch the scheme of an url between http and https
         *
         * @param string $url
+        *
         * @return string Switched URL
         */
        public static function switchScheme(string $url): string
@@ -518,6 +572,7 @@ class Network
         *
         * @param string $path
         * @param array  $additionalParams Associative array of parameters
+        *
         * @return string
         */
        public static function appendQueryParam(string $path, array $additionalParams): string
@@ -544,6 +599,7 @@ class Network
         *
         * @param string $etag          The page etag
         * @param string $last_modified The page last modification UTC date
+        *
         * @return void
         * @throws \Exception
         */
@@ -582,6 +638,7 @@ class Network
         * Check if the given URL is a local link
         *
         * @param string $url
+        *
         * @return bool
         */
        public static function isLocalLink(string $url): bool