# 工具基类
'''
工具基类----工具子类约束:
1.一个工具子类一个py文件
2.工具类文件名必须为:[指令名]+Tool.py
3.未完成的工具子类以__开头
4.
'''
import abc
import subprocess
import argparse
import shlex
import subprocess
import tempfile
import os
from myutils.ReturnParams import ReturnParams

class ToolBase(abc.ABC):
    def get_time_out(self):
        return 60*30

    def create_extparams(self):
        ext_params = ReturnParams()
        ext_params["is_user"] = False  # 是否要提交用户确认 -- 默认False
        ext_params["is_vulnerability"] = False  # 是否是脆弱点
        return ext_params

    def parse_sublist3r_command(self,command):
        parser = argparse.ArgumentParser(add_help=False,allow_abbrev=False)    #创建命令行参数解析器对象‌
        parser.add_argument("-o", "--output", type=str)     #添加需要解析的参数规则‌
        parser.add_argument("-oN", "--Noutput", type=str)   #nmap
        parser.add_argument("-oG","--NMG",type=str)         #nmap
        parser.add_argument("-output", "--nikto", type=str) #nikto
        args, _ = parser.parse_known_args(command.split()[1:])
        return args

    def parse_output_params(self,command, valid_flags=None):
        """
        解析 shell 命令中用于输出文件的参数。
        只检查完全匹配的 token,忽略诸如 "-oKexAlgorithms" 这样的参数。

        :param command: 完整的命令字符串
        :param valid_flags: 合法的输出参数列表,默认支持 -o, -oN, -oG, -output
        :return: dict,键为输出参数,值为对应的文件路径(如果有的话)
        """
        if command.strip().startswith("mkdir"):
            return ""
        #额外指令排除:ssh -o 不是输出结果到文件
        if "ssh" in command:
            return ""

        if valid_flags is None:
            valid_flags = ['-o', '-oN', '-oG', '-output']

        tokens = shlex.split(command)
        output_params = {}
        output_file = ""
        i = 0
        while i < len(tokens):
            token = tokens[i]
            # 如果 token 完全匹配有效的参数,则认为它是输出参数
            if token in valid_flags:
                # 如果紧跟着有值,则把它作为输出文件路径,否则为 None
                if i + 1 < len(tokens):
                    #output_params[token] = tokens[i + 1]
                    #i += 2
                    output_file = tokens[i+1]
                # else:
                #     output_params[token] = None
                #     i += 1
                break  # 一般只有一个输出参数
            else:
                i += 1
        return output_file

    def read_output_file(self,filename):
        """
        读取 -o 参数指定的文件内容
        """
        content = ""
        try:
            with open(filename, "r") as f:
                content = f.read()
            with open(filename,'w') as f:
                f.write("")
        except FileNotFoundError:
            print(f"错误: 文件 {filename} 不存在")
        except PermissionError:
            print(f"错误: 无权限读取文件 {filename}")
        return content

    def do_worker_script(self,str_instruction,timeout,ext_params):
        # 创建临时文件保存输出
        with tempfile.NamedTemporaryFile(delete=False) as tmpfile:
            output_file = tmpfile.name

        # 构建并执行 script 命令
        script_cmd = f"script -c '{str_instruction}' {output_file}"
        try:
            result = subprocess.run(script_cmd, shell=True, text=True,timeout=timeout)
            # 读取输出文件内容
            with open(output_file, 'r') as f:
                output = f.read()
                lines = output.splitlines()
                # 跳过第一行(Script started)和最后一行(Script done)
                ftp_output = lines[1:-1]
                output = '\n'.join(ftp_output)
        except subprocess.TimeoutExpired:
            output = "命令超时返回"
            try:
                with open(output_file, 'r') as f:
                    partial_output = f.read()
                if partial_output:
                    output += f"\n部分输出:\n{partial_output}"
            except FileNotFoundError:
                pass  # 文件可能未创建
        except subprocess.CalledProcessError as e:
            output = f"错误: {e}"
        finally:
            # 删除临时文件
            try:
                os.remove(output_file)
            except FileNotFoundError:
                pass  # 文件可能未创建
        return output

    def execute_instruction(self, instruction_old):
        '''
        执行指令:验证合法性 -> 执行 -> 分析结果
        *****如果指令要做验证,只做白名单,所有逻辑不是全放开就是白名单*****
        :param instruction_old:
        :return:
            bool:true-正常返回给大模型处理下一步,false-结果不返回给大模型,2--需要人工确认的指令
            str:执行的指令
            str:执行指令的结果-解析过滤后的结果--也是提交给LLM的结果
            str:执行指令的结果-原结果
            object:补充参数-封装一个对象: 0-不知是否攻击成功,1-明确存在漏洞,2-明确不存在漏洞
        '''

        ext_params = self.create_extparams()
        # 第一步:验证指令合法性
        instruction,timeout = self.validate_instruction(instruction_old)
        if not instruction:
            ext_params.is_user= True
            return False,instruction_old,"该指令暂不执行!由用户确认是否要兼容支持","",ext_params      #未
        #过滤修改后的指令是否需要判重?同样指令再执行结果一致?待定---#?

        # 第二步:执行指令
        #print(f"执行指令:{instruction}")
        output = ""
        stdout = ""
        stderr = ""
        try:
            if timeout == 0:
                result = subprocess.run(instruction, shell=True, capture_output=True, text=True,timeout=60*30)
            elif timeout >0:
                result = subprocess.run(instruction, shell=True, capture_output=True, text=True,  timeout=timeout)
            else:
                print("timeout参数错误,需要自查程序逻辑!")
            #output = result.stdout if result.stdout else result.stderr
            #-o 的命令需要处理
            parsed_arg = self.parse_output_params(instruction)
            if parsed_arg:
                fole_out = self.read_output_file(parsed_arg)
                stderr =""
                stdout = fole_out
            else:
                stderr = result.stderr
                stdout = result.stdout

            # 第三步:分析执行结果
            # if result.returncode == 0:  # 执行正确
            #     output = stdout
            if stdout:
                output = stdout
            else:
                if result.returncode ==28:    #curl 命令超时
                    output = "指令执行超时!"
                else:   #执行错误只拿错误信息
                    output = stderr
            if isinstance(output, bytes):  # 若是bytes则转成str
                output = output.decode('utf-8', errors='ignore')

            analysis = self.analyze_result(output, instruction, stderr, stdout)
            if not analysis:  # analysis为“” 不提交LLM
                ext_params.is_user = True
            return True, instruction, analysis, output, ext_params

        except subprocess.TimeoutExpired:
            ext_params.is_user = True   #对于超时的也需要人工进行确认,是否是预期的超时
            return False, instruction, f"执行超时:{str(timeout)}秒", "", ext_params  # 执行失败,提交给人工确认指令的正确性
        except Exception as e:
            ext_params.is_user = True
            return False,instruction,f"执行失败:{str(e)}","",ext_params #执行失败,提交给人工确认指令的正确性

    @abc.abstractmethod
    def validate_instruction(self, instruction:str):
        """指令合法性分析
        :return:
        str:非空代表合法(函数内会涉及修改指令),为空代表非法已取消指令
        int:表示超时时间,0不设置超时时间,正值具体的超时时间设置。单位秒
        """
        pass

    @abc.abstractmethod
    def analyze_result(self, result:str,instruction:str,stderr:str,stdout:str) -> str:
        """分析执行结果"""
        pass

def read_output_file_test(filename):
    """
    读取 -o 参数指定的文件内容
    """
    try:
        with open(filename, "r") as f:
            return f.read()
    except FileNotFoundError:
        print(f"错误: 文件 {filename} 不存在")
    except PermissionError:
        print(f"错误: 无权限读取文件 {filename}")
    return ""

if __name__ =="__main__":
    pass