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