<?php
/*
Plugin Name: Package Installator
Description: A plugin to manage system packages (e.g., php-xml) with a modern UI.
Version: 1.2.1
Author: Tlloancy (in zusammenarbeit mit xAI)
License: GPL-2.0+
License URI: https://www.gnu.org/licenses/gpl-2.0.html
Text Domain: package-installator
Domain Path: /languages
*/
if (!defined('ABSPATH')) {
    exit;
}

// === Gestion session intelligente : ouverture uniquement sur les pages concernées ===
function pkginstpro_maybe_start_session() {
    $needed_pages = ['wpkginst-terminal', 'wpkginst-log', 'wpkginst-terminal']; // ajoute tes slugs si besoin
    if (isset($_GET['page']) && in_array($_GET['page'], $needed_pages)) {
        if (session_status() === PHP_SESSION_NONE) {
            session_start();
        }
    }
}
add_action('admin_init', 'pkginstpro_maybe_start_session');

// Fermeture systématique en fin de requête (libère le verrou pour REST API)
add_action('shutdown', function() {
    if (session_status() === PHP_SESSION_ACTIVE) {
        session_write_close();
    }
});

define('WPKGINST_PLUGIN_DIR', plugin_dir_path(__FILE__));
define('WPKGINST_PLUGIN_URL', plugin_dir_url(__FILE__));

// Composer + phpseclib (use en haut, une seule fois)
require_once WPKGINST_PLUGIN_DIR . 'vendor/autoload.php';
use phpseclib3\Net\SSH2;
use phpseclib3\Crypt\PublicKeyLoader;

require_once WPKGINST_PLUGIN_DIR . 'includes/ssh-handler.php';
require_once WPKGINST_PLUGIN_DIR . 'includes/ajax-handlers.php';

function wpkginst_enqueue_assets() {
    if (isset($_GET['page']) && ($_GET['page'] === 'wpkginst-package-manager' || $_GET['page'] === 'wpkginst-settings')) {
        wp_enqueue_script('react', WPKGINST_PLUGIN_URL . 'assets/js/vendor/react.min.js', [], '18.2.0', true);
        wp_enqueue_script('react-dom', WPKGINST_PLUGIN_URL . 'assets/js/vendor/react-dom.min.js', ['react'], '18.2.0', true);
        wp_enqueue_script('axios', WPKGINST_PLUGIN_URL . 'assets/js/vendor/axios.min.js', [], '1.6.0', true);
        wp_enqueue_script(
            'react-select',
            WPKGINST_PLUGIN_URL . 'assets/js/vendor/react-select.min.js',
            ['react'],
            '5.8.0',
            ['in_footer' => true, 'strategy' => 'defer', 'type' => 'module']
        );
        wp_enqueue_script(
            'wpkginst-package-manager',
            WPKGINST_PLUGIN_URL . 'assets/js/package-manager.js',
            ['react', 'react-dom', 'axios', 'react-select'],
            '1.0.0',
            ['in_footer' => true, 'strategy' => 'defer', 'type' => 'module']
        );
        wp_enqueue_style('tailwind', WPKGINST_PLUGIN_URL . 'assets/css/tailwind.min.css', [], '2.2.19');
        wp_enqueue_style('wpkginst-styles', WPKGINST_PLUGIN_URL . 'assets/css/styles.css', ['tailwind'], '1.0.0');
        wp_localize_script('wpkginst-package-manager', 'wpkginstAjax', [
            'ajax_url' => admin_url('admin-ajax.php'),
            'nonce' => wp_create_nonce('wpkginst_nonce')
        ]);
        wp_add_inline_script(
            'wpkginst-package-manager',
            "jQuery(document).ready(function($) {
                $('input[name=\"wpkginst_ssh_auth_type\"]').on('change', function() {
                    if ($(this).val() === 'key') {
                        $('#wpkginst_ssh_private_key_row').show();
                        $('#wpkginst_ssh_password_row').hide();
                    } else {
                        $('#wpkginst_ssh_private_key_row').hide();
                        $('#wpkginst_ssh_password_row').show();
                    }
                });
            });",
            'after'
        );
    }
}
add_action('admin_enqueue_scripts', 'wpkginst_enqueue_assets');

add_filter('script_loader_tag', function ($tag, $handle, $src) {
    if (in_array($handle, ['react-select', 'wpkginst-package-manager'])) {
        return str_replace('<script ', '<script type="module" defer ', $tag);
    }
    return $tag;
}, 10, 3);

function wpkginst_admin_menu() {
    add_menu_page(__('Package Installator', 'package-installator'), __('Package Installator', 'package-installator'), 'manage_options', 'wpkginst-package-manager', 'wpkginst_admin_page', 'dashicons-admin-tools', 80);
    add_submenu_page('wpkginst-package-manager', __('SSH Settings', 'package-installator'), __('SSH Settings', 'package-installator'), 'manage_options', 'wpkginst-settings', 'wpkginst_settings_page');
    add_submenu_page('wpkginst-package-manager', __('Terminal SSH', 'package-installator'), __('Terminal SSH', 'package-installator'), 'manage_options', 'wpkginst-terminal', 'wpkginst_terminal_page');
    add_submenu_page('wpkginst-package-manager', __('Command Log', 'package-installator'), __('Historique Commandes', 'package-installator'), 'manage_options', 'wpkginst-log', 'wpkginst_log_page');
}
add_action('admin_menu', 'wpkginst_admin_menu');

function wpkginst_admin_page() {

    $ssh_status = wpkginst_test_ssh_connection();

    ?>

    <div class="wrap">

        <h1><?php esc_html_e('Package Installator', 'package-installator'); ?></h1>

        <?php if (!$ssh_status['success']) : ?>

            <div class="notice notice-error is-dismissible">

                <p><?php echo esc_html__('Please configure SSH settings to manage packages.', 'package-installator'); ?> <a href="<?php echo esc_url(admin_url('admin.php?page=wpkginst-settings')); ?>"><?php echo esc_html__('Go to SSH Settings', 'package-installator'); ?></a></p>

            </div>

        <?php endif; ?>

        <div class="flex items-center mb-4">

            <span class="text-lg font-semibold mr-2"><?php esc_html_e('État SSH :', 'package-installator'); ?></span>

            <span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium <?php echo $ssh_status['success'] ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'; ?>">

                <p class="ml-4 text-red-600"><?php echo esc_html(get_option('wpkginst_ssh_last_error', __('Aucune erreur disponible', 'package-installator'))); ?></p>

                <span class="ml-2 h-3 w-3 rounded-full <?php echo $ssh_status['success'] ? 'bg-green-500' : 'bg-red-500'; ?>"></span>

            </span>

            <?php if (!$ssh_status['success']) : ?>

                <p class="ml-4 text-red-600"><?php echo esc_html(get_option('wpkginst_ssh_last_error', __('Aucune erreur disponible', 'package-installator'))); ?></p>

            <?php endif; ?>

        </div>

        <div id="wpkginst-package-manager"></div>

    </div>

    <?php

}



function wpkginst_settings_page() {

    if (isset($_POST['wpkginst_save_settings']) && check_admin_referer('wpkginst_save_settings_nonce')) {

        update_option('wpkginst_ssh_host', sanitize_text_field($_POST['wpkginst_ssh_host']));

        update_option('wpkginst_ssh_username', sanitize_text_field($_POST['wpkginst_ssh_username']));

        update_option('wpkginst_ssh_port', absint($_POST['wpkginst_ssh_port']));

        update_option('wpkginst_ssh_auth_type', sanitize_text_field($_POST['wpkginst_ssh_auth_type']));

        if ($_POST['wpkginst_ssh_auth_type'] === 'key') {

            update_option('wpkginst_ssh_private_key', sanitize_textarea_field($_POST['wpkginst_ssh_private_key']));

            update_option('wpkginst_ssh_password', '');

        } else {

            update_option('wpkginst_ssh_password', sanitize_text_field($_POST['wpkginst_ssh_password']));

            update_option('wpkginst_ssh_private_key', '');

        }

        echo '<div class="notice notice-success is-dismissible"><p>' . esc_html__('Settings saved.', 'package-installator') . '</p></div>';

    }

    $auth_type = get_option('wpkginst_ssh_auth_type', 'key');

    $ssh_status = wpkginst_test_ssh_connection();

    ?>

    <div class="wrap">

        <h1><?php esc_html_e('SSH Settings', 'package-installator'); ?></h1>

        <div class="flex items-center mb-4">

            <span class="text-lg font-semibold mr-2"><?php esc_html_e('État SSH :', 'package-installator'); ?></span>

            <span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium <?php echo $ssh_status['success'] ? 'bg-green-100 text-green-800' : 'bg-red-100 text-red-800'; ?>">

                <?php echo $ssh_status['success'] ? __('Connecté', 'package-installator') : __('Non connecté', 'package-installator'); ?>

                <span class="ml-2 h-3 w-3 rounded-full <?php echo $ssh_status['success'] ? 'bg-green-500' : 'bg-red-500'; ?>"></span>

            </span>

            <?php if (!$ssh_status['success']) : ?>

                <p class="ml-4 text-red-600"><?php echo esc_html(get_option('wpkginst_ssh_last_error', __('Aucune erreur disponible', 'package-installator'))); ?></p>

            <?php endif; ?>

        </div>

        <form method="post" action="">

            <?php wp_nonce_field('wpkginst_save_settings_nonce'); ?>

            <table class="form-table">

                <tr>

                    <th><label for="wpkginst_ssh_host"><?php esc_html_e('SSH Host', 'package-installator'); ?></label></th>

                    <td><input type="text" id="wpkginst_ssh_host" name="wpkginst_ssh_host" value="<?php echo esc_attr(get_option('wpkginst_ssh_host', 'localhost')); ?>" class="regular-text"></td>

                </tr>

                <tr>

                    <th><label for="wpkginst_ssh_username"><?php esc_html_e('SSH Username', 'package-installator'); ?></label></th>

                    <td><input type="text" id="wpkginst_ssh_username" name="wpkginst_ssh_username" value="<?php echo esc_attr(get_option('wpkginst_ssh_username', 'root')); ?>" class="regular-text"></td>

                </tr>

                <tr>

                    <th><label for="wpkginst_ssh_port"><?php esc_html_e('SSH Port', 'package-installator'); ?></label></th>

                    <td><input type="number" id="wpkginst_ssh_port" name="wpkginst_ssh_port" value="<?php echo esc_attr(get_option('wpkginst_ssh_port', 22)); ?>" class="regular-text"></td>

                </tr>

                <tr>

                    <th><label><?php esc_html_e('Authentication Type', 'package-installator'); ?></label></th>

                    <td>

                        <label><input type="radio" name="wpkginst_ssh_auth_type" value="key" <?php checked($auth_type, 'key'); ?>> <?php esc_html_e('Private Key', 'package-installator'); ?></label>

                        <label><input type="radio" name="wpkginst_ssh_auth_type" value="password" <?php checked($auth_type, 'password'); ?>> <?php esc_html_e('Password', 'package-installator'); ?></label>

                    </td>

                </tr>

                <tr id="wpkginst_ssh_private_key_row" style="<?php echo $auth_type === 'key' ? '' : 'display:none;'; ?>">

                    <th><label for="wpkginst_ssh_private_key"><?php esc_html_e('SSH Private Key', 'package-installator'); ?></label></th>

                    <td><textarea id="wpkginst_ssh_private_key" name="wpkginst_ssh_private_key" rows="10" cols="50" class="large-text"><?php echo esc_textarea(get_option('wpkginst_ssh_private_key', '')); ?></textarea></td>

                </tr>

                <tr id="wpkginst_ssh_password_row" style="<?php echo $auth_type === 'password' ? '' : 'display:none;'; ?>">

                    <th><label for="wpkginst_ssh_password"><?php esc_html_e('SSH Password', 'package-installator'); ?></label></th>

                    <td><input type="password" id="wpkginst_ssh_password" name="wpkginst_ssh_password" value="<?php echo esc_attr(get_option('wpkginst_ssh_password', '')); ?>" class="regular-text"></td>

                </tr>

            </table>

            <input type="hidden" name="wpkginst_save_settings" value="1">

            <?php submit_button(__('Save Settings', 'package-installator')); ?>

            <button type="submit" name="wpkginst_test_connection" class="mt-2 px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"><?php esc_html_e('Test Connection', 'package-installator'); ?></button>

        </form>

    </div>

    <?php

}

register_activation_hook(__FILE__, 'wpkginst_create_log_table');

function wpkginst_create_log_table() {
    global $wpdb;
    $table = $wpdb->prefix . 'wpkginst_command_log';
    $charset_collate = $wpdb->get_charset_collate();

    $sql = "CREATE TABLE $table (
        id bigint(20) NOT NULL AUTO_INCREMENT,
        user_id bigint(20) UNSIGNED NOT NULL,
        username varchar(60) NOT NULL,
        user_ip varchar(45) NOT NULL,
        command text NOT NULL,
        output longtext NULL,
        success tinyint(1) NOT NULL DEFAULT 1,
        executed_at datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
        PRIMARY KEY (id),
        KEY user_id (user_id),
        KEY executed_at (executed_at)
    ) $charset_collate;";

    require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
    dbDelta($sql);
}

function wpkginst_terminal_page() {
    if (!current_user_can('manage_options')) {
        wp_die(__('Unauthorized access.', 'package-installator'));
    }

    $authenticated = false;
    if (isset($_POST['wp_password'])) {
        $user = wp_get_current_user();
        if (wp_check_password($_POST['wp_password'], $user->data->user_pass, $user->ID)) {
            $_SESSION['wpkginst_terminal_ok'] = true;
            session_write_close();
            $authenticated = true;
        } else {
            echo '<div class="notice notice-error"><p>' . esc_html__('Incorrect WordPress password.', 'package-installator') . '</p></div>';
        }
    }
    if (!empty($_SESSION['wpkginst_terminal_ok'])) {
        $authenticated = true;
    }

if (!$authenticated) {
    ?>
    <div class="wrap">
        <h1><?php esc_html_e('SSH Terminal - Secure Access', 'package-installator'); ?></h1>
        <div class="card max-w-lg mx-auto">
            <form method="post">
                <p>
                    <label><strong><?php esc_html_e('Enter your WordPress password:', 'package-installator'); ?></strong></label><br>
                    <input type="password" name="wp_password" required class="regular-text" autocomplete="current-password">
                </p>
                <p>
                    <input type="submit" value="<?php esc_attr_e('Open Root Terminal', 'package-installator'); ?>" class="button button-primary button-large">
                </p>
            </form>
        </div>
    </div>
    <?php
    return;
}

    // Mode non-interactif uniquement
    ?>
<div class="wrap">
    <h1><?php esc_html_e('Root Command Executor (non-interactive)', 'package-installator'); ?></h1>
    <p><?php esc_html_e('Write your Bash command and click Run. No interactive tools like vim/nano/top.', 'package-installator'); ?></p>
    <textarea id="command-input" style="width:100%; height:200px; font-family: monospace;" placeholder="<?php esc_attr_e('ex: ls -la /\napt update && apt upgrade -y', 'package-installator'); ?>"></textarea>
    <button id="run-btn" class="button button-primary"><?php esc_html_e('Run', 'package-installator'); ?></button>
    <button id="clear-btn" class="button"><?php esc_html_e('Clear Output', 'package-installator'); ?></button>
    <pre id="output" style="background:#000; color:#0f0; padding:10px; height:400px; overflow:auto; margin-top:20px;"></pre>
    <script>
        document.getElementById('run-btn').addEventListener('click', function() {
            const cmd = document.getElementById('command-input').value.trim();
            const output = document.getElementById('output');
            if (!cmd) return;
            output.textContent = '<?php echo esc_js(__('Running command...', 'package-installator')); ?>\n';
            fetch(ajaxurl, {
                method: 'POST',
                headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
                body: new URLSearchParams({
                    action: 'wpkginst_run_command',
                    command: cmd,
                    nonce: '<?php echo wp_create_nonce("wpkginst_run_cmd"); ?>'
                })
            })
            .then(r => r.json())
            .then(res => {
                if (res.success) {
                    output.textContent += res.data + '\n';
                } else {
                    output.textContent += '<?php echo esc_js(__('ERROR:', 'package-installator')); ?> ' + res.data + '\n';
                }
            })
            .catch(err => output.textContent += '<?php echo esc_js(__('AJAX Error:', 'package-installator')); ?> ' + err + '\n');
        });
        document.getElementById('clear-btn').addEventListener('click', () => document.getElementById('output').textContent = '');
    </script>
</div>
<?php
}

function wpkginst_log_page() {
    if (!current_user_can('manage_options')) {
        wp_die(__('Unauthorized access.', 'package-installator'));
    }

    global $wpdb;
    $table = $wpdb->prefix . 'wpkginst_command_log';
    $logs = $wpdb->get_results("SELECT * FROM $table ORDER BY executed_at DESC LIMIT 200");

    ?>
    <div class="wrap">
        <h1><?php esc_html_e('Root command history', 'package-installator'); ?></h1>
        <table class="wp-list-table widefat fixed striped">
            <thead>
                <tr>
                    <th><?php esc_html_e('Date/Time', 'package-installator'); ?></th>
                    <th><?php esc_html_e('User', 'package-installator'); ?></th>
                    <th><?php esc_html_e('IP', 'package-installator'); ?></th>
                    <th><?php esc_html_e('Command', 'package-installator'); ?></th>
                    <th><?php esc_html_e('Status', 'package-installator'); ?></th>
                </tr>
            </thead>
            <tbody>
                <?php foreach ($logs as $log): ?>
                <tr>
                    <td><?php echo esc_html($log->executed_at); ?></td>
                    <td><?php echo esc_html($log->username); ?> (ID: <?php echo esc_html($log->user_id); ?>)</td>
                    <td><?php echo esc_html($log->user_ip); ?></td>
                    <td><code><?php echo esc_html($log->command); ?></code></td>
                    <td><?php echo $log->success ? '<span style="color:green">' . __('OK', 'package-installator') . '</span>' : '<span style="color:red">' . __('FAILURE', 'package-installator') . '</span>'; ?></td>
                </tr>
                <?php endforeach; ?>
            </tbody>
        </table>
        <p><strong><?php esc_html_e('Warning:', 'package-installator'); ?></strong> <?php esc_html_e('This history is critical. Regularly backup your database.', 'package-installator'); ?></p>
    </div>
    <?php
}

// WebSocket désactivé
function wpkginst_render_terminal() {
    echo '<div class="notice notice-warning"><p>' . esc_html__('Interactive terminal disabled. Use the Run mode above.', 'package-installator') . '</p></div>';
}

add_action('wp_ajax_wpkginst_run_command', 'wpkginst_ajax_run_command');
function wpkginst_ajax_run_command() {
    check_ajax_referer('wpkginst_run_cmd', 'nonce');
	
	// Ouvre la session SI elle n'est pas déjà ouverte (cas rare mais safe)
    if (session_status() === PHP_SESSION_NONE) {
        session_start();
    }
	
    if (!current_user_can('manage_options') || empty($_SESSION['wpkginst_terminal_ok'])) {
        wpkginst_log_command('ACCESS DENIED (no session or capability)', '', false);
        wp_send_json_error('Accès refusé - Session expirée ou droits insuffisants.');
    }
	
	session_write_close();  // ferme IMMÉDIATEMENT après le check

    $command = trim($_POST['command'] ?? '');
    if ($command === '') {
        wpkginst_log_command('(empty command)', '', false);
        wp_send_json_error('Aucune commande fournie.');
    }

    $success = false;
    $output = 'SSH connection failed';

    $ssh = new SSH2(get_option('wpkginst_ssh_host', 'localhost'), (int)get_option('wpkginst_ssh_port', 22));
    $auth = get_option('wpkginst_ssh_auth_type','key') === 'key'
        ? PublicKeyLoader::load(get_option('wpkginst_ssh_private_key'))
        : get_option('wpkginst_ssh_password');

    if ($ssh->login(get_option('wpkginst_ssh_username','root'), $auth)) {
        $ssh->setTimeout(300);
        $output = $ssh->exec($command . ' 2>&1');
        $success = true; // Si exec renvoie quelque chose, on considère OK (même code retour ≠0)
    } else {
        $output = 'SSH login failed';
    }

    // UN SEUL LOG, à la fin, avec tout
    wpkginst_log_command($command, $output, $success);

    $date = current_time('d/m/Y H:i:s');
    wp_send_json_success("Commande exécutée le $date\n\nOutput:\n" . $output);
}

function wpkginst_log_command($command, $output = '', $success = true) {
    global $wpdb;
    $user = wp_get_current_user();

    $wpdb->insert(
        $wpdb->prefix . 'wpkginst_command_log',
        [
            'user_id'   => $user->ID,
            'username'  => $user->user_login,
            'user_ip'   => $_SERVER['REMOTE_ADDR'] ?? 'unknown',
            'command'   => $command,
            'output'    => substr($output, 0, 16777215), // limite MySQL MEDIUMTEXT
            'success'   => $success ? 1 : 0,
            'executed_at' => current_time('mysql')
        ],
        ['%d', '%s', '%s', '%s', '%s', '%d', '%s']
    );
}
