#!/usr/bin/env python

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

import re

from xml.sax.handler import ContentHandler

from lib.core.common import Backend
from lib.core.common import parseXmlFile
from lib.core.common import sanitizeStr
from lib.core.data import kb
from lib.core.data import paths
from lib.core.enums import DBMS
from lib.parse.handler import FingerprintHandler

class MSSQLBannerHandler(ContentHandler):
    """
    This class defines methods to parse and extract information from the
    given Microsoft SQL Server banner based upon the data in XML file
    """

    def __init__(self, banner, info):
        ContentHandler.__init__(self)

        # 初始化banner
        self._banner = sanitizeStr(banner or "")
        # 初始化是否在version标签中
        self._inVersion = False
        # 初始化是否在servicepack标签中
        self._inServicePack = False
        # 初始化release
        self._release = None
        # 初始化version
        self._version = ""
        # 初始化versionAlt
        self._versionAlt = None
        # 初始化servicePack
        self._servicePack = ""
        # 初始化info
        self._info = info

    def _feedInfo(self, key, value):
        value = sanitizeStr(value)

        if value in (None, "None"):
            return

        self._info[key] = value

    def startElement(self, name, attrs):
        # 如果标签名为signatures，则获取release属性
        if name == "signatures":
            self._release = sanitizeStr(attrs.get("release"))

        # 如果标签名为version，则设置_inVersion为True
        elif name == "version":
            self._inVersion = True

        # 如果标签名为servicepack，则设置_inServicePack为True
        elif name == "servicepack":
            self._inServicePack = True

    def characters(self, content):
        # 如果在version标签中，则将content添加到_version中
        if self._inVersion:
            self._version += sanitizeStr(content)
        # 如果在servicepack标签中，则将content添加到_servicePack中
        elif self._inServicePack:
            self._servicePack += sanitizeStr(content)

    def endElement(self, name):
        # 如果标签名为signature，则进行匹配
        if name == "signature":
            for version in (self._version, self._versionAlt):
                # 如果version不为空，且banner不为空，且banner中包含version，则将release、version、servicePack添加到info中
                if version and self._banner and re.search(r" %s[\.\ ]+" % re.escape(version), self._banner):
                    self._feedInfo("dbmsRelease", self._release)
                    self._feedInfo("dbmsVersion", self._version)
                    self._feedInfo("dbmsServicePack", self._servicePack)
                    break

            # 重置version、versionAlt、servicePack
            self._version = ""
            self._versionAlt = None
            self._servicePack = ""

        # 如果标签名为version，则将_version中的空格去除，并尝试匹配
        elif name == "version":
            self._inVersion = False
            self._version = self._version.replace(" ", "")

            # 尝试匹配
            match = re.search(r"\A(?P<major>\d+)\.00\.(?P<build>\d+)\Z", self._version)
            # 如果匹配成功，则将versionAlt设置为"%s.0.%s.0" % (match.group('major'), match.group('build'))
            self._versionAlt = "%s.0.%s.0" % (match.group('major'), match.group('build')) if match else None

        # 如果标签名为servicepack，则将_servicePack中的空格去除
        elif name == "servicepack":
            self._inServicePack = False
            self._servicePack = self._servicePack.replace(" ", "")

def bannerParser(banner):
    """
    This function calls a class to extract information from the given
    DBMS banner based upon the data in XML file
    """

    xmlfile = None

    # 如果数据库类型为MSSQL，则设置xmlfile为paths.MSSQL_XML
    if Backend.isDbms(DBMS.MSSQL):
        xmlfile = paths.MSSQL_XML
    # 如果数据库类型为MYSQL，则设置xmlfile为paths.MYSQL_XML
    elif Backend.isDbms(DBMS.MYSQL):
        xmlfile = paths.MYSQL_XML
    # 如果数据库类型为ORACLE，则设置xmlfile为paths.ORACLE_XML
    elif Backend.isDbms(DBMS.ORACLE):
        xmlfile = paths.ORACLE_XML
    # 如果数据库类型为PGSQL，则设置xmlfile为paths.PGSQL_XML
    elif Backend.isDbms(DBMS.PGSQL):
        xmlfile = paths.PGSQL_XML

    # 如果xmlfile为空，则返回
    if not xmlfile:
        return

    # 如果数据库类型为MSSQL，则使用MSSQLBannerHandler解析xmlfile，并使用FingerprintHandler解析paths.GENERIC_XML
    if Backend.isDbms(DBMS.MSSQL):
        handler = MSSQLBannerHandler(banner, kb.bannerFp)
        parseXmlFile(xmlfile, handler)

        handler = FingerprintHandler(banner, kb.bannerFp)
        parseXmlFile(paths.GENERIC_XML, handler)
    # 否则，使用FingerprintHandler解析xmlfile和paths.GENERIC_XML
    else:
        handler = FingerprintHandler(banner, kb.bannerFp)
        parseXmlFile(xmlfile, handler)
        parseXmlFile(paths.GENERIC_XML, handler)
