'site.disconnected', 'site_uuid' => $sentinelo_site_uuid, 'timestamp' => time(), 'data' => ['url' => site_url()], ]); $sentinelo_signature = hash_hmac('sha256', $sentinelo_payload, $sentinelo_webhook_secret); wp_remote_post( trailingslashit($sentinelo_saas_url) . 'api/webhooks/site.disconnected', [ 'headers' => [ 'Content-Type' => 'application/json', 'X-Sentinelo-Site-UUID' => $sentinelo_site_uuid, 'X-Sentinelo-Event' => 'site.disconnected', 'X-Sentinelo-Timestamp' => time(), 'X-Sentinelo-Signature' => 'sha256=' . $sentinelo_signature, ], 'body' => $sentinelo_payload, 'timeout' => 10, 'blocking' => false, ] ); } // Drop custom tables $sentinelo_prefix = $wpdb->prefix . 'SENTINELO_'; $sentinelo_tables = [ $sentinelo_prefix . 'backups', $sentinelo_prefix . 'security_scans', $sentinelo_prefix . 'webhook_queue', $sentinelo_prefix . 'restores', $sentinelo_prefix . 'audits', ]; foreach ($sentinelo_tables as $sentinelo_table) { $wpdb->query($wpdb->prepare('DROP TABLE IF EXISTS %i', $sentinelo_table)); } // Delete all Sentinelo options AND their transients. A bare LIKE 'SENTINELO_%' // misses transients, which WordPress stores under the _transient_ / // _transient_timeout_ (and site-transient) prefixes — they would leak otherwise. $wpdb->query($wpdb->prepare( "DELETE FROM %i WHERE option_name LIKE %s" . " OR option_name LIKE %s OR option_name LIKE %s" . " OR option_name LIKE %s OR option_name LIKE %s", $wpdb->options, $wpdb->esc_like('SENTINELO_') . '%', $wpdb->esc_like('_transient_SENTINELO_') . '%', $wpdb->esc_like('_transient_timeout_SENTINELO_') . '%', $wpdb->esc_like('_site_transient_SENTINELO_') . '%', $wpdb->esc_like('_site_transient_timeout_SENTINELO_') . '%' )); // Remove everything the agent wrote, in either edition's layout: logs, backup work // files (an archive kept on the server after a storage refusal carries the full // database dump), rollback snapshots. Only the logs used to go, and the rest stayed // on the client's disk for good. foreach (sentinelo_storage_roots() as $sentinelo_root) { if (!is_dir($sentinelo_root) || is_link($sentinelo_root)) { continue; } $sentinelo_files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($sentinelo_root, RecursiveDirectoryIterator::SKIP_DOTS), RecursiveIteratorIterator::CHILD_FIRST ); foreach ($sentinelo_files as $sentinelo_file) { if ($sentinelo_file->isDir() && !$sentinelo_file->isLink()) { rmdir($sentinelo_file->getPathname()); } else { unlink($sentinelo_file->getPathname()); } } rmdir($sentinelo_root); }