import requests
import shlex
import re
import json
from bs4 import BeautifulSoup
from tools.ToolBase import ToolBase

class CurlTool(ToolBase):
    # def __init__(self):
    #     super.__init__()
    #     self.headers = {}
    #     self.url = None
    #     self.verify_ssl = True

    #解析指令到requests
    def parse_curl_to_requests(self,curl_command):
        # Split command preserving quoted strings
        parts = shlex.split(curl_command)
        if parts[0] != 'curl':
            raise ValueError("Command must start with 'curl'")

        # Parse curl flags and arguments
        i = 1
        while i < len(parts):
            arg = parts[i]
            if arg == '-k' or arg == '--insecure':
                self.verify_ssl = False
                i += 1
            elif arg == '-s' or arg == '--silent':
                # Silencing isn't needed for requests, just skip
                i += 1
            elif arg == '-H' or arg == '--header':
                if i + 1 >= len(parts):
                    raise ValueError("Missing header value after -H")
                header_str = parts[i + 1]
                header_name, header_value = header_str.split(':', 1)
                self.headers[header_name.strip()] = header_value.strip()
                i += 2
            elif not arg.startswith('-'):
                if self.url is None:
                    self.url = arg
                i += 1
            else:
                i += 1

        if self.url is None:
            raise ValueError("No URL found in curl command")

        #return url, headers, verify_ssl

    def validate_instruction(self, instruction_old):
        #instruction  = instruction_old
        #指令过滤
        timeout = 0
        #添加-i 返回信息头
        parts = instruction_old.split()
        if 'base64 -d' in instruction_old:
            return instruction_old
        if '-i' not in parts and '--include' not in parts:
            url_index = next((i for i, p in enumerate(parts) if p.startswith(('http://', 'https://'))), None)
            if url_index is not None:
                # 在URL前插入 -i 参数‌:ml-citation{ref="1" data="citationList"}
                parts.insert(url_index, '-i')
            else:
                # 无URL时直接在末尾添加
                parts.append('-i')
        return ' '.join(parts),timeout

    # def execute_instruction(self, instruction_old):
    #     '''
    #     执行指令:验证合法性 -> 执行 -> 分析结果
    #     :param instruction_old:
    #     :return:
    #         bool:true-正常返回给大模型,false-结果不返回给大模型
    #         str:执行的指令
    #         str:执行指令的结果
    #     '''
    #
    #     # 第一步:验证指令合法性
    #     instruction = self.validate_instruction(instruction_old)
    #     if not instruction:
    #         return False,instruction_old,"该指令暂不执行!"
    #
    #     # 第二步:执行指令  --- 基于request使用
    #     #print(f"执行指令:{instruction}")
    #     output = ""
    #
    #     # 第三步:分析执行结果
    #     analysis = self.analyze_result(output,instruction)
    #     #指令和结果入数据库
    #     #?
    #     if not analysis:    #analysis为“” 不提交LLM
    #         return False,instruction,analysis
    #     return True,instruction, analysis

    def get_ssl_info(self,stderr,stdout):
        # --------------------------
        # 解释信息的安全意义:
        #
        # - 如果证书的 Common Name 与请求的 IP 不匹配(如这里的 'crnn.f3322.net'),
        #   则可能表明服务隐藏了真实身份或存在配置错误,这在后续攻击中可以作为信息收集的一部分。
        #
        # - TLS 连接信息(如 TLS1.3 和加密套件)有助于判断是否存在弱加密或旧版协议问题。
        #
        # - HTTP 状态和 Content-Type 帮助确认返回的是一个合法的 Web 服务,
        #   而 HTML Title 暗示了实际运行的是 SoftEther VPN Server,可能存在默认配置或已知漏洞。
        #
        # 这些信息可以作为进一步探测、漏洞验证和渗透测试的依据。
        # --------------------------
        # 从 stderr 中提取证书及 TLS 信息
        # 提取 Common Name(CN)
        cn_match = re.search(r"common name:\s*([^\s]+)", stderr, re.IGNORECASE)
        cert_cn = cn_match.group(1) if cn_match else "N/A"

        # 提取 TLS 连接信息(例如 TLS1.3 及加密套件)
        tls_match = re.search(r"SSL connection using\s+([^\n]+)", stderr, re.IGNORECASE)
        tls_info = tls_match.group(1).strip() if tls_match else "N/A"

        # 提取 Issuer 信息
        issuer_match = re.search(r"issuer:\s*(.+)", stderr, re.IGNORECASE)
        issuer_info = issuer_match.group(1).strip() if issuer_match else "N/A"

        # 从 stdout 中提取 HTTP 响应头和 HTML 标题
        # 分离 HTTP 头部和 body(假设头部与 body 用两个换行符分隔)
        parts = stdout.split("\n\n", 1)
        headers_part = parts[0]
        body_part = parts[1] if len(parts) > 1 else ""

        # 从头部中提取状态行和部分常见头部信息
        lines = headers_part.splitlines()
        http_status = lines[0] if lines else "N/A"
        content_type_match = re.search(r"Content-Type:\s*(.*)", headers_part, re.IGNORECASE)
        content_type = content_type_match.group(1).strip() if content_type_match else "N/A"

        # 使用 BeautifulSoup 提取 HTML <title>
        soup = BeautifulSoup(body_part, "html.parser")
        html_title = soup.title.string.strip() if soup.title and soup.title.string else "N/A"

        # --------------------------
        # 输出提取的信息
        # print("=== 提取的有用信息 ===")
        # print("HTTP 状态行:", http_status)
        # print("Content-Type:", content_type)
        # print("HTML Title:", html_title)
        # print("TLS 连接信息:", tls_info)
        # print("证书 Common Name:", cert_cn)
        # print("证书 Issuer:", issuer_info)
        result = f"HTTP 状态行:{http_status},Content-Type:{content_type},HTML Title:{html_title},TLS 连接信息:{tls_info},证书 Common Name:{cert_cn},证书 Issuer:{issuer_info}"
        return result

    def get_info_xpost(self,stdout,stderr):
        """
            从 subprocess.run 执行 curl 后的结果中提取关键信息:
              - HTTP 状态码
              - 常见响应头(Content-Type, Content-Length)
              - HTML 页面标题(如果内容为 HTML)
              - 返回正文的前200字符(body_snippet)
              - TLS/证书相关信息(从详细调试信息 stderr 中提取)

            对于未匹配到的信息,返回“Not found”或空字符串。
            """
        info = {}

        # 处理 stdout: 拆分响应头与正文(假设用空行分隔)
        parts = re.split(r'\r?\n\r?\n', stdout, maxsplit=1)
        #***************解析方式一
        # headers_str = parts[0] if parts else ""
        # body = parts[1] if len(parts) > 1 else ""
        #
        # # 提取 HTTP 状态码(从响应头第一行中获取,例如 "HTTP/1.1 202 OK")
        # header_lines = headers_str.splitlines()
        # ***************解析方式二
        if len(parts) == 2:
            headers_str, body = parts
        else:
            # 如果没有拆分成功,可能 stdout 中只有正文,则从 stderr 尝试提取 HTTP 状态行
            headers_str = ""
            body = stdout

            # 如果没有在 stdout 中找到头信息,则尝试从 stderr 中提取(部分信息可能在 stderr 中)
        if not headers_str:
            header_lines = stderr.splitlines()
        else:
            header_lines = headers_str.splitlines()
        #**************************

        if header_lines:
            status_line = header_lines[0]
            status_match = re.search(r'HTTP/\d+\.\d+\s+(\d+)', status_line)
            info['status_code'] = status_match.group(1) if status_match else "Unknown"
        else:
            info['status_code'] = "No headers found"

        # 提取常见响应头
        content_type = "Not found"
        content_length = "Not found"
        for line in header_lines:
            if line.lower().startswith("content-type:"):
                info['content_type'] = line.split(":", 1)[1].strip()
            elif line.lower().startswith("content-length:"):
                info['content_length'] = line.split(":", 1)[1].strip()
        # 如果未匹配到,则设置默认值
        info.setdefault('content_type', "Not found")
        info.setdefault('content_length', "Not found")

        # 如果内容为 HTML,则使用 BeautifulSoup 提取 <title> 标签内容
        if "html" in info['content_type'].lower():
            try:
                soup = BeautifulSoup(body, "html.parser")
                if soup.title and soup.title.string:
                    info['html_title'] = soup.title.string.strip()
                else:
                    info['html_title'] = "Not found"
            except Exception as e:
                info['html_title'] = f"Error: {e}"
        else:
            info['html_title'] = "N/A"

        # 保存部分正文内容,便于后续分析
        info['body_snippet'] = body[:200]  # 前500字符

        # 处理 stderr 中的 TLS/证书信息:只提取包含关键字的行
        tls_info_lines = []
        cert_info_lines = []
        for line in stderr.splitlines():
            # 过滤与 TLS/SSL 握手、证书相关的信息
            if "SSL connection using" in line or "TLS" in line:
                tls_info_lines.append(line.strip())
            if "certificate" in line.lower():
                cert_info_lines.append(line.strip())
        info['tls_info'] = tls_info_lines if tls_info_lines else "Not found"
        info['certificate_info'] = cert_info_lines if cert_info_lines else "Not found"

        # 可选:保留完整的 verbose 信息以便后续分析
        #info['verbose'] = stderr
        #转换成字符串
        result = json.dumps(info,ensure_ascii=False)
        return result

    def analyze_result(self, result,instruction,stderr,stdout):
        #指令结果分析
        if("-H "in instruction and "Host:" in instruction):# 基于证书域名进行跨站劫持
            if "HTTP/1.1 200" in result:    # and "<urlset" in result:
                #result = "存在问题:成功通过 Host 头获取 sitemap 内容。"
                result = "返回200,存在问题"   #要进一步分析返回内容
            else:
                result = "未检测到问题"
        elif("-H "in instruction and "Range:" in instruction):
            if "HTTP/1.1 200 OK" in result:
                result = "正常返回了200 OK页面"
            elif "HTTP/1.1 416" in result:
                 if "Content-Range: bytes" in result:
                     result = "返回 416 且 Content-Range 正常:服务器对 Range 请求处理正确。"
                 elif "Content-Range:" in result:
                     result = "返回 416 但缺少或异常 Content-Range 头"
            else:#"返回其他状态码(", response.status_code, "):需要进一步分析。"
                result = "服务器对Range请求处理正确"
        elif("-H "in instruction and "Referer:" in instruction):
            if "HTTP/1.1 200" in result:
                result="该漏洞无法利用"
            else: #保留原结果
                pass
        elif("resource=config.php" in instruction):
            if "base64: 无效的输入" in result:
                result="该漏洞无法利用"
        elif("Date:" in instruction):  #保留原结果
            print("")
        elif("-kv https://" in instruction or "-vk https://"  in instruction):
            result = self.get_ssl_info(stderr,stdout)
        elif("-X POST " in instruction):
            result = self.get_info_xpost(stdout,stderr)
        elif("-v " in instruction): #curl -v http://192.168.204.137:8180/manager/html --user admin:admin 常规解析curl返回内容
            result = self.get_info_xpost(stdout,stderr)
        else:   #非处理命令的结果,暂时不提交LLM
            result =""
        return result

if __name__ =="__main__":
    import subprocess
    CT = CurlTool()
    strinstruction = "curl -kv -X POST -d \"username=admin&password=admin\" https://58.216.217.70/vpn/index.html --connect-timeout 10"
    instruction,time_out = CT.validate_instruction(strinstruction)
    if instruction:
        result = subprocess.run(instruction, shell=True, capture_output=True, text=True)
        res = CT.analyze_result(result.stdout,instruction,result.stderr,result.stdout)
        print(res)