]> git.mxchange.org Git - friendica.git/blob - src/Util/BaseURL.php
66526ff2dbb10d773b2641beb4673a77a0801a36
[friendica.git] / src / Util / BaseURL.php
1 <?php
2
3 namespace Friendica\Util;
4
5 use Friendica\Core\Config\Configuration;
6
7 /**
8  * A class which checks and contains the basic
9  * environment for the BaseURL (url, urlpath, ssl_policy, hostname, scheme)
10  */
11 class BaseURL
12 {
13         /**
14          * No SSL necessary
15          */
16         const SSL_POLICY_NONE = 0;
17
18         /**
19          * SSL is necessary
20          */
21         const SSL_POLICY_FULL = 1;
22
23         /**
24          * SSL is optional, but preferred
25          */
26         const SSL_POLICY_SELFSIGN = 2;
27
28         /**
29          * Define the Default SSL scheme
30          */
31         const DEFAULT_SSL_SCHEME = self::SSL_POLICY_SELFSIGN;
32
33         /**
34          * The Friendica Config
35          * @var Configuration
36          */
37         private $config;
38
39         /**
40          * The server side variables
41          * @var array
42          */
43         private $server;
44
45         /**
46          * The hostname of the Base URL
47          * @var string
48          */
49         private $hostname;
50
51         /**
52          * The SSL_POLICY of the Base URL
53          * @var int
54          */
55         private $sslPolicy;
56
57         /**
58          * The URL sub-path of the Base URL
59          * @var string
60          */
61         private $urlPath;
62
63         /**
64          * The full URL
65          * @var string
66          */
67         private $url;
68
69         /**
70          * The current scheme of this call
71          * @var string
72          */
73         private $scheme;
74
75         /**
76          * Returns the hostname of this node
77          * @return string
78          */
79         public function getHostname()
80         {
81                 return $this->hostname;
82         }
83
84         /**
85          * Returns the current scheme of this call
86          * @return string
87          */
88         public function getScheme()
89         {
90                 return $this->scheme;
91         }
92
93         /**
94          * Returns the SSL policy of this node
95          * @return int
96          */
97         public function getSSLPolicy()
98         {
99                 return $this->sslPolicy;
100         }
101
102         /**
103          * Returns the sub-path of this URL
104          * @return string
105          */
106         public function getUrlPath()
107         {
108                 return $this->urlPath;
109         }
110
111         /**
112          * Returns the full URL of this call
113          *
114          * Note: $ssl parameter value doesn't directly correlate with the resulting protocol
115          *
116          * @param bool $ssl True, if ssl should get used
117          *
118          * @return string
119          */
120         public function get($ssl = false)
121         {
122                 if ($this->sslPolicy === self::SSL_POLICY_SELFSIGN && $ssl) {
123                         return Network::switchScheme($this->url);
124                 }
125
126                 return $this->url;
127         }
128
129         /**
130          * Save current parts of the base Url
131          *
132          * @param string? $hostname
133          * @param int?    $sslPolicy
134          * @param string? $urlPath
135          *
136          * @return bool true, if successful
137          */
138         public function save($hostname = null, $sslPolicy = null, $urlPath = null)
139         {
140                 $success = true;
141
142                 if (!empty($hostname) && $hostname !== $this->hostname) {
143                         $this->hostname  = $hostname;
144                         if (!$this->config->set('config', 'hostname', $this->hostname)) {
145                                 $success = false;
146                         }
147                 }
148
149                 if (isset($sslPolicy) && $sslPolicy !== $this->sslPolicy) {
150                         $this->sslPolicy = $sslPolicy;
151                         if (!$this->config->set('system', 'ssl_policy', $this->sslPolicy)) {
152                                 $success = false;
153                         }
154                 }
155
156                 if (isset($urlPath) && $urlPath !== $this->urlPath) {
157                         $this->urlPath   = $urlPath;
158                         if (!$this->config->set('system', 'urlpath', $this->urlPath)) {
159                                 $success = false;
160                         }
161                 }
162
163                 $this->determineBaseUrl();
164                 if (!$this->config->set('system', 'url', $this->url)) {
165                         $success = false;
166                 }
167
168                 return $success;
169         }
170
171         /**
172          * Save the current url as base URL
173          *
174          * @param $url
175          *
176          * @return bool true, if the save was successful
177          */
178         public function saveByURL($url)
179         {
180                 $parsed = @parse_url($url);
181
182                 if (empty($parsed)) {
183                         return false;
184                 }
185
186                 $hostname = $parsed['host'];
187                 if (!empty($hostname) && !empty($parsed['port'])) {
188                         $hostname .= ':' . $parsed['port'];
189                 }
190
191                 $urlPath = null;
192                 if (!empty($parsed['path'])) {
193                         $urlPath = trim($parsed['path'], '\\/');
194                 }
195
196                 $sslPolicy = null;
197                 if (!empty($parsed['scheme'])) {
198                         if ($parsed['scheme'] == 'https') {
199                                 $sslPolicy = BaseURL::SSL_POLICY_FULL;
200                         }
201                 }
202
203                 return $this->save($hostname, $sslPolicy, $urlPath);
204         }
205
206         /**
207          * Checks, if a redirect to the HTTPS site would be necessary
208          *
209          * @return bool
210          */
211         public function checkRedirectHttps()
212         {
213                 return $this->config->get('system', 'force_ssl')
214                         && ($this->getScheme() == "http")
215                         && intval($this->getSSLPolicy()) == BaseURL::SSL_POLICY_FULL
216                         && strpos($this->get(), 'https://') === 0
217                         && !empty($this->server['REQUEST_METHOD'])
218                         && $this->server['REQUEST_METHOD'] === 'GET';
219         }
220
221         /**
222          * @param Configuration $config The Friendica configuration
223          * @param array         $server The $_SERVER array
224          */
225         public function __construct(Configuration $config, array $server)
226         {
227                 $this->config = $config;
228                 $this->server = $server;
229
230                 $this->determineSchema();
231                 $this->checkConfig();
232         }
233
234         /**
235          * Check the current config during loading
236          */
237         public function checkConfig()
238         {
239                 $this->hostname  = $this->config->get('config', 'hostname');
240                 $this->urlPath   = $this->config->get('system', 'urlpath');
241                 $this->sslPolicy = $this->config->get('system', 'ssl_policy');
242                 $this->url       = $this->config->get('system', 'url');
243
244                 if (empty($this->hostname)) {
245                         $this->determineHostname();
246
247                         if (!empty($this->hostname)) {
248                                 $this->config->set('config', 'hostname', $this->hostname);
249                         }
250                 }
251
252                 if (!isset($this->urlPath)) {
253                         $this->determineURLPath();
254                         $this->config->set('system', 'urlpath', $this->urlPath);
255                 }
256
257                 if (!isset($this->sslPolicy)) {
258                         if ($this->scheme == 'https') {
259                                 $this->sslPolicy = self::SSL_POLICY_FULL;
260                         } else {
261                                 $this->sslPolicy = self::DEFAULT_SSL_SCHEME;
262                         }
263                         $this->config->set('system', 'ssl_policy', $this->sslPolicy);
264                 }
265
266                 if (empty($this->url)) {
267                         $this->determineBaseUrl();
268
269                         if (!empty($url)) {
270                                 $this->config->set('system', 'url', $this->url);
271                         }
272                 }
273         }
274
275         /**
276          * Determines the hostname of this node if not set already
277          */
278         private function determineHostname()
279         {
280                 $this->hostname = '';
281
282                 if (!empty($this->server['SERVER_NAME'])) {
283                         $this->hostname = $this->server['SERVER_NAME'];
284
285                         if (!empty($this->server['SERVER_PORT']) && $this->server['SERVER_PORT'] != 80 && $this->server['SERVER_PORT'] != 443) {
286                                 $this->hostname .= ':' . $this->server['SERVER_PORT'];
287                         }
288                 }
289         }
290
291         /**
292          * Figure out if we are running at the top of a domain or in a sub-directory
293          */
294         private function determineURLPath()
295         {
296                 $this->urlPath = '';
297
298                 /*
299                  * The automatic path detection in this function is currently deactivated,
300                  * see issue https://github.com/friendica/friendica/issues/6679
301                  *
302                  * The problem is that the function seems to be confused with some url.
303                  * These then confuses the detection which changes the url path.
304                  */
305
306                 /* Relative script path to the web server root
307                  * Not all of those $_SERVER properties can be present, so we do by inverse priority order
308                  */
309                 $relative_script_path = '';
310                 $relative_script_path = defaults($this->server, 'REDIRECT_URL', $relative_script_path);
311                 $relative_script_path = defaults($this->server, 'REDIRECT_URI', $relative_script_path);
312                 $relative_script_path = defaults($this->server, 'REDIRECT_SCRIPT_URL', $relative_script_path);
313                 $relative_script_path = defaults($this->server, 'SCRIPT_URL', $relative_script_path);
314                 $relative_script_path = defaults($this->server, 'REQUEST_URI', $relative_script_path);
315
316                 /* $relative_script_path gives /relative/path/to/friendica/module/parameter
317                  * QUERY_STRING gives pagename=module/parameter
318                  *
319                  * To get /relative/path/to/friendica we perform dirname() for as many levels as there are slashes in the QUERY_STRING
320                  */
321                 if (!empty($relative_script_path)) {
322                         // Module
323                         if (!empty($this->server['QUERY_STRING'])) {
324                                 $this->urlPath = trim(rdirname($relative_script_path, substr_count(trim($this->server['QUERY_STRING'], '/'), '/') + 1), '/');
325                         } else {
326                                 // Root page
327                                 $this->urlPath = trim($relative_script_path, '/');
328                         }
329                 }
330         }
331
332         /**
333          * Determine the full URL based on all parts
334          */
335         private function determineBaseUrl()
336         {
337                 $scheme = 'http';
338
339                 if ($this->sslPolicy == self::SSL_POLICY_FULL) {
340                         $scheme = 'https';
341                 }
342
343                 $this->url = $scheme . '://' . $this->hostname . (!empty($this->urlPath) ? '/' . $this->urlPath : '' );
344         }
345
346         /**
347          * Determine the scheme of the current used link
348          */
349         private function determineSchema()
350         {
351                 $this->scheme = 'http';
352
353                 if (!empty($this->server['HTTPS']) ||
354                         !empty($this->server['HTTP_FORWARDED']) && preg_match('/proto=https/', $this->server['HTTP_FORWARDED']) ||
355                         !empty($this->server['HTTP_X_FORWARDED_PROTO']) && $this->server['HTTP_X_FORWARDED_PROTO'] == 'https' ||
356                         !empty($this->server['HTTP_X_FORWARDED_SSL']) && $this->server['HTTP_X_FORWARDED_SSL'] == 'on' ||
357                         !empty($this->server['FRONT_END_HTTPS']) && $this->server['FRONT_END_HTTPS'] == 'on' ||
358                         !empty($this->server['SERVER_PORT']) && (intval($this->server['SERVER_PORT']) == 443) // XXX: reasonable assumption, but isn't this hardcoding too much?
359                 ) {
360                         $this->scheme = 'https';
361                 }
362         }
363 }