PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 4.6.0
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v4.6.0
5.13.0 5.12.1 5.12.0 5.11.1 5.11.0 5.10.2 5.10.1 trunk 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.1.0 1.1.1 1.1.2 1.1.3 1.2.0 1.3.0 1.3.1 1.3.2 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.1.0 4.1.1 4.1.2 4.1.3 4.10.0 4.11.0 4.12.0 4.13.0 4.13.2 4.13.3 4.13.4 4.13.5 4.14.0 4.14.1 4.14.2 4.15.0 4.15.1 4.15.2 4.15.3 4.2.0 4.3.0 4.3.1 4.4.1 4.4.2 4.5.0 4.6.0 5.0.1 5.0.2 5.0.3 5.0.4 5.0.5 5.0.6 5.0.7 5.0.8 5.1.0 5.1.1 5.1.2 5.1.3 5.1.4 5.1.5 5.1.6 5.1.7 5.10.0 5.2.0 5.2.1 5.2.2 5.3.0 5.3.1 5.3.2 5.3.3 5.6.0 5.6.1 5.7.0 5.7.1 5.8.0 5.8.1 5.8.2
matomo / app / core / Url.php
matomo / app / core Last commit date
API 4 years ago Access 5 years ago Application 4 years ago Archive 4 years ago ArchiveProcessor 4 years ago Archiver 5 years ago AssetManager 4 years ago Auth 5 years ago Category 5 years ago CliMulti 4 years ago Columns 5 years ago Concurrency 4 years ago Config 5 years ago Container 4 years ago CronArchive 4 years ago DataAccess 4 years ago DataFiles 5 years ago DataTable 4 years ago Db 4 years ago DeviceDetector 5 years ago Email 5 years ago Exception 4 years ago Http 5 years ago Intl 4 years ago Mail 5 years ago Measurable 5 years ago Menu 5 years ago Metrics 4 years ago Notification 4 years ago Period 4 years ago Plugin 4 years ago ProfessionalServices 5 years ago Report 5 years ago ReportRenderer 5 years ago Scheduler 4 years ago Segment 5 years ago Session 4 years ago Settings 5 years ago Tracker 4 years ago Translation 4 years ago UpdateCheck 5 years ago Updater 4 years ago Updates 4 years ago Validators 5 years ago View 4 years ago ViewDataTable 5 years ago Visualization 5 years ago Widget 5 years ago .htaccess 6 years ago Access.php 4 years ago Archive.php 4 years ago ArchiveProcessor.php 4 years ago AssetManager.php 5 years ago Auth.php 5 years ago AuthResult.php 5 years ago BaseFactory.php 5 years ago Cache.php 5 years ago CacheId.php 5 years ago CliMulti.php 4 years ago Common.php 4 years ago Config.php 4 years ago Console.php 5 years ago Context.php 5 years ago Cookie.php 4 years ago CronArchive.php 4 years ago DataArray.php 4 years ago DataTable.php 4 years ago Date.php 4 years ago Db.php 4 years ago DbHelper.php 4 years ago Development.php 5 years ago ErrorHandler.php 5 years ago EventDispatcher.php 5 years ago ExceptionHandler.php 4 years ago FileIntegrity.php 5 years ago Filechecks.php 5 years ago Filesystem.php 4 years ago FrontController.php 4 years ago Http.php 5 years ago IP.php 5 years ago Log.php 5 years ago LogDeleter.php 4 years ago Mail.php 4 years ago Metrics.php 5 years ago NoAccessException.php 5 years ago Nonce.php 4 years ago Notification.php 5 years ago NumberFormatter.php 5 years ago Option.php 5 years ago Period.php 5 years ago Piwik.php 4 years ago Plugin.php 4 years ago Profiler.php 5 years ago ProxyHeaders.php 5 years ago ProxyHttp.php 4 years ago QuickForm2.php 5 years ago RankingQuery.php 5 years ago ReportRenderer.php 5 years ago Segment.php 5 years ago Sequence.php 5 years ago Session.php 4 years ago SettingsPiwik.php 5 years ago SettingsServer.php 5 years ago Singleton.php 5 years ago Site.php 5 years ago SupportedBrowser.php 4 years ago TCPDF.php 5 years ago Theme.php 5 years ago Timer.php 5 years ago Tracker.php 5 years ago Twig.php 4 years ago Unzip.php 5 years ago UpdateCheck.php 5 years ago Updater.php 5 years ago UpdaterErrorException.php 5 years ago Updates.php 5 years ago Url.php 4 years ago UrlHelper.php 4 years ago Version.php 4 years ago View.php 4 years ago bootstrap.php 5 years ago dispatch.php 5 years ago testMinimumPhpVersion.php 5 years ago
Url.php
810 lines
1 <?php
2 /**
3 * Matomo - free/libre analytics platform
4 *
5 * @link https://matomo.org
6 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
7 *
8 */
9 namespace Piwik;
10
11 use Exception;
12 use Matomo\Network\IPUtils;
13
14 /**
15 * Provides URL related helper methods.
16 *
17 * This class provides simple methods that can be used to parse and modify
18 * the current URL. It is most useful when plugins need to redirect the current
19 * request to a URL and when they need to link to other parts of Piwik in
20 * HTML.
21 *
22 * ### Examples
23 *
24 * **Redirect to a different controller action**
25 *
26 * public function myControllerAction()
27 * {
28 * $url = Url::getCurrentQueryStringWithParametersModified(array(
29 * 'module' => 'DevicesDetection',
30 * 'action' => 'index'
31 * ));
32 * Url::redirectToUrl($url);
33 * }
34 *
35 * **Link to a different controller action in a template**
36 *
37 * public function myControllerAction()
38 * {
39 * $url = Url::getCurrentQueryStringWithParametersModified(array(
40 * 'module' => 'UserCountryMap',
41 * 'action' => 'realtimeMap',
42 * 'changeVisitAlpha' => 0,
43 * 'removeOldVisits' => 0
44 * ));
45 * $view = new View("@MyPlugin/myPopup");
46 * $view->realtimeMapUrl = $url;
47 * return $view->render();
48 * }
49 *
50 */
51 class Url
52 {
53 /**
54 * Returns the current URL.
55 *
56 * @return string eg, `"http://example.org/dir1/dir2/index.php?param1=value1&param2=value2"`
57 * @api
58 */
59 public static function getCurrentUrl()
60 {
61 return self::getCurrentScheme() . '://'
62 . self::getCurrentHost()
63 . self::getCurrentScriptName(false)
64 . self::getCurrentQueryString();
65 }
66
67 /**
68 * Returns the current URL without the query string.
69 *
70 * @param bool $checkTrustedHost Whether to do trusted host check. Should ALWAYS be true,
71 * except in {@link Piwik\Plugin\Controller}.
72 * @return string eg, `"http://example.org/dir1/dir2/index.php"` if the current URL is
73 * `"http://example.org/dir1/dir2/index.php?param1=value1&param2=value2"`.
74 * @api
75 */
76 public static function getCurrentUrlWithoutQueryString($checkTrustedHost = true)
77 {
78 return self::getCurrentScheme() . '://'
79 . self::getCurrentHost($default = 'unknown', $checkTrustedHost)
80 . self::getCurrentScriptName(false);
81 }
82
83 /**
84 * Returns the current URL without the query string and without the name of the file
85 * being executed.
86 *
87 * @return string eg, `"http://example.org/dir1/dir2/"` if the current URL is
88 * `"http://example.org/dir1/dir2/index.php?param1=value1&param2=value2"`.
89 * @api
90 */
91 public static function getCurrentUrlWithoutFileName()
92 {
93 return self::getCurrentScheme() . '://'
94 . self::getCurrentHost()
95 . self::getCurrentScriptPath();
96 }
97
98 /**
99 * Returns the path to the script being executed. The script file name is not included.
100 *
101 * @return string eg, `"/dir1/dir2/"` if the current URL is
102 * `"http://example.org/dir1/dir2/index.php?param1=value1&param2=value2"`
103 * @api
104 */
105 public static function getCurrentScriptPath()
106 {
107 $queryString = self::getCurrentScriptName();
108
109 //add a fake letter case /test/test2/ returns /test which is not expected
110 $urlDir = dirname($queryString . 'x');
111 $urlDir = str_replace('\\', '/', $urlDir);
112 // if we are in a subpath we add a trailing slash
113 if (strlen($urlDir) > 1) {
114 $urlDir .= '/';
115 }
116 return $urlDir;
117 }
118
119 /**
120 * Returns the path to the script being executed. Includes the script file name.
121 *
122 * @param bool $removePathInfo If true (default value) then the PATH_INFO will be stripped.
123 * @return string eg, `"/dir1/dir2/index.php"` if the current URL is
124 * `"http://example.org/dir1/dir2/index.php?param1=value1&param2=value2"`
125 * @api
126 */
127 public static function getCurrentScriptName($removePathInfo = true)
128 {
129 $url = '';
130
131 // insert extra path info if proxy_uri_header is set and enabled
132 if (isset(Config::getInstance()->General['proxy_uri_header'])
133 && Config::getInstance()->General['proxy_uri_header'] == 1
134 && !empty($_SERVER['HTTP_X_FORWARDED_URI'])
135 ) {
136 $url .= $_SERVER['HTTP_X_FORWARDED_URI'];
137 }
138
139 if (!empty($_SERVER['REQUEST_URI'])) {
140 $url .= $_SERVER['REQUEST_URI'];
141
142 // strip http://host (Apache+Rails anomaly)
143 if (preg_match('~^https?://[^/]+($|/.*)~D', $url, $matches)) {
144 $url = $matches[1];
145 }
146
147 // strip parameters
148 if (($pos = mb_strpos($url, "?")) !== false) {
149 $url = mb_substr($url, 0, $pos);
150 }
151
152 // strip path_info
153 if ($removePathInfo && !empty($_SERVER['PATH_INFO'])) {
154 $url = mb_substr($url, 0, -mb_strlen($_SERVER['PATH_INFO']));
155 }
156 }
157
158 /**
159 * SCRIPT_NAME is our fallback, though it may not be set correctly
160 *
161 * @see http://php.net/manual/en/reserved.variables.php
162 */
163 if (empty($url)) {
164 if (isset($_SERVER['SCRIPT_NAME'])) {
165 $url = $_SERVER['SCRIPT_NAME'];
166 } elseif (isset($_SERVER['SCRIPT_FILENAME'])) {
167 $url = $_SERVER['SCRIPT_FILENAME'];
168 } elseif (isset($_SERVER['argv'])) {
169 $url = $_SERVER['argv'][0];
170 }
171 }
172
173 if (!isset($url[0]) || $url[0] !== '/') {
174 $url = '/' . $url;
175 }
176
177 // A hash part should actually be never send to the server, as browsers automatically remove them from the request
178 // The same happens for tools like cUrl. While Apache won't answer requests that contain them, Nginx would handle them
179 // and the hash part would be included in REQUEST_URI. Therefor we always remove any hash parts here.
180 if (mb_strpos($url, '#')) {
181 $url = mb_substr($url, 0, mb_strpos($url, '#'));
182 }
183
184 return $url;
185 }
186
187 /**
188 * Returns the current URL's protocol.
189 *
190 * @return string `'https'` or `'http'`
191 * @api
192 */
193 public static function getCurrentScheme()
194 {
195 if (self::isPiwikConfiguredToAssumeSecureConnection()) {
196 return 'https';
197 }
198 return self::getCurrentSchemeFromRequestHeader();
199 }
200
201 /**
202 * Validates the **Host** HTTP header (untrusted user input). Used to prevent Host header
203 * attacks.
204 *
205 * @param string|bool $host Contents of Host: header from the HTTP request. If `false`, gets the
206 * value from the request.
207 * @return bool `true` if valid; `false` otherwise.
208 */
209 public static function isValidHost($host = false)
210 {
211 // only do trusted host check if it's enabled
212 if (isset(Config::getInstance()->General['enable_trusted_host_check'])
213 && Config::getInstance()->General['enable_trusted_host_check'] == 0
214 ) {
215 return true;
216 }
217
218 if ($host === false) {
219 $host = self::getHostFromServerVariable();
220 if (empty($host)) {
221 // if no current host, assume valid
222 return true;
223 }
224 }
225
226 // if host is in hardcoded allowlist, assume it's valid
227 if (in_array($host, self::getAlwaysTrustedHosts())) {
228 return true;
229 }
230
231 $trustedHosts = self::getTrustedHosts();
232
233 // Only punctuation we allow is '[', ']', ':', '.', '_' and '-'
234 $hostLength = strlen($host);
235 if ($hostLength !== strcspn($host, '`~!@#$%^&*()+={}\\|;"\'<>,?/ ')) {
236 return false;
237 }
238
239 // if no trusted hosts, just assume it's valid
240 if (empty($trustedHosts)) {
241 self::saveTrustedHostnameInConfig($host);
242 return true;
243 }
244
245 // Escape trusted hosts for preg_match call below
246 foreach ($trustedHosts as &$trustedHost) {
247 $trustedHost = preg_quote($trustedHost);
248 }
249 $trustedHosts = str_replace("/", "\\/", $trustedHosts);
250
251 $untrustedHost = mb_strtolower($host);
252 $untrustedHost = rtrim($untrustedHost, '.');
253
254 $hostRegex = mb_strtolower('/(^|.)' . implode('$|', $trustedHosts) . '$/');
255
256 $result = preg_match($hostRegex, $untrustedHost);
257 return 0 !== $result;
258 }
259
260 /**
261 * Records one host, or an array of hosts in the config file,
262 * if user is Super User
263 *
264 * @static
265 * @param $host string|array
266 * @return bool
267 */
268 public static function saveTrustedHostnameInConfig($host)
269 {
270 return self::saveHostsnameInConfig($host, 'General', 'trusted_hosts');
271 }
272
273 public static function saveCORSHostnameInConfig($host)
274 {
275 return self::saveHostsnameInConfig($host, 'General', 'cors_domains');
276 }
277
278 protected static function saveHostsnameInConfig($host, $domain, $key)
279 {
280 if (Piwik::hasUserSuperUserAccess()
281 && file_exists(Config::getLocalConfigPath())
282 ) {
283 $config = Config::getInstance()->$domain;
284 if (!is_array($host)) {
285 $host = array($host);
286 }
287 $host = array_filter($host);
288 if (empty($host)) {
289 return false;
290 }
291 $config[$key] = $host;
292 Config::getInstance()->$domain = $config;
293 Config::getInstance()->forceSave();
294 return true;
295 }
296 return false;
297 }
298
299 /**
300 * Returns the current host.
301 *
302 * @param bool $checkIfTrusted Whether to do trusted host check. Should ALWAYS be true,
303 * except in Controller.
304 * @return string|bool eg, `"demo.piwik.org"` or false if no host found.
305 */
306 public static function getHost($checkIfTrusted = true)
307 {
308 $host = self::getHostFromServerVariable();
309
310 if (strlen($host) && (!$checkIfTrusted || self::isValidHost($host))) {
311 return $host;
312 }
313
314 // HTTP/1.0 request doesn't include Host: header
315 if (isset($_SERVER['SERVER_ADDR'])) {
316 return $_SERVER['SERVER_ADDR'];
317 }
318
319 return false;
320 }
321
322 protected static function getHostFromServerVariable()
323 {
324 try {
325 // this fails when trying to get the hostname before the config was initialized
326 // e.g. for loading the domain specific configuration file
327 // in such a case we always use HTTP_HOST
328 $preferServerName = Config::getInstance()->General['host_validation_use_server_name'];
329 } catch (\Exception $e) {
330 $preferServerName = false;
331 }
332
333 if ($preferServerName && strlen($host = self::getHostFromServerNameVar())) {
334 return $host;
335 } elseif (isset($_SERVER['HTTP_HOST']) && strlen($host = $_SERVER['HTTP_HOST'])) {
336 return $host;
337 }
338
339 return false;
340 }
341
342 /**
343 * Sets the host. Useful for CLI scripts, eg. core:archive command
344 *
345 * @param $host string
346 */
347 public static function setHost($host)
348 {
349 $_SERVER['SERVER_NAME'] = $host;
350 $_SERVER['HTTP_HOST'] = $host;
351 unset($_SERVER['SERVER_PORT']);
352 }
353
354 /**
355 * Returns the current host.
356 *
357 * @param string $default Default value to return if host unknown
358 * @param bool $checkTrustedHost Whether to do trusted host check. Should ALWAYS be true,
359 * except in Controller.
360 * @return string eg, `"example.org"` if the current URL is
361 * `"http://example.org/dir1/dir2/index.php?param1=value1&param2=value2"`
362 * @api
363 */
364 public static function getCurrentHost($default = 'unknown', $checkTrustedHost = true)
365 {
366 $hostHeaders = array();
367
368 $config = Config::getInstance()->General;
369 if (isset($config['proxy_host_headers'])) {
370 $hostHeaders = $config['proxy_host_headers'];
371 }
372
373 if (!is_array($hostHeaders)) {
374 $hostHeaders = array();
375 }
376
377 $host = self::getHost($checkTrustedHost);
378 $default = Common::sanitizeInputValue($host ? $host : $default);
379
380 return IP::getNonProxyIpFromHeader($default, $hostHeaders);
381 }
382
383 /**
384 * Returns the query string of the current URL.
385 *
386 * @return string eg, `"?param1=value1&param2=value2"` if the current URL is
387 * `"http://example.org/dir1/dir2/index.php?param1=value1&param2=value2"`
388 * @api
389 */
390 public static function getCurrentQueryString()
391 {
392 $url = '';
393 if (isset($_SERVER['QUERY_STRING'])
394 && !empty($_SERVER['QUERY_STRING'])
395 ) {
396 $url .= "?" . $_SERVER['QUERY_STRING'];
397 }
398 return $url;
399 }
400
401 /**
402 * Returns an array mapping query parameter names with query parameter values for
403 * the current URL.
404 *
405 * @return array If current URL is `"http://example.org/dir1/dir2/index.php?param1=value1&param2=value2"`
406 * this will return:
407 *
408 * array(
409 * 'param1' => string 'value1',
410 * 'param2' => string 'value2'
411 * )
412 * @api
413 */
414 public static function getArrayFromCurrentQueryString()
415 {
416 $queryString = self::getCurrentQueryString();
417 $urlValues = UrlHelper::getArrayFromQueryString($queryString);
418 return $urlValues;
419 }
420
421 /**
422 * Modifies the current query string with the supplied parameters and returns
423 * the result. Parameters in the current URL will be overwritten with values
424 * in `$params` and parameters absent from the current URL but present in `$params`
425 * will be added to the result.
426 *
427 * @param array $params set of parameters to modify/add in the current URL
428 * eg, `array('param3' => 'value3')`
429 * @return string eg, `"?param2=value2&param3=value3"`
430 * @api
431 */
432 public static function getCurrentQueryStringWithParametersModified($params)
433 {
434 $urlValues = self::getArrayFromCurrentQueryString();
435 foreach ($params as $key => $value) {
436 $urlValues[$key] = $value;
437 }
438 $query = self::getQueryStringFromParameters($urlValues);
439 if (strlen($query) > 0) {
440 return '?' . $query;
441 }
442 return '';
443 }
444
445 /**
446 * Converts an array of parameters name => value mappings to a query
447 * string. Values must already be URL encoded before you call this function.
448 *
449 * @param array $parameters eg. `array('param1' => 10, 'param2' => array(1,2))`
450 * @return string eg. `"param1=10&param2[]=1&param2[]=2"`
451 * @api
452 */
453 public static function getQueryStringFromParameters($parameters)
454 {
455 $query = '';
456 foreach ($parameters as $name => $value) {
457 if (is_null($value) || $value === false) {
458 continue;
459 }
460 if (is_array($value)) {
461 foreach ($value as $theValue) {
462 $query .= $name . "[]=" . $theValue . "&";
463 }
464 } else {
465 $query .= $name . "=" . $value . "&";
466 }
467 }
468 $query = substr($query, 0, -1);
469 return $query;
470 }
471
472 public static function getQueryStringFromUrl($url)
473 {
474 return parse_url($url, PHP_URL_QUERY);
475 }
476
477 /**
478 * Redirects the user to the referrer. If no referrer exists, the user is redirected
479 * to the current URL without query string.
480 *
481 * @api
482 */
483 public static function redirectToReferrer()
484 {
485 $referrer = self::getReferrer();
486 if ($referrer !== false) {
487 self::redirectToUrl($referrer);
488 }
489 self::redirectToUrl(self::getCurrentUrlWithoutQueryString());
490 }
491
492 private static function redirectToUrlNoExit($url)
493 {
494 if (UrlHelper::isLookLikeUrl($url)
495 || strpos($url, 'index.php') === 0
496 ) {
497 Common::sendResponseCode(302);
498 Common::sendHeader("X-Robots-Tag: noindex");
499 Common::sendHeader("Location: $url");
500 } else {
501 echo "Invalid URL to redirect to.";
502 }
503
504 if (Common::isPhpCliMode()) {
505 throw new Exception("If you were using a browser, Matomo would redirect you to this URL: $url \n\n");
506 }
507 }
508
509 /**
510 * Redirects the user to the specified URL.
511 *
512 * @param string $url
513 * @throws Exception
514 * @api
515 */
516 public static function redirectToUrl($url)
517 {
518 // Close the session manually.
519 // We should not have to call this because it was registered via register_shutdown_function,
520 // but it is not always called fast enough
521 Session::close();
522
523 self::redirectToUrlNoExit($url);
524
525 exit;
526 }
527
528 /**
529 * If the page is using HTTP, redirect to the same page over HTTPS
530 */
531 public static function redirectToHttps()
532 {
533 if (ProxyHttp::isHttps()) {
534 return;
535 }
536 $url = self::getCurrentUrl();
537 $url = str_replace("http://", "https://", $url);
538 self::redirectToUrl($url);
539 }
540
541 /**
542 * Returns the **HTTP_REFERER** `$_SERVER` variable, or `false` if not found.
543 *
544 * @return string|false
545 * @api
546 */
547 public static function getReferrer()
548 {
549 if (!empty($_SERVER['HTTP_REFERER'])) {
550 return $_SERVER['HTTP_REFERER'];
551 }
552 return false;
553 }
554
555 /**
556 * Returns `true` if the URL points to something on the same host, `false` if otherwise.
557 *
558 * @param string $url
559 * @return bool True if local; false otherwise.
560 * @api
561 */
562 public static function isLocalUrl($url)
563 {
564 if (empty($url)) {
565 return true;
566 }
567
568 // handle host name mangling
569 $requestUri = isset($_SERVER['SCRIPT_URI']) ? $_SERVER['SCRIPT_URI'] : '';
570 $parseRequest = @parse_url($requestUri);
571 $hosts = array(self::getHost(), self::getCurrentHost());
572 if (!empty($parseRequest['host'])) {
573 $hosts[] = $parseRequest['host'];
574 }
575
576 // drop port numbers from hostnames and IP addresses
577 $hosts = array_map(array('self', 'getHostSanitized'), $hosts);
578
579 $disableHostCheck = Config::getInstance()->General['enable_trusted_host_check'] == 0;
580 // compare scheme and host
581 $parsedUrl = @parse_url($url);
582 $host = IPUtils::sanitizeIp(@$parsedUrl['host']);
583 return !empty($host)
584 && ($disableHostCheck || in_array($host, $hosts))
585 && !empty($parsedUrl['scheme'])
586 && in_array($parsedUrl['scheme'], array('http', 'https'));
587 }
588
589 /**
590 * Checks whether the given host is a local host like `127.0.0.1` or `localhost`.
591 *
592 * @param string $host
593 * @return bool
594 */
595 public static function isLocalHost($host)
596 {
597 if (empty($host)) {
598 return false;
599 }
600
601 // remove port
602 $hostWithoutPort = explode(':', $host);
603 array_pop($hostWithoutPort);
604 $hostWithoutPort = implode(':', $hostWithoutPort);
605
606 $localHostnames = Url::getLocalHostnames();
607 return in_array($host, $localHostnames, true)
608 || in_array($hostWithoutPort, $localHostnames, true);
609 }
610
611 public static function getTrustedHostsFromConfig()
612 {
613 $hosts = self::getHostsFromConfig('General', 'trusted_hosts');
614
615 // Case user wrote in the config, http://example.com/test instead of example.com
616 foreach ($hosts as &$host) {
617 if (UrlHelper::isLookLikeUrl($host)) {
618 $host = parse_url($host, PHP_URL_HOST);
619 }
620 }
621 return $hosts;
622 }
623
624 public static function getTrustedHosts()
625 {
626 return self::getTrustedHostsFromConfig();
627 }
628
629 public static function getCorsHostsFromConfig()
630 {
631 return self::getHostsFromConfig('General', 'cors_domains');
632 }
633
634 /**
635 * Returns hostname, without port numbers
636 *
637 * @param $host
638 * @return array
639 */
640 public static function getHostSanitized($host)
641 {
642 if (!class_exists("Matomo\\Network\\IPUtils")) {
643 throw new Exception("Matomo\\Network\\IPUtils could not be found, maybe you are using Matomo from git and need to update Composer. $ php composer.phar update");
644 }
645 return IPUtils::sanitizeIp($host);
646 }
647
648 protected static function getHostsFromConfig($domain, $key)
649 {
650 $config = @Config::getInstance()->$domain;
651
652 if (!isset($config[$key])) {
653 return array();
654 }
655
656 $hosts = $config[$key];
657 if (!is_array($hosts)) {
658 return array();
659 }
660 return $hosts;
661 }
662
663 /**
664 * Returns the host part of any valid URL.
665 *
666 * @param string $url Any fully qualified URL
667 * @return string|null The actual host in lower case or null if $url is not a valid fully qualified URL.
668 */
669 public static function getHostFromUrl($url)
670 {
671 if (!is_string($url)) {
672 return null;
673 }
674
675 $urlHost = parse_url($url, PHP_URL_HOST);
676
677 if (empty($urlHost)) {
678 return null;
679 }
680
681 return mb_strtolower($urlHost);
682 }
683
684 /**
685 * Checks whether any of the given URLs has the given host. If not, we will also check whether any URL uses a
686 * subdomain of the given host. For instance if host is "example.com" and a URL is "http://www.example.com" we
687 * consider this as valid and return true. The always trusted hosts such as "127.0.0.1" are considered valid as well.
688 *
689 * @param $host
690 * @param $urls
691 * @return bool
692 */
693 public static function isHostInUrls($host, $urls)
694 {
695 if (empty($host)) {
696 return false;
697 }
698
699 $host = mb_strtolower($host);
700
701 if (!empty($urls)) {
702 foreach ($urls as $url) {
703 if (mb_strtolower($url) === $host) {
704 return true;
705 }
706
707 $siteHost = self::getHostFromUrl($url);
708
709 if ($siteHost === $host) {
710 return true;
711 }
712
713 if (Common::stringEndsWith($siteHost, '.' . $host)) {
714 // allow subdomains
715 return true;
716 }
717 }
718 }
719
720 return in_array($host, self::getAlwaysTrustedHosts());
721 }
722
723 /**
724 * List of hosts that are never checked for validity.
725 *
726 * @return array
727 */
728 private static function getAlwaysTrustedHosts()
729 {
730 return self::getLocalHostnames();
731 }
732
733 /**
734 * @return array
735 */
736 public static function getLocalHostnames()
737 {
738 return array('localhost', '127.0.0.1', '::1', '[::1]', '[::]', '0000::1', '0177.0.0.1', '2130706433', '[0:0:0:0:0:ffff:127.0.0.1]');
739 }
740
741 /**
742 * @return bool
743 */
744 public static function isSecureConnectionAssumedByPiwikButNotForcedYet()
745 {
746 $isSecureConnectionLikelyNotUsed = Url::isSecureConnectionLikelyNotUsed();
747 $hasSessionCookieSecureFlag = ProxyHttp::isHttps();
748 $isSecureConnectionAssumedByPiwikButNotForcedYet = Url::isPiwikConfiguredToAssumeSecureConnection() && !SettingsPiwik::isHttpsForced();
749
750 return $isSecureConnectionLikelyNotUsed
751 && $hasSessionCookieSecureFlag
752 && $isSecureConnectionAssumedByPiwikButNotForcedYet;
753 }
754
755 /**
756 * @return string
757 */
758 protected static function getCurrentSchemeFromRequestHeader()
759 {
760 if (isset($_SERVER['HTTP_X_FORWARDED_SCHEME']) && strtolower($_SERVER['HTTP_X_FORWARDED_SCHEME']) === 'https') {
761 return 'https';
762 }
763
764 if (isset($_SERVER['HTTP_X_URL_SCHEME']) && strtolower($_SERVER['HTTP_X_URL_SCHEME']) === 'https') {
765 return 'https';
766 }
767
768 if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'http') {
769 return 'http';
770 }
771
772 if ((isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS'] == 'on' || $_SERVER['HTTPS'] === true))
773 || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')
774 ) {
775
776 return 'https';
777 }
778 return 'http';
779 }
780
781 protected static function isSecureConnectionLikelyNotUsed()
782 {
783 return Url::getCurrentSchemeFromRequestHeader() == 'http';
784 }
785
786 /**
787 * @return bool
788 */
789 protected static function isPiwikConfiguredToAssumeSecureConnection()
790 {
791 $assume_secure_protocol = @Config::getInstance()->General['assume_secure_protocol'];
792 return (bool) $assume_secure_protocol;
793 }
794
795 public static function getHostFromServerNameVar()
796 {
797 $host = @$_SERVER['SERVER_NAME'];
798 if (!empty($host)) {
799 if (strpos($host, ':') === false
800 && !empty($_SERVER['SERVER_PORT'])
801 && $_SERVER['SERVER_PORT'] != 80
802 && $_SERVER['SERVER_PORT'] != 443
803 ) {
804 $host .= ':' . $_SERVER['SERVER_PORT'];
805 }
806 }
807 return $host;
808 }
809 }
810