PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 1.3.1
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v1.3.1
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 6 years ago Access 6 years ago Application 6 years ago Archive 6 years ago ArchiveProcessor 6 years ago Archiver 6 years ago AssetManager 6 years ago Auth 6 years ago Category 6 years ago CliMulti 6 years ago Columns 6 years ago Composer 6 years ago Concurrency 6 years ago Config 6 years ago Container 6 years ago CronArchive 6 years ago DataAccess 5 years ago DataFiles 6 years ago DataTable 6 years ago Db 6 years ago DeviceDetector 5 years ago Email 6 years ago Exception 6 years ago Http 6 years ago Intl 6 years ago Mail 6 years ago Measurable 6 years ago Menu 6 years ago Metrics 6 years ago Notification 6 years ago Period 6 years ago Plugin 6 years ago ProfessionalServices 6 years ago Report 6 years ago ReportRenderer 6 years ago Scheduler 6 years ago Segment 6 years ago Session 6 years ago Settings 6 years ago Tracker 5 years ago Translation 6 years ago UpdateCheck 6 years ago Updater 6 years ago Updates 6 years ago Validators 6 years ago View 6 years ago ViewDataTable 6 years ago Visualization 6 years ago Widget 6 years ago .htaccess 6 years ago Access.php 6 years ago Archive.php 6 years ago ArchiveProcessor.php 6 years ago AssetManager.php 6 years ago Auth.php 6 years ago BaseFactory.php 6 years ago Cache.php 6 years ago CacheId.php 6 years ago CliMulti.php 6 years ago Common.php 6 years ago Config.php 6 years ago Console.php 6 years ago Context.php 6 years ago Cookie.php 5 years ago CronArchive.php 5 years ago DataArray.php 6 years ago DataTable.php 6 years ago Date.php 6 years ago Db.php 6 years ago DbHelper.php 6 years ago Development.php 6 years ago DeviceDetectorFactory.php 6 years ago ErrorHandler.php 6 years ago EventDispatcher.php 6 years ago ExceptionHandler.php 6 years ago FileIntegrity.php 6 years ago Filechecks.php 6 years ago Filesystem.php 6 years ago FrontController.php 6 years ago Http.php 6 years ago IP.php 6 years ago Log.php 6 years ago LogDeleter.php 6 years ago Mail.php 6 years ago Metrics.php 6 years ago MetricsFormatter.php 6 years ago Nonce.php 5 years ago Notification.php 6 years ago NumberFormatter.php 6 years ago Option.php 5 years ago Period.php 6 years ago Piwik.php 6 years ago Plugin.php 6 years ago Profiler.php 6 years ago ProxyHeaders.php 6 years ago ProxyHttp.php 6 years ago QuickForm2.php 6 years ago RankingQuery.php 6 years ago Registry.php 6 years ago ReportRenderer.php 6 years ago ScheduledTask.php 6 years ago Segment.php 6 years ago Sequence.php 6 years ago Session.php 6 years ago SettingsPiwik.php 6 years ago SettingsServer.php 6 years ago Singleton.php 6 years ago Site.php 6 years ago TCPDF.php 6 years ago TaskScheduler.php 6 years ago Theme.php 6 years ago Timer.php 6 years ago Tracker.php 6 years ago Translate.php 6 years ago Twig.php 6 years ago Unzip.php 6 years ago UpdateCheck.php 6 years ago Updater.php 6 years ago Updates.php 6 years ago Url.php 6 years ago UrlHelper.php 6 years ago Version.php 5 years ago View.php 6 years ago bootstrap.php 6 years ago dispatch.php 6 years ago testMinimumPhpVersion.php 6 years ago
Url.php
765 lines
1 <?php
2 /**
3 * Piwik - 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 Piwik\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 = strpos($url, "?")) !== false) {
149 $url = substr($url, 0, $pos);
150 }
151
152 // strip path_info
153 if ($removePathInfo && !empty($_SERVER['PATH_INFO'])) {
154 $url = substr($url, 0, -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 return $url;
177 }
178
179 /**
180 * Returns the current URL's protocol.
181 *
182 * @return string `'https'` or `'http'`
183 * @api
184 */
185 public static function getCurrentScheme()
186 {
187 if (self::isPiwikConfiguredToAssumeSecureConnection()) {
188 return 'https';
189 }
190 return self::getCurrentSchemeFromRequestHeader();
191 }
192
193 /**
194 * Validates the **Host** HTTP header (untrusted user input). Used to prevent Host header
195 * attacks.
196 *
197 * @param string|bool $host Contents of Host: header from the HTTP request. If `false`, gets the
198 * value from the request.
199 * @return bool `true` if valid; `false` otherwise.
200 */
201 public static function isValidHost($host = false)
202 {
203 // only do trusted host check if it's enabled
204 if (isset(Config::getInstance()->General['enable_trusted_host_check'])
205 && Config::getInstance()->General['enable_trusted_host_check'] == 0
206 ) {
207 return true;
208 }
209
210 if ($host === false) {
211 $host = @$_SERVER['HTTP_HOST'];
212 if (empty($host)) {
213 // if no current host, assume valid
214
215 return true;
216 }
217 }
218
219 // if host is in hardcoded whitelist, assume it's valid
220 if (in_array($host, self::getAlwaysTrustedHosts())) {
221 return true;
222 }
223
224 $trustedHosts = self::getTrustedHosts();
225
226 // Only punctuation we allow is '[', ']', ':', '.', '_' and '-'
227 $hostLength = strlen($host);
228 if ($hostLength !== strcspn($host, '`~!@#$%^&*()+={}\\|;"\'<>,?/ ')) {
229 return false;
230 }
231
232 // if no trusted hosts, just assume it's valid
233 if (empty($trustedHosts)) {
234 self::saveTrustedHostnameInConfig($host);
235 return true;
236 }
237
238 // Escape trusted hosts for preg_match call below
239 foreach ($trustedHosts as &$trustedHost) {
240 $trustedHost = preg_quote($trustedHost);
241 }
242 $trustedHosts = str_replace("/", "\\/", $trustedHosts);
243
244 $untrustedHost = Common::mb_strtolower($host);
245 $untrustedHost = rtrim($untrustedHost, '.');
246
247 $hostRegex = Common::mb_strtolower('/(^|.)' . implode('$|', $trustedHosts) . '$/');
248
249 $result = preg_match($hostRegex, $untrustedHost);
250 return 0 !== $result;
251 }
252
253 /**
254 * Records one host, or an array of hosts in the config file,
255 * if user is Super User
256 *
257 * @static
258 * @param $host string|array
259 * @return bool
260 */
261 public static function saveTrustedHostnameInConfig($host)
262 {
263 return self::saveHostsnameInConfig($host, 'General', 'trusted_hosts');
264 }
265
266 public static function saveCORSHostnameInConfig($host)
267 {
268 return self::saveHostsnameInConfig($host, 'General', 'cors_domains');
269 }
270
271 protected static function saveHostsnameInConfig($host, $domain, $key)
272 {
273 if (Piwik::hasUserSuperUserAccess()
274 && file_exists(Config::getLocalConfigPath())
275 ) {
276 $config = Config::getInstance()->$domain;
277 if (!is_array($host)) {
278 $host = array($host);
279 }
280 $host = array_filter($host);
281 if (empty($host)) {
282 return false;
283 }
284 $config[$key] = $host;
285 Config::getInstance()->$domain = $config;
286 Config::getInstance()->forceSave();
287 return true;
288 }
289 return false;
290 }
291
292 /**
293 * Returns the current host.
294 *
295 * @param bool $checkIfTrusted Whether to do trusted host check. Should ALWAYS be true,
296 * except in Controller.
297 * @return string|bool eg, `"demo.piwik.org"` or false if no host found.
298 */
299 public static function getHost($checkIfTrusted = true)
300 {
301 // HTTP/1.1 request
302 if (isset($_SERVER['HTTP_HOST'])
303 && strlen($host = $_SERVER['HTTP_HOST'])
304 && (!$checkIfTrusted
305 || self::isValidHost($host))
306 ) {
307 return $host;
308 }
309
310 // HTTP/1.0 request doesn't include Host: header
311 if (isset($_SERVER['SERVER_ADDR'])) {
312 return $_SERVER['SERVER_ADDR'];
313 }
314
315 return false;
316 }
317
318 /**
319 * Sets the host. Useful for CLI scripts, eg. core:archive command
320 *
321 * @param $host string
322 */
323 public static function setHost($host)
324 {
325 $_SERVER['HTTP_HOST'] = $host;
326 }
327
328 /**
329 * Returns the current host.
330 *
331 * @param string $default Default value to return if host unknown
332 * @param bool $checkTrustedHost Whether to do trusted host check. Should ALWAYS be true,
333 * except in Controller.
334 * @return string eg, `"example.org"` if the current URL is
335 * `"http://example.org/dir1/dir2/index.php?param1=value1&param2=value2"`
336 * @api
337 */
338 public static function getCurrentHost($default = 'unknown', $checkTrustedHost = true)
339 {
340 $hostHeaders = array();
341
342 $config = Config::getInstance()->General;
343 if (isset($config['proxy_host_headers'])) {
344 $hostHeaders = $config['proxy_host_headers'];
345 }
346
347 if (!is_array($hostHeaders)) {
348 $hostHeaders = array();
349 }
350
351 $host = self::getHost($checkTrustedHost);
352 $default = Common::sanitizeInputValue($host ? $host : $default);
353
354 return IP::getNonProxyIpFromHeader($default, $hostHeaders);
355 }
356
357 /**
358 * Returns the query string of the current URL.
359 *
360 * @return string eg, `"?param1=value1&param2=value2"` if the current URL is
361 * `"http://example.org/dir1/dir2/index.php?param1=value1&param2=value2"`
362 * @api
363 */
364 public static function getCurrentQueryString()
365 {
366 $url = '';
367 if (isset($_SERVER['QUERY_STRING'])
368 && !empty($_SERVER['QUERY_STRING'])
369 ) {
370 $url .= "?" . $_SERVER['QUERY_STRING'];
371 }
372 return $url;
373 }
374
375 /**
376 * Returns an array mapping query paramater names with query parameter values for
377 * the current URL.
378 *
379 * @return array If current URL is `"http://example.org/dir1/dir2/index.php?param1=value1&param2=value2"`
380 * this will return:
381 *
382 * array(
383 * 'param1' => string 'value1',
384 * 'param2' => string 'value2'
385 * )
386 * @api
387 */
388 public static function getArrayFromCurrentQueryString()
389 {
390 $queryString = self::getCurrentQueryString();
391 $urlValues = UrlHelper::getArrayFromQueryString($queryString);
392 return $urlValues;
393 }
394
395 /**
396 * Modifies the current query string with the supplied parameters and returns
397 * the result. Parameters in the current URL will be overwritten with values
398 * in `$params` and parameters absent from the current URL but present in `$params`
399 * will be added to the result.
400 *
401 * @param array $params set of parameters to modify/add in the current URL
402 * eg, `array('param3' => 'value3')`
403 * @return string eg, `"?param2=value2&param3=value3"`
404 * @api
405 */
406 public static function getCurrentQueryStringWithParametersModified($params)
407 {
408 $urlValues = self::getArrayFromCurrentQueryString();
409 foreach ($params as $key => $value) {
410 $urlValues[$key] = $value;
411 }
412 $query = self::getQueryStringFromParameters($urlValues);
413 if (strlen($query) > 0) {
414 return '?' . $query;
415 }
416 return '';
417 }
418
419 /**
420 * Converts an array of parameters name => value mappings to a query
421 * string. Values must already be URL encoded before you call this function.
422 *
423 * @param array $parameters eg. `array('param1' => 10, 'param2' => array(1,2))`
424 * @return string eg. `"param1=10&param2[]=1&param2[]=2"`
425 * @api
426 */
427 public static function getQueryStringFromParameters($parameters)
428 {
429 $query = '';
430 foreach ($parameters as $name => $value) {
431 if (is_null($value) || $value === false) {
432 continue;
433 }
434 if (is_array($value)) {
435 foreach ($value as $theValue) {
436 $query .= $name . "[]=" . $theValue . "&";
437 }
438 } else {
439 $query .= $name . "=" . $value . "&";
440 }
441 }
442 $query = substr($query, 0, -1);
443 return $query;
444 }
445
446 public static function getQueryStringFromUrl($url)
447 {
448 return parse_url($url, PHP_URL_QUERY);
449 }
450
451 /**
452 * Redirects the user to the referrer. If no referrer exists, the user is redirected
453 * to the current URL without query string.
454 *
455 * @api
456 */
457 public static function redirectToReferrer()
458 {
459 $referrer = self::getReferrer();
460 if ($referrer !== false) {
461 self::redirectToUrl($referrer);
462 }
463 self::redirectToUrl(self::getCurrentUrlWithoutQueryString());
464 }
465
466 private static function redirectToUrlNoExit($url)
467 {
468 if (UrlHelper::isLookLikeUrl($url)
469 || strpos($url, 'index.php') === 0
470 ) {
471 Common::sendResponseCode(302);
472 Common::sendHeader("X-Robots-Tag: noindex");
473 Common::sendHeader("Location: $url");
474 } else {
475 echo "Invalid URL to redirect to.";
476 }
477
478 if (Common::isPhpCliMode()) {
479 throw new Exception("If you were using a browser, Matomo would redirect you to this URL: $url \n\n");
480 }
481 }
482
483 /**
484 * Redirects the user to the specified URL.
485 *
486 * @param string $url
487 * @throws Exception
488 * @api
489 */
490 public static function redirectToUrl($url)
491 {
492 // Close the session manually.
493 // We should not have to call this because it was registered via register_shutdown_function,
494 // but it is not always called fast enough
495 Session::close();
496
497 self::redirectToUrlNoExit($url);
498
499 exit;
500 }
501
502 /**
503 * If the page is using HTTP, redirect to the same page over HTTPS
504 */
505 public static function redirectToHttps()
506 {
507 if (ProxyHttp::isHttps()) {
508 return;
509 }
510 $url = self::getCurrentUrl();
511 $url = str_replace("http://", "https://", $url);
512 self::redirectToUrl($url);
513 }
514
515 /**
516 * Returns the **HTTP_REFERER** `$_SERVER` variable, or `false` if not found.
517 *
518 * @return string|false
519 * @api
520 */
521 public static function getReferrer()
522 {
523 if (!empty($_SERVER['HTTP_REFERER'])) {
524 return $_SERVER['HTTP_REFERER'];
525 }
526 return false;
527 }
528
529 /**
530 * Returns `true` if the URL points to something on the same host, `false` if otherwise.
531 *
532 * @param string $url
533 * @return bool True if local; false otherwise.
534 * @api
535 */
536 public static function isLocalUrl($url)
537 {
538 if (empty($url)) {
539 return true;
540 }
541
542 // handle host name mangling
543 $requestUri = isset($_SERVER['SCRIPT_URI']) ? $_SERVER['SCRIPT_URI'] : '';
544 $parseRequest = @parse_url($requestUri);
545 $hosts = array(self::getHost(), self::getCurrentHost());
546 if (!empty($parseRequest['host'])) {
547 $hosts[] = $parseRequest['host'];
548 }
549
550 // drop port numbers from hostnames and IP addresses
551 $hosts = array_map(array('self', 'getHostSanitized'), $hosts);
552
553 $disableHostCheck = Config::getInstance()->General['enable_trusted_host_check'] == 0;
554 // compare scheme and host
555 $parsedUrl = @parse_url($url);
556 $host = IPUtils::sanitizeIp(@$parsedUrl['host']);
557 return !empty($host)
558 && ($disableHostCheck || in_array($host, $hosts))
559 && !empty($parsedUrl['scheme'])
560 && in_array($parsedUrl['scheme'], array('http', 'https'));
561 }
562
563 /**
564 * Checks whether the given host is a local host like `127.0.0.1` or `localhost`.
565 *
566 * @param string $host
567 * @return bool
568 */
569 public static function isLocalHost($host)
570 {
571 if (empty($host)) {
572 return false;
573 }
574
575 // remove port
576 $hostWithoutPort = explode(':', $host);
577 array_pop($hostWithoutPort);
578 $hostWithoutPort = implode(':', $hostWithoutPort);
579
580 $localHostnames = Url::getLocalHostnames();
581 return in_array($host, $localHostnames, true)
582 || in_array($hostWithoutPort, $localHostnames, true);
583 }
584
585 public static function getTrustedHostsFromConfig()
586 {
587 $hosts = self::getHostsFromConfig('General', 'trusted_hosts');
588
589 // Case user wrote in the config, http://example.com/test instead of example.com
590 foreach ($hosts as &$host) {
591 if (UrlHelper::isLookLikeUrl($host)) {
592 $host = parse_url($host, PHP_URL_HOST);
593 }
594 }
595 return $hosts;
596 }
597
598 public static function getTrustedHosts()
599 {
600 return self::getTrustedHostsFromConfig();
601 }
602
603 public static function getCorsHostsFromConfig()
604 {
605 return self::getHostsFromConfig('General', 'cors_domains');
606 }
607
608 /**
609 * Returns hostname, without port numbers
610 *
611 * @param $host
612 * @return array
613 */
614 public static function getHostSanitized($host)
615 {
616 if (!class_exists("Piwik\\Network\\IPUtils")) {
617 throw new Exception("Piwik\\Network\\IPUtils could not be found, maybe you are using Matomo from git and need to update Composer. $ php composer.phar update");
618 }
619 return IPUtils::sanitizeIp($host);
620 }
621
622 protected static function getHostsFromConfig($domain, $key)
623 {
624 $config = @Config::getInstance()->$domain;
625
626 if (!isset($config[$key])) {
627 return array();
628 }
629
630 $hosts = $config[$key];
631 if (!is_array($hosts)) {
632 return array();
633 }
634 return $hosts;
635 }
636
637 /**
638 * Returns the host part of any valid URL.
639 *
640 * @param string $url Any fully qualified URL
641 * @return string|null The actual host in lower case or null if $url is not a valid fully qualified URL.
642 */
643 public static function getHostFromUrl($url)
644 {
645 $parsedUrl = parse_url($url);
646
647 if (empty($parsedUrl['host'])) {
648 return;
649 }
650
651 return Common::mb_strtolower($parsedUrl['host']);
652 }
653
654 /**
655 * Checks whether any of the given URLs has the given host. If not, we will also check whether any URL uses a
656 * subdomain of the given host. For instance if host is "example.com" and a URL is "http://www.example.com" we
657 * consider this as valid and return true. The always trusted hosts such as "127.0.0.1" are considered valid as well.
658 *
659 * @param $host
660 * @param $urls
661 * @return bool
662 */
663 public static function isHostInUrls($host, $urls)
664 {
665 if (empty($host)) {
666 return false;
667 }
668
669 $host = Common::mb_strtolower($host);
670
671 if (!empty($urls)) {
672 foreach ($urls as $url) {
673 if (Common::mb_strtolower($url) === $host) {
674 return true;
675 }
676
677 $siteHost = self::getHostFromUrl($url);
678
679 if ($siteHost === $host) {
680 return true;
681 }
682
683 if (Common::stringEndsWith($siteHost, '.' . $host)) {
684 // allow subdomains
685 return true;
686 }
687 }
688 }
689
690 return in_array($host, self::getAlwaysTrustedHosts());
691 }
692
693 /**
694 * List of hosts that are never checked for validity.
695 *
696 * @return array
697 */
698 private static function getAlwaysTrustedHosts()
699 {
700 return self::getLocalHostnames();
701 }
702
703 /**
704 * @return array
705 */
706 public static function getLocalHostnames()
707 {
708 return array('localhost', '127.0.0.1', '::1', '[::1]');
709 }
710
711 /**
712 * @return bool
713 */
714 public static function isSecureConnectionAssumedByPiwikButNotForcedYet()
715 {
716 $isSecureConnectionLikelyNotUsed = Url::isSecureConnectionLikelyNotUsed();
717 $hasSessionCookieSecureFlag = ProxyHttp::isHttps();
718 $isSecureConnectionAssumedByPiwikButNotForcedYet = Url::isPiwikConfiguredToAssumeSecureConnection() && !SettingsPiwik::isHttpsForced();
719
720 return $isSecureConnectionLikelyNotUsed
721 && $hasSessionCookieSecureFlag
722 && $isSecureConnectionAssumedByPiwikButNotForcedYet;
723 }
724
725 /**
726 * @return string
727 */
728 protected static function getCurrentSchemeFromRequestHeader()
729 {
730 if (isset($_SERVER['HTTP_X_FORWARDED_SCHEME']) && strtolower($_SERVER['HTTP_X_FORWARDED_SCHEME']) === 'https') {
731 return 'https';
732 }
733
734 if (isset($_SERVER['HTTP_X_URL_SCHEME']) && strtolower($_SERVER['HTTP_X_URL_SCHEME']) === 'https') {
735 return 'https';
736 }
737
738 if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'http') {
739 return 'http';
740 }
741
742 if ((isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS'] == 'on' || $_SERVER['HTTPS'] === true))
743 || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')
744 ) {
745
746 return 'https';
747 }
748 return 'http';
749 }
750
751 protected static function isSecureConnectionLikelyNotUsed()
752 {
753 return Url::getCurrentSchemeFromRequestHeader() == 'http';
754 }
755
756 /**
757 * @return bool
758 */
759 protected static function isPiwikConfiguredToAssumeSecureConnection()
760 {
761 $assume_secure_protocol = @Config::getInstance()->General['assume_secure_protocol'];
762 return (bool) $assume_secure_protocol;
763 }
764 }
765