PHP Classes

File: public_html/webdoors/terminal/index.php

Recommend this page to a friend!
  Packages of Matthew Asham   Binkterm PHP   public_html/webdoors/terminal/index.php   Download  
File: public_html/webdoors/terminal/index.php
Role: Example script
Content type: text/plain
Description: Example script
Class: Binkterm PHP
Bulletin board system based on the Web
Author: By
Last change:
Date: 5 days ago
Size: 15,705 bytes
 

Contents

Class file image Download
<?php /** * Terminal WebDoor - SSH and Telnet Gateway * * This webdoor provides web-based terminal access to remote SSH and Telnet hosts. * It connects to an external proxy server (e.g., terminalgateway) that handles * the actual SSH/Telnet connections. * * Protocol Support: * - SSH: Requires proxy server with SSH client support * - Telnet: Requires proxy server with Telnet client support * * The frontend sends connection requests with protocol type ('ssh' or 'telnet') * to the proxy server via Socket.IO events: * - 'connect-ssh' for SSH connections * - 'connect-telnet' for Telnet connections * * Configuration: * - Hosts are defined in config/webdoors.json under the 'terminal' key * - Each host must specify: hostname, port, and proto ('ssh' or 'telnet') * - The proxy server address is configured via TERMINAL_PROXY_HOST and TERMINAL_PROXY_PORT */ // Include WebDoor SDK (handles autoload, database, and session initialization) require_once __DIR__ . '/../_doorsdk/php/helpers.php'; use BinktermPHP\Auth; use BinktermPHP\Binkp\Config\BinkpConfig; use BinktermPHP\Config; use BinktermPHP\GameConfig; use BinktermPHP\Template; // Check authentication (optional for terminal) $auth = new Auth(); $user = $auth->getCurrentUser(); $username=''; // Load terminal configuration // Load terminal configuration $terminalEnabled = Config::env('TERMINAL_ENABLED', 'false') === 'true'; $terminalHost = Config::env('TERMINAL_HOST', 'revpol.lovelybits.org'); $terminalPort = Config::env('TERMINAL_PORT', '22'); $terminalProxyHost = Config::env('TERMINAL_PROXY_HOST', 'terminal.lovelybits.org'); $terminalProxyPort = Config::env('TERMINAL_PROXY_PORT', '443'); $terminalTitle = 'Terminal Gateway'; // TODO: Read webdoors.json for list of hosts we're allowed to connect to $terminalHost = Config::env('TERMINAL_HOST', 'revpol.lovelybits.org'); $terminalPort = Config::env('TERMINAL_PORT', '22'); // Always include the primary system host at top. $terminalHosts[] = ["hostname" => $terminalHost, "port" => $terminalPort, "proto" => "ssh"]; $gameConfig = GameConfig::getGameConfig("terminal"); // Guard against null GameConfig or missing 'hosts' key $configHosts = []; if (is_array($gameConfig) && isset($gameConfig['hosts']) && is_array($gameConfig['hosts'])) { $configHosts = $gameConfig['hosts']; } $terminalHosts = array_merge($terminalHosts, $configHosts); // Check if terminal is enabled. if (!$terminalEnabled) { $template = new Template(); $template->renderResponse('error.twig', [ 'error' => 'Terminal access is currently disabled.' ]); exit(); } // Get system name from BinkP config try { $binkpConfig = BinkpConfig::getInstance(); $systemName = $binkpConfig->getSystemName(); } catch (\Exception $e) { $systemName = \BinktermPHP\Config::SYSTEM_NAME; } ?><!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title><?php echo htmlspecialchars($terminalTitle); ?> - <?php echo htmlspecialchars($systemName); ?></title> <!-- Favicons --> <link rel="icon" type="image/svg+xml" href="{{ favicon_svg }}"> <link rel="icon" type="image/svg+xml" href="{{ favicon_svg }}"> <link rel="icon" type="image/x-icon" href="{{ favicon_ico }}"> <link rel="apple-touch-icon" href="{{ favicon_png }}"> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet"> <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet"> <link href="<?php echo htmlspecialchars(Config::getStylesheet()); ?>?v=<?php echo time(); ?>" rel="stylesheet"> <link rel="stylesheet" href="/webdoors/terminal/assets/xterm.css" /> <style> body { background: linear-gradient(135deg, #2d3748 0%, #1a202c 100%); min-height: 100vh; } .terminal-container { background: rgba(255, 255, 255, 0.05); border-radius: 12px; padding: 30px; margin-top: 20px; backdrop-filter: blur(10px); border: 1px solid rgba(255, 255, 255, 0.1); } #terminal { width: 100%; height: 60vh; border-radius: 8px; box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3); overflow: hidden; background: #000; } .login-form { background: linear-gradient(135deg, #4a5568 0%, #2d3748 100%); padding: 30px; border-radius: 12px; color: #fff; box-shadow: 0 10px 40px rgba(0, 0, 0, 0.4); border: 1px solid rgba(255, 255, 255, 0.1); } .login-form h3 { color: #f8fafc; font-weight: 400; text-align: center; margin-bottom: 25px; } .login-form input { width: 100%; padding: 15px; margin: 12px 0; border: 1px solid rgba(255, 255, 255, 0.2); background: rgba(255, 255, 255, 0.1); color: #fff; border-radius: 8px; font-size: 16px; backdrop-filter: blur(10px); transition: all 0.3s ease; } .login-form input::placeholder { color: rgba(255, 255, 255, 0.7); } .login-form input:focus { outline: none; border-color: #60a5fa; background: rgba(255, 255, 255, 0.15); box-shadow: 0 0 0 3px rgba(96, 165, 250, 0.2); } .login-form button { width: 100%; padding: 15px; background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%); color: #fff; border: none; border-radius: 8px; cursor: pointer; font-size: 16px; font-weight: 500; transition: all 0.3s ease; box-shadow: 0 4px 20px rgba(59, 130, 246, 0.3); } .login-form button:hover { background: linear-gradient(135deg, #2563eb 0%, #1e40af 100%); transform: translateY(-2px); box-shadow: 0 6px 25px rgba(59, 130, 246, 0.4); } .login-form select { width: 100%; padding: 15px; margin: 12px 0; border: 1px solid rgba(255, 255, 255, 0.2); background: rgba(255, 255, 255, 0.1); color: #fff; border-radius: 8px; font-size: 16px; backdrop-filter: blur(10px); transition: all 0.3s ease; cursor: pointer; } .login-form select:focus { outline: none; border-color: #60a5fa; background: rgba(255, 255, 255, 0.15); box-shadow: 0 0 0 3px rgba(96, 165, 250, 0.2); } .login-form select option { background: #2d3748; color: #fff; } #auth-fields { transition: opacity 0.3s ease, max-height 0.3s ease; overflow: hidden; } #auth-fields[style*="display: none"] { opacity: 0; max-height: 0; } .hidden { display: none; } /* Terminal specific navbar styling */ .navbar-dark { background: rgba(13, 110, 253, 0.9) !important; backdrop-filter: blur(10px); } </style> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script> <script src="https://code.jquery.com/jquery-3.7.0.min.js"></script> <script src="/webdoors/terminal/assets/xterm.js"></script> <script src="/webdoors/terminal/assets/xterm-addon-fit.js"></script> <script src="/webdoors/terminal/assets/xterm-addon-web-links.js"></script> <script src="/webdoors/terminal/assets/socket.io.min.js"></script> </head> <body> <main class="container"> <div class="terminal-container"> <div class="text-center mb-4"> <h1 class="text-white"> <i class="fas fa-terminal me-2"></i> <?php echo htmlspecialchars($terminalTitle); ?> </h1> </div> <div id="login-form" class="login-form"> <h3>Terminal Connection</h3> <label for="sshhost" style="display: block; margin-bottom: 8px; color: rgba(255, 255, 255, 0.9); font-weight: 500;">Connect to:</label> <select name="sshhost" id="sshhost" onchange="updateAuthFields()"> <?php foreach($terminalHosts as $host){ $proto = isset($host['proto']) ? $host['proto'] : 'ssh'; $displayName = $host['hostname'] . ' (' . strtoupper($proto) . ')'; echo '<option value='.$host['hostname'].':'.$host['port'].':'.$proto.'>'.$displayName.'</option>'; } ?> </select> <div id="auth-fields"> <input type="text" id="username" placeholder="Username" value="<?php echo $username;?>"> <input type="password" id="password" placeholder="Password" > </div> <button onclick="startConnection()">Connect</button> </div> <div id="terminal" class="hidden"></div> </div> </main> <script> // Pre-configured proxy server settings const PROXY_HOST = '<?php echo addslashes($terminalProxyHost); ?>'; const PROXY_PORT = <?php echo intval($terminalProxyPort); ?>; // Remote host settings let REMOTE_HOST = '';//<?php echo addslashes($terminalHost); ?>'; let REMOTE_PORT = '';//<?php echo intval($terminalPort); ?>'; let REMOTE_PROTO = 'ssh'; // Initialize xterm.js const terminal = new Terminal({ cursorBlink: true, theme: { background: '#000000', foreground: '#ffffff' } }); // Add fit addon const fitAddon = new FitAddon.FitAddon(); terminal.loadAddon(fitAddon); // Add web links addon const webLinksAddon = new WebLinksAddon.WebLinksAddon(); terminal.loadAddon(webLinksAddon); // Initialize terminal but don't open it yet let terminalInitialized = false; // Socket.IO connection let socket; // Update auth fields visibility based on protocol function updateAuthFields() { const hostValue = document.getElementById('sshhost').value; const parts = hostValue.split(':'); const proto = parts[2] || 'ssh'; const authFields = document.getElementById('auth-fields'); if (proto === 'telnet') { authFields.style.display = 'none'; } else { authFields.style.display = 'block'; } } function startConnection() { const hostValue = document.getElementById('sshhost').value; const parts = hostValue.split(':'); REMOTE_HOST = parts[0]; REMOTE_PORT = parts[1]; REMOTE_PROTO = parts[2] || 'ssh'; // For SSH, require username and password let username = ''; let password = ''; if (REMOTE_PROTO === 'ssh') { username = document.getElementById('username').value; password = document.getElementById('password').value; if (!username || !password) { alert('Please enter both username and password'); return; } } // Hide login form and show terminal document.getElementById('login-form').classList.add('hidden'); document.getElementById('terminal').classList.remove('hidden'); // Initialize terminal if not already done if (!terminalInitialized) { terminal.open(document.getElementById('terminal')); fitAddon.fit(); terminalInitialized = true; } connect(username, password); } function connect(username, password) { const serverUrl = `https://${PROXY_HOST}:${PROXY_PORT}`; console.log('Connecting to:', serverUrl); socket = io(serverUrl); socket.on('connect', function() { console.log('Socket.IO connected successfully'); terminal.write('\r\n\x1b[32mConnected to terminal server\x1b[0m\r\n'); // Initiate connection (SSH or Telnet based on protocol) const connectionType = REMOTE_PROTO === 'telnet' ? 'connect-telnet' : 'connect-ssh'; console.log(`Sending ${connectionType} request:`, { host: REMOTE_HOST, port: REMOTE_PORT, username, proto: REMOTE_PROTO }); socket.emit(connectionType, { host: REMOTE_HOST, port: REMOTE_PORT, username: username, password: password, proto: REMOTE_PROTO }); }); socket.on('data', function(data) { terminal.write(data); }); socket.on('connection-status', function(status) { const protoName = REMOTE_PROTO.toUpperCase(); if (status.status === 'connected') { terminal.write(`\r\n\x1b[32m${protoName} connection established\x1b[0m\r\n`); } else if (status.status === 'disconnected') { terminal.write(`\r\n\x1b[31m${protoName} connection closed\x1b[0m\r\n`); } }); socket.on('error', function(error) { console.error('Socket.IO error:', error); terminal.write(`\r\n\x1b[31mError: ${error.message}\x1b[0m\r\n`); }); socket.on('disconnect', function() { console.log('Socket.IO disconnected'); terminal.write('\r\n\x1b[31mConnection lost. Attempting to reconnect...\x1b[0m\r\n'); }); socket.on('connect_error', function(error) { console.error('Socket.IO connection error:', error); terminal.write(`\r\n\x1b[31mConnection Error: ${error.message}\x1b[0m\r\n`); }); } // Send data from terminal to server terminal.onData(function(data) { if (socket && socket.connected) { socket.emit('input', data); } }); // Handle terminal resize terminal.onResize(function(size) { if (socket && socket.connected) { socket.emit('resize', { rows: size.rows, cols: size.cols }); } }); // Handle window resize window.addEventListener('resize', function() { fitAddon.fit(); }); // Allow Enter key to submit form document.addEventListener('keypress', function(e) { if (e.key === 'Enter' && !document.getElementById('login-form').classList.contains('hidden')) { startConnection(); } }); // Initialize auth fields visibility on page load document.addEventListener('DOMContentLoaded', function() { updateAuthFields(); }); </script> </body> </html>