3 namespace Friendica\Util;
5 use Friendica\Core\Config\Configuration;
8 * A class which checks and contains the basic
9 * environment for the BaseURL (url, urlpath, ssl_policy, hostname, scheme)
16 const SSL_POLICY_NONE = 0;
21 const SSL_POLICY_FULL = 1;
24 * SSL is optional, but preferred
26 const SSL_POLICY_SELFSIGN = 2;
29 * Define the Default SSL scheme
31 const DEFAULT_SSL_SCHEME = self::SSL_POLICY_SELFSIGN;
34 * The Friendica Config
40 * The server side variables
46 * The hostname of the Base URL
52 * The SSL_POLICY of the Base URL
58 * The URL sub-path of the Base URL
70 * The current scheme of this call
76 * Returns the hostname of this node
79 public function getHostname()
81 return $this->hostname;
85 * Returns the current scheme of this call
88 public function getScheme()
94 * Returns the SSL policy of this node
97 public function getSSLPolicy()
99 return $this->sslPolicy;
103 * Returns the sub-path of this URL
106 public function getUrlPath()
108 return $this->urlPath;
112 * Returns the full URL of this call
114 * Note: $ssl parameter value doesn't directly correlate with the resulting protocol
116 * @param bool $ssl True, if ssl should get used
120 public function get($ssl = false)
122 if ($this->sslPolicy === self::SSL_POLICY_SELFSIGN && $ssl) {
123 return Network::switchScheme($this->url);
130 * Save current parts of the base Url
132 * @param string? $hostname
133 * @param int? $sslPolicy
134 * @param string? $urlPath
136 * @return bool true, if successful
138 public function save($hostname = null, $sslPolicy = null, $urlPath = null)
140 $currHostname = $this->hostname;
141 $currSSLPolicy = $this->sslPolicy;
142 $currURLPath = $this->urlPath;
144 if (!empty($hostname) && $hostname !== $this->hostname) {
145 if ($this->config->set('config', 'hostname', $hostname)) {
146 $this->hostname = $hostname;
152 if (isset($sslPolicy) && $sslPolicy !== $this->sslPolicy) {
153 if ($this->config->set('system', 'ssl_policy', $sslPolicy)) {
154 $this->sslPolicy = $sslPolicy;
156 $this->hostname = $currHostname;
157 $this->config->set('config', 'hostname', $this->hostname);
162 if (isset($urlPath) && $urlPath !== $this->urlPath) {
163 if ($this->config->set('system', 'urlpath', $urlPath)) {
164 $this->urlPath = $urlPath;
166 $this->hostname = $currHostname;
167 $this->sslPolicy = $currSSLPolicy;
168 $this->config->set('config', 'hostname', $this->hostname);
169 $this->config->set('system', 'ssl_policy', $this->sslPolicy);
174 $this->determineBaseUrl();
175 if (!$this->config->set('system', 'url', $this->url)) {
176 $this->hostname = $currHostname;
177 $this->sslPolicy = $currSSLPolicy;
178 $this->urlPath = $currURLPath;
179 $this->determineBaseUrl();
181 $this->config->set('config', 'hostname', $this->hostname);
182 $this->config->set('system', 'ssl_policy', $this->sslPolicy);
183 $this->config->set('system', 'urlpath', $this->urlPath);
191 * Save the current url as base URL
195 * @return bool true, if the save was successful
197 public function saveByURL($url)
199 $parsed = @parse_url($url);
201 if (empty($parsed)) {
205 $hostname = $parsed['host'];
206 if (!empty($hostname) && !empty($parsed['port'])) {
207 $hostname .= ':' . $parsed['port'];
211 if (!empty($parsed['path'])) {
212 $urlPath = trim($parsed['path'], '\\/');
216 if (!empty($parsed['scheme'])) {
217 if ($parsed['scheme'] == 'https') {
218 $sslPolicy = BaseURL::SSL_POLICY_FULL;
222 return $this->save($hostname, $sslPolicy, $urlPath);
226 * Checks, if a redirect to the HTTPS site would be necessary
230 public function checkRedirectHttps()
232 return $this->config->get('system', 'force_ssl')
233 && ($this->getScheme() == "http")
234 && intval($this->getSSLPolicy()) == BaseURL::SSL_POLICY_FULL
235 && strpos($this->get(), 'https://') === 0
236 && !empty($this->server['REQUEST_METHOD'])
237 && $this->server['REQUEST_METHOD'] === 'GET';
241 * @param Configuration $config The Friendica configuration
242 * @param array $server The $_SERVER array
244 public function __construct(Configuration $config, array $server)
246 $this->config = $config;
247 $this->server = $server;
249 $this->determineSchema();
250 $this->checkConfig();
254 * Check the current config during loading
256 public function checkConfig()
258 $this->hostname = $this->config->get('config', 'hostname');
259 $this->urlPath = $this->config->get('system', 'urlpath');
260 $this->sslPolicy = $this->config->get('system', 'ssl_policy');
261 $this->url = $this->config->get('system', 'url');
263 if (empty($this->hostname)) {
264 $this->determineHostname();
266 if (!empty($this->hostname)) {
267 $this->config->set('config', 'hostname', $this->hostname);
271 if (!isset($this->urlPath)) {
272 $this->determineURLPath();
273 $this->config->set('system', 'urlpath', $this->urlPath);
276 if (!isset($this->sslPolicy)) {
277 if ($this->scheme == 'https') {
278 $this->sslPolicy = self::SSL_POLICY_FULL;
280 $this->sslPolicy = self::DEFAULT_SSL_SCHEME;
282 $this->config->set('system', 'ssl_policy', $this->sslPolicy);
285 if (empty($this->url)) {
286 $this->determineBaseUrl();
289 $this->config->set('system', 'url', $this->url);
295 * Determines the hostname of this node if not set already
297 private function determineHostname()
299 $this->hostname = '';
301 if (!empty($this->server['SERVER_NAME'])) {
302 $this->hostname = $this->server['SERVER_NAME'];
304 if (!empty($this->server['SERVER_PORT']) && $this->server['SERVER_PORT'] != 80 && $this->server['SERVER_PORT'] != 443) {
305 $this->hostname .= ':' . $this->server['SERVER_PORT'];
311 * Figure out if we are running at the top of a domain or in a sub-directory
313 private function determineURLPath()
318 * The automatic path detection in this function is currently deactivated,
319 * see issue https://github.com/friendica/friendica/issues/6679
321 * The problem is that the function seems to be confused with some url.
322 * These then confuses the detection which changes the url path.
325 /* Relative script path to the web server root
326 * Not all of those $_SERVER properties can be present, so we do by inverse priority order
328 $relative_script_path = '';
329 $relative_script_path = defaults($this->server, 'REDIRECT_URL', $relative_script_path);
330 $relative_script_path = defaults($this->server, 'REDIRECT_URI', $relative_script_path);
331 $relative_script_path = defaults($this->server, 'REDIRECT_SCRIPT_URL', $relative_script_path);
332 $relative_script_path = defaults($this->server, 'SCRIPT_URL', $relative_script_path);
333 $relative_script_path = defaults($this->server, 'REQUEST_URI', $relative_script_path);
335 /* $relative_script_path gives /relative/path/to/friendica/module/parameter
336 * QUERY_STRING gives pagename=module/parameter
338 * To get /relative/path/to/friendica we perform dirname() for as many levels as there are slashes in the QUERY_STRING
340 if (!empty($relative_script_path)) {
342 if (!empty($this->server['QUERY_STRING'])) {
343 $this->urlPath = trim(rdirname($relative_script_path, substr_count(trim($this->server['QUERY_STRING'], '/'), '/') + 1), '/');
346 $this->urlPath = trim($relative_script_path, '/');
352 * Determine the full URL based on all parts
354 private function determineBaseUrl()
358 if ($this->sslPolicy == self::SSL_POLICY_FULL) {
362 $this->url = $scheme . '://' . $this->hostname . (!empty($this->urlPath) ? '/' . $this->urlPath : '' );
366 * Determine the scheme of the current used link
368 private function determineSchema()
370 $this->scheme = 'http';
372 if (!empty($this->server['HTTPS']) ||
373 !empty($this->server['HTTP_FORWARDED']) && preg_match('/proto=https/', $this->server['HTTP_FORWARDED']) ||
374 !empty($this->server['HTTP_X_FORWARDED_PROTO']) && $this->server['HTTP_X_FORWARDED_PROTO'] == 'https' ||
375 !empty($this->server['HTTP_X_FORWARDED_SSL']) && $this->server['HTTP_X_FORWARDED_SSL'] == 'on' ||
376 !empty($this->server['FRONT_END_HTTPS']) && $this->server['FRONT_END_HTTPS'] == 'on' ||
377 !empty($this->server['SERVER_PORT']) && (intval($this->server['SERVER_PORT']) == 443) // XXX: reasonable assumption, but isn't this hardcoding too much?
379 $this->scheme = 'https';