#!/usr/bin/python3

import json
import sqlite3
from db import *
from globalSettings import *
from global_func import *
from all_paths import UPLOAD_PEM_PATH
import cgi
import os
import shutil
import cgitb
from datetime import datetime
import subprocess
import time
import signal
cgitb.enable()


conn = sqlite3.connect(DBPath)
conn.text_factory = str
c = conn.cursor()

device = DeviceName

if (GUI == true):
    form = cgi.FieldStorage()
    request_Type = form.getvalue('requestType')

else:
    print("1.Update Enable SSH")
    print("1.Read Enable SSH")
    choice = int(input("select cgi Function===>"))
    if choice == 1:
        request_Type = 'udpate_enable_ssh'
    if choice == 1:
        request_Type = 'read_enable_ssh'
    else:
        print("Enter Valid choice")


def udpate_enable_ssh():
    cmd = "update"
    cmd_id = get_ccm_command_id()
    enable_ssh = form.getvalue('type')
    enabled = form.getvalue('enabled')
    if (CCM == true):
        message = "set,element=enable_ssh,args=14,uuid={0},action={1},enable_ssh={2},enabled={3},device={4}"\
            .format(cmd_id, cmd, enable_ssh,enabled, device)
        send_ccm_msg(message)
        response = get_ccm_response(cmd_id)
        print(response)
        delete_ccm_response(cmd_id)
    else:
        c.execute("update enable_ssh SET enable_ssh =?, enabled =? ", (enable_ssh, enabled))
        conn.commit()
        print(json.JSONEncoder().encode({"description": "Enable SSH updated Successfully"}))

def udpate_reverse_ssh():
    cmd = "update_reverse"
    cmd_id = get_ccm_command_id()
    enable_reverse = form.getvalue('enable_reverse')
    rev_enabled = form.getvalue('rev_enabled')
    remote_ip = form.getvalue("rs_remote_ip")
    remote_port = form.getvalue("rs_remote_port")
    username = form.getvalue("rs_remote_user")
    local_port = form.getvalue("rs_local_port")
    rs_key_file_name = form.getvalue("rs_key_file_name")

    fileitem = form['rs_key_file'] if 'rs_key_file' in form else None
    saved_filename = rs_key_file_name
    if fileitem is not None and fileitem.filename:
        filename = os.path.basename(fileitem.filename)
        saved_filename = f"{filename}"
        filepath = os.path.join(UPLOAD_PEM_PATH, saved_filename)

        os.makedirs(UPLOAD_PEM_PATH, exist_ok=True)

        with open(filepath, 'wb') as f:
            shutil.copyfileobj(fileitem.file, f)
        
        os.chmod(filepath, 0o600)
        os.chown(filepath, uid=33, gid=33)

    if (CCM == true):
        message = "set,element=enable_ssh,args=14,uuid={0},action={1},enable_reverse={2},rev_enabled={3},remote_ip={4},remote_port={5},username={6},local_port={7},key_path={8},device={9}"\
            .format(cmd_id, cmd, enable_reverse,rev_enabled, remote_ip, remote_port, username, local_port, saved_filename, device)
        send_ccm_msg(message)
        response = get_ccm_response(cmd_id)
        print(response)
        delete_ccm_response(cmd_id)
    else:
        c.execute("update enable_ssh SET enable_reverse =?, rev_enabled =?, remote_ip =?, remote_port =?, username =?, local_port =?, key_path =? ", (enable_reverse, rev_enabled, remote_ip, remote_port, username, local_port, saved_filename))
        conn.commit()
        print(json.JSONEncoder().encode({"description": "Enable SSH updated Successfully"}))

def connectReverseSSH():
    c.execute("SELECT remote_ip, remote_port, username, local_port, key_path FROM enable_ssh")
    data = c.fetchone()
    remote_ip = data[0]
    remote_port = data[1]
    username = data[2]
    local_port = data[3]
    key_file = data[4]
    filepath = os.path.join(UPLOAD_PEM_PATH,  key_file)

    key_filename = os.path.basename(filepath)
    pub_key_path = os.path.join(UPLOAD_PEM_PATH, f"{key_filename}.pub")

    ssh_dir = os.path.expanduser("~/.ssh")
    auth_keys = os.path.join(ssh_dir, "authorized_keys")

    try:
        os.chmod(filepath, 0o600)

        # Ensure ~/.ssh exists
        os.makedirs(ssh_dir, exist_ok=True)
        os.chmod(ssh_dir, 0o700)

        # Generate public key
        subprocess.run(["ssh-keygen", "-y", "-f", filepath], check=True, stdout=open(pub_key_path, "w"))
        with open(pub_key_path, "r") as pub_key_file:
            pub_key = pub_key_file.read()

        # Append to authorized_keys if not already present
        if not os.path.exists(auth_keys) or pub_key.strip() not in open(auth_keys).read():
            with open(auth_keys, "a") as f:
                f.write(pub_key.strip() + "\n")
            os.chmod(auth_keys, 0o600)

    except Exception as e:
        print(json.JSONEncoder().encode({"description": f"Failed to prepare key for SSH: {e}", "status": "error"}))
        return
    
    # cmd = f"ssh -N -T -R {remote_port}:localhost:{local_port} -i {filepath} {username}@{remote_ip}"
    cmd = [
        "ssh", 
        "-N", "-T",
        "-o", "ExitOnForwardFailure=yes",
        "-o", "StrictHostKeyChecking=no",
        "-o", "UserKnownHostsFile=/dev/null",
        "-i", filepath,
        "-R", f"{remote_port}:localhost:{22}",
        f"{username}@{remote_ip}"
    ]
    try:
        proc = subprocess.Popen(
            cmd,
            stdout=subprocess.DEVNULL,
            stderr=subprocess.DEVNULL,
            stdin=subprocess.DEVNULL,
            preexec_fn=os.setsid  # ensures it runs independently from the parent
        )
        time.sleep(2)
        returncode = proc.poll()

        if returncode is not None:
            # SSH failed
            stderr_output = proc.stderr.read().strip()
            return json.dumps({"description": f"SSH failed: {stderr_output}", "status": "error"})


        # Save the PID
        with open(PID_FILE, "w") as f:
            f.write(str(proc.pid))

        print(json.JSONEncoder().encode({"description": "Reverse SSH connected Successfully", "status" : "success"}))
    except Exception as e:
        print(json.JSONEncoder().encode({"description": f"Error : {e}", "status" : "error"}))

def disconnectReverseSSH():
    if os.path.exists(PID_FILE):
        try:
            with open(PID_FILE, "r") as f:
                pid = int(f.read().strip())
            os.kill(pid, signal.SIGTERM)
            os.remove(PID_FILE)
            print(json.JSONEncoder().encode({"description": "Reverse SSH disconnected", "status": "success"}))
        except Exception as e:
            print(json.JSONEncoder().encode({"description": f"Failed to disconnect: {e}", "status": "error"}))
    else:
        print(json.JSONEncoder().encode({"description": "No reverse SSH connection found", "status": "error"}))
    
def reverseSSHStatus():
    if os.path.exists(PID_FILE):
        try:
            with open(PID_FILE, "r") as f:
                pid = int(f.read().strip())
            # Check if the process exists
            os.kill(pid, 0)
            print(json.JSONEncoder().encode({"description": "Reverse SSH is connected", "status": "success", "connected" : True}))
        except ProcessLookupError:
            os.remove(PID_FILE)
            print(json.JSONEncoder().encode({"description": "Reverse SSH process not found", "status": "error", "connected" : False}))
        except Exception as e:
            print(json.JSONEncoder().encode({"description": f"Status check failed: {e}", "status": "error", "connected" : False}))
    else:
        print(json.JSONEncoder().encode({"description": "Reverse SSH not connected", "status": "error", "connected" : False}))


def read_enable_ssh():
    c.execute("SELECT enable_ssh, enabled FROM enable_ssh")
    data = c.fetchone()
    print(json.JSONEncoder().encode({"enable_ssh" : data[0], "enabled" : data[1]}))

def read_reverse_ssh():
    c.execute("SELECT enable_reverse, rev_enabled, remote_ip, remote_port, username, local_port, key_path FROM enable_ssh")
    data = c.fetchone()
    print(json.JSONEncoder().encode({"enable_reverse" : data[0], "rev_enabled" : data[1], "remote_ip" : data[2], "remote_port" : data[3], "username" : data[4], "local_port" : data[5], "key_path" : data[6]}))
    

print("Content-type:text/html\r\n\r\n")

if(request_Type == 'udpate_enable_ssh'):
    udpate_enable_ssh()
elif(request_Type == 'udpate_reverse_ssh'):
    udpate_reverse_ssh()
elif(request_Type == 'read_reverse_ssh'):
    read_reverse_ssh()
elif(request_Type == 'connectReverseSSH'):
    connectReverseSSH()
elif(request_Type == 'disconnectReverseSSH'):
    disconnectReverseSSH()
elif(request_Type == 'reverseSSHStatus'):
    reverseSSHStatus()
else:
    read_enable_ssh()