| <?php |
|
|
| |
| |
| |
| |
| |
| |
|
|
| namespace Piwik\Tracker; |
|
|
| use Piwik\Cache as PiwikCache; |
| use Piwik\Common; |
| use Piwik\Container\StaticContainer; |
| use Piwik\DeviceDetector\DeviceDetectorFactory; |
| use Piwik\Exception\UnexpectedWebsiteFoundException; |
| use Matomo\Network\IP; |
| use Piwik\Piwik; |
| use Piwik\Plugins\SitesManager\SiteUrls; |
| use Piwik\Tracker\Visit\ReferrerSpamFilter; |
| use Piwik\Config; |
|
|
| |
| |
| |
| class VisitExcluded |
| { |
| |
| |
| |
| private $spamFilter; |
|
|
| private $siteCache = array(); |
|
|
| public $request; |
| public $idSite; |
| public $userAgent; |
| public $ip; |
|
|
| public function __construct(Request $request) |
| { |
| $this->spamFilter = new ReferrerSpamFilter(); |
| $this->request = $request; |
|
|
| try { |
| $this->idSite = $request->getIdSite(); |
| } catch (UnexpectedWebsiteFoundException $e) { |
| |
| |
| $this->idSite = 0; |
| } |
| $userAgent = $request->getUserAgent(); |
| $this->userAgent = Common::unsanitizeInputValue($userAgent); |
| $this->ip = $request->getIp(); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| public function isExcluded() |
| { |
| $excluded = false; |
|
|
| if ($this->isNonHumanBot()) { |
| Common::printDebug('Search bot detected, visit excluded'); |
| $excluded = true; |
| } |
|
|
| |
| |
| |
| |
| |
| if (!$excluded) { |
| $toRecord = $this->request->getParam($parameterForceRecord = 'rec'); |
| if (!$toRecord) { |
| Common::printDebug(@$_SERVER['REQUEST_METHOD'] . ' parameter ' . $parameterForceRecord . ' not found in URL, request excluded'); |
| $excluded = true; |
| Common::printDebug("'$parameterForceRecord' parameter not found."); |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| Piwik::postEvent('Tracker.isExcludedVisit', array(&$excluded, $this->request)); |
|
|
| |
| |
| |
| |
|
|
| |
| if (!$excluded) { |
| $excluded = $this->request->isRequestExcluded(); |
| if ($excluded) { |
| Common::printDebug("Request is excluded."); |
| } |
| } |
|
|
| |
| if (!$excluded) { |
| $excluded = $this->isIgnoreCookieFound(); |
| if ($excluded) { |
| Common::printDebug("Ignore cookie found."); |
| } |
| } |
|
|
| |
| if (!$excluded) { |
| $excluded = $this->isVisitorIpExcluded(); |
| if ($excluded) { |
| Common::printDebug("IP excluded."); |
| } |
| } |
|
|
| |
| if (!$excluded) { |
| $excluded = $this->isUserAgentExcluded(); |
| if ($excluded) { |
| Common::printDebug("User agent excluded."); |
| } |
| } |
|
|
| |
| $generalConfig = Config::getInstance()->Tracker; |
| if ($generalConfig['enable_spam_filter']) { |
| if (!$excluded) { |
| $excluded = $this->isReferrerSpamExcluded(); |
| if ($excluded) { |
| Common::printDebug("Referrer URL is listed as spam."); |
| } |
| } |
| } else { |
| Common::printDebug("Spam list is disabled."); |
| } |
|
|
| |
| if (!$excluded) { |
| $excluded = $this->isUrlExcluded(); |
| if ($excluded) { |
| Common::printDebug("Unknown URL is not allowed to track."); |
| } |
| } |
|
|
| if (!$excluded) { |
| if ($this->isPrefetchDetected()) { |
| $excluded = true; |
| Common::printDebug("Prefetch request detected, not a real visit so we Ignore this visit/pageview"); |
| } |
| } |
|
|
| if ($excluded) { |
| Common::printDebug("Visitor excluded."); |
| return true; |
| } |
|
|
| return false; |
| } |
|
|
| protected function isPrefetchDetected() |
| { |
| return (isset($_SERVER["HTTP_X_PURPOSE"]) |
| && in_array($_SERVER["HTTP_X_PURPOSE"], array("preview", "instant"))) |
| || (isset($_SERVER['HTTP_X_MOZ']) |
| && $_SERVER['HTTP_X_MOZ'] == "prefetch"); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| protected function isNonHumanBot() |
| { |
| $allowBots = $this->request->getParam('bots'); |
|
|
| $deviceDetector = StaticContainer::get(DeviceDetectorFactory::class)->makeInstance($this->userAgent, $this->request->getClientHints()); |
|
|
| return !$allowBots |
| && ($deviceDetector->isBot() || $this->isIpInRange()); |
| } |
|
|
| private function isIpInRange() |
| { |
| $cache = PiwikCache::getTransientCache(); |
|
|
| $ip = IP::fromBinaryIP($this->ip); |
| $key = 'VisitExcludedIsIpInRange' . $ip->toString(); |
|
|
| if ($cache->contains($key)) { |
| $isInRanges = $cache->fetch($key); |
| } else { |
| if ($this->isChromeDataSaverUsed($ip)) { |
| $isInRanges = false; |
| } else { |
| $isInRanges = $ip->isInRanges($this->getBotIpRanges()); |
| } |
|
|
| $cache->save($key, $isInRanges); |
| } |
|
|
| return $isInRanges; |
| } |
|
|
| public function isChromeDataSaverUsed(IP $ip) |
| { |
| |
| return !empty($_SERVER['HTTP_VIA']) |
| && false !== strpos(strtolower($_SERVER['HTTP_VIA']), 'chrome-compression-proxy') |
| && $ip->isInRanges($this->getGoogleBotIpRanges()); |
| } |
|
|
| protected function getBotIpRanges() |
| { |
| return array_merge($this->getGoogleBotIpRanges(), array( |
| // Live/Bing/MSN |
| '64.4.0.0/18', |
| '65.52.0.0/14', |
| '157.54.0.0/15', |
| '157.56.0.0/14', |
| '157.60.0.0/16', |
| '207.46.0.0/16', |
| '207.68.128.0/18', |
| '207.68.192.0/20', |
| '131.253.26.0/20', |
| '131.253.24.0/20', |
|
|
| // Yahoo |
| '72.30.198.0/20', |
| '72.30.196.0/20', |
| '98.137.207.0/20', |
| // Chinese bot hammering websites |
| '1.202.218.8', |
| )); |
| } |
|
|
| private function getGoogleBotIpRanges() |
| { |
| return array( |
| '216.239.32.0/19', |
| '64.233.160.0/19', |
| '66.249.80.0/20', |
| '72.14.192.0/18', |
| '209.85.128.0/17', |
| '66.102.0.0/20', |
| '74.125.0.0/16', |
| '64.18.0.0/20', |
| '207.126.144.0/20', |
| '173.194.0.0/16', |
| ); |
| } |
|
|
| |
| |
| |
| |
| protected function isIgnoreCookieFound() |
| { |
| if (IgnoreCookie::isIgnoreCookieFound()) { |
| Common::printDebug('Matomo ignore cookie was found, visit not tracked.'); |
| return true; |
| } |
|
|
| return false; |
| } |
|
|
| |
| |
| |
| |
| |
| protected function isVisitorIpExcluded() |
| { |
| $excludedIps = $this->getAttributes('excluded_ips', 'global_excluded_ips'); |
|
|
| if (!empty($excludedIps)) { |
| $ip = IP::fromBinaryIP($this->ip); |
| if ($ip->isInRanges($excludedIps)) { |
| Common::printDebug('Visitor IP ' . $ip->toString() . ' is excluded from being tracked'); |
| return true; |
| } |
| } |
|
|
| return false; |
| } |
|
|
| private function getAttributes($siteAttribute, $globalAttribute) |
| { |
| if (!isset($this->siteCache[$this->idSite])) { |
| $this->siteCache[$this->idSite] = array(); |
| } |
| try { |
| if (empty($this->siteCache[$this->idSite])) { |
| $this->siteCache[$this->idSite] = Cache::getCacheWebsiteAttributes($this->idSite); |
| } |
| if (isset($this->siteCache[$this->idSite][$siteAttribute])) { |
| return $this->siteCache[$this->idSite][$siteAttribute]; |
| } |
| } catch (UnexpectedWebsiteFoundException $e) { |
| $cached = Cache::getCacheGeneral(); |
| if ($globalAttribute && isset($cached[$globalAttribute])) { |
| return $cached[$globalAttribute]; |
| } |
| } |
| } |
|
|
| |
| |
| |
| |
| protected function isUrlExcluded() |
| { |
| $excludedUrls = $this->getAttributes('exclude_unknown_urls', null); |
| $siteUrls = $this->getAttributes('urls', null); |
|
|
| if (!empty($excludedUrls) && !empty($siteUrls)) { |
| $url = $this->request->getParam('url'); |
| $parsedUrl = parse_url($url); |
|
|
| $trackingUrl = new SiteUrls(); |
| $urls = $trackingUrl->groupUrlsByHost(array($this->idSite => $siteUrls)); |
|
|
| $idSites = $trackingUrl->getIdSitesMatchingUrl($parsedUrl, $urls); |
| $isUrlExcluded = !isset($idSites) || !in_array($this->idSite, $idSites); |
|
|
| return $isUrlExcluded; |
| } |
|
|
| return false; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| protected function isUserAgentExcluded(): bool |
| { |
| $excludedAgents = $this->getAttributes('excluded_user_agents', 'global_excluded_user_agents'); |
|
|
| if (!empty($excludedAgents)) { |
| foreach ($excludedAgents as $excludedUserAgent) { |
| |
| if (stripos($this->userAgent, $excludedUserAgent) !== false) { |
| return true; |
| } |
| |
| if ( |
| @preg_match($excludedUserAgent, '') !== false |
| && preg_match($excludedUserAgent, $this->userAgent) === 1 |
| ) { |
| return true; |
| } |
| } |
| } |
|
|
| return false; |
| } |
|
|
| |
| |
| |
| |
| |
| protected function isReferrerSpamExcluded() |
| { |
| return $this->spamFilter->isSpam($this->request); |
| } |
| } |
|
|