#!/usr/bin/env python

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

# 尝试导入cPickle(一个更快的pickle实现),如果失败则导入普通的pickle
try:
    import cPickle as pickle
except:
    import pickle

# 导入所需的标准库
import base64  # 用于Base64编码/解码
import binascii  # 用于二进制和ASCII转换
import codecs  # 用于编码转换
import json  # 用于JSON处理
import re  # 用于正则表达式
import sys  # 用于系统相关操作
import time  # 用于时间相关操作

# 导入自定义模块和第三方库
from lib.core.bigarray import BigArray  # 用于处理大型数组
from lib.core.compat import xrange  # 兼容Python2/3的range函数
from lib.core.data import conf  # 配置数据
from lib.core.data import kb  # 知识库数据
from lib.core.settings import INVALID_UNICODE_PRIVATE_AREA  # Unicode私有区域设置
from lib.core.settings import IS_TTY  # 是否为终端环境
from lib.core.settings import IS_WIN  # 是否为Windows系统
from lib.core.settings import NULL  # 空值常量
from lib.core.settings import PICKLE_PROTOCOL  # pickle协议版本
from lib.core.settings import SAFE_HEX_MARKER  # 安全的十六进制标记
from lib.core.settings import UNICODE_ENCODING  # Unicode编码设置
from thirdparty import six  # Python 2/3兼容库
from thirdparty.six import unichr as _unichr  # 兼容的unichr函数
from thirdparty.six.moves import collections_abc as _collections  # 集合类型

# 尝试导入HTML转义函数,适配不同Python版本
try:
    from html import escape as htmlEscape
except ImportError:
    from cgi import escape as htmlEscape

def base64pickle(value):
    """
    将输入值序列化(使用pickle)并编码为Base64格式
    
    参数:
        value: 要序列化和编码的值
    返回:
        Base64编码的字符串
    
    示例:
    >>> base64unpickle(base64pickle([1, 2, 3])) == [1, 2, 3]
    True
    """

    retVal = None

    try:
        # 尝试使用指定协议进行pickle序列化,然后Base64编码
        retVal = encodeBase64(pickle.dumps(value, PICKLE_PROTOCOL), binary=False)
    except:
        # 如果失败,发出警告
        warnMsg = "problem occurred while serializing "
        warnMsg += "instance of a type '%s'" % type(value)
        singleTimeWarnMessage(warnMsg)

        try:
            # 尝试不指定协议进行序列化
            retVal = encodeBase64(pickle.dumps(value), binary=False)
        except:
            # 如果还是失败,则将值转为字符串后再序列化
            retVal = encodeBase64(pickle.dumps(str(value), PICKLE_PROTOCOL), binary=False)

    return retVal

def base64unpickle(value):
    """
    将Base64编码的值解码并反序列化(使用pickle)
    
    参数:
        value: Base64编码的字符串
    返回:
        反序列化后的Python对象
    
    示例:
    >>> type(base64unpickle('gAJjX19idWlsdGluX18Kb2JqZWN0CnEBKYFxAi4=')) == object
    True
    """

    retVal = None

    try:
        # 尝试解码并反序列化
        retVal = pickle.loads(decodeBase64(value))
    except TypeError:
        # 如果失败,尝试将输入转换为bytes后再处理
        retVal = pickle.loads(decodeBase64(bytes(value)))

    return retVal

def htmlUnescape(value):
    """
    将HTML转义的字符转换回原始字符
    
    参数:
        value: 包含HTML转义字符的字符串
    返回:
        转换后的字符串
    
    示例:
    >>> htmlUnescape('a&lt;b') == 'a<b'
    True
    """

    retVal = value

    if value and isinstance(value, six.string_types):
        # 定义HTML转义字符的替换规则
        replacements = (("&lt;", '<'), ("&gt;", '>'), ("&quot;", '"'), ("&nbsp;", ' '), ("&amp;", '&'), ("&apos;", "'"))
        # 逐个替换HTML转义字符
        for code, value in replacements:
            retVal = retVal.replace(code, value)

        try:
            # 处理十六进制格式的HTML实体(如&#x41;)
            retVal = re.sub(r"&#x([^ ;]+);", lambda match: _unichr(int(match.group(1), 16)), retVal)
        except (ValueError, OverflowError):
            pass

    return retVal

def singleTimeWarnMessage(message):  # 交叉引用的函数
    """
    向标准输出打印一次性警告消息
    """
    sys.stdout.write(message)
    sys.stdout.write("\n")
    sys.stdout.flush()

def filterNone(values):  # 交叉引用的函数
    """
    过滤掉可迭代对象中的None值
    """
    return [_ for _ in values if _] if isinstance(values, _collections.Iterable) else values

def isListLike(value):  # 交叉引用的函数
    """
    判断一个值是否类似列表(list, tuple, set或BigArray)
    """
    return isinstance(value, (list, tuple, set, BigArray))

def shellExec(cmd):  # 交叉引用的函数
    """
    执行shell命令(未实现)
    """
    raise NotImplementedError

def jsonize(data):
    """
    将数据序列化为JSON格式
    
    参数:
        data: 要序列化的数据
    返回:
        JSON字符串
    
    示例:
    >>> jsonize({'foo':'bar'})
    '{\\n    "foo": "bar"\\n}'
    """

    return json.dumps(data, sort_keys=False, indent=4)

def dejsonize(data):
    """
    将JSON字符串反序列化为Python对象
    
    参数:
        data: JSON字符串
    返回:
        Python对象
    
    示例:
    >>> dejsonize('{\\n    "foo": "bar"\\n}') == {u'foo': u'bar'}
    True
    """

    return json.loads(data)

def rot13(data):
    """
    对文本进行ROT13编码/解码
    ROT13是一种简单的替换密码,将字母移动13位
    
    参数:
        data: 要编码/解码的文本
    返回:
        编码/解码后的文本
    
    示例:
    >>> rot13('foobar was here!!')
    'sbbone jnf urer!!'
    >>> rot13('sbbone jnf urer!!')
    'foobar was here!!'
    """

    # 参考: https://stackoverflow.com/a/62662878
    retVal = ""
    # 创建字母表(包含小写和大写字母各重复一次,用于13位移动)
    alphabit = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ"
    # 对每个字符进行处理
    for char in data:
        # 如果是字母则移动13位,否则保持不变
        retVal += alphabit[alphabit.index(char) + 13] if char in alphabit else char
    return retVal

def decodeHex(value, binary=True):
    """
    将十六进制值解码为原始形式
    
    参数:
        value: 十六进制字符串
        binary: 是否返回二进制格式
    返回:
        解码后的值
    
    示例:
    >>> decodeHex("313233") == b"123"
    True
    >>> decodeHex("313233", binary=False) == u"123"
    True
    """

    retVal = value

    # 如果输入是二进制格式,转换为文本
    if isinstance(value, six.binary_type):
        value = getText(value)

    # 如果以"0x"开头,去掉这个前缀
    if value.lower().startswith("0x"):
        value = value[2:]

    try:
        # 尝试使用codecs解码
        retVal = codecs.decode(value, "hex")
    except LookupError:
        # 如果失败,使用binascii解码
        retVal = binascii.unhexlify(value)

    # 如果不需要二进制格式,转换为文本
    if not binary:
        retVal = getText(retVal)

    return retVal

def encodeHex(value, binary=True):
    """
    将值编码为十六进制格式
    
    参数:
        value: 要编码的值
        binary: 是否返回二进制格式
    返回:
        十六进制编码的值
    
    示例:
    >>> encodeHex(b"123") == b"313233"
    True
    >>> encodeHex("123", binary=False)
    '313233'
    >>> encodeHex(b"123"[0]) == b"31"
    True
    """

    # 如果是整数,转换为Unicode字符
    if isinstance(value, int):
        value = six.unichr(value)

    # 如果是Unicode字符串,编码为bytes
    if isinstance(value, six.text_type):
        value = value.encode(UNICODE_ENCODING)

    try:
        # 尝试使用codecs编码
        retVal = codecs.encode(value, "hex")
    except LookupError:
        # 如果失败,使用binascii编码
        retVal = binascii.hexlify(value)

    # 如果不需要二进制格式,转换为文本
    if not binary:
        retVal = getText(retVal)

    return retVal

def decodeBase64(value, binary=True, encoding=None):
    """
    将Base64编码的值解码
    
    参数:
        value: Base64编码的字符串
        binary: 是否返回二进制格式
        encoding: 指定编码
    返回:
        解码后的值
    
    示例:
    >>> decodeBase64("MTIz") == b"123"
    True
    >>> decodeBase64("MTIz", binary=False)
    '123'
    >>> decodeBase64("A-B_CDE") == decodeBase64("A+B/CDE")
    True
    >>> decodeBase64(b"MTIzNA") == b"1234"
    True
    >>> decodeBase64("MTIzNA") == b"1234"
    True
    >>> decodeBase64("MTIzNA==") == b"1234"
    True
    """

    if value is None:
        return None

    # 设置填充字符
    padding = b'=' if isinstance(value, bytes) else '='

    # 参考: https://stackoverflow.com/a/49459036
    # 如果没有填充字符,添加填充
    if not value.endswith(padding):
        value += 3 * padding

    # 参考: https://en.wikipedia.org/wiki/Base64#URL_applications
    # 参考: https://perldoc.perl.org/MIME/Base64.html
    # 将URL安全的Base64字符替换为标准Base64字符
    if isinstance(value, bytes):
        value = value.replace(b'-', b'+').replace(b'_', b'/')
    else:
        value = value.replace('-', '+').replace('_', '/')

    # 解码Base64
    retVal = base64.b64decode(value)

    # 如果不需要二进制格式,转换为文本
    if not binary:
        retVal = getText(retVal, encoding)

    return retVal

def encodeBase64(value, binary=True, encoding=None, padding=True, safe=False):
    """
    将值编码为Base64格式
    
    参数:
        value: 要编码的值
        binary: 是否返回二进制格式
        encoding: 指定编码
        padding: 是否添加填充字符
        safe: 是否使用URL安全的字符
    返回:
        Base64编码的值
    
    示例:
    >>> encodeBase64(b"123") == b"MTIz"
    True
    >>> encodeBase64(u"1234", binary=False)
    'MTIzNA=='
    >>> encodeBase64(u"1234", binary=False, padding=False)
    'MTIzNA'
    >>> encodeBase64(decodeBase64("A-B_CDE"), binary=False, safe=True)
    'A-B_CDE'
    """

    if value is None:
        return None

    # 如果是Unicode字符串,编码为bytes
    if isinstance(value, six.text_type):
        value = value.encode(encoding or UNICODE_ENCODING)

    # 编码为Base64
    retVal = base64.b64encode(value)

    # 如果不需要二进制格式,转换为文本
    if not binary:
        retVal = getText(retVal, encoding)

    # 如果需要URL安全格式
    if safe:
        padding = False

        # 参考: https://en.wikipedia.org/wiki/Base64#URL_applications
        # 参考: https://perldoc.perl.org/MIME/Base64.html
        # 将标准Base64字符替换为URL安全的字符
        if isinstance(retVal, bytes):
            retVal = retVal.replace(b'+', b'-').replace(b'/', b'_')
        else:
            retVal = retVal.replace('+', '-').replace('/', '_')

    # 如果不需要填充,移除填充字符
    if not padding:
        retVal = retVal.rstrip(b'=' if isinstance(retVal, bytes) else '=')

    return retVal

def getBytes(value, encoding=None, errors="strict", unsafe=True):
    """
    将Unicode值转换为字节表示
    
    参数:
        value: Unicode字符串
        encoding: 指定编码
        errors: 错误处理方式
        unsafe: 是否允许不安全的字符
    返回:
        字节表示
    
    示例:
    >>> getBytes(u"foo\\\\x01\\\\x83\\\\xffbar") == b"foo\\x01\\x83\\xffbar"
    True
    """

    retVal = value

    # 如果没有指定编码,使用配置中的编码或默认编码
    if encoding is None:
        encoding = conf.get("encoding") or UNICODE_ENCODING

    # 验证编码是否有效
    try:
        codecs.lookup(encoding)
    except (LookupError, TypeError):
        encoding = UNICODE_ENCODING

    # 如果是Unicode字符串
    if isinstance(value, six.text_type):
        if INVALID_UNICODE_PRIVATE_AREA:
            if unsafe:
                # 处理Unicode私有区域字符
                for char in xrange(0xF0000, 0xF00FF + 1):
                    value = value.replace(_unichr(char), "%s%02x" % (SAFE_HEX_MARKER, char - 0xF0000))

            # 编码为字节
            retVal = value.encode(encoding, errors)

            if unsafe:
                # 处理安全的十六进制标记
                retVal = re.sub(r"%s([0-9a-f]{2})" % SAFE_HEX_MARKER, lambda _: decodeHex(_.group(1)), retVal)
        else:
            try:
                # 尝试使用指定编码
                retVal = value.encode(encoding, errors)
            except UnicodeError:
                # 如果失败,使用Unicode编码并替换无法编码的字符
                retVal = value.encode(UNICODE_ENCODING, errors="replace")

            if unsafe:
                # 处理转义序列
                retVal = re.sub(b"\\\\x([0-9a-f]{2})", lambda _: decodeHex(_.group(1)), retVal)

    return retVal

def getOrds(value):
    """
    返回字符串中每个字符的序号(ord)值
    
    参数:
        value: 字符串
    返回:
        序号值列表
    
    示例:
    >>> getOrds(u'fo\\xf6bar')
    [102, 111, 246, 98, 97, 114]
    >>> getOrds(b"fo\\xc3\\xb6bar")
    [102, 111, 195, 182, 98, 97, 114]
    """

    return [_ if isinstance(_, int) else ord(_) for _ in value]

def getUnicode(value, encoding=None, noneToNull=False):
    """
    返回值的Unicode表示
    
    参数:
        value: 要转换的值
        encoding: 指定编码
        noneToNull: 是否将None转换为NULL
    返回:
        Unicode字符串
    
    示例:
    >>> getUnicode('test') == u'test'
    True
    >>> getUnicode(1) == u'1'
    True
    >>> getUnicode(None) == 'None'
    True
    """

    # 检查时间限制
    if conf.get("timeLimit") and kb.get("startTime") and (time.time() - kb.startTime > conf.timeLimit):
        raise SystemExit

    # 如果需要将None转换为NULL
    if noneToNull and value is None:
        return NULL

    # 如果已经是Unicode字符串,直接返回
    if isinstance(value, six.text_type):
        return value
    # 如果是字节字符串
    elif isinstance(value, six.binary_type):
        # 获取可能的编码列表
        candidates = filterNone((encoding, kb.get("pageEncoding") if kb.get("originalPage") else None, conf.get("encoding"), UNICODE_ENCODING, sys.getfilesystemencoding()))
        # 根据内容特征调整编码优先级
        if all(_ in value for _ in (b'<', b'>')):
            pass
        elif any(_ in value for _ in (b":\\", b'/', b'.')) and b'\n' not in value:
            candidates = filterNone((encoding, sys.getfilesystemencoding(), kb.get("pageEncoding") if kb.get("originalPage") else None, UNICODE_ENCODING, conf.get("encoding")))
        elif conf.get("encoding") and b'\n' not in value:
            candidates = filterNone((encoding, conf.get("encoding"), kb.get("pageEncoding") if kb.get("originalPage") else None, sys.getfilesystemencoding(), UNICODE_ENCODING))

        # 尝试使用不同的编码解码
        for candidate in candidates:
            try:
                return six.text_type(value, candidate)
            except (UnicodeDecodeError, LookupError):
                pass

        try:
            # 尝试使用指定编码或页面编码
            return six.text_type(value, encoding or (kb.get("pageEncoding") if kb.get("originalPage") else None) or UNICODE_ENCODING)
        except UnicodeDecodeError:
            # 如果失败,使用可逆的错误处理方式
            return six.text_type(value, UNICODE_ENCODING, errors="reversible")
    # 如果是类列表对象
    elif isListLike(value):
        value = list(getUnicode(_, encoding, noneToNull) for _ in value)
        return value
    else:
        try:
            # 尝试直接转换为Unicode
            return six.text_type(value)
        except UnicodeDecodeError:
            # 如果失败,忽略错误转换为字符串
            return six.text_type(str(value), errors="ignore")

def getText(value, encoding=None):
    """
    返回值的文本表示(注意:在Python2中不一定是Unicode)
    
    参数:
        value: 要转换的值
        encoding: 指定编码
    返回:
        文本字符串
    
    示例:
    >>> getText(b"foobar")
    'foobar'
    >>> isinstance(getText(u"fo\\u2299bar"), six.text_type)
    True
    """

    retVal = value

    # 如果是字节字符串,转换为Unicode
    if isinstance(value, six.binary_type):
        retVal = getUnicode(value, encoding)

    # 在Python2中,尝试转换为str类型
    if six.PY2:
        try:
            retVal = str(retVal)
        except:
            pass

    return retVal

def stdoutEncode(value):
    """
    返回适合写入stdout的值的二进制表示
    
    参数:
        value: 要编码的值
    返回:
        编码后的值
    """

    value = value or ""

    # 在Windows终端环境下获取代码页
    if IS_WIN and IS_TTY and kb.get("codePage", -1) is None:
        output = shellExec("chcp")
        match = re.search(r": (\d{3,})", output or "")

        if match:
            try:
                candidate = "cp%s" % match.group(1)
                codecs.lookup(candidate)
            except LookupError:
                pass
            else:
                kb.codePage = candidate

        kb.codePage = kb.codePage or ""

    # 如果是Unicode字符串
    if isinstance(value, six.text_type):
        # 获取编码
        encoding = kb.get("codePage") or getattr(sys.stdout, "encoding", None) or UNICODE_ENCODING

        while True:
            try:
                # 尝试编码
                retVal = value.encode(encoding)
                break
            except UnicodeEncodeError as ex:
                # 如果编码失败,用问号替换无法编码的字符
                value = value[:ex.start] + "?" * (ex.end - ex.start) + value[ex.end:]

                # 发出警告消息
                warnMsg = "cannot properly display (some) Unicode characters "
                warnMsg += "inside your terminal ('%s') environment. All " % encoding
                warnMsg += "unhandled occurrences will result in "
                warnMsg += "replacement with '?' character. Please, find "
                warnMsg += "proper character representation inside "
                warnMsg += "corresponding output files"
                singleTimeWarnMessage(warnMsg)

        # 在Python3中转换回Unicode
        if six.PY3:
            retVal = getUnicode(retVal, encoding)

    else:
        retVal = value

    return retVal

def getConsoleLength(value):
    """
    返回字符串在控制台中的显示宽度
    
    参数:
        value: 要计算长度的字符串
    返回:
        显示宽度
    
    示例:
    >>> getConsoleLength("abc")
    3
    >>> getConsoleLength(u"\\u957f\\u6c5f")
    4
    """

    if isinstance(value, six.text_type):
        # 对于Unicode字符串,CJK字符占用2个宽度,其他字符占用1个宽度
        retVal = sum((2 if ord(_) >= 0x3000 else 1) for _ in value)
    else:
        # 对于非Unicode字符串,使用长度作为宽度
        retVal = len(value)

    return retVal
