]> git.mxchange.org Git - friendica.git/blobdiff - src/App/BaseURL.php
[frio] Add Mute Author Server button to post actions
[friendica.git] / src / App / BaseURL.php
index 7c286514a6206e46747da3def5e3d62486f166d0..cc20343d1c4f8a5659e8224ef07616c5f372ac74 100644 (file)
 <?php
+/**
+ * @copyright Copyright (C) 2010-2023, the Friendica project
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>.
+ *
+ */
 
 namespace Friendica\App;
 
-use Friendica\Core\Config\IConfiguration;
+use Friendica\Core\Config\Capability\IManageConfigValues;
 use Friendica\Core\System;
-use Friendica\Util\Network;
 use Friendica\Util\Strings;
 use Friendica\Network\HTTPException;
+use GuzzleHttp\Psr7\ServerRequest;
+use GuzzleHttp\Psr7\Uri;
+use Psr\Http\Message\UriInterface;
+use Psr\Log\LoggerInterface;
 
 /**
  * A class which checks and contains the basic
  * environment for the BaseURL (url, urlpath, ssl_policy, hostname, scheme)
  */
-class BaseURL
+class BaseURL extends Uri implements UriInterface
 {
-       /**
-        * No SSL necessary
-        */
-       const SSL_POLICY_NONE = 0;
-
-       /**
-        * SSL is necessary
-        */
-       const SSL_POLICY_FULL = 1;
-
-       /**
-        * SSL is optional, but preferred
-        */
-       const SSL_POLICY_SELFSIGN = 2;
-
-       /**
-        * Define the Default SSL scheme
-        */
-       const DEFAULT_SSL_SCHEME = self::SSL_POLICY_SELFSIGN;
-
-       /**
-        * The Friendica Config
-        *
-        * @var IConfiguration
-        */
-       private $config;
-
-       /**
-        * The server side variables
-        *
-        * @var array
-        */
-       private $server;
-
-       /**
-        * The hostname of the Base URL
-        *
-        * @var string
-        */
-       private $hostname;
-
-       /**
-        * The SSL_POLICY of the Base URL
-        *
-        * @var int
-        */
-       private $sslPolicy;
-
-       /**
-        * The URL sub-path of the Base URL
-        *
-        * @var string
-        */
-       private $urlPath;
-
-       /**
-        * The full URL
-        *
-        * @var string
-        */
-       private $url;
-
-       /**
-        * The current scheme of this call
-        *
-        * @var string
-        */
-       private $scheme;
-
-       /**
-        * Returns the hostname of this node
-        *
-        * @return string
-        */
-       public function getHostname()
-       {
-               return $this->hostname;
-       }
-
-       /**
-        * Returns the current scheme of this call
-        *
-        * @return string
-        */
-       public function getScheme()
-       {
-               return $this->scheme;
-       }
-
-       /**
-        * Returns the SSL policy of this node
-        *
-        * @return int
-        */
-       public function getSSLPolicy()
-       {
-               return $this->sslPolicy;
-       }
-
-       /**
-        * Returns the sub-path of this URL
-        *
-        * @return string
-        */
-       public function getUrlPath()
-       {
-               return $this->urlPath;
-       }
-
-       /**
-        * Returns the full URL of this call
-        *
-        * Note: $ssl parameter value doesn't directly correlate with the resulting protocol
-        *
-        * @param bool $ssl True, if ssl should get used
-        *
-        * @return string
-        */
-       public function get($ssl = false)
-       {
-               if ($this->sslPolicy === self::SSL_POLICY_SELFSIGN && $ssl) {
-                       return Network::switchScheme($this->url);
-               }
-
-               return $this->url;
-       }
-
-       /**
-        * Save current parts of the base Url
-        *
-        * @param string? $hostname
-        * @param int?    $sslPolicy
-        * @param string? $urlPath
-        *
-        * @return bool true, if successful
-        */
-       public function save($hostname = null, $sslPolicy = null, $urlPath = null)
-       {
-               $currHostname  = $this->hostname;
-               $currSSLPolicy = $this->sslPolicy;
-               $currURLPath   = $this->urlPath;
-
-               if (!empty($hostname) && $hostname !== $this->hostname) {
-                       if ($this->config->set('config', 'hostname', $hostname)) {
-                               $this->hostname = $hostname;
-                       } else {
-                               return false;
-                       }
-               }
-
-               if (isset($sslPolicy) && $sslPolicy !== $this->sslPolicy) {
-                       if ($this->config->set('system', 'ssl_policy', $sslPolicy)) {
-                               $this->sslPolicy = $sslPolicy;
-                       } else {
-                               $this->hostname = $currHostname;
-                               $this->config->set('config', 'hostname', $this->hostname);
-                               return false;
-                       }
-               }
-
-               if (isset($urlPath) && $urlPath !== $this->urlPath) {
-                       if ($this->config->set('system', 'urlpath', $urlPath)) {
-                               $this->urlPath = $urlPath;
-                       } else {
-                               $this->hostname  = $currHostname;
-                               $this->sslPolicy = $currSSLPolicy;
-                               $this->config->set('config', 'hostname', $this->hostname);
-                               $this->config->set('system', 'ssl_policy', $this->sslPolicy);
-                               return false;
-                       }
-               }
-
-               $this->determineBaseUrl();
-               if (!$this->config->set('system', 'url', $this->url)) {
-                       $this->hostname  = $currHostname;
-                       $this->sslPolicy = $currSSLPolicy;
-                       $this->urlPath   = $currURLPath;
-                       $this->determineBaseUrl();
-
-                       $this->config->set('config', 'hostname', $this->hostname);
-                       $this->config->set('system', 'ssl_policy', $this->sslPolicy);
-                       $this->config->set('system', 'urlpath', $this->urlPath);
-                       return false;
-               }
-
-               return true;
-       }
-
-       /**
-        * Save the current url as base URL
-        *
-        * @param $url
-        *
-        * @return bool true, if the save was successful
-        */
-       public function saveByURL($url)
-       {
-               $parsed = @parse_url($url);
-
-               if (empty($parsed)) {
-                       return false;
-               }
-
-               $hostname = $parsed['host'];
-               if (!empty($hostname) && !empty($parsed['port'])) {
-                       $hostname .= ':' . $parsed['port'];
-               }
-
-               $urlPath = null;
-               if (!empty($parsed['path'])) {
-                       $urlPath = trim($parsed['path'], '\\/');
-               }
-
-               $sslPolicy = null;
-               if (!empty($parsed['scheme'])) {
-                       if ($parsed['scheme'] == 'https') {
-                               $sslPolicy = BaseURL::SSL_POLICY_FULL;
-                       }
-               }
-
-               return $this->save($hostname, $sslPolicy, $urlPath);
-       }
-
-       /**
-        * Checks, if a redirect to the HTTPS site would be necessary
-        *
-        * @return bool
-        */
-       public function checkRedirectHttps()
-       {
-               return $this->config->get('system', 'force_ssl') &&
-                      ($this->getScheme() == "http") &&
-                      intval($this->getSSLPolicy()) == BaseURL::SSL_POLICY_FULL &&
-                      strpos($this->get(), 'https://') === 0 &&
-                      !empty($this->server['REQUEST_METHOD']) &&
-                      $this->server['REQUEST_METHOD'] === 'GET';
-       }
-
-       /**
-        * @param IConfiguration $config The Friendica IConfiguration
-        * @param array         $server The $_SERVER array
-        */
-       public function __construct(IConfiguration $config, array $server)
+       public function __construct(IManageConfigValues $config, LoggerInterface $logger, array $server = [])
        {
-               $this->config = $config;
-               $this->server = $server;
-
-               $this->determineSchema();
-               $this->checkConfig();
-       }
-
-       /**
-        * Check the current config during loading
-        */
-       public function checkConfig()
-       {
-               $this->hostname  = $this->config->get('config', 'hostname');
-               $this->urlPath   = $this->config->get('system', 'urlpath');
-               $this->sslPolicy = $this->config->get('system', 'ssl_policy');
-               $this->url       = $this->config->get('system', 'url');
-
-               if (empty($this->hostname)) {
-                       $this->determineHostname();
-
-                       if (!empty($this->hostname)) {
-                               $this->config->set('config', 'hostname', $this->hostname);
-                       }
-               }
-
-               if (!isset($this->urlPath)) {
-                       $this->determineURLPath();
-                       $this->config->set('system', 'urlpath', $this->urlPath);
-               }
-
-               if (!isset($this->sslPolicy)) {
-                       if ($this->scheme == 'https') {
-                               $this->sslPolicy = self::SSL_POLICY_FULL;
-                       } else {
-                               $this->sslPolicy = self::DEFAULT_SSL_SCHEME;
-                       }
-                       $this->config->set('system', 'ssl_policy', $this->sslPolicy);
-               }
-
-               if (empty($this->url)) {
-                       $this->determineBaseUrl();
-
-                       if (!empty($this->url)) {
-                               $this->config->set('system', 'url', $this->url);
-                       }
+               $url = $config->get('system', 'url');
+               if (empty($url)) {
+                       $logger->critical('Invalid config - Missing system.url');
+                       $url = ServerRequest::getUriFromGlobals()
+                                                               ->withQuery('')
+                                                               ->withPath($this->determineURLPath($server));
                }
-       }
-
-       /**
-        * Determines the hostname of this node if not set already
-        */
-       private function determineHostname()
-       {
-               $this->hostname = '';
-
-               if (!empty($this->server['SERVER_NAME'])) {
-                       $this->hostname = $this->server['SERVER_NAME'];
 
-                       if (!empty($this->server['SERVER_PORT']) && $this->server['SERVER_PORT'] != 80 && $this->server['SERVER_PORT'] != 443) {
-                               $this->hostname .= ':' . $this->server['SERVER_PORT'];
-                       }
-               }
+               parent::__construct($url);
        }
 
        /**
-        * Figure out if we are running at the top of a domain or in a sub-directory
+        * Figure out if we are running at the top of a domain or in a subdirectory
         */
-       private function determineURLPath()
+       private function determineURLPath(array $server): string
        {
-               $this->urlPath = '';
-
-               /*
-                * The automatic path detection in this function is currently deactivated,
-                * see issue https://github.com/friendica/friendica/issues/6679
-                *
-                * The problem is that the function seems to be confused with some url.
-                * These then confuses the detection which changes the url path.
-                */
-
                /* Relative script path to the web server root
                 * Not all of those $_SERVER properties can be present, so we do by inverse priority order
                 */
-               $relative_script_path =
-                       ($this->server['REDIRECT_URL']        ?? '') ?:
-                       ($this->server['REDIRECT_URI']        ?? '') ?:
-                       ($this->server['REDIRECT_SCRIPT_URL'] ?? '') ?:
-                       ($this->server['SCRIPT_URL']          ?? '') ?:
-                        $this->server['REQUEST_URI']         ?? '';
-
-               /* $relative_script_path gives /relative/path/to/friendica/module/parameter
+               $relativeScriptPath =
+                       ($server['REDIRECT_URL'] ?? '') ?:
+                               ($server['REDIRECT_URI'] ?? '') ?:
+                                       ($server['REDIRECT_SCRIPT_URL'] ?? '') ?:
+                                               ($server['SCRIPT_URL'] ?? '') ?:
+                                                       $server['REQUEST_URI'] ?? '';
+
+               /* $relativeScriptPath gives /relative/path/to/friendica/module/parameter
                 * QUERY_STRING gives pagename=module/parameter
                 *
                 * To get /relative/path/to/friendica we perform dirname() for as many levels as there are slashes in the QUERY_STRING
                 */
-               if (!empty($relative_script_path)) {
+               if (!empty($relativeScriptPath)) {
                        // Module
-                       if (!empty($this->server['QUERY_STRING'])) {
-                               $this->urlPath = trim(dirname($relative_script_path, substr_count(trim($this->server['QUERY_STRING'], '/'), '/') + 1), '/');
+                       if (!empty($server['QUERY_STRING'])) {
+                               return trim(dirname($relativeScriptPath, substr_count(trim($server['QUERY_STRING'], '/'), '/') + 1), '/');
                        } else {
                                // Root page
-                               $this->urlPath = trim($relative_script_path, '/');
+                               $scriptPathParts = explode('?', $relativeScriptPath, 2);
+                               return trim($scriptPathParts[0], '/');
                        }
                }
-       }
-
-       /**
-        * Determine the full URL based on all parts
-        */
-       private function determineBaseUrl()
-       {
-               $scheme = 'http';
-
-               if ($this->sslPolicy == self::SSL_POLICY_FULL) {
-                       $scheme = 'https';
-               }
-
-               $this->url = $scheme . '://' . $this->hostname . (!empty($this->urlPath) ? '/' . $this->urlPath : '');
-       }
 
-       /**
-        * Determine the scheme of the current used link
-        */
-       private function determineSchema()
-       {
-               $this->scheme = 'http';
-
-               if (!empty($this->server['HTTPS']) ||
-                   !empty($this->server['HTTP_FORWARDED']) && preg_match('/proto=https/', $this->server['HTTP_FORWARDED']) ||
-                   !empty($this->server['HTTP_X_FORWARDED_PROTO']) && $this->server['HTTP_X_FORWARDED_PROTO'] == 'https' ||
-                   !empty($this->server['HTTP_X_FORWARDED_SSL']) && $this->server['HTTP_X_FORWARDED_SSL'] == 'on' ||
-                   !empty($this->server['FRONT_END_HTTPS']) && $this->server['FRONT_END_HTTPS'] == 'on' ||
-                   !empty($this->server['SERVER_PORT']) && (intval($this->server['SERVER_PORT']) == 443) // XXX: reasonable assumption, but isn't this hardcoding too much?
-               ) {
-                       $this->scheme = 'https';
-               }
+               return '';
        }
 
        /**
@@ -402,15 +90,15 @@ class BaseURL
         *
         * @return string The cleaned url
         */
-       public function remove(string $origURL)
+       public function remove(string $origURL): string
        {
                // Remove the hostname from the url if it is an internal link
                $nurl = Strings::normaliseLink($origURL);
-               $base = Strings::normaliseLink($this->get());
+               $base = Strings::normaliseLink($this->__toString());
                $url  = str_replace($base . '/', '', $nurl);
 
-               // if it is an external link return the orignal value
-               if ($url == Strings::normaliseLink($origURL)) {
+               // if it is an external link return the original value
+               if ($url === $nurl) {
                        return $origURL;
                } else {
                        return $url;
@@ -424,23 +112,29 @@ class BaseURL
         * @param string $toUrl The destination URL (Default is empty, which is the default page of the Friendica node)
         * @param bool   $ssl   if true, base URL will try to get called with https:// (works just for relative paths)
         *
+        * @throws HTTPException\FoundException
+        * @throws HTTPException\MovedPermanentlyException
+        * @throws HTTPException\TemporaryRedirectException
+        *
         * @throws HTTPException\InternalServerErrorException In Case the given URL is not relative to the Friendica node
         */
-       public function redirect($toUrl = '', $ssl = false)
+       public function redirect(string $toUrl = '', bool $ssl = false)
        {
                if (!empty(parse_url($toUrl, PHP_URL_SCHEME))) {
-                       throw new HTTPException\InternalServerErrorException("'$toUrl is not a relative path, please use System::externalRedirectTo");
+                       throw new HTTPException\InternalServerErrorException("$toUrl is not a relative path, please use System::externalRedirectTo");
                }
 
-               $redirectTo = $this->get($ssl) . '/' . ltrim($toUrl, '/');
+               $redirectTo = $this->__toString() . '/' . ltrim($toUrl, '/');
                System::externalRedirect($redirectTo);
        }
 
-       /**
-        * Returns the base url as string
-        */
-       public function __toString()
+       public function isLocalUrl(string $url): bool
+       {
+               return strpos(Strings::normaliseLink($url), Strings::normaliseLink((string)$this)) === 0;
+       }
+
+       public function isLocalUri(UriInterface $uri): bool
        {
-               return $this->get();
+               return $this->isLocalUrl((string)$uri);
        }
 }