PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 5.7.2
Defender Security – Malware Scanner, Login Security & Firewall v5.7.2
6.3.0 6.2.3 6.2.4 6.2.0 6.2.1 6.2.2 6.1.0 5.3.1 5.4.0 5.4.1 5.5.0 5.5.1 5.6.0 5.6.1 5.6.2 5.7.0 5.7.1 5.7.2 5.8.0 5.8.1 5.9.0 6.0.0 6.0.1 3.0.1 3.1.0 All 141 releases
defender-security / src / component / ip / class-antibot-global-firewall.php

class-antibot-global-firewall.php in Defender Security – Malware Scanner, Login Security & Firewall 5.7.2, at src/component/ip/class-antibot-global-firewall.php

801 lines 22.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Responsible for handling AntiBot Global Firewall.
4 *
5 * @package WP_Defender\Component\IP
6 */
7
8 namespace WP_Defender\Component\IP;
9
10 use Exception;
11 use Generator;
12 use WP_Defender\Behavior\WPMUDEV;
13 use WP_Defender\Component;
14 use WP_Defender\Controller\Firewall;
15 use WP_Defender\Integrations\Antibot_Global_Firewall_Client;
16 use WP_Defender\Model\Antibot_Global_Firewall as Antibot_Global_Firewall_Model;
17 use WP_Defender\Model\Setting\Antibot_Global_Firewall_Setting;
18 use WP_Defender\Traits\Defender_Dashboard_Client;
19 use WP_Error;
20
21 /**
22 * Provides methods for downloading IPs from AntiBot Global Firewall, storing, deleting and checking IPs in DB table.
23 */
24 class Antibot_Global_Firewall extends Component {
25 use Defender_Dashboard_Client;
26
27 public const REASON_SLUG = 'antibot_global_firewall';
28
29 public const LOG_FILE_NAME = 'antibot-global-firewall.log';
30
31 public const DOWNLOAD_SYNC_SCHEDULE = 'twicedaily';
32
33 public const DOWNLOAD_SYNC_NEXT_RUN_OPTION = 'wpdef_antibot_global_firewall_download_sync_next_run';
34
35 public const LAST_SYNC_OPTION = 'wpdef_antibot_global_firewall_last_sync';
36
37 public const NOTICE_SLUG = 'wpdef_show_antibot_global_firewall_notice';
38
39 public const BLOCKLIST_STATS_KEY = 'wpdef_antibot_global_firewall_stats';
40
41 public const IS_SWITCHING_TO_PLUGIN_IN_PROGRESS = 'wpdef_antibot_global_firewall_switching_to_plugin_in_progress';
42
43 public const GLOBAL_NOTICE_TIME_OPTION = 'wpdef_antibot_global_firewall_global_notice_time';
44
45 public const GLOBAL_NOTICE_DELAY_DAYS_OPTION = 60;
46
47 /**
48 * The AntiBot Global Firewall model for storing IPs.
49 *
50 * @var Antibot_Global_Firewall_Model
51 */
52 private $model;
53
54 /**
55 * The AntiBot Global Firewall setting model.
56 *
57 * @var Antibot_Global_Firewall_Setting
58 */
59 private $model_setting;
60
61 /**
62 * The WPMUDEV object.
63 *
64 * @var WPMUDEV
65 */
66 private $wpmudev;
67
68 /**
69 * The client object for AntiBot Global Firewall.
70 *
71 * @var Antibot_Global_Firewall_Client
72 */
73 private $antibot_client;
74
75 /**
76 * Lock file name for firewall updates.
77 *
78 * @var string
79 */
80 protected string $lock_filename = 'antibot_global_firewall.lock';
81
82 /**
83 * Initializes the class with the Antibot_Global_Firewall_Model, Antibot_Global_Firewall_Setting, WPMUDEV and Antibot_Global_Firewall_Client instances.
84 *
85 * @param Antibot_Global_Firewall_Model $model The AntiBot Global Firewall model.
86 * @param Antibot_Global_Firewall_Setting $model_setting The AntiBot Global FIrewall setting model.
87 * @param WPMUDEV $wpmudev The WPMUDEV object.
88 * @param Antibot_Global_Firewall_Client $antibot_client The client object for AntiBot Global Firewall API.
89 */
90 public function __construct( Antibot_Global_Firewall_Model $model, Antibot_Global_Firewall_Setting $model_setting, WPMUDEV $wpmudev, Antibot_Global_Firewall_Client $antibot_client ) {
91 $this->model = $model;
92 $this->model_setting = $model_setting;
93 $this->wpmudev = $wpmudev;
94 $this->antibot_client = $antibot_client;
95
96 add_action( 'wpdef_confirm_antibot_toggle_on_hosting', array( $this, 'confirm_toggle_on_hosting' ) );
97 add_action( 'wp_loaded', array( $this, 'clear_antibot_on_disconnection' ) );
98 }
99
100 /**
101 * Check if AntiBot Global Firewall feature is enabled.
102 *
103 * @return bool True for enabled or false for disabled.
104 */
105 public function is_enabled(): bool {
106 $is_enabled = $this->model_setting->enabled;
107
108 /**
109 * Filter to enable or disable the AntiBot Global Firewall.
110 *
111 * @param bool $is_enabled True for enabled or false for disabled.
112 */
113 return (bool) apply_filters( 'wpdef_antibot_enabled', $is_enabled );
114 }
115
116 /**
117 * Check if the AntiBot Global Firewall is enabled on WPMU DEV.
118 *
119 * @return bool True if the AntiBot Global Firewall is enabled, false otherwise.
120 */
121 public function hosting_is_enabled(): bool {
122 return true === defender_get_hosting_feature_state( 'antibot' );
123 }
124
125 /**
126 * Check if the AntiBot Global Firewall is enabled based on managed by.
127 *
128 * @return bool True if the AntiBot Global Firewall is enabled, false otherwise.
129 */
130 public function frontend_is_enabled(): bool {
131 return 'plugin' === $this->get_managed_by() ?
132 ! $this->is_expired_membership_type() && $this->is_enabled() :
133 $this->hosting_is_enabled();
134 }
135
136 /**
137 * Check if the AntiBot Global Firewall is enabled and the site is connected to the HUB.
138 *
139 * @return bool True if the AntiBot Global Firewall is active, false otherwise.
140 */
141 public function is_active(): bool {
142 return $this->frontend_is_enabled() && $this->is_site_connected_to_hub_via_hcm_or_dash();
143 }
144
145 /**
146 * Check if the AntiBot Global Firewall is active via plugin.
147 *
148 * @since 5.1.1
149 * @return bool True if the AntiBot Global Firewall is active via plugin, false otherwise.
150 */
151 public function is_active_via_plugin(): bool {
152 return 'plugin' === $this->get_managed_by() && $this->is_enabled() && $this->is_site_connected_to_hub_via_hcm_or_dash();
153 }
154
155 /**
156 * Check if the AntiBot Global Firewall is active via hosting.
157 *
158 * @since 5.6.0
159 * @return bool True if the AntiBot Global Firewall is active via hosting, false otherwise.
160 */
161 public function is_active_via_hosting(): bool {
162 return 'hosting' === $this->get_managed_by() && $this->hosting_is_enabled();
163 }
164
165 /**
166 * Check if the IP is blocked.
167 *
168 * @param string $ip The IP address.
169 *
170 * @return bool True if the IP is blocked, false otherwise.
171 */
172 public function is_ip_blocked( string $ip ): bool {
173 $result = $this->model->get_by_ip( $ip );
174
175 return isset( $result->unlocked ) && 1 !== $result->unlocked;
176 }
177
178 /**
179 * Delete complete blocklist.
180 */
181 public function delete_blocklist() {
182 $this->model->truncate();
183
184 delete_site_option( self::LAST_SYNC_OPTION );
185 }
186
187 /**
188 * Check if the Unlock Me Captcha should be displayed.
189 *
190 * @param array $ips The IP addresses.
191 *
192 * @return bool True if the IP is blocked by AntiBot Global Firewall, false otherwise.
193 */
194 public function is_displayed( array $ips ): bool {
195 if ( ! $this->is_enabled() ) {
196 return false;
197 }
198
199 foreach ( $ips as $ip ) {
200 if ( $this->is_ip_blocked( $ip ) ) {
201 return true;
202 }
203 }
204
205 return false;
206 }
207
208 /**
209 * Get the Unlock Me Captcha button text.
210 *
211 * @return string The button text.
212 */
213 public static function get_button_text(): string {
214 return esc_html__( 'AntiBot Unlock Me', 'defender-security' );
215 }
216
217 /**
218 * Handle the download and store blocklist.
219 *
220 * @since 4.8.0
221 * @return void
222 */
223 public function download_and_store_blocklist(): void {
224 if ( ! $this->is_site_connected_to_hub_via_hcm_or_dash() ) {
225 return;
226 }
227
228 if ( $this->has_lock() ) {
229 $this->log( 'Fallback as already a process is running', Firewall::FIREWALL_LOG );
230 return;
231 }
232
233 $this->create_lock();
234 $file_path = $this->download_blocklist();
235
236 if ( ! empty( $file_path ) ) {
237 $this->store_blocklist( $file_path );
238 }
239 $this->remove_lock();
240 }
241
242 /**
243 * Download blocklist.
244 *
245 * @since 4.8.0
246 * @return string|void
247 */
248 private function download_blocklist() {
249 $response = $this->antibot_client->get_blocklist_download( $this->model_setting->mode );
250
251 if ( is_wp_error( $response ) ) {
252 $this->log( sprintf( 'AntiBot Global Firewall Error: %s', $response->get_error_message() ), Firewall::FIREWALL_LOG );
253 return;
254 } elseif ( isset( $response['status'] ) && 'error' === $response['status'] ) {
255 $this->log( sprintf( 'AntiBot Global Firewall Error: %s', $response['message'] ), Firewall::FIREWALL_LOG );
256 return;
257 } elseif ( empty( $response['data']['download_url'] ) && empty( $response['data']['hashes']['sha256'] ) ) {
258 $this->log( 'AntiBot Global Firewall Error: Download link not found in the response.', Firewall::FIREWALL_LOG );
259 return;
260 }
261
262 $file_url = $response['data']['download_url'];
263
264 if ( ! function_exists( 'download_url' ) ) {
265 require_once ABSPATH . 'wp-admin/includes/file.php';
266 }
267
268 $tmp_file = download_url( $file_url );
269
270 if ( is_wp_error( $tmp_file ) ) {
271 $this->log( 'AntiBot Global Firewall Error: Unable to download file', Firewall::FIREWALL_LOG );
272 return;
273 }
274
275 $expected_file_hash = '';
276 $response = wp_remote_get( $response['data']['hashes']['sha256'] );
277 if ( wp_remote_retrieve_response_code( $response ) === 200 ) {
278 $expected_file_hash = trim( wp_remote_retrieve_body( $response ) );
279 }
280
281 $file_hash = hash_file( 'sha256', $tmp_file );
282 if ( $file_hash !== $expected_file_hash ) {
283 $this->log( 'AntiBot Global Firewall Error: File is not legit. Hash does not match.', Firewall::FIREWALL_LOG );
284 return;
285 }
286
287 $this->log( 'AntiBot Global Firewall: Downloaded ' . $this->model_setting->get_mode_label() . ' blocklist successfully.', Firewall::FIREWALL_LOG );
288
289 return $tmp_file;
290 }
291
292 /**
293 * Store blocklist.
294 *
295 * @param string $file_path File path.
296 *
297 * @since 4.8.0
298 * @return void
299 */
300 private function store_blocklist( string $file_path ): void {
301 try {
302 $generator = $this->read_file_generator( $file_path );
303
304 $this->delete_blocklist();
305 $this->model->bulk_insert( $generator );
306
307 self::set_last_sync();
308
309 $this->log( 'AntiBot Global Firewall: IPs stored in the DB table.', Firewall::FIREWALL_LOG );
310 } catch ( Exception $e ) {
311 $this->log( 'AntiBot Global Firewall Error: ' . $e->getMessage(), Firewall::FIREWALL_LOG );
312 }
313
314 // Remove temporary file.
315 wp_delete_file( $file_path );
316 }
317
318 /**
319 * Read file generator.
320 *
321 * @param string $file_path File path.
322 *
323 * @return Generator
324 * @throws Exception If file not found or could not open the file.
325 */
326 private function read_file_generator( $file_path ) {
327 if ( ! file_exists( $file_path ) ) {
328 throw new Exception( 'File not found.' );
329 }
330
331 global $wp_filesystem;
332 $lines = $wp_filesystem->get_contents_array( $file_path );
333
334 if ( empty( $lines ) || ! is_array( $lines ) ) {
335 throw new Exception( 'Could not retrieve the file contents!' );
336 }
337
338 try {
339 foreach ( $lines as $line ) {
340 yield trim( $line );
341 }
342 } catch ( Exception $exception ) {
343 throw $exception;
344 }
345 }
346
347 /**
348 * Get the blocklisted IP count.
349 *
350 * @return string The blocklisted IP count.
351 */
352 public function get_blocklisted_ip_count(): string {
353 // Check if the feature is enabled.
354 if ( ! $this->frontend_is_enabled() ) {
355 return '0';
356 }
357 // Since from v5.0.2 one method is used for counting.
358 return number_format( $this->get_cached_blocklisted_ips() );
359 }
360
361 /**
362 * Get the last sync timestamp.
363 *
364 * @return false|int The last sync timestamp.
365 */
366 public static function get_last_sync() {
367 return get_site_option( self::LAST_SYNC_OPTION, false );
368 }
369
370 /**
371 * Set the last sync timestamp.
372 */
373 public static function set_last_sync(): void {
374 update_site_option( self::LAST_SYNC_OPTION, time() );
375 }
376
377 /**
378 * Check if the IPs should be downloaded from Antibot service.
379 *
380 * @return bool True if the IPs should be downloaded, false otherwise.
381 */
382 public function maybe_download(): bool {
383 if ( $this->wpmudev->is_wpmu_hosting() && 'plugin' !== $this->get_managed_by() ) {
384 return false;
385 }
386
387 $last_sync = self::get_last_sync();
388
389 if ( false === $last_sync ) {
390 return true;
391 }
392
393 return time() - $last_sync >= 12 * HOUR_IN_SECONDS;
394 }
395
396 /**
397 * Get the default managed by.
398 *
399 * @return string The default managed by.
400 */
401 public function get_default_managed_by(): string {
402 return $this->wpmudev->is_wpmu_hosting() ? 'hosting' : 'plugin';
403 }
404
405 /**
406 * Get the AntiBot managed by.
407 *
408 * @return string The AntiBot managed by.
409 */
410 public function get_managed_by(): string {
411 $managed_by = $this->model_setting->managed_by;
412
413 return '' !== $managed_by ? $managed_by : $this->get_default_managed_by();
414 }
415
416 /**
417 * Get the AntiBot managed by label.
418 *
419 * @return string The AntiBot managed by label.
420 */
421 public function get_managed_by_label(): string {
422 return 'plugin' === $this->get_managed_by() ?
423 esc_html__( 'Defender Plugin', 'defender-security' ) :
424 esc_html__( 'WPMU DEV Hosting', 'defender-security' );
425 }
426
427 /**
428 * Set the AntiBot managed by.
429 *
430 * @param string $managed_by The managed by value.
431 *
432 * @return bool True if the managed by is set, false otherwise.
433 */
434 public function set_managed_by( string $managed_by ): bool {
435 if ( in_array( $managed_by, Antibot_Global_Firewall_Setting::MANAGED_BY_ALLOWED, true ) ) {
436 $this->model_setting->managed_by = $managed_by;
437 $this->model_setting->save();
438
439 return true;
440 }
441
442 return false;
443 }
444
445 /**
446 * Switch the AntiBot managed by.
447 *
448 * @return string|false The managed by value if it's switched, false otherwise.
449 */
450 public function switch_managed_by() {
451 $managed_by = 'plugin' === $this->get_managed_by() ? 'hosting' : 'plugin';
452
453 if ( $this->set_managed_by( $managed_by ) ) {
454 if ( 'plugin' === $managed_by ) {
455 return false === $this->managed_by_plugin_action() ? false : $managed_by;
456 } else {
457 return false === $this->managed_by_hosting_action() ? false : $managed_by;
458 }
459 }
460
461 return false;
462 }
463
464 /**
465 * Action when the AntiBot Global Firewall is enabled/disabled by plugin.
466 *
467 * @param bool $enabled True to enable, false to disable.
468 *
469 * @return bool True if the enabled/disabled action is successful, false otherwise.
470 */
471 public function managed_by_plugin_action( bool $enabled = true ): bool {
472 if ( $enabled ) {
473 set_site_transient( self::IS_SWITCHING_TO_PLUGIN_IN_PROGRESS, true, 30 );
474
475 // Disable AntiBot on hosting if it's currently enabled.
476 $this->toggle_on_hosting( false, 0.1 );
477
478 // Schedule a single event to confirm AntiBot is disabled on hosting after 15 seconds.
479 if ( ! wp_next_scheduled( 'wpdef_confirm_antibot_toggle_on_hosting' ) ) {
480 wp_schedule_single_event( time() + 15, 'wpdef_confirm_antibot_toggle_on_hosting', array( false ) );
481 }
482
483 // Enable AntiBot on plugin side.
484 $this->model_setting->enabled = true;
485 $this->model_setting->save();
486
487 // Download IPs.
488 $this->download_and_store_blocklist();
489 } else {
490 // Disable AntiBot on plugin side.
491 $this->model_setting->enabled = false;
492 $this->model_setting->save();
493
494 // Remove IPs.
495 $this->delete_blocklist();
496 }
497
498 return true;
499 }
500
501 /**
502 * Action when the AntiBot Global Firewall is enabled/disabled by hosting.
503 *
504 * @param bool $enabled True to enable, false to disable.
505 * @return bool True if the enabled/disabled action is successful, false otherwise.
506 */
507 public function managed_by_hosting_action( bool $enabled = true ): bool {
508 if ( $enabled ) {
509 wp_clear_scheduled_hook( 'wpdef_confirm_antibot_toggle_on_hosting' );
510 delete_site_transient( self::IS_SWITCHING_TO_PLUGIN_IN_PROGRESS );
511
512 // Disable AntiBot on plugin side.
513 $this->model_setting->enabled = false;
514 $this->model_setting->save();
515
516 // Remove IPs from DB table.
517 $this->delete_blocklist();
518
519 // Enable AntiBot on hosting if it's currently disabled.
520 $result = $this->toggle_on_hosting( true );
521 if ( is_wp_error( $result ) ) {
522 return false;
523 }
524 } else {
525 $result = $this->toggle_on_hosting( false );
526 if ( is_wp_error( $result ) ) {
527 return false;
528 }
529 }
530
531 return true;
532 }
533
534 /**
535 * Toggle the AntiBot Global Firewall on hosting.
536 *
537 * @param bool $enable True to enable, false to disable.
538 * @param float $timeout The timeout for the request.
539 *
540 * @return bool|WP_Error True if the AntiBot Global Firewall is toggled, false if not, WP_Error otherwise.
541 */
542 public function toggle_on_hosting( bool $enable, float $timeout = 30 ) {
543 if ( ! $this->wpmudev->is_wpmu_hosting() ) {
544 return false;
545 }
546
547 $hosting_enable = defender_get_hosting_feature_state( 'antibot' );
548
549 if (
550 ( $enable && ! $hosting_enable ) ||
551 ( ! $enable && $hosting_enable )
552 ) {
553 $this->attach_behavior( WPMUDEV::class, WPMUDEV::class );
554
555 $body = array( 'is_active' => $enable );
556 if ( $enable ) {
557 $body['mode'] = $this->get_hosting_mode();
558 }
559
560 $data = $this->make_wpmu_request(
561 WPMUDEV::API_ANTIBOT_GLOBAL_FIREWALL,
562 $body,
563 array(
564 'method' => 'PUT',
565 'timeout' => $timeout,
566 )
567 );
568
569 if ( $timeout > 1 && is_wp_error( $data ) ) {
570 $this->log( 'AntiBot Global Firewall Error: ' . $data->get_error_message(), Firewall::FIREWALL_LOG );
571 return $data;
572 }
573 }
574
575 return true;
576 }
577
578 /**
579 * Fetches the number of blocklisted IPs from cache or from the Blocklist API.
580 *
581 * If the value is not cached, it will fetch the number of blocklisted IPs from the Blocklist API and cache it.
582 *
583 * @return int The number of blocklisted IPs.
584 */
585 public function get_cached_blocklisted_ips(): int {
586 $mode = $this->frontend_mode();
587 $stats_key = self::BLOCKLIST_STATS_KEY . '_' . $mode;
588 $cached_data = get_site_transient( $stats_key );
589 if ( false !== $cached_data ) {
590 return (int) $cached_data;
591 }
592
593 $blocklist_stats = $this->antibot_client->get_blocklist_stats();
594
595 if ( is_wp_error( $blocklist_stats ) ) {
596 $this->log( 'AntiBot Global Firewall Error: ' . $blocklist_stats->get_error_message(), Firewall::FIREWALL_LOG );
597 return 0;
598 }
599
600 $blocklisted_ips_key = Antibot_Global_Firewall_Setting::MODE_BASIC === $mode ? 'blocked_ips' : 'strict_blocked_ips';
601 if ( empty( $blocklist_stats[ $blocklisted_ips_key ] ) ) {
602 $this->log( 'AntiBot Global Firewall Error: Stats missing for mode: ' . $mode, Firewall::FIREWALL_LOG );
603 return 0;
604 }
605
606 $blocklisted_ips = $blocklist_stats[ $blocklisted_ips_key ];
607 set_site_transient( $stats_key, $blocklisted_ips, 12 * HOUR_IN_SECONDS );
608
609 return $blocklisted_ips;
610 }
611
612 /**
613 * Confirm the toggle on hosting.
614 *
615 * @param bool $enable True to enable, false to disable.
616 *
617 * @return void
618 */
619 public function confirm_toggle_on_hosting( bool $enable ): void {
620 $this->toggle_on_hosting( $enable );
621 delete_site_transient( self::IS_SWITCHING_TO_PLUGIN_IN_PROGRESS );
622 }
623
624 /**
625 * Logs IP-related messages if logging is enabled via filter.
626 *
627 * @param string $message The message to be logged.
628 */
629 public function log_ip_message( string $message ): void {
630 /**
631 * Filters whether IP logging is enabled.
632 *
633 * This filter allows developers to enable or disable IP logging globally.
634 * Returning false will prevent the log message from being written.
635 *
636 * @param bool $is_enabled Whether IP logging is enabled. Default true.
637 * @since 5.1.0
638 */
639 $is_logging_enabled = (bool) apply_filters( 'wpdef_antibot_global_firewall_ip_log', true );
640
641 if ( ! $is_logging_enabled ) {
642 return;
643 }
644
645 $this->log( $message, self::LOG_FILE_NAME );
646 }
647
648 /**
649 * Get the AntiBot mode status in DB.
650 *
651 * @return string The AntiBot mode.
652 */
653 public function get_mode(): string {
654 return $this->model_setting->mode;
655 }
656
657 /**
658 * Get the AntiBot mode on WPMU DEV.
659 *
660 * @return string The AntiBot mode label.
661 */
662 public function get_hosting_mode(): string {
663 $mode = defender_get_hosting_feature_state( 'antibot_mode' );
664
665 return '' !== $mode ? $mode : Antibot_Global_Firewall_Setting::MODE_BASIC;
666 }
667
668 /**
669 * Get AntiBot mode based on managed by.
670 *
671 * @return string The AntiBot mode.
672 */
673 public function frontend_mode(): string {
674 return 'plugin' === $this->get_managed_by() ?
675 $this->get_mode() :
676 $this->get_hosting_mode();
677 }
678
679 /**
680 * Switch the AntiBot mode.
681 *
682 * @return string|false|WP_Error The AntiBot mode value if it's switched, false otherwise.
683 */
684 public function switch_mode() {
685 if ( 'plugin' === $this->get_managed_by() ) {
686 $mode = $this->get_mode();
687
688 $this->model_setting->mode = Antibot_Global_Firewall_Setting::MODE_STRICT === $mode
689 ? Antibot_Global_Firewall_Setting::MODE_BASIC
690 : Antibot_Global_Firewall_Setting::MODE_STRICT;
691 $this->model_setting->save();
692
693 $this->download_and_store_blocklist();
694
695 delete_site_transient( self::BLOCKLIST_STATS_KEY . '_' . $mode );
696 } else {
697 if ( ! $this->wpmudev->is_wpmu_hosting() ) {
698 return false;
699 }
700
701 $mode = $this->get_hosting_mode();
702
703 $this->attach_behavior( WPMUDEV::class, WPMUDEV::class );
704 $data = $this->make_wpmu_request(
705 WPMUDEV::API_ANTIBOT_GLOBAL_FIREWALL,
706 array(
707 'is_active' => $this->hosting_is_enabled(),
708 'mode' => Antibot_Global_Firewall_Setting::MODE_STRICT === $mode
709 ? Antibot_Global_Firewall_Setting::MODE_BASIC
710 : Antibot_Global_Firewall_Setting::MODE_STRICT,
711 ),
712 array( 'method' => 'PUT' )
713 );
714
715 if ( is_wp_error( $data ) ) {
716 $this->log( 'AntiBot Global Firewall Error: ' . $data->get_error_message(), Firewall::FIREWALL_LOG );
717 return $data;
718 }
719
720 delete_site_transient( self::BLOCKLIST_STATS_KEY . '_' . $mode );
721 }
722
723 return $this->frontend_mode();
724 }
725
726 /**
727 * Clear antibot table when site is disconnected from HUB.
728 *
729 * @return void
730 */
731 public function clear_antibot_on_disconnection(): void {
732 if ( $this->is_site_connected_to_hub_via_hcm_or_dash() ) {
733 return;
734 }
735 if ( $this->get_cached_blocklisted_ips() <= 0 ) {
736 return;
737 }
738 $this->delete_blocklist();
739 delete_site_transient( self::BLOCKLIST_STATS_KEY . '_' . $this->get_mode() );
740 delete_site_transient( self::BLOCKLIST_STATS_KEY . '_' . $this->get_hosting_mode() );
741 $this->log( 'Antibot table cleared due to site disconnection.', self::LOG_FILE_NAME );
742 }
743
744 /**
745 * Get the timestamp for when the notice should start showing.
746 *
747 * @return false|int The timestamp or false if not set.
748 */
749 public function get_global_notice_time() {
750 return (int) get_site_option( self::GLOBAL_NOTICE_TIME_OPTION, 0 );
751 }
752
753 /**
754 * Set a timestamp for when the notice should start showing.
755 *
756 * @return void
757 */
758 public function maybe_set_notice_time(): void {
759 if ( 0 === $this->get_global_notice_time() ) {
760 $future = time() + ( DAY_IN_SECONDS * self::GLOBAL_NOTICE_DELAY_DAYS_OPTION );
761 update_site_option( self::GLOBAL_NOTICE_TIME_OPTION, $future );
762 }
763 }
764
765 /**
766 * Disable the global notice.
767 *
768 * @return void
769 */
770 public function dismiss_global_notice(): void {
771 update_site_option( self::GLOBAL_NOTICE_TIME_OPTION, -1 );
772 }
773
774 /**
775 * Determine if the global notice should be shown.
776 *
777 * @return bool True if the notice should be shown, false otherwise.
778 */
779 public function should_show_global_notice(): bool {
780 // Is FREE or PRO plugin?
781 if ( $this->wpmudev->is_pro() ) {
782 return false;
783 }
784
785 // Is AntiBot enabled?
786 if ( $this->frontend_is_enabled() ) {
787 return false;
788 }
789
790 // Has 60 days passed since installation?
791 $notice_time = $this->get_global_notice_time();
792
793 // No notice time or marked as dismissed (-1) or not yet reached the time.
794 if ( false === $notice_time || -1 === $notice_time || time() < $notice_time ) {
795 return false;
796 }
797
798 return true;
799 }
800 }
801