from flask import Flask, render_template, request, jsonify, session
import subprocess
import os
import threading
import uuid
from datetime import datetime
import shlex

app = Flask(__name__)
app.secret_key = os.urandom(24)  # Случайный секретный ключ


class CommandSession:
    def __init__(self):
        self.sessions = {}

    def create_session(self):
        session_id = str(uuid.uuid4())
        self.sessions[session_id] = {
            'created_at': datetime.now(),
            'working_dir': os.getcwd(),
            'history': []
        }
        return session_id

    def execute_command(self, session_id, command):
        if session_id not in self.sessions:
            return "Сессия не найдена"

        session_data = self.sessions[session_id]

        # Добавляем команду в историю
        session_data['history'].append({
            'command': command,
            'timestamp': datetime.now()
        })

        try:
            # Выполняем команду в текущей директории сессии
            process = subprocess.Popen(
                command,
                shell=True,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                text=True,
                cwd=session_data['working_dir'],
                executable='/bin/bash' if os.name != 'nt' else None
            )

            # Ждем завершения с таймаутом
            try:
                stdout, stderr = process.communicate(timeout=60)
                return_code = process.returncode
            except subprocess.TimeoutExpired:
                process.kill()
                stdout, stderr = process.communicate()
                return f"Ошибка: команда выполнялась слишком долго и была остановлена\n\nSTDOUT:\n{stdout}\nSTDERR:\n{stderr}"

            # Формируем вывод
            output = ""
            if stdout:
                output += f"STDOUT:\n{stdout}\n"
            if stderr:
                output += f"STDERR:\n{stderr}\n"
            if return_code != 0:
                output += f"Код возврата: {return_code}"

            return output.strip()

        except Exception as e:
            return f"Ошибка выполнения: {str(e)}"

    def change_directory(self, session_id, new_dir):
        if session_id not in self.sessions:
            return False, "Сессия не найдена"

        try:
            if new_dir == "~":
                new_dir = os.path.expanduser("~")
            elif new_dir == "-":
                # Возврат к предыдущей директории (можно реализовать при необходимости)
                new_dir = os.getcwd()

            new_dir = os.path.abspath(new_dir)

            if os.path.exists(new_dir) and os.path.isdir(new_dir):
                os.chdir(new_dir)
                self.sessions[session_id]['working_dir'] = new_dir
                return True, f"Директория изменена на: {new_dir}"
            else:
                return False, f"Директория не существует: {new_dir}"

        except Exception as e:
            return False, f"Ошибка смены директории: {str(e)}"


command_manager = CommandSession()


@app.route('/')
def index():
    if 'session_id' not in session:
        session['session_id'] = command_manager.create_session()
    return render_template('index.html')


@app.route('/execute', methods=['POST'])
def execute_command():
    if 'session_id' not in session:
        return jsonify({'error': 'Сессия не найдена'})

    data = request.get_json()
    command = data.get('command', '').strip()

    if not command:
        return jsonify({'error': 'Пустая команда'})

    # Обработка команды cd
    if command.startswith('cd '):
        new_dir = command[3:].strip()
        success, message = command_manager.change_directory(session['session_id'], new_dir)
        return jsonify({
            'output': message,
            'command': command,
            'timestamp': datetime.now().strftime('%H:%M:%S'),
            'current_dir': command_manager.sessions[session['session_id']]['working_dir']
        })

    # Выполняем команду
    output = command_manager.execute_command(session['session_id'], command)

    return jsonify({
        'output': output,
        'command': command,
        'timestamp': datetime.now().strftime('%H:%M:%S'),
        'current_dir': command_manager.sessions[session['session_id']]['working_dir']
    })


@app.route('/sessions', methods=['GET'])
def list_sessions():
    return jsonify({
        'active_sessions': len(command_manager.sessions),
        'current_session': session.get('session_id'),
        'current_dir': command_manager.sessions.get(session.get('session_id', ''), {}).get('working_dir', '')
    })


@app.route('/history', methods=['GET'])
def command_history():
    if 'session_id' not in session:
        return jsonify({'error': 'Сессия не найдена'})

    session_data = command_manager.sessions.get(session['session_id'])
    if not session_data:
        return jsonify({'error': 'Данные сессии не найдены'})

    history = session_data.get('history', [])[-10:]  # Последние 10 команд
    formatted_history = [
        {
            'command': item['command'],
            'timestamp': item['timestamp'].strftime('%H:%M:%S')
        }
        for item in history
    ]

    return jsonify({'history': formatted_history})


@app.route('/clear-session', methods=['POST'])
def clear_session():
    session_id = session.get('session_id')
    if session_id in command_manager.sessions:
        del command_manager.sessions[session_id]
    session.pop('session_id', None)
    return jsonify({'message': 'Сессия очищена'})


@app.route('/system-info', methods=['GET'])
def system_info():
    """Базовая информация о системе"""
    try:
        # Информация о системе
        system_info = {
            'platform': os.uname().sysname if hasattr(os, 'uname') else os.name,
            'current_user': os.getenv('USER', 'Unknown'),
            'cpu_count': os.cpu_count(),
            'current_directory': os.getcwd()
        }

        return jsonify(system_info)
    except Exception as e:
        return jsonify({'error': f'Не удалось получить информацию о системе: {str(e)}'})


if __name__ == '__main__':
    # Создаем папку для шаблонов если её нет
    os.makedirs('templates', exist_ok=True)

    # Создаем HTML шаблон
    with open('templates/index.html', 'w', encoding='utf-8') as f:
        f.write('''<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Web Shell - Полный доступ</title>
    <style>
        :root {
            --bg-color: #1e1e1e;
            --text-color: #00ff00;
            --error-color: #ff4444;
            --warning-color: #ffaa00;
            --info-color: #4488ff;
        }

        body {
            font-family: 'Courier New', monospace;
            background-color: var(--bg-color);
            color: var(--text-color);
            margin: 0;
            padding: 20px;
            line-height: 1.4;
        }

        .header {
            border-bottom: 1px solid #333;
            padding-bottom: 10px;
            margin-bottom: 20px;
        }

        .warning {
            background-color: var(--warning-color);
            color: #000;
            padding: 10px;
            border-radius: 5px;
            margin: 10px 0;
            font-weight: bold;
        }

        .terminal {
            background-color: #000;
            border: 1px solid #333;
            border-radius: 5px;
            padding: 20px;
            height: 60vh;
            overflow-y: auto;
            margin-bottom: 20px;
        }

        .output {
            margin-bottom: 10px;
            white-space: pre-wrap;
            word-break: break-all;
        }

        .command-line {
            display: flex;
            align-items: center;
            background-color: #000;
            padding: 10px;
            border-radius: 5px;
            border: 1px solid #333;
        }

        .prompt {
            margin-right: 10px;
            color: var(--info-color);
            font-weight: bold;
        }

        #command-input {
            background: transparent;
            border: none;
            color: var(--text-color);
            font-family: 'Courier New', monospace;
            font-size: 16px;
            outline: none;
            flex-grow: 1;
        }

        .timestamp {
            color: #888;
            font-size: 12px;
        }

        .error {
            color: var(--error-color);
        }

        .info {
            color: var(--info-color);
        }

        .user-command {
            color: #ffff00;
            font-weight: bold;
        }

        .controls {
            display: flex;
            gap: 10px;
            margin: 10px 0;
            flex-wrap: wrap;
        }

        button {
            background-color: #333;
            color: var(--text-color);
            border: 1px solid #555;
            padding: 8px 16px;
            border-radius: 4px;
            cursor: pointer;
            font-family: 'Courier New', monospace;
        }

        button:hover {
            background-color: #444;
        }

        .status-bar {
            display: flex;
            justify-content: space-between;
            margin-top: 10px;
            font-size: 12px;
            color: #888;
        }
    </style>
</head>
<body>
    <div class="header">
        <h1>Web Shell - Полный доступ к системе</h1>
        <div class="warning">
            ⚠️ ВНИМАНИЕ: Полный доступ к оболочке системы! Будьте осторожны с командами.
        </div>
    </div>

    <div class="controls">
        <button onclick="clearTerminal()">Очистить терминал</button>
        <button onclick="clearSession()">Новая сессия</button>
        <button onclick="showHistory()">История команд</button>
        <button onclick="showSystemInfo()">Информация о системе</button>
    </div>

    <div class="terminal" id="terminal">
        <div class="output info">
            Добро пожаловать в Web Shell с полным доступом!<br>
            Доступны все команды системы. Таймаут выполнения: 60 секунд.<br>
            Текущая директория: <span id="current-dir"></span>
        </div>
    </div>

    <div class="command-line">
        <span class="prompt" id="prompt">$</span>
        <input type="text" id="command-input" placeholder="Введите любую команду системы...">
    </div>

    <div class="status-bar">
        <span id="session-info">Загрузка...</span>
        <span id="command-counter">Команд выполнено: 0</span>
    </div>

    <script>
        const terminal = document.getElementById('terminal');
        const commandInput = document.getElementById('command-input');
        const sessionInfo = document.getElementById('session-info');
        const commandCounter = document.getElementById('command-counter');
        const currentDirElement = document.getElementById('current-dir');
        const promptElement = document.getElementById('prompt');

        let commandCount = 0;

        // Фокус на поле ввода
        commandInput.focus();

        // Загружаем начальную информацию
        loadSessionInfo();
        loadSystemInfo();

        // Обработка отправки команды
        commandInput.addEventListener('keypress', function(e) {
            if (e.key === 'Enter') {
                const command = commandInput.value.trim();
                if (command) {
                    executeCommand(command);
                    commandInput.value = '';
                }
            }
        });

        function executeCommand(command) {
            // Показываем команду в терминале
            addOutput('$ ' + command, 'user-command');

            // Отправляем на сервер
            fetch('/execute', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify({command: command})
            })
            .then(response => response.json())
            .then(data => {
                if (data.error) {
                    addOutput(data.error, 'error');
                } else {
                    addOutput(data.output, 'command-output');
                    // Обновляем текущую директорию
                    if (data.current_dir) {
                        currentDirElement.textContent = data.current_dir;
                        promptElement.textContent = `[${data.current_dir.split('/').pop() || data.current_dir}] $`;
                    }
                }
                commandCount++;
                commandCounter.textContent = `Команд выполнено: ${commandCount}`;
            })
            .catch(error => {
                addOutput('Ошибка сети: ' + error, 'error');
            });
        }

        function addOutput(text, className) {
            const output = document.createElement('div');
            output.className = `output ${className}`;
            output.textContent = text;
            terminal.appendChild(output);
            terminal.scrollTop = terminal.scrollHeight;
        }

        function clearTerminal() {
            terminal.innerHTML = '<div class="output info">Терминал очищен</div>';
            loadSystemInfo();
        }

        function clearSession() {
            if (confirm('Вы уверены, что хотите начать новую сессию?')) {
                fetch('/clear-session', {method: 'POST'})
                    .then(() => {
                        location.reload();
                    });
            }
        }

        function showHistory() {
            fetch('/history')
                .then(response => response.json())
                .then(data => {
                    if (data.error) {
                        addOutput(data.error, 'error');
                    } else {
                        let historyText = 'Последние команды:\\n';
                        data.history.forEach((item, index) => {
                            historyText += `${index + 1}. [${item.timestamp}] ${item.command}\\n`;
                        });
                        addOutput(historyText, 'info');
                    }
                });
        }

        function showSystemInfo() {
            fetch('/system-info')
                .then(response => response.json())
                .then(data => {
                    if (data.error) {
                        addOutput(data.error, 'error');
                    } else {
                        let infoText = 'Информация о системе:\\n';
                        for (const [key, value] of Object.entries(data)) {
                            infoText += `${key}: ${value}\\n`;
                        }
                        addOutput(infoText, 'info');
                    }
                });
        }

        function loadSessionInfo() {
            fetch('/sessions')
                .then(response => response.json())
                .then(data => {
                    sessionInfo.textContent = `Активных сессий: ${data.active_sessions}`;
                    if (data.current_dir) {
                        currentDirElement.textContent = data.current_dir;
                        promptElement.textContent = `[${data.current_dir.split('/').pop() || data.current_dir}] $`;
                    }
                });
        }

        function loadSystemInfo() {
            // Загружаем базовую информацию о системе при старте
            showSystemInfo();
        }

        // Автодополнение по Tab
        commandInput.addEventListener('keydown', function(e) {
            if (e.key === 'Tab') {
                e.preventDefault();
                // Базовая реализация автодополнения
                const currentInput = commandInput.value;
                // Можно расширить для реального автодополнения
            }
        });
    </script>
</body>
</html>''')

    print("Запуск Web Shell с полным доступом...")
    print("⚠️  ПРЕДУПРЕЖДЕНИЕ: Этот сервер предоставляет полный доступ к системе!")
    print("🚫 Не используйте в production без дополнительных мер безопасности!")
    print("🌐 Сервер запущен: http://0.0.0.0:5000")

    app.run(host='0.0.0.0', port=5000, debug=False)