PluginProbe
Vigilant – 100% Free Security Suite: Firewall, 2FA, Login, Headers, Scanner… / 2.11.4
Vigilant – 100% Free Security Suite: Firewall, 2FA, Login, Headers, Scanner… v2.11.4
2.11.11 2.11.10 2.11.9 2.11.7 2.11.8 2.11.6 2.11.5 2.11.4 2.11.3 2.11.1 2.11.2 2.11.0 2.10.5 2.10.4 2.10.3 2.10.2 2.10.1 2.10.0 2.9.9 2.9.8 2.9.6 2.9.7 2.9.5 2.9.4 2.9.3 All 86 releases
vigilante / includes / class-deactivator.php

class-deactivator.php in Vigilant – 100% Free Security Suite: Firewall, 2FA, Login, Headers, Scanner… 2.11.4, at includes/class-deactivator.php

311 lines 11.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Deactivator Class
4 *
5 * Handles plugin deactivation tasks
6 *
7 * @package Vigilante
8 */
9
10 // Prevent direct access
11 if ( ! defined( 'ABSPATH' ) ) {
12 exit;
13 }
14
15 /**
16 * Class Vigilante_Deactivator
17 *
18 * Fired during plugin deactivation
19 */
20 class Vigilante_Deactivator {
21
22 /**
23 * Run deactivation tasks
24 */
25 public static function deactivate() {
26 // ALWAYS remove htaccess rules using the centralized manager
27 self::remove_htaccess_rules();
28
29 // ALWAYS remove wp-config security constants and restore originals
30 self::remove_wpconfig_security();
31
32 // Clear scheduled events
33 self::clear_scheduled_events();
34
35 // Send deactivation email
36 self::send_deactivation_email();
37
38 // Clear transients
39 delete_transient( 'vigilante_activated' );
40 delete_transient( 'vigilante_restore_on_deactivate' );
41 delete_transient( 'vigilante_backup_error' );
42
43 // Audit Alerts: clear every engine transient (per-category counters and
44 // cooldowns, plus the immediate anti-duplicate keys). Names are dynamic
45 // (one per category, md5 per event), so a prefix sweep is the only way.
46 global $wpdb;
47 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- One-off cleanup on deactivate; dynamic transient names cannot be enumerated individually.
48 $wpdb->query( "DELETE FROM {$wpdb->options} WHERE option_name LIKE '\\_transient\\_vigilante\\_aa\\_%' OR option_name LIKE '\\_transient\\_timeout\\_vigilante\\_aa\\_%'" );
49
50 // Flush rewrite rules
51 flush_rewrite_rules();
52 }
53
54 /**
55 * Remove htaccess rules using the centralized manager
56 */
57 private static function remove_htaccess_rules() {
58 // Use the centralized manager
59 require_once VIGILANTE_INCLUDES_DIR . 'class-htaccess-manager.php';
60
61 $manager = Vigilante_Htaccess_Manager::get_instance();
62
63 // Remove our blocks
64 $manager->remove_block( '# BEGIN Vigilante Protection', '# END Vigilante Protection' );
65 $manager->remove_block( '# BEGIN Vigilante Security Headers', '# END Vigilante Security Headers' );
66 }
67
68 /**
69 * Remove wp-config security constants and restore original values
70 */
71 private static function remove_wpconfig_security() {
72 $wpconfig_path = ABSPATH . 'wp-config.php';
73
74 if ( ! file_exists( $wpconfig_path ) ) {
75 return;
76 }
77
78 // Initialize WP_Filesystem
79 global $wp_filesystem;
80 if ( ! function_exists( 'WP_Filesystem' ) ) {
81 require_once ABSPATH . 'wp-admin/includes/file.php';
82 }
83 WP_Filesystem();
84
85 if ( ! $wp_filesystem || ! $wp_filesystem->is_writable( $wpconfig_path ) ) {
86 return;
87 }
88
89 $content = $wp_filesystem->get_contents( $wpconfig_path );
90
91 if ( false === $content || empty( $content ) ) {
92 return;
93 }
94
95 $modified = false;
96
97 // Step 1: Remove our Vigilante block
98 if ( strpos( $content, '/* BEGIN Vigilante Security Constants */' ) !== false ) {
99 $lines = explode( "\n", $content );
100 $new_lines = array();
101 $inside_block = false;
102
103 foreach ( $lines as $line ) {
104 if ( strpos( $line, '/* BEGIN Vigilante Security Constants */' ) !== false ) {
105 $inside_block = true;
106 continue;
107 }
108
109 if ( strpos( $line, '/* END Vigilante Security Constants */' ) !== false ) {
110 $inside_block = false;
111 continue;
112 }
113
114 if ( ! $inside_block ) {
115 $new_lines[] = $line;
116 }
117 }
118
119 $content = implode( "\n", $new_lines );
120 $modified = true;
121 }
122
123 // Step 2: Uncomment original constants (restore [VIGILANTE_ORIGINAL] lines)
124 $original_marker = '// [VIGILANTE_ORIGINAL] ';
125 if ( strpos( $content, $original_marker ) !== false ) {
126 $pattern = '/^(\s*)' . preg_quote( $original_marker, '/' ) . '(.+)$/m';
127 $content = preg_replace( $pattern, '$1$2', $content );
128 $modified = true;
129 }
130
131 if ( ! $modified ) {
132 return;
133 }
134
135 // Clean up multiple empty lines
136 $content = preg_replace( '/\n{3,}/', "\n\n", $content );
137
138 // Safety check: must still have basic wp-config content
139 if ( strpos( $content, 'DB_NAME' ) === false ) {
140 return; // Don't write if it would corrupt wp-config
141 }
142
143 $wp_filesystem->put_contents( $wpconfig_path, $content, FS_CHMOD_FILE );
144 }
145
146 /**
147 * Clear all scheduled cron events
148 */
149 private static function clear_scheduled_events() {
150 $events = array(
151 'vigilante_daily_maintenance',
152 'vigilante_hourly_checks',
153 'vigilante_file_integrity_scan',
154 'vigilante_password_expiry_reminder',
155 'vigilante_analyzer_weekly_scan',
156 'vigilante_plugin_status_check',
157 );
158
159 foreach ( $events as $event ) {
160 $timestamp = wp_next_scheduled( $event );
161 if ( $timestamp ) {
162 wp_unschedule_event( $timestamp, $event );
163 }
164 }
165
166 // Clear all events with our prefix
167 wp_unschedule_hook( 'vigilante_daily_maintenance' );
168 wp_unschedule_hook( 'vigilante_hourly_checks' );
169 wp_unschedule_hook( 'vigilante_file_integrity_scan' );
170 wp_unschedule_hook( 'vigilante_password_expiry_reminder' );
171 wp_unschedule_hook( 'vigilante_analyzer_weekly_scan' );
172 wp_unschedule_hook( 'vigilante_plugin_status_check' );
173 // Post-update verification single events (scheduled with per-update args).
174 wp_unschedule_hook( 'vigilante_fi_postupdate_verify' );
175 }
176
177 /**
178 * Send deactivation notification email
179 */
180 private static function send_deactivation_email() {
181 $settings = new Vigilante_Settings();
182 $email_settings = $settings->get_section( 'email' );
183
184 if ( empty( $email_settings['send_deactivation_email'] ) ) {
185 return;
186 }
187
188 if ( ! class_exists( 'Vigilante_Email_Template' ) ) {
189 require_once VIGILANTE_INCLUDES_DIR . 'class-email-template.php';
190 }
191
192 $to = Vigilante_Email_Template::get_admin_recipients();
193
194 $site_name = get_bloginfo( 'name' );
195 $site_url = get_site_url();
196
197 // Get current user info
198 $current_user = wp_get_current_user();
199 $user_info = $current_user->ID > 0
200 ? $current_user->user_login . ' (' . $current_user->user_email . ')'
201 : __( 'Unknown', 'vigilante' );
202
203 $subject = sprintf(
204 /* translators: %s: Site name */
205 __( '[%s] Vigilant Deactivated', 'vigilante' ),
206 $site_name
207 );
208
209 $body = Vigilante_Email_Template::warning_box( __( 'Security protection has been disabled. Please ensure you have alternative security measures in place.', 'vigilante' ) );
210 $body .= Vigilante_Email_Template::data_table( array(
211 __( 'Site', 'vigilante' ) => $site_name,
212 __( 'URL', 'vigilante' ) => $site_url,
213 __( 'Date', 'vigilante' ) => wp_date( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ) ),
214 __( 'Deactivated by', 'vigilante' ) => $user_info,
215 ) );
216
217 Vigilante_Email_Template::send( $to, $subject, __( 'Plugin deactivated', 'vigilante' ), $body );
218 }
219
220 /**
221 * Full uninstall - removes all data
222 * Called from uninstall.php
223 */
224 public static function uninstall() {
225 // Drop database tables
226 $database = new Vigilante_Database();
227 $database->drop_tables();
228
229 // Remove all options
230 delete_option( 'vigilante_options' );
231 delete_option( 'vigilante_db_version' );
232 delete_option( 'vigilante_purge_2_11_0_done' );
233 delete_option( 'vigilante_baseline_redaction' );
234 if ( is_multisite() ) {
235 // The network one was in uninstall.php and missing here, so after
236 // deleting the data and reactivating, the one-off sweep never ran
237 // again and the per-site copies stayed where they were.
238 delete_site_option( 'vigilante_baseline_sweep' );
239 }
240 delete_option( 'vigilante_activated_time' );
241 delete_option( 'vigilante_dismissed_notices' );
242 delete_option( 'vigilante_backup_timestamp' );
243 delete_option( 'vigilante_last_integrity_results' );
244 delete_option( 'vigilante_last_integrity_scan' );
245 delete_option( 'vigilante_critical_files_baseline' );
246 if ( is_multisite() ) {
247 delete_site_option( 'vigilante_critical_files_baseline' );
248 }
249 delete_option( 'vigilante_analyzer_last_scan' );
250 delete_option( 'vigilante_analyzer_history' );
251 delete_option( 'vigilante_analyzer_fix_log' );
252
253 // Remove transients
254 delete_transient( 'vigilante_activated' );
255 delete_transient( 'vigilante_restore_on_deactivate' );
256 delete_transient( 'vigilante_backup_error' );
257 delete_transient( 'vigilante_file_integrity_last_scan' );
258
259 // Audit Alerts: remove every engine transient (counters, cooldowns and
260 // the immediate anti-duplicate keys), including their timeout twins.
261 // Names are dynamic (md5 per event), so a prefix sweep is the only way.
262 global $wpdb;
263 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching -- One-off uninstall cleanup; dynamic transient names cannot be enumerated individually.
264 $wpdb->query( "DELETE FROM {$wpdb->options} WHERE option_name LIKE '\\_transient\\_vigilante\\_aa\\_%' OR option_name LIKE '\\_transient\\_timeout\\_vigilante\\_aa\\_%'" );
265
266 // Remove backup directory
267 self::remove_backup_directory();
268
269 // Clear scheduled events
270 self::clear_scheduled_events();
271
272 // Remove htaccess rules
273 self::remove_htaccess_rules();
274
275 // Remove wp-config security constants and restore originals
276 self::remove_wpconfig_security();
277 }
278
279 /**
280 * Remove backup directory and its contents
281 */
282 private static function remove_backup_directory() {
283 $backup_dirs = array(
284 WP_CONTENT_DIR . '/vigilante-backups',
285 );
286
287 if ( defined( 'VIGILANTE_BACKUP_DIR' ) ) {
288 $backup_dirs[] = VIGILANTE_BACKUP_DIR;
289 }
290
291 // Initialize WP_Filesystem
292 global $wp_filesystem;
293 if ( ! function_exists( 'WP_Filesystem' ) ) {
294 require_once ABSPATH . 'wp-admin/includes/file.php';
295 }
296 WP_Filesystem();
297
298 if ( ! $wp_filesystem ) {
299 return;
300 }
301
302 foreach ( $backup_dirs as $backup_dir ) {
303 if ( ! $wp_filesystem->is_dir( $backup_dir ) ) {
304 continue;
305 }
306
307 // Remove directory and contents recursively
308 $wp_filesystem->delete( $backup_dir, true );
309 }
310 }
311 }