PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / trunk
Matomo Analytics – Powerful, Privacy-First Insights for WordPress vtrunk
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 / Tracker / VisitExcluded.php
matomo / app / core / Tracker Last commit date
Config 4 months ago Db 3 months ago Handler 2 years ago Visit 1 month ago Action.php 3 months ago ActionPageview.php 2 years ago BotRequest.php 3 months ago BotRequestProcessor.php 1 month ago Cache.php 6 months ago Db.php 1 year ago Failures.php 6 months ago FingerprintSalt.php 1 year ago GoalManager.php 1 month ago Handler.php 2 years ago IgnoreCookie.php 1 year ago LogTable.php 1 year ago Model.php 6 months ago PageUrl.php 2 weeks ago Request.php 1 month ago RequestHandlerTrait.php 4 months ago RequestProcessor.php 1 month ago RequestSet.php 6 months ago Response.php 3 months ago ScheduledTasksRunner.php 1 year ago Settings.php 3 months ago TableLogAction.php 6 months ago TrackerCodeGenerator.php 1 year ago TrackerConfig.php 1 month ago Visit.php 3 months ago VisitExcluded.php 3 months ago VisitInterface.php 3 months ago Visitor.php 1 month ago VisitorNotFoundInDb.php 1 month ago VisitorRecognizer.php 1 year ago
VisitExcluded.php
324 lines
1 <?php
2
3 /**
4 * Matomo - free/libre analytics platform
5 *
6 * @link https://matomo.org
7 * @license https://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
8 */
9 namespace Piwik\Tracker;
10
11 use Piwik\Cache as PiwikCache;
12 use Piwik\Common;
13 use Piwik\Container\StaticContainer;
14 use Piwik\DeviceDetector\DeviceDetectorFactory;
15 use Piwik\Exception\UnexpectedWebsiteFoundException;
16 use Matomo\Network\IP;
17 use Piwik\Piwik;
18 use Piwik\Plugins\SitesManager\SiteUrls;
19 use Piwik\Tracker\Visit\ReferrerSpamFilter;
20 use Piwik\Config;
21 /**
22 * This class contains the logic to exclude some visitors from being tracked as per user settings
23 */
24 class VisitExcluded
25 {
26 /**
27 * @var ReferrerSpamFilter
28 */
29 private $spamFilter;
30 private $siteCache = array();
31 public $request;
32 public $idSite;
33 public $userAgent;
34 public $ip;
35 public function __construct(\Piwik\Tracker\Request $request)
36 {
37 $this->spamFilter = new ReferrerSpamFilter();
38 $this->request = $request;
39 try {
40 $this->idSite = $request->getIdSite();
41 } catch (UnexpectedWebsiteFoundException $e) {
42 // most checks will still work on a global scope and we still want to be able to test if this is a valid
43 // visit or not
44 $this->idSite = 0;
45 }
46 $userAgent = $request->getUserAgent();
47 $this->userAgent = Common::unsanitizeInputValue($userAgent);
48 $this->ip = $request->getIp();
49 }
50 /**
51 * Test if the current visitor is excluded from the statistics.
52 *
53 * Plugins can for example exclude visitors based on the
54 * - IP
55 * - If a given cookie is found
56 *
57 * @return bool True if the visit must not be saved, false otherwise
58 */
59 public function isExcluded()
60 {
61 $excluded = \false;
62 if ($this->isNonHumanBot()) {
63 Common::printDebug('Search bot detected, visit excluded');
64 $excluded = \true;
65 }
66 /*
67 * Requests built with piwik.js will contain a rec=1 parameter. This is used as
68 * an indication that the request is made by a JS enabled device. By default, Piwik
69 * doesn't track non-JS visitors.
70 */
71 if (!$excluded) {
72 $toRecord = $this->request->getParam($parameterForceRecord = 'rec');
73 if (!$toRecord) {
74 Common::printDebug(@$_SERVER['REQUEST_METHOD'] . ' parameter ' . $parameterForceRecord . ' not found in URL, request excluded');
75 $excluded = \true;
76 Common::printDebug("'{$parameterForceRecord}' parameter not found.");
77 }
78 }
79 /**
80 * Triggered on every tracking request.
81 *
82 * This event can be used to tell the Tracker not to record this particular action or visit.
83 *
84 * @param bool &$excluded Whether the request should be excluded or not. Initialized
85 * to `false`. Event subscribers should set it to `true` in
86 * order to exclude the request.
87 * @param Request $request The request object which contains all of the request's information
88 *
89 */
90 Piwik::postEvent('Tracker.isExcludedVisit', array(&$excluded, $this->request));
91 /*
92 * Following exclude operations happen after the hook.
93 * These are of higher priority and should not be overwritten by plugins.
94 */
95 // Checking if in config some requests are excluded
96 if (!$excluded) {
97 $excluded = $this->request->isRequestExcluded();
98 if ($excluded) {
99 Common::printDebug("Request is excluded.");
100 }
101 }
102 // Checking if the Piwik ignore cookie is set
103 if (!$excluded) {
104 $excluded = $this->isIgnoreCookieFound();
105 if ($excluded) {
106 Common::printDebug("Ignore cookie found.");
107 }
108 }
109 // Checking for excluded IPs
110 if (!$excluded) {
111 $excluded = $this->isVisitorIpExcluded();
112 if ($excluded) {
113 Common::printDebug("IP excluded.");
114 }
115 }
116 // Check if user agent should be excluded
117 if (!$excluded) {
118 $excluded = $this->isUserAgentExcluded();
119 if ($excluded) {
120 Common::printDebug("User agent excluded.");
121 }
122 }
123 // Check if Referrer URL is a known spam
124 $generalConfig = Config::getInstance()->Tracker;
125 if ($generalConfig['enable_spam_filter']) {
126 if (!$excluded) {
127 $excluded = $this->isReferrerSpamExcluded();
128 if ($excluded) {
129 Common::printDebug("Referrer URL is listed as spam.");
130 }
131 }
132 } else {
133 Common::printDebug("Spam list is disabled.");
134 }
135 // Check if request URL is excluded
136 if (!$excluded) {
137 $excluded = $this->isUrlExcluded();
138 if ($excluded) {
139 Common::printDebug("Unknown URL is not allowed to track.");
140 }
141 }
142 if (!$excluded) {
143 if ($this->isPrefetchDetected()) {
144 $excluded = \true;
145 Common::printDebug("Prefetch request detected, not a real visit so we Ignore this visit/pageview");
146 }
147 }
148 if ($excluded) {
149 Common::printDebug("Visitor excluded.");
150 return \true;
151 }
152 return \false;
153 }
154 protected function isPrefetchDetected()
155 {
156 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";
157 }
158 /**
159 * Live/Bing/MSN bot and Googlebot are evolving to detect cloaked websites.
160 * As a result, these sophisticated bots exhibit characteristics of
161 * browsers (cookies enabled, executing JavaScript, etc).
162 *
163 * @see \DeviceDetector\Parser\Bot
164 *
165 * @return boolean
166 */
167 protected function isNonHumanBot()
168 {
169 $allowBots = $this->request->getParam('bots');
170 $deviceDetector = StaticContainer::get(DeviceDetectorFactory::class)->makeInstance($this->userAgent, $this->request->getClientHints());
171 return !$allowBots && ($deviceDetector->isBot() || $this->isIpInRange());
172 }
173 private function isIpInRange()
174 {
175 $cache = PiwikCache::getTransientCache();
176 $ip = IP::fromBinaryIP($this->ip);
177 $key = 'VisitExcludedIsIpInRange' . $ip->toString();
178 if ($cache->contains($key)) {
179 $isInRanges = $cache->fetch($key);
180 } else {
181 if ($this->isChromeDataSaverUsed($ip)) {
182 $isInRanges = \false;
183 } else {
184 $isInRanges = $ip->isInRanges($this->getBotIpRanges());
185 }
186 $cache->save($key, $isInRanges);
187 }
188 return $isInRanges;
189 }
190 public function isChromeDataSaverUsed(IP $ip)
191 {
192 // see https://github.com/piwik/piwik/issues/7733
193 return !empty($_SERVER['HTTP_VIA']) && \false !== strpos(strtolower($_SERVER['HTTP_VIA']), 'chrome-compression-proxy') && $ip->isInRanges($this->getGoogleBotIpRanges());
194 }
195 protected function getBotIpRanges()
196 {
197 return array_merge($this->getGoogleBotIpRanges(), array(
198 // Live/Bing/MSN
199 '64.4.0.0/18',
200 '65.52.0.0/14',
201 '157.54.0.0/15',
202 '157.56.0.0/14',
203 '157.60.0.0/16',
204 '207.46.0.0/16',
205 '207.68.128.0/18',
206 '207.68.192.0/20',
207 '131.253.26.0/20',
208 '131.253.24.0/20',
209 // Yahoo
210 '72.30.198.0/20',
211 '72.30.196.0/20',
212 '98.137.207.0/20',
213 // Chinese bot hammering websites
214 '1.202.218.8',
215 ));
216 }
217 private function getGoogleBotIpRanges()
218 {
219 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');
220 }
221 /**
222 * Looks for the ignore cookie that users can set in the Piwik admin screen.
223 * @return bool
224 */
225 protected function isIgnoreCookieFound()
226 {
227 if (\Piwik\Tracker\IgnoreCookie::isIgnoreCookieFound()) {
228 Common::printDebug('Matomo ignore cookie was found, visit not tracked.');
229 return \true;
230 }
231 return \false;
232 }
233 /**
234 * Checks if the visitor ip is in the excluded list
235 *
236 * @return bool
237 */
238 protected function isVisitorIpExcluded()
239 {
240 $excludedIps = $this->getAttributes('excluded_ips', 'global_excluded_ips');
241 if (!empty($excludedIps)) {
242 $ip = IP::fromBinaryIP($this->ip);
243 if ($ip->isInRanges($excludedIps)) {
244 Common::printDebug('Visitor IP ' . $ip->toString() . ' is excluded from being tracked');
245 return \true;
246 }
247 }
248 return \false;
249 }
250 private function getAttributes($siteAttribute, $globalAttribute)
251 {
252 if (!isset($this->siteCache[$this->idSite])) {
253 $this->siteCache[$this->idSite] = array();
254 }
255 try {
256 if (empty($this->siteCache[$this->idSite])) {
257 $this->siteCache[$this->idSite] = \Piwik\Tracker\Cache::getCacheWebsiteAttributes($this->idSite);
258 }
259 if (isset($this->siteCache[$this->idSite][$siteAttribute])) {
260 return $this->siteCache[$this->idSite][$siteAttribute];
261 }
262 } catch (UnexpectedWebsiteFoundException $e) {
263 $cached = \Piwik\Tracker\Cache::getCacheGeneral();
264 if ($globalAttribute && isset($cached[$globalAttribute])) {
265 return $cached[$globalAttribute];
266 }
267 }
268 }
269 /**
270 * Checks if request URL is excluded
271 * @return bool
272 */
273 protected function isUrlExcluded()
274 {
275 $excludedUrls = $this->getAttributes('exclude_unknown_urls', null);
276 $siteUrls = $this->getAttributes('urls', null);
277 if (!empty($excludedUrls) && !empty($siteUrls)) {
278 $url = $this->request->getParam('url');
279 $parsedUrl = parse_url($url);
280 $trackingUrl = new SiteUrls();
281 $urls = $trackingUrl->groupUrlsByHost(array($this->idSite => $siteUrls));
282 $idSites = $trackingUrl->getIdSitesMatchingUrl($parsedUrl, $urls);
283 $isUrlExcluded = !isset($idSites) || !in_array($this->idSite, $idSites);
284 return $isUrlExcluded;
285 }
286 return \false;
287 }
288 /**
289 * Returns true if the specified user agent should be excluded for the current site or not.
290 *
291 * Visits whose user agent string contains one of the excluded_user_agents strings for the
292 * site being tracked (or one of the global strings) will be excluded. Regular expressions
293 * are also supported.
294 *
295 * @internal param string $this ->userAgent The user agent string.
296 */
297 protected function isUserAgentExcluded() : bool
298 {
299 $excludedAgents = $this->getAttributes('excluded_user_agents', 'global_excluded_user_agents');
300 if (!empty($excludedAgents)) {
301 foreach ($excludedAgents as $excludedUserAgent) {
302 // if the excluded user agent string part is in this visit's user agent, this visit should be excluded
303 if (stripos($this->userAgent, $excludedUserAgent) !== \false) {
304 return \true;
305 }
306 // if the string is a valid regex, and the user agent matches, this visit should be excluded
307 if (@preg_match($excludedUserAgent, '') !== \false && preg_match($excludedUserAgent, $this->userAgent) === 1) {
308 return \true;
309 }
310 }
311 }
312 return \false;
313 }
314 /**
315 * Returns true if the Referrer is a known spammer.
316 *
317 * @return bool
318 */
319 protected function isReferrerSpamExcluded()
320 {
321 return $this->spamFilter->isSpam($this->request);
322 }
323 }
324