#!/usr/bin/env python

"""
Copyright (c) 2006-2024 sqlmap developers (https://sqlmap.org/)
See the file 'LICENSE' for copying permission
"""

# 导入所需的Python标准库
import base64  # 用于Base64编码解码
import datetime  # 处理日期和时间
import io  # 处理流式IO操作
import re  # 正则表达式支持
import time  # 时间相关功能

# 导入自定义和第三方库
from lib.core.bigarray import BigArray  # 用于处理大型数组
from lib.core.convert import getBytes  # 字符串转字节函数
from lib.core.convert import getText  # 字节转字符串函数
from lib.core.settings import VERSION  # 获取版本信息
from thirdparty.six.moves import BaseHTTPServer as _BaseHTTPServer  # HTTP服务器基类
from thirdparty.six.moves import http_client as _http_client  # HTTP客户端

# HAR(HTTP Archive)格式参考文档
# Reference: https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/HAR/Overview.html
#            http://www.softwareishard.com/har/viewer/

class HTTPCollectorFactory(object):
    """HTTP收集器工厂类,用于创建HTTP收集器实例"""
    def __init__(self, harFile=False):
        self.harFile = harFile

    def create(self):
        """创建并返回一个新的HTTP收集器实例"""
        return HTTPCollector()

class HTTPCollector(object):
    """HTTP收集器类,用于收集HTTP请求和响应信息"""
    def __init__(self):
        self.messages = BigArray()  # 存储请求-响应对
        self.extendedArguments = {}  # 存储扩展参数

    def setExtendedArguments(self, arguments):
        """设置扩展参数"""
        self.extendedArguments = arguments

    def collectRequest(self, requestMessage, responseMessage, startTime=None, endTime=None):
        """收集一对请求-响应消息"""
        self.messages.append(RawPair(requestMessage, responseMessage,
                                     startTime=startTime, endTime=endTime,
                                     extendedArguments=self.extendedArguments))

    def obtain(self):
        """获取HAR格式的日志数据"""
        return {"log": {
            "version": "1.2",
            "creator": {"name": "sqlmap", "version": VERSION},
            "entries": [pair.toEntry().toDict() for pair in self.messages],
        }}

class RawPair(object):
    """原始请求-响应对类"""
    def __init__(self, request, response, startTime=None, endTime=None, extendedArguments=None):
        self.request = getBytes(request)  # 请求数据
        self.response = getBytes(response)  # 响应数据
        self.startTime = startTime  # 开始时间
        self.endTime = endTime  # 结束时间
        self.extendedArguments = extendedArguments or {}  # 扩展参数

    def toEntry(self):
        """转换为Entry对象"""
        return Entry(request=Request.parse(self.request), response=Response.parse(self.response),
                     startTime=self.startTime, endTime=self.endTime,
                     extendedArguments=self.extendedArguments)

class Entry(object):
    """HAR条目类,表示一个完整的请求-响应交互"""
    def __init__(self, request, response, startTime, endTime, extendedArguments):
        self.request = request  # 请求对象
        self.response = response  # 响应对象
        self.startTime = startTime or 0  # 开始时间
        self.endTime = endTime or 0  # 结束时间
        self.extendedArguments = extendedArguments  # 扩展参数

    def toDict(self):
        """转换为字典格式"""
        out = {
            "request": self.request.toDict(),
            "response": self.response.toDict(),
            "cache": {},  # 缓存信息
            "timings": {  # 时间统计
                "send": -1,
                "wait": -1,
                "receive": -1,
            },
            "time": int(1000 * (self.endTime - self.startTime)),  # 总耗时(毫秒)
            "startedDateTime": "%s%s" % (datetime.datetime.fromtimestamp(self.startTime).isoformat(), time.strftime("%z")) if self.startTime else None
        }
        out.update(self.extendedArguments)
        return out

class Request(object):
    """HTTP请求类"""
    def __init__(self, method, path, httpVersion, headers, postBody=None, raw=None, comment=None):
        self.method = method  # 请求方法(GET/POST等)
        self.path = path  # 请求路径
        self.httpVersion = httpVersion  # HTTP版本
        self.headers = headers or {}  # 请求头
        self.postBody = postBody  # POST请求体
        self.comment = comment.strip() if comment else comment  # 注释
        self.raw = raw  # 原始请求数据

    @classmethod
    def parse(cls, raw):
        """解析原始请求数据"""
        request = HTTPRequest(raw)
        return cls(method=request.command,
                   path=request.path,
                   httpVersion=request.request_version,
                   headers=request.headers,
                   postBody=request.rfile.read(),
                   comment=request.comment,
                   raw=raw)

    @property
    def url(self):
        """构建完整URL"""
        host = self.headers.get("Host", "unknown")
        return "http://%s%s" % (host, self.path)

    def toDict(self):
        """转换为字典格式"""
        out = {
            "httpVersion": self.httpVersion,
            "method": self.method,
            "url": self.url,
            "headers": [dict(name=key.capitalize(), value=value) for key, value in self.headers.items()],
            "cookies": [],
            "queryString": [],
            "headersSize": -1,
            "bodySize": -1,
            "comment": getText(self.comment),
        }

        if self.postBody:
            contentType = self.headers.get("Content-Type")
            out["postData"] = {
                "mimeType": contentType,
                "text": getText(self.postBody).rstrip("\r\n"),
            }

        return out

class Response(object):
    """HTTP响应类"""
    extract_status = re.compile(b'\\((\\d{3}) (.*)\\)')  # 用于提取状态码的正则表达式

    def __init__(self, httpVersion, status, statusText, headers, content, raw=None, comment=None):
        self.raw = raw  # 原始响应数据
        self.httpVersion = httpVersion  # HTTP版本
        self.status = status  # 状态码
        self.statusText = statusText  # 状态描述
        self.headers = headers  # 响应头
        self.content = content  # 响应内容
        self.comment = comment.strip() if comment else comment  # 注释

    @classmethod
    def parse(cls, raw):
        """解析原始响应数据"""
        altered = raw
        comment = b""

        # 处理特殊格式的响应
        if altered.startswith(b"HTTP response [") or altered.startswith(b"HTTP redirect ["):
            stream = io.BytesIO(raw)
            first_line = stream.readline()
            parts = cls.extract_status.search(first_line)
            status_line = "HTTP/1.0 %s %s" % (getText(parts.group(1)), getText(parts.group(2)))
            remain = stream.read()
            altered = getBytes(status_line) + b"\r\n" + remain
            comment = first_line

        response = _http_client.HTTPResponse(FakeSocket(altered))
        response.begin()

        try:
            content = response.read()
        except _http_client.IncompleteRead:
            content = raw[raw.find(b"\r\n\r\n") + 4:].rstrip(b"\r\n")

        return cls(httpVersion="HTTP/1.1" if response.version == 11 else "HTTP/1.0",
                   status=response.status,
                   statusText=response.reason,
                   headers=response.msg,
                   content=content,
                   comment=comment,
                   raw=raw)

    def toDict(self):
        """转换为字典格式"""
        content = {
            "mimeType": self.headers.get("Content-Type"),
            "text": self.content,
            "size": len(self.content or "")
        }

        # 检测是否为二进制内容
        binary = set([b'\0', b'\1'])
        if any(c in binary for c in self.content):
            content["encoding"] = "base64"
            content["text"] = getText(base64.b64encode(self.content))
        else:
            content["text"] = getText(content["text"])

        return {
            "httpVersion": self.httpVersion,
            "status": self.status,
            "statusText": self.statusText,
            "headers": [dict(name=key.capitalize(), value=value) for key, value in self.headers.items() if key.lower() != "uri"],
            "cookies": [],
            "content": content,
            "headersSize": -1,
            "bodySize": -1,
            "redirectURL": "",
            "comment": getText(self.comment),
        }

class FakeSocket(object):
    """模拟Socket类,用于HTTP响应解析
    原始来源: https://stackoverflow.com/questions/24728088/python-parse-http-response-string
    """
    def __init__(self, response_text):
        self._file = io.BytesIO(response_text)

    def makefile(self, *args, **kwargs):
        return self._file

class HTTPRequest(_BaseHTTPServer.BaseHTTPRequestHandler):
    """HTTP请求处理类
    原始来源: https://stackoverflow.com/questions/4685217/parse-raw-http-headers
    """
    def __init__(self, request_text):
        self.comment = None
        self.rfile = io.BytesIO(request_text)
        self.raw_requestline = self.rfile.readline()

        # 处理特殊格式的请求
        if self.raw_requestline.startswith(b"HTTP request ["):
            self.comment = self.raw_requestline
            self.raw_requestline = self.rfile.readline()

        self.error_code = self.error_message = None
        self.parse_request()

    def send_error(self, code, message):
        """记录错误信息"""
        self.error_code = code
        self.error_message = message
