<?php
/**
 * Quick Shell extension.
 *
 * @category   Extension
 * @package    Pandora FMS
 * @subpackage QuickShell
 * @version    1.0.0
 * @license    See below
 *
 *    ______                 ___                    _______ _______ ________
 *   |   __ \.-----.--.--.--|  |.-----.----.-----. |    ___|   |   |     __|
 *  |    __/|  _  |     |  _  ||  _  |   _|  _  | |    ___|       |__     |
 * |___|   |___._|__|__|_____||_____|__| |___._| |___|   |__|_|__|_______|
 *
 * ============================================================================
 * Copyright (c) 2005-2021 Artica Soluciones Tecnologicas
 * Please see http://pandorafms.org for full contribution list
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation for version 2.
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * ============================================================================
 */

// Begin.
global $config;

require_once $config['homedir'].'/include/functions_agents.php';
require_once $config['homedir'].'/godmode/wizards/Wizard.main.php';


/**
 * Undocumented function
 *
 * @param string $url    Url.
 * @param array  $params Params.
 *
 * @return mixed Result
 */
function curl(string $url, array $params)
{
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, $params);
    $get_result = curl_exec($ch);

    curl_close($ch);

    return $get_result;
}


/**
 * Show Quick Shell interface.
 *
 * @return void
 */
function quickShell()
{
    global $config;

    check_login();

    if (check_acl($config['id_user'], 0, 'PM') === false) {
        db_pandora_audit(
            AUDIT_LOG_ACL_VIOLATION,
            'Trying to access Profile Management'
        );
        include 'general/noaccess.php';
        return;
    }

    $agent_id = get_parameter('id_agente', 0);
    $username = get_parameter('username', null);
    $method = get_parameter('method', null);
    $method_port = get_parameter('port', null);

    // Retrieve main IP Address.
    $address = agents_get_address($agent_id);

    ui_require_css_file('wizard');
    ui_require_css_file('discovery');

    // Settings.
    // WebSocket host, where client should connect.
    if (isset($config['ws_port']) === false) {
        config_update_value('ws_port', 8080);
    }

    if (empty($config['ws_proxy_url']) === true) {
        $ws_url = 'http://'.$_SERVER['SERVER_ADDR'].':'.$config['ws_port'];
    } else {
        preg_match('/\/\/(.*)/', $config['ws_proxy_url'], $matches);
        if (isset($_SERVER['HTTPS']) === true) {
            $ws_url = 'https://'.$matches[1];
        } else {
            $ws_url = 'http://'.$matches[1];
        }
    }

    // Gotty settings. Internal communication (WS).
    if (isset($config['gotty_host']) === false) {
        config_update_value('gotty_host', '127.0.0.1');
    }

    if (isset($config['gotty_telnet_port']) === false) {
        config_update_value('gotty_telnet_port', 8082);
    }

    if (isset($config['gotty_ssh_port']) === false) {
        config_update_value('gotty_ssh_port', 8081);
    }

    // Context to allow self-signed certs.
    $context = stream_context_create(
        [
            'http' => [ 'method' => 'GET'],
            'ssl'  => [
                'verify_peer'      => false,
                'verify_peer_name' => false,
            ],
        ]
    );

    // Username. Retrieve from form.
    if (empty($username) === true) {
        // No username provided, ask for it.
        $wiz = new Wizard();

        $test = curl($ws_url, []);
        if ($test === false) {
            ui_print_error_message(__('WebService engine has not been started, please check documentation.'));
            $wiz->printForm(
                [
                    'form' => [
                        'method' => 'POST',
                        'action' => '#',
                        'id'     => 'retry_form',
                    ],
                ]
            );

            html_print_action_buttons(
                html_print_submit_button(
                    __('Retry'),
                    'submit',
                    false,
                    [
                        'icon' => 'next',
                        'form' => 'retry_form',
                    ],
                    true
                )
            );
            return;
        }

        $wiz->printForm(
            [
                'form'   => [
                    'action' => '#',
                    'class'  => 'wizard',
                    'method' => 'post',
                    'id'     => 'connect_form',
                ],
                'inputs' => [
                    [
                        'label'     => __('Username'),
                        'arguments' => [
                            'type' => 'text',
                            'name' => 'username',
                        ],
                    ],
                    [
                        'label'     => __('Port'),
                        'arguments' => [
                            'type'  => 'text',
                            'id'    => 'port',
                            'name'  => 'port',
                            'value' => 22,
                        ],
                    ],
                    [
                        'label'     => __('Method'),
                        'arguments' => [
                            'type'   => 'select',
                            'name'   => 'method',
                            'fields' => [
                                'ssh'    => __('SSH'),
                                'telnet' => __('Telnet'),
                            ],
                            'script' => "p=22; if(this.value == 'telnet') { p=23; } $('#text-port').val(p);",
                        ],
                    ],
                ],
            ],
            false,
            true
        );

        html_print_action_buttons(
            html_print_submit_button(
                __('Connect'),
                'submit',
                false,
                [
                    'icon' => 'cog',
                    'form' => 'connect_form',
                ],
                true
            )
        );
        return;
    }

    // Initialize Gotty Client.
    $host = $config['gotty_host'];
    if ($method == 'ssh') {
        // SSH.
        $port = $config['gotty_ssh_port'];
        $command_arguments = "var args = '?arg=".$username.'@'.$address;
        $command_arguments .= '&arg=-p '.$method_port."';";
    } else if ($method == 'telnet') {
        // Telnet.
        $port = $config['gotty_telnet_port'];
        $username = preg_replace('/[^a-zA-Z0-9\-\.]/', '', $username);
        $command_arguments = "var args = '?arg=-l ".$username;
        $command_arguments .= '&arg='.$address;
        $command_arguments .= '&arg='.$method_port."&arg=-E';";
    } else {
        ui_print_error_message(__('Please use SSH or Telnet.'));
        return;
    }

    // If rediretion is enabled, we will try to connect using
    // http:// or https:// endpoint.
    $test = get_headers($ws_url, false, $context);
    if ($test === false) {
        if (empty($wiz) === true) {
            $wiz = new Wizard();
        }

        ui_print_error_message(__('WebService engine has not been started, please check documentation.'));
        echo $wiz->printGoBackButton('#');
        return;
    }

    // Check credentials.
    $auth_str = '';
    $gotty_url = $host.':'.$port;
    if (empty($config['gotty_user']) === false
        && empty($config['gotty_pass']) === false
    ) {
        $auth_str = io_safe_output($config['gotty_user']);
        $auth_str .= ':'.io_output_password($config['gotty_pass']);
        $gotty_url = $auth_str.'@'.$host.':'.$port;
    }

    $r = file_get_contents('http://'.$gotty_url.'/js/hterm.js');
    if (empty($r) === true) {
        if (empty($wiz) === true) {
            $wiz = new Wizard();
        }

        ui_print_error_message(__('WebService engine is not working properly, please check documentation.'));
        echo $wiz->printGoBackButton('#');
        return;
    }

    // Override gotty client settings.
    if (empty($auth_str) === true) {
        $r .= "var gotty_auth_token = '';";
    } else {
        $r .= "var gotty_auth_token = '";
        $r .= $auth_str."';";
    }

    // Set websocket target and method.
    $gotty = file_get_contents('http://'.$gotty_url.'/js/gotty.js');
    $url = "var url = (httpsEnabled ? 'wss://' : 'ws://') + window.location.host + window.location.pathname + 'ws';";
    if (empty($config['ws_proxy_url']) === true) {
        $new = "var url = (httpsEnabled ? 'wss://' : 'ws://')";
        $new .= " + window.location.host + ':";
        $new .= $config['ws_port'].'/'.$method."';";
    } else {
        $new = "var url = '";
        $new .= $config['ws_proxy_url'].'/'.$method."';";
    }

    // Update firefox issue.
    $original = '    this.iframe_.src = \'#\';';
    $trick = 'this.iframe_.src = \'javascript:\';';

    $r = str_replace($original, $trick, $r);

    // Update url.
    $gotty = str_replace($url, $new, $gotty);

    // Update websocket arguments.
    $args = 'var args = window.location.search;';
    $new = $command_arguments;

    // Update arguments.
    $gotty = str_replace($args, $new, $gotty);

    ?>
    <style>#terminal {
        height: 650px;
        width: 100%;
        margin: 0px;
        padding: 0;
      }
      #terminal > iframe {
        position: relative!important;
      }
    </style>
    <div id="terminal"></div>
    <script type="text/javascript">
    <?php echo $r; ?>
    </script>
    <script type="text/javascript">
    <?php echo $gotty; ?>
    </script>
    <?php

}


/**
 * Provide an interface where configure all settings.
 *
 * @return void
 */
function quickShellSettings()
{
    global $config;

    ui_require_css_file('wizard');
    ui_require_css_file('discovery');

    // Gotty settings. Internal communication (WS).
    if (isset($config['gotty_host']) === false) {
        config_update_value('gotty_host', '127.0.0.1');
    }

    if (isset($config['gotty_telnet_port']) === false) {
        config_update_value('gotty_telnet_port', 8082);
    }

    if (isset($config['gotty_ssh_port']) === false) {
        config_update_value('gotty_ssh_port', 8081);
    }

    // Parser.
    if (get_parameter('update_config', false) !== false) {
        // Gotty settings. Internal communication (WS).
        $gotty = get_parameter(
            'gotty',
            ''
        );
        $gotty_host = get_parameter(
            'gotty_host',
            ''
        );
        $gotty_ssh_port = get_parameter(
            'gotty_ssh_port',
            ''
        );
        $gotty_telnet_port = get_parameter(
            'gotty_telnet_port',
            ''
        );

        $gotty_user = get_parameter(
            'gotty_user',
            ''
        );

        $gotty_pass = get_parameter(
            'gotty_pass',
            ''
        );

        $gotty_pass = io_input_password($gotty_pass);

        $changes = 0;
        $critical = 0;
        if ($config['gotty'] != $gotty) {
            config_update_value('gotty', $gotty);
            $changes++;
            $critical++;
        }

        if ($config['gotty_host'] != $gotty_host) {
            config_update_value('gotty_host', $gotty_host);
            $changes++;
        }

        if ($config['gotty_telnet_port'] != $gotty_telnet_port) {
            config_update_value('gotty_telnet_port', $gotty_telnet_port);
            $changes++;
        }

        if ($config['gotty_ssh_port'] != $gotty_ssh_port) {
            config_update_value('gotty_ssh_port', $gotty_ssh_port);
            $changes++;
        }

        if ($config['gotty_user'] != $gotty_user) {
            config_update_value('gotty_user', $gotty_user);
            $changes++;
            $critical++;
        }

        if ($config['gotty_pass'] != $gotty_pass) {
            $gotty_pass = io_input_password($gotty_pass);
            config_update_value('gotty_pass', $gotty_pass);
            $changes++;
            $critical++;
        }
    }

    if ($changes > 0) {
        $msg = __('%d Updated', $changes);
        if ($critical > 0) {
            $msg = __(
                '%d Updated, please restart WebSocket engine service',
                $changes
            );
        }

        ui_print_success_message($msg);
    }

    // Form. Using old style.
    echo '<fieldset class="margin-bottom-10">';
    echo '<legend>'.__('Quickshell').'</legend>';

    $t = new StdClass();
    $t->data = [];
    $t->width = '100%';
    $t->class = 'filter-table-adv';
    $t->data = [];
    $t->style = [];
    $t->style[0] = 'width: 50%;';

    $t->data[0][] = html_print_label_input_block(
        __('Gotty path'),
        html_print_input_text(
            'gotty',
            $config['gotty'],
            '',
            30,
            100,
            true
        )
    );

    $t->data[0][] = html_print_label_input_block(
        __('Gotty host'),
        html_print_input_text(
            'gotty_host',
            $config['gotty_host'],
            '',
            30,
            100,
            true
        )
    );

    $t->data[1][] = html_print_label_input_block(
        __('Gotty ssh port'),
        html_print_input_text(
            'gotty_ssh_port',
            $config['gotty_ssh_port'],
            '',
            30,
            100,
            true
        )
    );

    $t->data[1][] = html_print_label_input_block(
        __('Gotty telnet port'),
        html_print_input_text(
            'gotty_telnet_port',
            $config['gotty_telnet_port'],
            '',
            30,
            100,
            true
        )
    );

    $hidden = new stdClass();
    $hidden->data = [];
    $hidden->width = '100%';
    $hidden->class = 'filter-table-adv';
    $hidden->data = [];
    $hidden->style[0] = 'width: 50%;';

    $hidden->data[0][] = html_print_label_input_block(
        __('Gotty user'),
        html_print_input_text(
            'gotty_user',
            $config['gotty_user'],
            '',
            30,
            100,
            true
        )
    );

    $hidden->data[0][] = html_print_label_input_block(
        __('Gotty password'),
        html_print_input_password(
            'gotty_pass',
            io_output_password($config['gotty_pass']),
            '',
            30,
            100,
            true
        )
    );

    html_print_table($t);

    ui_print_toggle(
        [
            'content'         => html_print_table($hidden, true),
            'name'            => __('Advanced options'),
            'clean'           => false,
            'main_class'      => 'no-border-imp',
            'container_class' => 'no-border-imp',
        ]
    );

    echo '</fieldset>';

}


// This extension is usefull only if the agent has associated IP.
$agent_id = get_parameter('id_agente');
if (empty($agent_id) === false
    && get_parameter('sec2', '') == 'operation/agentes/ver_agente'
) {
    $address = agents_get_address($agent_id);
    if (empty($address) === false) {
        // Extension registration.
        extensions_add_opemode_tab_agent(
            // TabId.
            'quick_shell',
            // TabName.
            __('QuickShell'),
            // TabIcon.
            'images/quick-shell@svg.svg',
            // TabFunction.
            'quickShell',
            // Version.
            'N/A',
            // Acl.
            'PM'
        );
    }
}

extensions_add_godmode_function('quickShellSettings');