#!/usr/bin/env python

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

# 导入所需的模块和函数
from lib.core.common import checkFile  # 检查文件是否存在和可访问
from lib.core.common import getSafeExString  # 安全地获取异常的字符串表示
from lib.core.common import openFile  # 打开文件的工具函数
from lib.core.common import unArrayizeValue  # 将数组值转换为单个值
from lib.core.common import UnicodeRawConfigParser  # 处理Unicode的配置文件解析器
from lib.core.convert import getUnicode  # 将输入转换为Unicode字符串
from lib.core.data import cmdLineOptions  # 命令行选项存储
from lib.core.data import conf  # 全局配置字典
from lib.core.data import logger  # 日志记录器
from lib.core.enums import OPTION_TYPE  # 选项类型枚举
from lib.core.exception import SqlmapMissingMandatoryOptionException  # 缺少必需选项异常
from lib.core.exception import SqlmapSyntaxException  # 语法错误异常
from lib.core.optiondict import optDict  # 选项字典

# 全局配置解析器对象
config = None

def configFileProxy(section, option, datatype):
    """
    解析配置文件并将设置保存到高级配置字典中。
    
    参数:
        section: 配置文件中的节名
        option: 选项名
        datatype: 数据类型
    """

    if config.has_option(section, option):  # 检查配置中是否存在该选项
        try:
            # 根据数据类型获取相应的值
            if datatype == OPTION_TYPE.BOOLEAN:  # 布尔类型
                value = config.getboolean(section, option) if config.get(section, option) else False
            elif datatype == OPTION_TYPE.INTEGER:  # 整数类型
                value = config.getint(section, option) if config.get(section, option) else 0
            elif datatype == OPTION_TYPE.FLOAT:  # 浮点数类型
                value = config.getfloat(section, option) if config.get(section, option) else 0.0
            else:  # 字符串类型
                value = config.get(section, option)
        except ValueError as ex:
            # 如果值转换失败，抛出语法错误异常
            errMsg = "error occurred while processing the option "
            errMsg += "'%s' in provided configuration file ('%s')" % (option, getUnicode(ex))
            raise SqlmapSyntaxException(errMsg)

        # 将值存储到全局配置字典中
        if value:
            conf[option] = value
        else:
            conf[option] = None
    else:
        # 如果选项不存在，记录调试信息
        debugMsg = "missing requested option '%s' (section " % option
        debugMsg += "'%s') into the configuration file, " % section
        debugMsg += "ignoring. Skipping to next."
        logger.debug(debugMsg)

def configFileParser(configFile):
    """
    解析配置文件的主函数。
    
    参数:
        configFile: 配置文件路径
    """

    global config

    # 记录开始解析的调试信息
    debugMsg = "parsing configuration file"
    logger.debug(debugMsg)

    # 检查配置文件是否存在和可访问
    checkFile(configFile)
    configFP = openFile(configFile, "rb")

    try:
        # 创建配置解析器实例并读取配置文件
        config = UnicodeRawConfigParser()
        if hasattr(config, "read_file"):  # Python 3
            config.read_file(configFP)
        else:  # Python 2
            config.readfp(configFP)
    except Exception as ex:
        # 如果解析失败，抛出语法错误异常
        errMsg = "you have provided an invalid and/or unreadable configuration file ('%s')" % getSafeExString(ex)
        raise SqlmapSyntaxException(errMsg)

    # 检查是否存在必需的Target节
    if not config.has_section("Target"):
        errMsg = "missing a mandatory section 'Target' in the configuration file"
        raise SqlmapMissingMandatoryOptionException(errMsg)

    # 检查必需选项
    mandatory = False

    # 检查Target节中是否至少存在一个必需的选项
    for option in ("direct", "url", "logFile", "bulkFile", "googleDork", "requestFile", "wizard"):
        if config.has_option("Target", option) and config.get("Target", option) or cmdLineOptions.get(option):
            mandatory = True
            break

    # 如果没有找到任何必需选项，抛出异常
    if not mandatory:
        errMsg = "missing a mandatory option in the configuration file "
        errMsg += "(direct, url, logFile, bulkFile, googleDork, requestFile or wizard)"
        raise SqlmapMissingMandatoryOptionException(errMsg)

    # 遍历所有选项并解析它们
    for family, optionData in optDict.items():
        for option, datatype in optionData.items():
            datatype = unArrayizeValue(datatype)
            configFileProxy(family, option, datatype)
