PluginProbe ʕ •ᴥ•ʔ
Independent Analytics – WordPress Analytics Plugin / 2.15.0
Independent Analytics – WordPress Analytics Plugin v2.15.0
2.15.0 2.14.10 trunk 1.1 1.10 1.10.1 1.11 1.12 1.13 1.14 1.15 1.16 1.17 1.17.1 1.17.2 1.17.3 1.17.4 1.18 1.18.1 1.19.0 1.19.1 1.2 1.20.0 1.21.0 1.22.0 1.22.1 1.23.0 1.23.1 1.24.0 1.24.1 1.25.0 1.25.1 1.26.0 1.27.0 1.28.0 1.28.1 1.28.2 1.28.3 1.29.0 1.3 1.30.0 1.30.1 1.4 1.5 1.6 1.7 1.8 1.9 2.0.0 2.0.1 2.1.4 2.1.5 2.1.6 2.10.0 2.10.1 2.10.2 2.10.3 2.10.4 2.11.0 2.11.1 2.11.10 2.11.2 2.11.3 2.11.4 2.11.5 2.11.6 2.11.7 2.11.8 2.11.9 2.12.0 2.12.1 2.12.2 2.13.1 2.13.2 2.13.5 2.13.6 2.14.0 2.14.1 2.14.2 2.14.4 2.14.6 2.14.7 2.14.8 2.14.9 2.2.0 2.2.1 2.3.1 2.3.2 2.4.2 2.4.3 2.5.0 2.5.1 2.6.0 2.6.1 2.6.2 2.6.3 2.6.4 2.7.0 2.7.1 2.7.2 2.7.3 2.8.2 2.8.3 2.8.4 2.8.5 2.8.6 2.8.7 2.8.8 2.8.9 2.9.2 2.9.3 2.9.4 2.9.5 2.9.6 2.9.7
independent-analytics / IAWP / Click_Tracking / Click_Processing_Job.php
independent-analytics / IAWP / Click_Tracking Last commit date
Click.php 8 months ago Click_Processing_Job.php 4 days ago Config_File_Manager.php 4 days ago Link_Rule.php 1 year ago Link_Rule_Finder.php 2 months ago Site.php 4 days ago
Click_Processing_Job.php
130 lines
1 <?php
2
3 namespace IAWP\Click_Tracking;
4
5 use IAWPSCOPED\Carbon\CarbonImmutable;
6 use IAWP\Cron_Job;
7 use IAWP\Models\Visitor;
8 use IAWP\Payload_Validator;
9 use IAWP\Utils\Security;
10 use IAWPSCOPED\Illuminate\Support\Str;
11 use IAWPSCOPED\League\Uri\Uri;
12 /** @internal */
13 class Click_Processing_Job extends Cron_Job
14 {
15 protected $name = 'iawp_click_processing';
16 protected $interval = 'every_minute';
17 public function handle() : void
18 {
19 // Periodically recreate the config file
20 \IAWP\Click_Tracking\Config_File_Manager::recreate();
21 if (\IAWPSCOPED\iawp_is_free()) {
22 self::unschedule();
23 return;
24 }
25 $click_data_file = $this->get_click_data_file();
26 if ($click_data_file === null) {
27 return;
28 }
29 $job_file = $this->create_job_file($click_data_file);
30 if ($job_file === null) {
31 return;
32 }
33 $job_handle = \fopen($job_file, 'r');
34 if ($job_handle === \false) {
35 return;
36 }
37 // The first line for the PHP file is an exit statement to keep the contents private. This
38 // should be skipped when parsing the file.
39 if (\pathinfo($job_file, \PATHINFO_EXTENSION) === 'php') {
40 \fgets($job_handle);
41 // Skip first line
42 }
43 while (($json = \fgets($job_handle)) !== \false) {
44 $event = \json_decode($json, \true);
45 if (\is_null($event)) {
46 continue;
47 }
48 $payload_validator = Payload_Validator::new($event['payload'], $event['signature']);
49 if (!$payload_validator->is_valid() || \is_null($payload_validator->resource())) {
50 continue;
51 }
52 if (\is_string($event['href']) && !$this->is_valid_href($event['href'])) {
53 continue;
54 }
55 $event['href'] = \sanitize_url($event['href']);
56 $event['classes'] = Security::string($event['classes']);
57 $event['ids'] = Security::string($event['ids']);
58 $click = \IAWP\Click_Tracking\Click::new(['href' => $event['href'], 'classes' => $event['classes'], 'ids' => $event['ids'], 'resource_id' => $payload_validator->resource()['id'], 'visitor_id' => Visitor::fetch_visitor_id_by_hash($event['visitor_token']), 'created_at' => CarbonImmutable::createFromTimestamp($event['created_at'], 'utc')]);
59 $click->track();
60 }
61 \fclose($job_handle);
62 \unlink($job_file);
63 }
64 private function get_click_data_file() : ?string
65 {
66 $avoid_temporary_directory = \defined('IAWP_AVOID_TEMPORARY_DIRECTORY') ? \IAWP_AVOID_TEMPORARY_DIRECTORY === \true : \false;
67 $suffix = \IAWP\Click_Tracking\Site::file_suffix();
68 if (!$avoid_temporary_directory) {
69 $text_file = Str::finish(\sys_get_temp_dir(), \DIRECTORY_SEPARATOR) . "iawp-click-data{$suffix}.txt";
70 if (\is_file($text_file) && \is_readable($text_file) && \is_writable($text_file)) {
71 return $text_file;
72 }
73 }
74 $php_file = \IAWPSCOPED\iawp_path_to("iawp-click-data{$suffix}.php");
75 if (\is_file($php_file)) {
76 return $php_file;
77 }
78 return null;
79 }
80 private function create_job_file(string $file) : ?string
81 {
82 if (!\is_readable($file) || !\is_writable($file)) {
83 return null;
84 }
85 $job_id = \rand();
86 $extension = \pathinfo($file, \PATHINFO_EXTENSION);
87 $job_file = Str::finish(\dirname($file), \DIRECTORY_SEPARATOR) . "iawp-click-data-{$job_id}.{$extension}";
88 $was_renamed = \rename($file, $job_file);
89 if (!$was_renamed || !\is_file($job_file)) {
90 return null;
91 }
92 return $job_file;
93 }
94 private function is_valid_href(string $href) : bool
95 {
96 if ($this->has_injection_attempt($href)) {
97 return \false;
98 }
99 $uri = Uri::createFromString($href);
100 $scheme = $uri->getScheme();
101 if ($scheme === null) {
102 return \false;
103 }
104 $is_http = $scheme === 'http' || $scheme === 'https';
105 if ($is_http && \filter_var($href, \FILTER_VALIDATE_URL) === \false) {
106 return \false;
107 }
108 return \true;
109 }
110 /**
111 * The goal of this method is not security. That's handled by the secure and robust functions
112 * that PHP and WordPress provide. The goal of this function is to detect when someone tried
113 * to use SQL injection so we can ignore the spam clicks.
114 *
115 * @param string $string
116 *
117 * @return bool
118 */
119 private function has_injection_attempt(string $string) : bool
120 {
121 $patterns = ['/select\\s.*\\sfrom/i', '/waitfor\\sdelay/i', '/pg_sleep/i'];
122 foreach ($patterns as $pattern) {
123 if (\preg_match($pattern, $string)) {
124 return \true;
125 }
126 }
127 return \false;
128 }
129 }
130