import asyncio
import sys
import os
import subprocess
import ctypes

# ⚠️ CHANGE THIS PASSWORD! Anyone with this password can access your PC's terminal.
PASSWORD = "12345" 
PORT = 8021

def elevate_privileges():
    try:
        if not ctypes.windll.shell32.IsUserAnAdmin():
            print("[!] Administrator privileges required. Prompting for UAC...")
            script = os.path.abspath(sys.argv[0])
            ctypes.windll.shell32.ShellExecuteW(
                None, "runas", sys.executable, f'"{script}"', None, 1
            )
            sys.exit(0)
    except Exception as e:
        print(f"[-] Failed to elevate privileges: {e}")
        sys.exit(1)

elevate_privileges()

def check_deps():
    try:
        import fastapi
        import uvicorn
    except ImportError:
        print("[*] Installing required web dependencies...")
        subprocess.check_call([sys.executable, "-m", "pip", "install", "fastapi", "uvicorn", "websockets"])

check_deps()

from fastapi import FastAPI, WebSocket, WebSocketDisconnect
from fastapi.responses import HTMLResponse, Response
import uvicorn

app = FastAPI()

def find_hackashell():
    base = os.path.dirname(os.path.abspath(__file__))
    candidates = [
        os.path.join(base, "HackaShell.py"),
        os.path.join(base, "HackaShell", "HackaShell.py"),
    ]
    for path in candidates:
        if os.path.exists(path):
            return path
    return None

HACKASHELL_PATH = find_hackashell()

html = """
<!DOCTYPE html>
<html>
<head>
    <title>HackaShell Connect</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/xterm@5.3.0/css/xterm.css" />
    <style>
        body { margin: 0; background: #1e1e1e; overflow: hidden; color: #d4d4d4; font-family: sans-serif; }
        #terminal { width: 100vw; height: 100vh; display: none; }
        #login { display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100vh; }
        input { padding: 10px; font-size: 16px; margin-bottom: 10px; border-radius: 5px; border: none; }
        button { padding: 10px 20px; font-size: 16px; border-radius: 5px; border: none; background: #4CAF50; color: white; cursor: pointer; }
        button:hover { background: #45a049; }
        .error { color: #ff5555; margin-top: 10px; display: none; }
    </style>
</head>
<body>
    <div id="login">
        <h2>HackaShell Remote Access</h2>
        <input type="password" id="pwd" placeholder="Enter Password">
        <button onclick="connect()">Connect</button>
        <p class="error" id="err">Incorrect password or connection failed.</p>
    </div>
    <div id="terminal"></div>

    <script src="https://cdn.jsdelivr.net/npm/xterm@5.3.0/lib/xterm.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/xterm-addon-fit@0.8.0/lib/xterm-addon-fit.js"></script>
    <script>
        let ws;
        function connect() {
            const pwd = document.getElementById('pwd').value;
            const err = document.getElementById('err');
            
            // 🔒 FIX: Automatically use wss:// for HTTPS (Cloudflare) and ws:// for HTTP (Localhost)
            const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
            ws = new WebSocket(`${protocol}//${window.location.host}/ws?token=${encodeURIComponent(pwd)}`);
            
            let lineBuffer = "";

            ws.onopen = () => {
                document.getElementById('login').style.display = 'none';
                document.getElementById('terminal').style.display = 'block';
                
                const term = new Terminal({ cursorBlink: true, fontSize: 14, fontFamily: 'Consolas, monospace', theme: { background: '#1e1e1e', foreground: '#d4d4d4' } });
                const fitAddon = new FitAddon.FitAddon();
                term.loadAddon(fitAddon);
                term.open(document.getElementById('terminal'));
                fitAddon.fit();
                window.addEventListener('resize', () => fitAddon.fit());

                term.onData(data => {
                    if (data === '\\r') { ws.send(lineBuffer + '\\n'); lineBuffer = ""; } 
                    else if (data === '\\x7f' || data === '\\b') { if (lineBuffer.length > 0) { lineBuffer = lineBuffer.slice(0, -1); term.write('\\b \\b'); } } 
                    else if (data >= String.fromCharCode(0x20) && data <= String.fromCharCode(0x7E)) { lineBuffer += data; term.write(data); }
                });

                ws.onmessage = event => term.write(event.data);
                ws.onclose = () => { term.write('\\r\\n[Connection closed. Refresh to reconnect.]\\r\\n'); };
            };

            ws.onerror = () => { err.style.display = 'block'; };
        }
    </script>
</body>
</html>
"""

@app.get("/")
async def get():
    return HTMLResponse(html)

@app.get("/favicon.ico")
async def favicon():
    return Response(status_code=204)

@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
    # 🔒 PASSWORD CHECK
    token = websocket.query_params.get("token")
    if token != PASSWORD:
        await websocket.close(code=1008) # Policy violation
        return

    await websocket.accept()

    if HACKASHELL_PATH is None:
        await websocket.send_text("[!] HackaShell.py not found!\r\n")
        await websocket.close()
        return

    proc = await asyncio.create_subprocess_exec(
        sys.executable, "-u", HACKASHELL_PATH,
        stdin=asyncio.subprocess.PIPE,
        stdout=asyncio.subprocess.PIPE,
        stderr=asyncio.subprocess.STDOUT,
        cwd=os.path.dirname(HACKASHELL_PATH)
    )

    async def read_output():
        try:
            while True:
                data = await proc.stdout.read(1024)
                if not data: break
                text = data.decode('utf-8', errors='replace').replace('\r\n', '\n').replace('\n', '\r\n')
                await websocket.send_text(text)
        except Exception: pass
        finally:
            try: await websocket.close()
            except: pass

    async def read_input():
        try:
            while True:
                data = await websocket.receive_text()
                if proc.stdin:
                    proc.stdin.write(data.encode('utf-8'))
                    await proc.stdin.drain()
        except WebSocketDisconnect: pass
        except Exception: pass
        finally:
            if proc.returncode is None: proc.terminate()

    await asyncio.gather(read_output(), read_input())
    if proc.returncode is None: proc.terminate()

if __name__ == "__main__":
    if HACKASHELL_PATH:
        print(f"[*] Found HackaShell at: {HACKASHELL_PATH}")
    print(f"[*] Starting HackaShell Connect on http://localhost:{PORT}")
    print("[*] Keep this window open while using the web interface.")
    uvicorn.run(app, host="127.0.0.1", port=PORT)