# Collective Knowledge (individual environment - setup)
# See CK LICENSE.txt for licensing details
# See CK COPYRIGHT.txt for copyright details
# Developer: Zaborovskiy Vladislav, example@nnthink.com

import os

# setup environment setup

def setup(i):
    """
    Input:  {
              cfg              - meta of this soft entry
              self_cfg         - meta of module soft
              ck_kernel        - import CK kernel module (to reuse functions)

              host_os_uoa      - host OS UOA
              host_os_uid      - host OS UID
              host_os_dict     - host OS meta

              target_os_uoa    - target OS UOA
              target_os_uid    - target OS UID
              target_os_dict   - target OS meta

              target_device_id - target device ID (if via ADB)

              tags             - list of tags used to search this entry

              env              - updated environment vars from meta
              customize        - updated customize vars from meta

              deps             - resolved dependencies for this soft

              interactive      - if 'yes', can ask questions, otherwise quiet
            }

    Output: {
              return       - return code =  0, if successful
                                         >  0, if error
              (error)      - error text if return > 0

              bat          - prepared string for bat file
            }

    """

    import os

    # Get variables
    ck=i['ck_kernel']
    s=''

    iv=i.get('interactive','')

    cus=i.get('customize',{})
    fp=cus.get('full_path','')

    hosd=i['host_os_dict']
    tosd=i['target_os_dict']

    winh=hosd.get('windows_base','')

    env=i['env'

    ep=cus['env_prefix']

    p1=os.path.dirname(fp)
    lib_path        = os.path.dirname(p1)
    install_path    = os.path.dirname(lib_path )
    bin_path        = os.path.join(install_path , 'python_deps_site', 'bin')

    env[ep]         = install_path
    env[ep+'_LIB']  = lib_path
    env[ep+'_BIN']  = bin_path

    # Path to bundled protobuf.
    pb=os.path.join(lib_path ,'external','protobuf_archive','python')

    if winh=='yes':
        s+='\nset PYTHONPATH='+lib_path +';'+pb+';%PYTHONPATH%\n'
    else:
        s+='\nexport PYTHONPATH='+lib_path +':'+pb+':${PYTHONPATH}\nexport PATH='+bin_path+':${PATH}\n'

    return {'return':0, 'bat':s}


async def answer_callback_query(
        self,
        callback_query_id: str,
        text: str = None,
        show_alert: bool = None,
        url: str = None,
        cache_time: int = 0
    ):

#  Pyrogram - Telegram MTProto API Client Library for Python
#  This file is part of Pyrogram.

from pyrogram import raw
from pyrogram.scaffold import Scaffold

class AnswerCallbackQuery(Scaffold):

"""Send answers to callback queries sent from inline keyboards.
        The answer will be displayed to the user as a notification at the top of the chat screen or as an alert.

        Parameters:
            callback_query_id (``str``):
                Unique identifier for the query to be answered.

            text (``str`` *optional*):
                Text of the notification. If not specified, nothing will be shown to the user, 0-200 characters.

            show_alert (``bool``, *optional*):
                If true, an alert will be shown by the client instead of a notification at the top of the chat screen.
                Defaults to False.

            url (``str``, *optional*):
                URL that will be opened by the user's client.
                If you have created a Game and accepted the conditions via @Botfather, specify the URL that opens your
                game – note that this will only work if the query comes from a callback_game button.
                Otherwise, you may use links like t.me/your_bot?start=XXXX that open your bot with a parameter.

            cache_time (``int``, *optional*):
                The maximum amount of time in seconds that the result of the callback query may be cached client-side.
                Telegram apps will support caching starting in version 3.14. Defaults to 0.

        Returns:
            ``bool``: True, on success.

        Example:
            .. code-block:: python

                # Answer only (remove the spinning circles)
                app.answer_callback_query(query_id)

                # Answer without alert
                app.answer_callback_query(query_id, text=text)

                # Answer with alert
                app.answer_callback_query(query_id, text=text, show_alert=True)
        """
        return await self.send(
            raw.functions.messages.SetBotCallbackAnswer(
                query_id=int(callback_query_id),
                cache_time=cache_time,
                alert=show_alert or None,
                message=text or None,
                url=url or None
            )
        )


        return await self.send(
            raw.functions.messages.SetBotCallbackAnswer(
                query_id=int(callback_query_id),
                cache_time=cache_time,
                alert=show_alert or None,
                message=text or None,
                url=url or None
            )
        )

#  Pyrogram - Telegram MTProto API Client Library for Python
#  This file is part of Pyrogram.

from pyrogram import raw
from pyrogram.scaffold import Scaffold

class AnswerCallbackQuery(Scaffold):
    async def answer_callback_query(
        self,
        callback_query_id: str,
        text: str = None,
        show_alert: bool = None,
        url: str = None,
        cache_time: int = 0
    ):
        """Send answers to callback queries sent from inline keyboards.
        The answer will be displayed to the user as a notification at the top of the chat screen or as an alert.

        Parameters:
            callback_query_id (``str``):
                Unique identifier for the query to be answered.

            text (``str`` *optional*):
                Text of the notification. If not specified, nothing will be shown to the user, 0-200 characters.

            show_alert (``bool``, *optional*):
                If true, an alert will be shown by the client instead of a notification at the top of the chat screen.
                Defaults to False.

            url (``str``, *optional*):
                URL that will be opened by the user's client.
                If you have created a Game and accepted the conditions via @Botfather, specify the URL that opens your
                game – note that this will only work if the query comes from a callback_game button.
                Otherwise, you may use links like t.me/your_bot?start=XXXX that open your bot with a parameter.

            cache_time (``int``, *optional*):
                The maximum amount of time in seconds that the result of the callback query may be cached client-side.
                Telegram apps will support caching starting in version 3.14. Defaults to 0.

        Returns:
            ``bool``: True, on success.

        Example:
            .. code-block:: python

                # Answer only (remove the spinning circles)
                app.answer_callback_query(query_id)

                # Answer without alert
                app.answer_callback_query(query_id, text=text)

                # Answer with alert
                app.answer_callback_query(query_id, text=text, show_alert=True)
        """


"""
author: b5mali4

Simple subPlugin example code:

class Checker(BaseChecker):
    # 检测逻辑
    def check_logic(self, request_raw):
        host, port = get_host_port(request_raw['url'])
        if port == 9012:
            self.result["status"] = True
            self.result["info"] = '%s开放了9012端口服务' % http_poc_url
            self.result['payload'] = http_poc_url

    #插件信息
    def init_plugin_info(self):
        name = "name1"
        imp_version = "< spring 2.1"
        description = "see http://xxxxx/"
        repair = "升级到titans1.8.3或者其以上版本"
        type = VulnType.OTHER
        author = "b5mali4"
        temp_info = {"repair": repair, "name": name, "type": type, "description": description,
                     "imp_version": imp_version, "author": author}
        self.info = dict(self.info, **temp_info)

To use:
>>> req1 = {"url": "http://127.0.0.1:8080/", "method": "get", "headers": "{\"Cookie\":\"SESSION\"}"}
>>> checker = Checker()
>>> checker.check_vuln(req1)
>>> print(checker.result)
"""
import time
import json
from common import log
from sys import version_info
from json.decoder import JSONDecodeError
from abc import ABCMeta, abstractmethod
from common.system_util import encode_b64
from plugins.base.vuln_enum import PluginSwith
from parser.base_traffic_parser import BaseTrafficParser
from parser.chrome_traffic_parser import ChromeTrafficParser
from model.system_config import SystemConfig, SystemConfigService
from exception.hunter_web_exception import BaseHunterException
from exception.request_classification_exception import PluginInfoError
from exception.request_classification_exception import RequestParseError
from exception.request_classification_exception import HeaderParseError

if version_info < (3, 0):
    from exceptions import ValueError as JSONDecodeError
else:
    from json.decoder import JSONDecodeError

class BaseChecker(object):
    __metaclass__ = ABCMeta
    """
    抽象插件父类
    """
    logger = log.get_default_logger()
    # 解析类
    PARSER_DIC = {BaseTrafficParser.DEAFAULT_PARSER: BaseTrafficParser,
                  BaseTrafficParser.CHROME_PARSER: ChromeTrafficParser}

"""
        初始化流程下:
        1.初始化插件基本信息
        2.检测插件信息必传字段是否完整

        disable表示是否禁用
        path表示插件的实际路径
        absolute_plugin_path 插件绝对路径
        relative_plugin_path 插件相对路径
        """
        self.info = dict()
        self.useable = PluginSwith.ON
        self.result = dict()
        self.init_plugin_info()
        self.absolute_plugin_path = None
        self.relative_plugin_path = None
        try:
            self.check_plugin_info()
        except BaseHunterException as e:
            BaseChecker.logger.exception("插件信息错误")

    def init_check_result(self):
        """
        初始化检测结果
        :return:
        """
        self.result = {'status': False, 'info': '没有漏洞(默认的描述信息)', 'error': [], 'details': self.info, 'payload': ''}

    @abstractmethod
    def init_plugin_info(self):
        """
        初始化加载插件
        :return:
        """
        pass

    def check_vuln(self, request_raw):
        """
        check_logic为基础检测逻辑，各个插件自己实现，首先判断插件是否为禁用状态
        :param request_raw:
        :return:
        """
        try:
            self.init_check_result()
            if self.useable == PluginSwith.ON:
                self.check_request_raw(request_raw)
                self.check_logic(request_raw)
                self.record_payload(request_raw)
        except BaseHunterException as e:
            BaseChecker.logger.exception("请求数据包格式出错")
        finally:
            return self.result

    @abstractmethod
    def check_logic(self, request_raw):
        """
        在里面对result进行赋值
        :param request_raw:
        :return:
        """
        pass

    async def async_check_vuln(self, request_raw):
        """
        新增异步函数
        :param package:
        :return:
        """
        return self.check_vuln(request_raw)

    def parse_headers(self, headers=dict()):
        """
        解析参数
        :param headers:
        :return:
        """
        if not isinstance(headers, dict):
            raise HeaderParseError()

    def check_request_raw(self, request_raw):
        """
        检查流量数据包中的必要字段是否完整
        :param request_raw:
        :return:
        """
        if not isinstance(request_raw, dict):
            raise BaseHunterException(
                "传入的request_raw不是一个dict类型，其类型为{type}".format(type=type(request_raw) if request_raw else None))

        keys = ["type", "url", "method", "headers"]
        miss_keys = []
        for key in keys:
            if key not in request_raw:
                miss_keys.append(key)
        if len(miss_keys) > 0:
            raise RequestParseError(request_raw, miss_keys)

    def check_plugin_info(self):
        """
        检查info的必要字段是否完整
        :param info:
        :return:
        """
        assert isinstance(self.info, dict), "传入的info不是一个dict类型"
        keys = ["tag", "author", "name", "imp_version", "description", "repair", "type"]
        miss_keys = []
        for key in keys:
            if key not in self.info:
                miss_keys.append(key)
        if len(miss_keys) > 0:
            raise PluginInfoError(self.info, miss_keys)

    def get_plugin_info(self):
        return self.info

    def parse_data(self, package):
        """
        解析data
        data为空
        data 为XML,data为合法str,data为非法str
        :param data:
        :return:
        """
        if isinstance(package, dict):
            if "data" in package:
                try:
                    result = json.loads(package['data'])
                except JSONDecodeError:
                    result = package['data']
            else:
                result = None
        if isinstance(package, str):
            try:
                result = json.loads(package)
            except JSONDecodeError:
                result = package
        return result

    def generate_uuid(self):
        """
        生成uuid，根据时间戳生成
        :return:
        """
        current_time = str(time.time())
        return encode_b64(current_time.split(".")[1])

    def generate_blind_poc(self):
        """
        生成poc，生成无回显poc ，根据选择的是Dns模块还是Socket模块，选择规则如下:
        1.Dns模块单独开启了，优先使用Dns模块
        2.Socket模块单独开启了，使用Socket模块
        3.Dns模块和Socket模块开启了，使用Dns模块
        4.都没开启则不调用

        根据Dns或者Socket模块开关获取poc 如下代码为检测命令执行
        Simple example usage code:

            blind_poc, check_bilnd_poc_url, hunter_log_api_token = self.generate_blind_poc()

            if not blind_poc["data"]:
                return

            if blind_poc["type"] == "dns":
                attack_payload = "http://%s" % (blind_poc["data"]) # 得到的是一个域名，域名前缀为uuid
            elif blind_poc["type"] == "socket":
                attack_payload = "http://%s:%s/%s" % (blind_poc["data"]["host"], blind_poc["data"]["port"], blind_poc["data"]["uuid"])

            if http_method == HttpMethod.GET or (http_method == HttpMethod.POST and content_type is None):
                payload = UrlDataClassification.add_poc_data(url=temp_url, http_method=http_method, content_type=content_type, poc="|wget %s" % (attack_payload))
                self.request(method=http_method, url=payload, data=temp_data, headers=temp_headers)

            elif http_method == HttpMethod.POST and content_type is not None and temp_data is not None:
                payload = UrlDataClassification.add_poc_data(url=temp_data, http_method=http_method, content_type=content_type, poc="|wget %s" % (attack_payload))
                self.request(method=http_method, url=temp_url, json=json.loads(payload), headers=temp_headers)

            req = requests.get(check_bilnd_poc_url, headers={"token": hunter_log_api_token})
            response = req.json()

            if "status" in response and response["status"] == 200:
                self.result['status'] = True
                self.result['info'] = '%s 存在一个命令执行漏洞' % request_raw['url']
                self.result['payload'] = payload

        :return:
        """
        system_config_single = SystemConfigService.get_single_instance(refresh=True)
        hunter_log_socket_switch = system_config_single.hunter_log_socket_switch
        hunter_log_dns_switch = system_config_single.hunter_log_dns_switch
        plugin_uuid = self.generate_uuid()

        # 生成poc , hunetr log平台查询api url，和 hunter_log 平台api 的查询token
        blind_poc = {"type": "dns", "data": None, "uuid": plugin_uuid}
        check_bilnd_poc_url = self.generate_check_bilnd_poc_url(system_config_single, plugin_uuid)
        hunter_log_api_token = system_config_single.hunter_log_token
        data = None
        if hunter_log_dns_switch:
            blind_poc["type"] = "dns"
            data = "%s.%s" % (plugin_uuid, system_config_single.hunter_log_dns_fake_root_domain)
        elif not hunter_log_dns_switch and hunter_log_socket_switch:
            blind_poc["type"] = "socket"
            data = {"host": system_config_single.hunter_log_socket_host,
                    "port": system_config_single.hunter_log_socket_port, "uuid": plugin_uuid}

        blind_poc["data"] = data
        return blind_poc, check_bilnd_poc_url, hunter_log_api_token

    def generate_check_bilnd_poc_url(self, system_config_single, plugin_uuid):
        """
        生成 hunetr log平台查询api url
        :param hunter_api_url:
        :param plugin_uuid:
        :return:
        """
        if str(system_config_single.hunter_api_url).endswith("/"):
            check_bilnd_poc_url = "{}{}".format(system_config_single.hunter_api_url, plugin_uuid)
        else:
            check_bilnd_poc_url = "{}/{}".format(system_config_single.hunter_api_url, plugin_uuid)
        return check_bilnd_poc_url

    @staticmethod
    def get_parser_name(request_raw):
        """
        获取解析器名
        :param request_raw:
        :return:
        """
        parser_name = request_raw["parser"] if "parser" in request_raw else BaseTrafficParser.CHROME_PARSER
        return parser_name

    @staticmethod
    def get_parser_class(request_raw):
        """
        根据名字获得解析器类
        :return:
        """
        parser_name = BaseChecker.get_parser_name(request_raw)
        parser_class = BaseChecker.PARSER_DIC[parser_name]
        return parser_class

    def record_payload(self, request_raw):
        """
        记录最终发送的payload数据包，展现原生数据格式
        :return:
        """
        pass


"""
        解析参数
        :param headers:
        :return:
        """
        if not isinstance(headers, dict):
            raise HeaderParseError()

"""
author: b5mali4

Simple subPlugin example code:

class Checker(BaseChecker):
    # 检测逻辑
    def check_logic(self, request_raw):
        host, port = get_host_port(request_raw['url'])
        if port == 9012:
            self.result["status"] = True
            self.result["info"] = '%s开放了9012端口服务' % http_poc_url
            self.result['payload'] = http_poc_url

    #插件信息
    def init_plugin_info(self):
        name = "name1"
        imp_version = "< spring 2.1"
        description = "see http://xxxxx/"
        repair = "升级到titans1.8.3或者其以上版本"
        type = VulnType.OTHER
        author = "b5mali4"
        temp_info = {"repair": repair, "name": name, "type": type, "description": description,
                     "imp_version": imp_version, "author": author}
        self.info = dict(self.info, **temp_info)

To use:
>>> req1 = {"url": "http://127.0.0.1:8080/", "method": "get", "headers": "{\"Cookie\":\"SESSION\"}"}
>>> checker = Checker()
>>> checker.check_vuln(req1)
>>> print(checker.result)
"""
import time
import json
from common import log
from sys import version_info
from json.decoder import JSONDecodeError
from abc import ABCMeta, abstractmethod
from common.system_util import encode_b64
from plugins.base.vuln_enum import PluginSwith
from parser.base_traffic_parser import BaseTrafficParser
from parser.chrome_traffic_parser import ChromeTrafficParser
from model.system_config import SystemConfig, SystemConfigService
from exception.hunter_web_exception import BaseHunterException
from exception.request_classification_exception import PluginInfoError
from exception.request_classification_exception import RequestParseError
from exception.request_classification_exception import HeaderParseError

if version_info < (3, 0):
    from exceptions import ValueError as JSONDecodeError
else:
    from json.decoder import JSONDecodeError

class BaseChecker(object):
    __metaclass__ = ABCMeta
    """
    抽象插件父类
    """
    logger = log.get_default_logger()
    # 解析类
    PARSER_DIC = {BaseTrafficParser.DEAFAULT_PARSER: BaseTrafficParser,
                  BaseTrafficParser.CHROME_PARSER: ChromeTrafficParser}

    def __init__(self):
        """
        初始化流程下:
        1.初始化插件基本信息
        2.检测插件信息必传字段是否完整

        disable表示是否禁用
        path表示插件的实际路径
        absolute_plugin_path 插件绝对路径
        relative_plugin_path 插件相对路径
        """
        self.info = dict()
        self.useable = PluginSwith.ON
        self.result = dict()
        self.init_plugin_info()
        self.absolute_plugin_path = None
        self.relative_plugin_path = None
        try:
            self.check_plugin_info()
        except BaseHunterException as e:
            BaseChecker.logger.exception("插件信息错误")

    def init_check_result(self):
        """
        初始化检测结果
        :return:
        """
        self.result = {'status': False, 'info': '没有漏洞(默认的描述信息)', 'error': [], 'details': self.info, 'payload': ''}

    @abstractmethod
    def init_plugin_info(self):
        """
        初始化加载插件
        :return:
        """
        pass

    def check_vuln(self, request_raw):
        """
        check_logic为基础检测逻辑，各个插件自己实现，首先判断插件是否为禁用状态
        :param request_raw:
        :return:
        """
        try:
            self.init_check_result()
            if self.useable == PluginSwith.ON:
                self.check_request_raw(request_raw)
                self.check_logic(request_raw)
                self.record_payload(request_raw)
        except BaseHunterException as e:
            BaseChecker.logger.exception("请求数据包格式出错")
        finally:
            return self.result

    @abstractmethod
    def check_logic(self, request_raw):
        """
        在里面对result进行赋值
        :param request_raw:
        :return:
        """
        pass

    async def async_check_vuln(self, request_raw):
        """
        新增异步函数
        :param package:
        :return:
        """
        return self.check_vuln(request_raw)

    def parse_headers(self, headers=dict()):

    def check_request_raw(self, request_raw):
        """
        检查流量数据包中的必要字段是否完整
        :param request_raw:
        :return:
        """
        if not isinstance(request_raw, dict):
            raise BaseHunterException(
                "传入的request_raw不是一个dict类型，其类型为{type}".format(type=type(request_raw) if request_raw else None))

        keys = ["type", "url", "method", "headers"]
        miss_keys = []
        for key in keys:
            if key not in request_raw:
                miss_keys.append(key)
        if len(miss_keys) > 0:
            raise RequestParseError(request_raw, miss_keys)

    def check_plugin_info(self):
        """
        检查info的必要字段是否完整
        :param info:
        :return:
        """
        assert isinstance(self.info, dict), "传入的info不是一个dict类型"
        keys = ["tag", "author", "name", "imp_version", "description", "repair", "type"]
        miss_keys = []
        for key in keys:
            if key not in self.info:
                miss_keys.append(key)
        if len(miss_keys) > 0:
            raise PluginInfoError(self.info, miss_keys)

    def get_plugin_info(self):
        return self.info

    def parse_data(self, package):
        """
        解析data
        data为空
        data 为XML,data为合法str,data为非法str
        :param data:
        :return:
        """
        if isinstance(package, dict):
            if "data" in package:
                try:
                    result = json.loads(package['data'])
                except JSONDecodeError:
                    result = package['data']
            else:
                result = None
        if isinstance(package, str):
            try:
                result = json.loads(package)
            except JSONDecodeError:
                result = package
        return result

    def generate_uuid(self):
        """
        生成uuid，根据时间戳生成
        :return:
        """
        current_time = str(time.time())
        return encode_b64(current_time.split(".")[1])

    def generate_blind_poc(self):
        """
        生成poc，生成无回显poc ，根据选择的是Dns模块还是Socket模块，选择规则如下:
        1.Dns模块单独开启了，优先使用Dns模块
        2.Socket模块单独开启了，使用Socket模块
        3.Dns模块和Socket模块开启了，使用Dns模块
        4.都没开启则不调用

        根据Dns或者Socket模块开关获取poc 如下代码为检测命令执行
        Simple example usage code:

            blind_poc, check_bilnd_poc_url, hunter_log_api_token = self.generate_blind_poc()

            if not blind_poc["data"]:
                return

            if blind_poc["type"] == "dns":
                attack_payload = "http://%s" % (blind_poc["data"]) # 得到的是一个域名，域名前缀为uuid
            elif blind_poc["type"] == "socket":
                attack_payload = "http://%s:%s/%s" % (blind_poc["data"]["host"], blind_poc["data"]["port"], blind_poc["data"]["uuid"])

            if http_method == HttpMethod.GET or (http_method == HttpMethod.POST and content_type is None):
                payload = UrlDataClassification.add_poc_data(url=temp_url, http_method=http_method, content_type=content_type, poc="|wget %s" % (attack_payload))
                self.request(method=http_method, url=payload, data=temp_data, headers=temp_headers)

            elif http_method == HttpMethod.POST and content_type is not None and temp_data is not None:
                payload = UrlDataClassification.add_poc_data(url=temp_data, http_method=http_method, content_type=content_type, poc="|wget %s" % (attack_payload))
                self.request(method=http_method, url=temp_url, json=json.loads(payload), headers=temp_headers)

            req = requests.get(check_bilnd_poc_url, headers={"token": hunter_log_api_token})
            response = req.json()

            if "status" in response and response["status"] == 200:
                self.result['status'] = True
                self.result['info'] = '%s 存在一个命令执行漏洞' % request_raw['url']
                self.result['payload'] = payload

        :return:
        """
        system_config_single = SystemConfigService.get_single_instance(refresh=True)
        hunter_log_socket_switch = system_config_single.hunter_log_socket_switch
        hunter_log_dns_switch = system_config_single.hunter_log_dns_switch
        plugin_uuid = self.generate_uuid()

        # 生成poc , hunetr log平台查询api url，和 hunter_log 平台api 的查询token
        blind_poc = {"type": "dns", "data": None, "uuid": plugin_uuid}
        check_bilnd_poc_url = self.generate_check_bilnd_poc_url(system_config_single, plugin_uuid)
        hunter_log_api_token = system_config_single.hunter_log_token
        data = None
        if hunter_log_dns_switch:
            blind_poc["type"] = "dns"
            data = "%s.%s" % (plugin_uuid, system_config_single.hunter_log_dns_fake_root_domain)
        elif not hunter_log_dns_switch and hunter_log_socket_switch:
            blind_poc["type"] = "socket"
            data = {"host": system_config_single.hunter_log_socket_host,
                    "port": system_config_single.hunter_log_socket_port, "uuid": plugin_uuid}

        blind_poc["data"] = data
        return blind_poc, check_bilnd_poc_url, hunter_log_api_token

    def generate_check_bilnd_poc_url(self, system_config_single, plugin_uuid):
        """
        生成 hunetr log平台查询api url
        :param hunter_api_url:
        :param plugin_uuid:
        :return:
        """
        if str(system_config_single.hunter_api_url).endswith("/"):
            check_bilnd_poc_url = "{}{}".format(system_config_single.hunter_api_url, plugin_uuid)
        else:
            check_bilnd_poc_url = "{}/{}".format(system_config_single.hunter_api_url, plugin_uuid)
        return check_bilnd_poc_url

    @staticmethod
    def get_parser_name(request_raw):
        """
        获取解析器名
        :param request_raw:
        :return:
        """
        parser_name = request_raw["parser"] if "parser" in request_raw else BaseTrafficParser.CHROME_PARSER
        return parser_name

    @staticmethod
    def get_parser_class(request_raw):
        """
        根据名字获得解析器类
        :return:
        """
        parser_name = BaseChecker.get_parser_name(request_raw)
        parser_class = BaseChecker.PARSER_DIC[parser_name]
        return parser_class

    def record_payload(self, request_raw):
        """
        记录最终发送的payload数据包，展现原生数据格式
        :return:
        """
        pass


"""
author: b5mali4

Simple subPlugin example code:

class Checker(BaseChecker):
    # 检测逻辑
    def check_logic(self, request_raw):
        host, port = get_host_port(request_raw['url'])
        if port == 9012:
            self.result["status"] = True
            self.result["info"] = '%s开放了9012端口服务' % http_poc_url
            self.result['payload'] = http_poc_url

    #插件信息
    def init_plugin_info(self):
        name = "name1"
        imp_version = "< spring 2.1"
        description = "see http://xxxxx/"
        repair = "升级到titans1.8.3或者其以上版本"
        type = VulnType.OTHER
        author = "b5mali4"
        temp_info = {"repair": repair, "name": name, "type": type, "description": description,
                     "imp_version": imp_version, "author": author}
        self.info = dict(self.info, **temp_info)

To use:
>>> req1 = {"url": "http://127.0.0.1:8080/", "method": "get", "headers": "{\"Cookie\":\"SESSION\"}"}
>>> checker = Checker()
>>> checker.check_vuln(req1)
>>> print(checker.result)
"""
import time
import json
from common import log
from sys import version_info
from json.decoder import JSONDecodeError
from abc import ABCMeta, abstractmethod
from common.system_util import encode_b64
from plugins.base.vuln_enum import PluginSwith
from parser.base_traffic_parser import BaseTrafficParser
from parser.chrome_traffic_parser import ChromeTrafficParser
from model.system_config import SystemConfig, SystemConfigService
from exception.hunter_web_exception import BaseHunterException
from exception.request_classification_exception import PluginInfoError
from exception.request_classification_exception import RequestParseError
from exception.request_classification_exception import HeaderParseError

if version_info < (3, 0):
    from exceptions import ValueError as JSONDecodeError
else:
    from json.decoder import JSONDecodeError

class BaseChecker(object):
    __metaclass__ = ABCMeta
    """
    抽象插件父类
    """
    logger = log.get_default_logger()
    # 解析类
    PARSER_DIC = {BaseTrafficParser.DEAFAULT_PARSER: BaseTrafficParser,
                  BaseTrafficParser.CHROME_PARSER: ChromeTrafficParser}

    def __init__(self):
        """
        初始化流程下:
        1.初始化插件基本信息
        2.检测插件信息必传字段是否完整

        disable表示是否禁用
        path表示插件的实际路径
        absolute_plugin_path 插件绝对路径
        relative_plugin_path 插件相对路径
        """
        self.info = dict()
        self.useable = PluginSwith.ON
        self.result = dict()
        self.init_plugin_info()
        self.absolute_plugin_path = None
        self.relative_plugin_path = None
        try:
            self.check_plugin_info()
        except BaseHunterException as e:
            BaseChecker.logger.exception("插件信息错误")

    def init_check_result(self):
        """
        初始化检测结果
        :return:
        """
        self.result = {'status': False, 'info': '没有漏洞(默认的描述信息)', 'error': [], 'details': self.info, 'payload': ''}

    @abstractmethod
    def init_plugin_info(self):
        """
        初始化加载插件
        :return:
        """
        pass

    def check_vuln(self, request_raw):
        """
        check_logic为基础检测逻辑，各个插件自己实现，首先判断插件是否为禁用状态
        :param request_raw:
        :return:
        """
        try:
            self.init_check_result()
            if self.useable == PluginSwith.ON:
                self.check_request_raw(request_raw)
                self.check_logic(request_raw)
                self.record_payload(request_raw)
        except BaseHunterException as e:
            BaseChecker.logger.exception("请求数据包格式出错")
        finally:
            return self.result

    @abstractmethod
    def check_logic(self, request_raw):
        """
        在里面对result进行赋值
        :param request_raw:
        :return:
        """
        pass

    async def async_check_vuln(self, request_raw):
        """
        新增异步函数
        :param package:
        :return:
        """
        return self.check_vuln(request_raw)

    def parse_headers(self, headers=dict()):
        """
        解析参数
        :param headers:
        :return:
        """
        if not isinstance(headers, dict):
            raise HeaderParseError()

    def check_request_raw(self, request_raw):
        """
        检查流量数据包中的必要字段是否完整
        :param request_raw:
        :return:
        """
        if not isinstance(request_raw, dict):
            raise BaseHunterException(
                "传入的request_raw不是一个dict类型，其类型为{type}".format(type=type(request_raw) if request_raw else None))

        keys = ["type", "url", "method", "headers"]
        miss_keys = []
        for key in keys:
            if key not in request_raw:
                miss_keys.append(key)
        if len(miss_keys) > 0:
            raise RequestParseError(request_raw, miss_keys)

    def check_plugin_info(self):
        """
        检查info的必要字段是否完整
        :param info:
        :return:
        """
        assert isinstance(self.info, dict), "传入的info不是一个dict类型"
        keys = ["tag", "author", "name", "imp_version", "description", "repair", "type"]
        miss_keys = []
        for key in keys:
            if key not in self.info:
                miss_keys.append(key)
        if len(miss_keys) > 0:
            raise PluginInfoError(self.info, miss_keys)

    def get_plugin_info(self):
        return self.info

    def parse_data(self, package):
        """
        解析data
        data为空
        data 为XML,data为合法str,data为非法str
        :param data:
        :return:
        """
        if isinstance(package, dict):
            if "data" in package:
                try:
                    result = json.loads(package['data'])
                except JSONDecodeError:
                    result = package['data']
            else:
                result = None
        if isinstance(package, str):
            try:
                result = json.loads(package)
            except JSONDecodeError:
                result = package
        return result

    def generate_uuid(self):
        """
        生成uuid，根据时间戳生成
        :return:
        """
        current_time = str(time.time())
        return encode_b64

    def generate_blind_poc(self):
        """
        生成poc，生成无回显poc ，根据选择的是Dns模块还是Socket模块，选择规则如下:
        1.Dns模块单独开启了，优先使用Dns模块
        2.Socket模块单独开启了，使用Socket模块
        3.Dns模块和Socket模块开启了，使用Dns模块
        4.都没开启则不调用

        根据Dns或者Socket模块开关获取poc 如下代码为检测命令执行
        Simple example usage code:

            blind_poc, check_bilnd_poc_url, hunter_log_api_token = self.generate_blind_poc()

            if not blind_poc["data"]:
                return

            if blind_poc["type"] == "dns":
                attack_payload = "http://%s" % (blind_poc["data"]) # 得到的是一个域名，域名前缀为uuid
            elif blind_poc["type"] == "socket":
                attack_payload = "http://%s:%s/%s" % (blind_poc["data"]["host"], blind_poc["data"]["port"], blind_poc["data"]["uuid"])

            if http_method == HttpMethod.GET or (http_method == HttpMethod.POST and content_type is None):
                payload = UrlDataClassification.add_poc_data(url=temp_url, http_method=http_method, content_type=content_type, poc="|wget %s" % (attack_payload))
                self.request(method=http_method, url=payload, data=temp_data, headers=temp_headers)

            elif http_method == HttpMethod.POST and content_type is not None and temp_data is not None:
                payload = UrlDataClassification.add_poc_data(url=temp_data, http_method=http_method, content_type=content_type, poc="|wget %s" % (attack_payload))
                self.request(method=http_method, url=temp_url, json=json.loads(payload), headers=temp_headers)

            req = requests.get(check_bilnd_poc_url, headers={"token": hunter_log_api_token})
            response = req.json()

            if "status" in response and response["status"] == 200:
                self.result['status'] = True
                self.result['info'] = '%s 存在一个命令执行漏洞' % request_raw['url']
                self.result['payload'] = payload

        :return:
        """
        system_config_single = SystemConfigService.get_single_instance(refresh=True)
        hunter_log_socket_switch = system_config_single.hunter_log_socket_switch
        hunter_log_dns_switch = system_config_single.hunter_log_dns_switch
        plugin_uuid = self.generate_uuid()

        # 生成poc , hunetr log平台查询api url，和 hunter_log 平台api 的查询token
        blind_poc = {"type": "dns", "data": None, "uuid": plugin_uuid}
        check_bilnd_poc_url = self.generate_check_bilnd_poc_url(system_config_single, plugin_uuid)
        hunter_log_api_token = system_config_single.hunter_log_token
        data = None
        if hunter_log_dns_switch:
            blind_poc["type"] = "dns"
            data = "%s.%s" % (plugin_uuid, system_config_single.hunter_log_dns_fake_root_domain)
        elif not hunter_log_dns_switch and hunter_log_socket_switch:
            blind_poc["type"] = "socket"
            data = {"host": system_config_single.hunter_log_socket_host,
                    "port": system_config_single.hunter_log_socket_port, "uuid": plugin_uuid}

        blind_poc["data"] = data
        return blind_poc, check_bilnd_poc_url, hunter_log_api_token

    def generate_check_bilnd_poc_url(self, system_config_single, plugin_uuid):
        """
        生成 hunetr log平台查询api url
        :param hunter_api_url:
        :param plugin_uuid:
        :return:
        """
        if str(system_config_single.hunter_api_url).endswith("/"):
            check_bilnd_poc_url = "{}{}".format(system_config_single.hunter_api_url, plugin_uuid)
        else:
            check_bilnd_poc_url = "{}/{}".format(system_config_single.hunter_api_url, plugin_uuid)
        return check_bilnd_poc_url

    @staticmethod
    def get_parser_name(request_raw):
        """
        获取解析器名
        :param request_raw:
        :return:
        """
        parser_name = request_raw["parser"] if "parser" in request_raw else BaseTrafficParser.CHROME_PARSER
        return parser_name

    @staticmethod
    def get_parser_class(request_raw):
        """
        根据名字获得解析器类
        :return:
        """
        parser_name = BaseChecker.get_parser_name(request_raw)
        parser_class = BaseChecker.PARSER_DIC[parser_name]
        return parser_class

    def record_payload(self, request_raw):
        """
        记录最终发送的payload数据包，展现原生数据格式
        :return:
        """
        pass


        if isinstance(package, dict):
            if "data" in package:
                try:
                    result = json.loads(package['data'])
                except JSONDecodeError:
                    result = package['data']
            else:
                result = None
        if isinstance(package, str):
            try:
                result = json.loads(package)
            except JSONDecodeError:
                result = package
        return result

"""
author: b5mali4

Simple subPlugin example code:

class Checker(BaseChecker):
    # 检测逻辑
    def check_logic(self, request_raw):
        host, port = get_host_port(request_raw['url'])
        if port == 9012:
            self.result["status"] = True
            self.result["info"] = '%s开放了9012端口服务' % http_poc_url
            self.result['payload'] = http_poc_url

    #插件信息
    def init_plugin_info(self):
        name = "name1"
        imp_version = "< spring 2.1"
        description = "see http://xxxxx/"
        repair = "升级到titans1.8.3或者其以上版本"
        type = VulnType.OTHER
        author = "b5mali4"
        temp_info = {"repair": repair, "name": name, "type": type, "description": description,
                     "imp_version": imp_version, "author": author}
        self.info = dict(self.info, **temp_info)

To use:
>>> req1 = {"url": "http://127.0.0.1:8080/", "method": "get", "headers": "{\"Cookie\":\"SESSION\"}"}
>>> checker = Checker()
>>> checker.check_vuln(req1)
>>> print(checker.result)
"""
import time
import json
from common import log
from sys import version_info
from json.decoder import JSONDecodeError
from abc import ABCMeta, abstractmethod
from common.system_util import encode_b64
from plugins.base.vuln_enum import PluginSwith
from parser.base_traffic_parser import BaseTrafficParser
from parser.chrome_traffic_parser import ChromeTrafficParser
from model.system_config import SystemConfig, SystemConfigService
from exception.hunter_web_exception import BaseHunterException
from exception.request_classification_exception import PluginInfoError
from exception.request_classification_exception import RequestParseError
from exception.request_classification_exception import HeaderParseError

if version_info < (3, 0):
    from exceptions import ValueError as JSONDecodeError
else:
    from json.decoder import JSONDecodeError

class BaseChecker(object):
    __metaclass__ = ABCMeta
    """
    抽象插件父类
    """
    logger = log.get_default_logger()
    # 解析类
    PARSER_DIC = {BaseTrafficParser.DEAFAULT_PARSER: BaseTrafficParser,
                  BaseTrafficParser.CHROME_PARSER: ChromeTrafficParser}

    def __init__(self):
        """
        初始化流程下:
        1.初始化插件基本信息
        2.检测插件信息必传字段是否完整

        disable表示是否禁用
        path表示插件的实际路径
        absolute_plugin_path 插件绝对路径
        relative_plugin_path 插件相对路径
        """
        self.info = dict()
        self.useable = PluginSwith.ON
        self.result = dict()
        self.init_plugin_info()
        self.absolute_plugin_path = None
        self.relative_plugin_path = None
        try:
            self.check_plugin_info()
        except BaseHunterException as e:
            BaseChecker.logger.exception("插件信息错误")

    def init_check_result(self):
        """
        初始化检测结果
        :return:
        """
        self.result = {'status': False, 'info': '没有漏洞(默认的描述信息)', 'error': [], 'details': self.info, 'payload': ''}

    @abstractmethod
    def init_plugin_info(self):
        """
        初始化加载插件
        :return:
        """
        pass

    def check_vuln(self, request_raw):
        """
        check_logic为基础检测逻辑，各个插件自己实现，首先判断插件是否为禁用状态
        :param request_raw:
        :return:
        """
        try:
            self.init_check_result()
            if self.useable == PluginSwith.ON:
                self.check_request_raw(request_raw)
                self.check_logic(request_raw)
                self.record_payload(request_raw)
        except BaseHunterException as e:
            BaseChecker.logger.exception("请求数据包格式出错")
        finally:
            return self.result

    @abstractmethod
    def check_logic(self, request_raw):
        """
        在里面对result进行赋值
        :param request_raw:
        :return:
        """
        pass

    async def async_check_vuln(self, request_raw):
        """
        新增异步函数
        :param package:
        :return:
        """
        return self.check_vuln(request_raw)

    def parse_headers(self, headers=dict()):
        """
        解析参数
        :param headers:
        :return:
        """
        if not isinstance(headers, dict):
            raise HeaderParseError()

    def check_request_raw(self, request_raw):
        """
        检查流量数据包中的必要字段是否完整
        :param request_raw:
        :return:
        """
        if not isinstance(request_raw, dict):
            raise BaseHunterException(
                "传入的request_raw不是一个dict类型，其类型为{type}".format(type=type(request_raw) if request_raw else None))

        keys = ["type", "url", "method", "headers"]
        miss_keys = []
        for key in keys:
            if key not in request_raw:
                miss_keys.append(key)
        if len(miss_keys) > 0:
            raise RequestParseError(request_raw, miss_keys)

    def check_plugin_info(self):
        """
        检查info的必要字段是否完整
        :param info:
        :return:
        """
        assert isinstance(self.info, dict), "传入的info不是一个dict类型"
        keys = ["tag", "author", "name", "imp_version", "description", "repair", "type"]
        miss_keys = []
        for key in keys:
            if key not in self.info:
                miss_keys.append(key)
        if len(miss_keys) > 0:
            raise PluginInfoError(self.info, miss_keys)

    def get_plugin_info(self):
        return self.info

    def parse_data(self, package):
        """
        解析data
        data为空
        data 为XML,data为合法str,data为非法str
        :param data:
        :return:
        """

    def generate_uuid(self):
        """
        生成uuid，根据时间戳生成
        :return:
        """
        current_time = str(time.time())
        return encode_b64(current_time.split(".")[1])

    def generate_blind_poc(self):
        """
        生成poc，生成无回显poc ，根据选择的是Dns模块还是Socket模块，选择规则如下:
        1.Dns模块单独开启了，优先使用Dns模块
        2.Socket模块单独开启了，使用Socket模块
        3.Dns模块和Socket模块开启了，使用Dns模块
        4.都没开启则不调用

        根据Dns或者Socket模块开关获取poc 如下代码为检测命令执行
        Simple example usage code:

            blind_poc, check_bilnd_poc_url, hunter_log_api_token = self.generate_blind_poc()

            if not blind_poc["data"]:
                return

            if blind_poc["type"] == "dns":
                attack_payload = "http://%s" % (blind_poc["data"]) # 得到的是一个域名，域名前缀为uuid
            elif blind_poc["type"] == "socket":
                attack_payload = "http://%s:%s/%s" % (blind_poc["data"]["host"], blind_poc["data"]["port"], blind_poc["data"]["uuid"])

            if http_method == HttpMethod.GET or (http_method == HttpMethod.POST and content_type is None):
                payload = UrlDataClassification.add_poc_data(url=temp_url, http_method=http_method, content_type=content_type, poc="|wget %s" % (attack_payload))
                self.request(method=http_method, url=payload, data=temp_data, headers=temp_headers)

            elif http_method == HttpMethod.POST and content_type is not None and temp_data is not None:
                payload = UrlDataClassification.add_poc_data(url=temp_data, http_method=http_method, content_type=content_type, poc="|wget %s" % (attack_payload))
                self.request(method=http_method, url=temp_url, json=json.loads(payload), headers=temp_headers)

            req = requests.get(check_bilnd_poc_url, headers={"token": hunter_log_api_token})
            response = req.json()

            if "status" in response and response["status"] == 200:
                self.result['status'] = True
                self.result['info'] = '%s 存在一个命令执行漏洞' % request_raw['url']
                self.result['payload'] = payload

        :return:
        """
        system_config_single = SystemConfigService.get_single_instance(refresh=True)
        hunter_log_socket_switch = system_config_single.hunter_log_socket_switch
        hunter_log_dns_switch = system_config_single.hunter_log_dns_switch
        plugin_uuid = self.generate_uuid()

        # 生成poc , hunetr log平台查询api url，和 hunter_log 平台api 的查询token
        blind_poc = {"type": "dns", "data": None, "uuid": plugin_uuid}
        check_bilnd_poc_url = self.generate_check_bilnd_poc_url(system_config_single, plugin_uuid)
        hunter_log_api_token = system_config_single.hunter_log_token
        data = None
        if hunter_log_dns_switch:
            blind_poc["type"] = "dns"
            data = "%s.%s" % (plugin_uuid, system_config_single.hunter_log_dns_fake_root_domain)
        elif not hunter_log_dns_switch and hunter_log_socket_switch:
            blind_poc["type"] = "socket"
            data = {"host": system_config_single.hunter_log_socket_host,
                    "port": system_config_single.hunter_log_socket_port, "uuid": plugin_uuid}

        blind_poc["data"] = data
        return blind_poc, check_bilnd_poc_url, hunter_log_api_token

    def generate_check_bilnd_poc_url(self, system_config_single, plugin_uuid):
        """
        生成 hunetr log平台查询api url
        :param hunter_api_url:
        :param plugin_uuid:
        :return:
        """
        if str(system_config_single.hunter_api_url).endswith("/"):
            check_bilnd_poc_url = "{}{}".format(system_config_single.hunter_api_url, plugin_uuid)
        else:
            check_bilnd_poc_url = "{}/{}".format(system_config_single.hunter_api_url, plugin_uuid)
        return check_bilnd_poc_url

    @staticmethod
    def get_parser_name(request_raw):
        """
        获取解析器名
        :param request_raw:
        :return:
        """
        parser_name = request_raw["parser"] if "parser" in request_raw else BaseTrafficParser.CHROME_PARSER
        return parser_name

    @staticmethod
    def get_parser_class(request_raw):
        """
        根据名字获得解析器类
        :return:
        """
        parser_name = BaseChecker.get_parser_name(request_raw)
        parser_class = BaseChecker.PARSER_DIC[parser_name]
        return parser_class

    def record_payload(self, request_raw):
        """
        记录最终发送的payload数据包，展现原生数据格式
        :return:
        """
        pass


try:
            self.check_plugin_info()
        except BaseHunterException as e:
            BaseChecker.logger.exception("插件信息错误")

"""
author: b5mali4

Simple subPlugin example code:

class Checker(BaseChecker):
    # 检测逻辑
    def check_logic(self, request_raw):
        host, port = get_host_port(request_raw['url'])
        if port == 9012:
            self.result["status"] = True
            self.result["info"] = '%s开放了9012端口服务' % http_poc_url
            self.result['payload'] = http_poc_url

    #插件信息
    def init_plugin_info(self):
        name = "name1"
        imp_version = "< spring 2.1"
        description = "see http://xxxxx/"
        repair = "升级到titans1.8.3或者其以上版本"
        type = VulnType.OTHER
        author = "b5mali4"
        temp_info = {"repair": repair, "name": name, "type": type, "description": description,
                     "imp_version": imp_version, "author": author}
        self.info = dict(self.info, **temp_info)

To use:
>>> req1 = {"url": "http://127.0.0.1:8080/", "method": "get", "headers": "{\"Cookie\":\"SESSION\"}"}
>>> checker = Checker()
>>> checker.check_vuln(req1)
>>> print(checker.result)
"""
import time
import json
from common import log
from sys import version_info
from json.decoder import JSONDecodeError
from abc import ABCMeta, abstractmethod
from common.system_util import encode_b64
from plugins.base.vuln_enum import PluginSwith
from parser.base_traffic_parser import BaseTrafficParser
from parser.chrome_traffic_parser import ChromeTrafficParser
from model.system_config import SystemConfig, SystemConfigService
from exception.hunter_web_exception import BaseHunterException
from exception.request_classification_exception import PluginInfoError
from exception.request_classification_exception import RequestParseError
from exception.request_classification_exception import HeaderParseError

if version_info < (3, 0):
    from exceptions import ValueError as JSONDecodeError
else:
    from json.decoder import JSONDecodeError

class BaseChecker(object):
    __metaclass__ = ABCMeta
    """
    抽象插件父类
    """
    logger = log.get_default_logger()
    # 解析类
    PARSER_DIC = {BaseTrafficParser.DEAFAULT_PARSER: BaseTrafficParser,
                  BaseTrafficParser.CHROME_PARSER: ChromeTrafficParser}

    def __init__(self):
        """
        初始化流程下:
        1.初始化插件基本信息
        2.检测插件信息必传字段是否完整

        disable表示是否禁用
        path表示插件的实际路径
        absolute_plugin_path 插件绝对路径
        relative_plugin_path 插件相对路径
        """
        self.info = dict()
        self.useable = PluginSwith.ON
        self.result = dict()
        self.init_plugin_info()
        self.absolute_plugin_path = None
        self.relative_plugin_path = None

    def init_check_result(self):
        """
        初始化检测结果
        :return:
        """
        self.result = {'status': False, 'info': '没有漏洞(默认的描述信息)', 'error': [], 'details': self.info, 'payload': ''}

    @abstractmethod
    def init_plugin_info(self):
        """
        初始化加载插件
        :return:
        """
        pass

    def check_vuln(self, request_raw):
        """
        check_logic为基础检测逻辑，各个插件自己实现，首先判断插件是否为禁用状态
        :param request_raw:
        :return:
        """
        try:
            self.init_check_result()
            if self.useable == PluginSwith.ON:
                self.check_request_raw(request_raw)
                self.check_logic(request_raw)
                self.record_payload(request_raw)
        except BaseHunterException as e:
            BaseChecker.logger.exception("请求数据包格式出错")
        finally:
            return self.result

    @abstractmethod
    def check_logic(self, request_raw):
        """
        在里面对result进行赋值
        :param request_raw:
        :return:
        """
        pass

    async def async_check_vuln(self, request_raw):
        """
        新增异步函数
        :param package:
        :return:
        """
        return self.check_vuln(request_raw)

    def parse_headers(self, headers=dict()):
        """
        解析参数
        :param headers:
        :return:
        """
        if not isinstance(headers, dict):
            raise HeaderParseError()

    def check_request_raw(self, request_raw):
        """
        检查流量数据包中的必要字段是否完整
        :param request_raw:
        :return:
        """
        if not isinstance(request_raw, dict):
            raise BaseHunterException(
                "传入的request_raw不是一个dict类型，其类型为{type}".format(type=type(request_raw) if request_raw else None))

        keys = ["type", "url", "method", "headers"]
        miss_keys = []
        for key in keys:
            if key not in request_raw:
                miss_keys.append(key)
        if len(miss_keys) > 0:
            raise RequestParseError(request_raw, miss_keys)

    def check_plugin_info(self):
        """
        检查info的必要字段是否完整
        :param info:
        :return:
        """
        assert isinstance(self.info, dict), "传入的info不是一个dict类型"
        keys = ["tag", "author", "name", "imp_version", "description", "repair", "type"]
        miss_keys = []
        for key in keys:
            if key not in self.info:
                miss_keys.append(key)
        if len(miss_keys) > 0:
            raise PluginInfoError(self.info, miss_keys)

    def get_plugin_info(self):
        return self.info

    def parse_data(self, package):
        """
        解析data
        data为空
        data 为XML,data为合法str,data为非法str
        :param data:
        :return:
        """
        if isinstance(package, dict):
            if "data" in package:
                try:
                    result = json.loads(package['data'])
                except JSONDecodeError:
                    result = package['data']
            else:
                result = None
        if isinstance(package, str):
            try:
                result = json.loads(package)
            except JSONDecodeError:
                result = package
        return result

    def generate_uuid(self):
        """
        生成uuid，根据时间戳生成
        :return:
        """
        current_time = str(time.time())
        return encode_b64(current_time.split(".")[1])

    def generate_blind_poc(self):
        """
        生成poc，生成无回显poc ，根据选择的是Dns模块还是Socket模块，选择规则如下:
        1.Dns模块单独开启了，优先使用Dns模块
        2.Socket模块单独开启了，使用Socket模块
        3.Dns模块和Socket模块开启了，使用Dns模块
        4.都没开启则不调用

        根据Dns或者Socket模块开关获取poc 如下代码为检测命令执行
        Simple example usage code:

            blind_poc, check_bilnd_poc_url, hunter_log_api_token = self.generate_blind_poc()

            if not blind_poc["data"]:
                return

            if blind_poc["type"] == "dns":
                attack_payload = "http://%s" % (blind_poc["data"]) # 得到的是一个域名，域名前缀为uuid
            elif blind_poc["type"] == "socket":
                attack_payload = "http://%s:%s/%s" % (blind_poc["data"]["host"], blind_poc["data"]["port"], blind_poc["data"]["uuid"])

            if http_method == HttpMethod.GET or (http_method == HttpMethod.POST and content_type is None):
                payload = UrlDataClassification.add_poc_data(url=temp_url, http_method=http_method, content_type=content_type, poc="|wget %s" % (attack_payload))
                self.request(method=http_method, url=payload, data=temp_data, headers=temp_headers)

            elif http_method == HttpMethod.POST and content_type is not None and temp_data is not None:
                payload = UrlDataClassification.add_poc_data(url=temp_data, http_method=http_method, content_type=content_type, poc="|wget %s" % (attack_payload))
                self.request(method=http_method, url=temp_url, json=json.loads(payload), headers=temp_headers)

            req = requests.get(check_bilnd_poc_url, headers={"token": hunter_log_api_token})
            response = req.json()

            if "status" in response and response["status"] == 200:
                self.result['status'] = True
                self.result['info'] = '%s 存在一个命令执行漏洞' % request_raw['url']
                self.result['payload'] = payload

        :return:
        """
        system_config_single = SystemConfigService.get_single_instance(refresh=True)
        hunter_log_socket_switch = system_config_single.hunter_log_socket_switch
        hunter_log_dns_switch = system_config_single.hunter_log_dns_switch
        plugin_uuid = self.generate_uuid()

        # 生成poc , hunetr log平台查询api url，和 hunter_log 平台api 的查询token
        blind_poc = {"type": "dns", "data": None, "uuid": plugin_uuid}
        check_bilnd_poc_url = self.generate_check_bilnd_poc_url(system_config_single, plugin_uuid)
        hunter_log_api_token = system_config_single.hunter_log_token
        data = None
        if hunter_log_dns_switch:
            blind_poc["type"] = "dns"
            data = "%s.%s" % (plugin_uuid, system_config_single.hunter_log_dns_fake_root_domain)
        elif not hunter_log_dns_switch and hunter_log_socket_switch:
            blind_poc["type"] = "socket"
            data = {"host": system_config_single.hunter_log_socket_host,
                    "port": system_config_single.hunter_log_socket_port, "uuid": plugin_uuid}

        blind_poc["data"] = data
        return blind_poc, check_bilnd_poc_url, hunter_log_api_token

    def generate_check_bilnd_poc_url(self, system_config_single, plugin_uuid):
        """
        生成 hunetr log平台查询api url
        :param hunter_api_url:
        :param plugin_uuid:
        :return:
        """
        if str(system_config_single.hunter_api_url).endswith("/"):
            check_bilnd_poc_url = "{}{}".format(system_config_single.hunter_api_url, plugin_uuid)
        else:
            check_bilnd_poc_url = "{}/{}".format(system_config_single.hunter_api_url, plugin_uuid)
        return check_bilnd_poc_url

    @staticmethod
    def get_parser_name(request_raw):
        """
        获取解析器名
        :param request_raw:
        :return:
        """
        parser_name = request_raw["parser"] if "parser" in request_raw else BaseTrafficParser.CHROME_PARSER
        return parser_name

    @staticmethod
    def get_parser_class(request_raw):
        """
        根据名字获得解析器类
        :return:
        """
        parser_name = BaseChecker.get_parser_name(request_raw)
        parser_class = BaseChecker.PARSER_DIC[parser_name]
        return parser_class

    def record_payload(self, request_raw):
        """
        记录最终发送的payload数据包，展现原生数据格式
        :return:
        """
        pass


import os

from ..common.dataset import RestrepoDataset, DTUDataset
from ..train_network.sample import DefaultSampleGenerator,\
    HartmannSampleGenerator, CompareWithReferenceSampleGenerator

def add_nn_arguments(parser):
    """Add arguments to a parser that are related with the NN computation.
    """
    parser.add_argument(
        "--lr",
        type=float,
        default=1e-3,
        help="Learning rate (default 1e-3)"
    )
    parser.add_argument(
        "--reducer",
        choices=["max", "average", "topK"],
        default="average",
        help=("The reducer to be used in order to compute the final "
              "similarity scores for each depth plane (defaut=average)")
    )
    parser.add_argument(
        "--merge_layer",
        choices=["dot-product", "cosine-similarity"],
        default="dot-product",
        help=("Merge layer used to compute the similarity between the features"
              " (default=dot-product)")
    )
    parser.add_argument(
        "--k",
        type=int,
        default=5,
        help="Parameter used only for the topK reducer (default=5)"
    )
    parser.add_argument(
        "--optimizer",
        choices=["Adam", "SGD"],
        default="Adam",
        help="The optimizer to be used (default=Adam)"
    )
    parser.add_argument(
        "--momentum",
        type=float,
        default=0.9,
        help=("Parameter used to update momentum in case of SGD optimizer"
              " (default=0.9)")
    )
    parser.add_argument(
        "--network_architecture",
        choices=[
            "simple_cnn",
            "simple_nn_for_training",
            "simple_nn_for_training_voxel_space",
            "hartmann"
        ],
        default="simple_nn_for_training",
        help="The network architecture to be used"
    )
    parser.add_argument(
        "--cnn_factory",
        choices=[
            "simple_cnn",
            "simple_cnn_ln",
            "dilated_cnn_receptive_field_25",
            "dilated_cnn_receptive_field_25_with_tanh",
            "hartmann_cnn"
        ],
        default="simple_cnn",
        help="NN architecture to be used for the Multi-View CNN"
    )
    parser.add_argument(
        "--loss",
        choices=[
            "categorical_crossentropy",
            "emd",
            "squared_emd",
            "expected_squared_error"
        ],
        default="emd",
        help="Type of loss used for training (default emd)"
    )
    parser.add_argument(
        "--padding",
        default=None,
        type=int,
        help="Zero padding around images"
    )
    parser.add_argument(
        "--weight_decay",
        type=float,
        default=0.0,
        help="L2 regularizer factor used for weight decay"
    )

def add_training_arguments(parser):
    """Add arguments to a parser that are related with the training process.
    """
    parser.add_argument(
        "--epochs",
        type=int,
        default=500,
        help="Number of epochs (default=150)"
    )
    parser.add_argument(
        "--steps_per_epoch",
        type=int,
        default=500,
        help="Total number of batches of samples (default=500)"
    )
    parser.add_argument(
        "--training_cached_samples",
        type=int,
        default=500,
        help="Number of samples kept in cache (default=500)"
    )
    parser.add_argument(
        "--n_test_samples",
        type=int,
        default=500,
        help="Number of samples used in the validation set (default=500)"
    )

    parser.add_argument(
        "--lr_epochs",
        type=lambda x: map(int, x.split(",")),
        default="50,80,100,120",
        help="Training epochs with diminishing learning rate"
    )
    parser.add_argument(
        "--lr_factor",
        type=float,
        default=None,
        help=("Factor according to which the learning rate will be diminished"
              " (default=None)")
    )

def add_generation_arguments(parser):
    """Add arguments to a parser that are related with the sample generation
    process.
    """
    parser.add_argument(
        "--patch_shape",
        type=lambda x: tuple(map(int, x.split(","))),
        default="11,11,3",
        help="The shape of a patch (default=(11,11,3)"
    )
    parser.add_argument(
        "--depth_planes",
        type=int,
        default=32,
        help="Number of depth planes to be used (default=32)"
    )
    parser.add_argument(
        "--neighbors",
        type=int,
        default=4,
        help=("Number of consecutive frames used to compute the pairs"
              " (default=4)")
    )
    parser.add_argument(
        "--target_distribution_factory",
        choices=["dirac", "guassian"],
        default="dirac",
        help="The type of the final depth distribution (default=dirac)"
    )
    parser.add_argument(
        "--stddev_factor",
        type=float,
        default=1.0,
        help=("Initial standard deviation for the gaussian depth distribution"
              " (default=1.0)")
    )
    parser.add_argument(
        "--std_is_distance",
        action="store_true",
        help=("When set the std is semantically equal to the distance between"
              " the minimum and maximum point in the ray, while otherwise it "
              "is the square of their corresponding distances")
    )
    parser.add_argument(
        "--expand_patch",
        action="store_true",
        help=("When set return also patches that are partially or entirely"
              " black if the point is outside the image boundaries")
    )
    parser.add_argument(
        "--sampling_policy",
        choices=[
            "sample_in_disparity",
            "sample_in_bbox",
            "sample_in_range",
            "sample_in_voxel_space",
            "tf_sample_in_bbox",
            "tf_sample_in_range",
            "full_tf_sample_in_bbox",
            "full_tf_sample_in_range"
        ],
        default="sample_in_bbox",
        help=("The sampling generation policy to compute the depth planes"
              " (default=sample_in_bbox)")
    )
    parser.add_argument(
        "--depth_range",
        type=lambda x: tuple(map(float, x.split(","))),
        default="3.0,7.0",
        help="The depth range used when sampling planes in range"
    )
    parser.add_argument(
        "--grid_shape",
        type=lambda x: tuple(map(int, x.split(","))),
        default="256,256,128",
        help="The number of voxels per axis (default=(256, 256, 128)"
    )
    parser.add_argument(
        "--maximum_number_of_marched_voxels",
        type=int,
        default=650,
        help=("The maximum number of voxels through which a ray can intersect"
              "(default=650)")
    )

def add_experiments_related_arguments(parser):
    parser.add_argument(
        "--training_set_name",
        default="BH",
        help="The name of the training set"
    )
    parser.add_argument(
        "--test_set_name",
        default="Downtown",
        help="The name of the test set"
    )
    parser.add_argument(
        "--credentials",
        default=os.path.join(os.path.dirname(__file__), ".credentials"),
        help="The credentials file for the Google API"
    )
    parser.add_argument(
        "--spreadsheet",
        default="Sheet1",
        help="The spreadsheet to save the results"
    )

def add_hartmann_related_arguments(parser):
    parser.add_argument(
        "--step_depth",
        default=15,
        type=int,
        help="Number of depth planes to skip before and after the gt"
    )

def add_metrics_related_arguments(parser):
    parser.add_argument(
        "--borders",
        default=40,
        type=int,
        help="The number of pixels to drop from the borders of the image"
    )
    parser.add_argument(
        "--truncate",
        default=float("inf"),
        type=float,
        help="Truncate all distances to this number if their larger"
    )
    parser.add_argument(
        "--min_distance",
        default=-1,
        type=float,
        help="Reduce the density of the points based on this distance"
    )
    parser.add_argument(
        "--consistency_threshold",
        default=0.75,
        type=float,
        help=("The value of the consistency level imposed when converting "
              " depth maps to point-clouds (default=0.75)")
    )
    parser.add_argument(
        "--n_neighbors",
        default=5,
        type=int,
        help=("Number of views considered during the consistency check "
              " (default=5)")
    )
    parser.add_argument(
        "--with_consistency_check",
        action="store_true",
        help=("When set transform depth maps to point cloud using a "
              " consistency check")
    )

def add_dataset_related_arguments(parser):
    parser.add_argument(
        "--select_neighbors_based_on",
        choices=[
            "filesystem",
            "distance"
        ],
        default="filesystem",
        help="Illumination condition used only for DTU dataset"
    )
    parser.add_argument(
        "--illumination_condition",
        choices=[
            "max",
            "0_r5000",
            "1_r5000",
            "2_r5000",
            "3_r5000",
            "4_r5000",
            "5_r5000",
            "6_r5000"
        ],
        default="max",
        help="Illumination condition used only for DTU dataset"
    )
    parser.add_argument(
        "--dataset_type",
        choices=["restrepo", "dtu"],
        default="restrepo",
        help="Choose the type of the dataset"
    )

def add_mrf_related_arguments(parser):
    parser.add_argument(
        "--initial_gamma_prior",
        type=float,
        default=0.05,
        help="The occupancy prior probabality per voxel (default=0.05)"
    )
    parser.add_argument(
        "--bp_iterations",
        type=int,
        default=3,
        help="Number of BP iterations to be used (default=3)"
    )

def add_indexing_related_arguments(parser):
    parser.add_argument(
        "--start_end",
        type=lambda x: tuple(map(int, x.split(","))),
        default="0,5",
        help="The starting and ending frame to be used (default=(0,5)"
    )
    parser.add_argument(
        "--skip_every",
        type=int,
        default=0,
        help="The number of frames to be skipped"
    )

parser.add_argument(
        "--forward_pass_factory",
        choices=[
            "multi_view_cnn",
            "multi_view_cnn_voxel_space",
            "hartmann_fp",
            "raynet"
        ],
        default="full_multi_view_cnn_cuda_fp",
        help="Choose the forward pass factory"
    )
    parser.add_argument(
        "--rays_batch",
        type=int,
        default=130000,
        help="Number of rays per batch (default=130000)"
    )

def get_actual_sampling_policy(name):
    if "sample_in_bbox" in name:
        return "sample_in_bbox"
    elif "sample_in_range" in name:
        return "sample_in_range"
    else:
        raise NotImplementedError("Not implemented yet")

def has_valid_argument(argument, valid_options):
    if argument not in valid_options:
        raise ValueError(
            "Expected argument %r but received %r" % (valid_options, argument)
        )
    else:
        pass

def get_input_output_shapes(name):
    return {
        "default": default_input_output_shape,
        "hartmann": hartmann_input_output_shape,
        "reference_wrt_others": reference_wrt_others_input_output_shape
    }[name]

def get_sample_generator(name):
    return {
        "default": DefaultSampleGenerator,
        "hartmann": HartmannSampleGenerator,
        "reference_wrt_others": CompareWithReferenceSampleGenerator
    }[name]

def default_input_output_shape(generation_params):
    neighbors = generation_params.neighbors
    depth_planes = generation_params.depth_planes
    patch_shape = generation_params.patch_shape

    # Find the number of image pairs given the number of the
    # adjacent images
    N = neighbors * (neighbors + 1) / 2
    dims = (depth_planes, N) + patch_shape

    return [dims]*2, [(depth_planes,)]

def hartmann_input_output_shape(generation_params):
    neighbors = generation_params.neighbors
    patch_shape = generation_params.patch_shape

    return [patch_shape]*(neighbors + 1), [(1, 1, 2)]

def reference_wrt_others_input_output_shape(generation_params):
    views = generation_params.neighbors
    depth_planes = generation_params.depth_planes
    patch_shape = generation_params.patch_shape

    # Find the number of image pairs given the number of the
    # adjacent images
    dims = (depth_planes, views) + patch_shape
    return [dims]*2, [(depth_planes,)]

def build_dataset(
    type,
    dir,
    illumination_condition,
    select_neighbors_based_on="filesystem"
):
    if type.lower() == "dtu":
        return DTUDataset(
            dir,
            illumination_condition,
            select_neighbors_based_on=select_neighbors_based_on
        )
    else:
        return RestrepoDataset(
            dir,
            select_neighbors_based_on=select_neighbors_based_on
        )


views = generation_params.neighbors
    depth_planes = generation_params.depth_planes
    patch_shape = generation_params.patch_shape

    # Find the number of image pairs given the number of the
    # adjacent images
    dims = (depth_planes, views) + patch_shape
    return [dims]*2, [(depth_planes,)]

import os

from ..common.dataset import RestrepoDataset, DTUDataset
from ..train_network.sample import DefaultSampleGenerator,\
    HartmannSampleGenerator, CompareWithReferenceSampleGenerator

def add_nn_arguments(parser):
    """Add arguments to a parser that are related with the NN computation.
    """
    parser.add_argument(
        "--lr",
        type=float,
        default=1e-3,
        help="Learning rate (default 1e-3)"
    )
    parser.add_argument(
        "--reducer",
        choices=["max", "average", "topK"],
        default="average",
        help=("The reducer to be used in order to compute the final "
              "similarity scores for each depth plane (defaut=average)")
    )
    parser.add_argument(
        "--merge_layer",
        choices=["dot-product", "cosine-similarity"],
        default="dot-product",
        help=("Merge layer used to compute the similarity between the features"
              " (default=dot-product)")
    )
    parser.add_argument(
        "--k",
        type=int,
        default=5,
        help="Parameter used only for the topK reducer (default=5)"
    )
    parser.add_argument(
        "--optimizer",
        choices=["Adam", "SGD"],
        default="Adam",
        help="The optimizer to be used (default=Adam)"
    )
    parser.add_argument(
        "--momentum",
        type=float,
        default=0.9,
        help=("Parameter used to update momentum in case of SGD optimizer"
              " (default=0.9)")
    )
    parser.add_argument(
        "--network_architecture",
        choices=[
            "simple_cnn",
            "simple_nn_for_training",
            "simple_nn_for_training_voxel_space",
            "hartmann"
        ],
        default="simple_nn_for_training",
        help="The network architecture to be used"
    )
    parser.add_argument(
        "--cnn_factory",
        choices=[
            "simple_cnn",
            "simple_cnn_ln",
            "dilated_cnn_receptive_field_25",
            "dilated_cnn_receptive_field_25_with_tanh",
            "hartmann_cnn"
        ],
        default="simple_cnn",
        help="NN architecture to be used for the Multi-View CNN"
    )
    parser.add_argument(
        "--loss",
        choices=[
            "categorical_crossentropy",
            "emd",
            "squared_emd",
            "expected_squared_error"
        ],
        default="emd",
        help="Type of loss used for training (default emd)"
    )
    parser.add_argument(
        "--padding",
        default=None,
        type=int,
        help="Zero padding around images"
    )
    parser.add_argument(
        "--weight_decay",
        type=float,
        default=0.0,
        help="L2 regularizer factor used for weight decay"
    )

def add_training_arguments(parser):
    """Add arguments to a parser that are related with the training process.
    """
    parser.add_argument(
        "--epochs",
        type=int,
        default=500,
        help="Number of epochs (default=150)"
    )
    parser.add_argument(
        "--steps_per_epoch",
        type=int,
        default=500,
        help="Total number of batches of samples (default=500)"
    )
    parser.add_argument(
        "--training_cached_samples",
        type=int,
        default=500,
        help="Number of samples kept in cache (default=500)"
    )
    parser.add_argument(
        "--n_test_samples",
        type=int,
        default=500,
        help="Number of samples used in the validation set (default=500)"
    )

    parser.add_argument(
        "--lr_epochs",
        type=lambda x: map(int, x.split(",")),
        default="50,80,100,120",
        help="Training epochs with diminishing learning rate"
    )
    parser.add_argument(
        "--lr_factor",
        type=float,
        default=None,
        help=("Factor according to which the learning rate will be diminished"
              " (default=None)")
    )

def add_generation_arguments(parser):
    """Add arguments to a parser that are related with the sample generation
    process.
    """
    parser.add_argument(
        "--patch_shape",
        type=lambda x: tuple(map(int, x.split(","))),
        default="11,11,3",
        help="The shape of a patch (default=(11,11,3)"
    )
    parser.add_argument(
        "--depth_planes",
        type=int,
        default=32,
        help="Number of depth planes to be used (default=32)"
    )
    parser.add_argument(
        "--neighbors",
        type=int,
        default=4,
        help=("Number of consecutive frames used to compute the pairs"
              " (default=4)")
    )
    parser.add_argument(
        "--target_distribution_factory",
        choices=["dirac", "guassian"],
        default="dirac",
        help="The type of the final depth distribution (default=dirac)"
    )
    parser.add_argument(
        "--stddev_factor",
        type=float,
        default=1.0,
        help=("Initial standard deviation for the gaussian depth distribution"
              " (default=1.0)")
    )
    parser.add_argument(
        "--std_is_distance",
        action="store_true",
        help=("When set the std is semantically equal to the distance between"
              " the minimum and maximum point in the ray, while otherwise it "
              "is the square of their corresponding distances")
    )
    parser.add_argument(
        "--expand_patch",
        action="store_true",
        help=("When set return also patches that are partially or entirely"
              " black if the point is outside the image boundaries")
    )
    parser.add_argument(
        "--sampling_policy",
        choices=[
            "sample_in_disparity",
            "sample_in_bbox",
            "sample_in_range",
            "sample_in_voxel_space",
            "tf_sample_in_bbox",
            "tf_sample_in_range",
            "full_tf_sample_in_bbox",
            "full_tf_sample_in_range"
        ],
        default="sample_in_bbox",
        help=("The sampling generation policy to compute the depth planes"
              " (default=sample_in_bbox)")
    )
    parser.add_argument(
        "--depth_range",
        type=lambda x: tuple(map(float, x.split(","))),
        default="3.0,7.0",
        help="The depth range used when sampling planes in range"
    )
    parser.add_argument(
        "--grid_shape",
        type=lambda x: tuple(map(int, x.split(","))),
        default="256,256,128",
        help="The number of voxels per axis (default=(256, 256, 128)"
    )
    parser.add_argument(
        "--maximum_number_of_marched_voxels",
        type=int,
        default=650,
        help=("The maximum number of voxels through which a ray can intersect"
              "(default=650)")
    )

def add_experiments_related_arguments(parser):
    parser.add_argument(
        "--training_set_name",
        default="BH",
        help="The name of the training set"
    )
    parser.add_argument(
        "--test_set_name",
        default="Downtown",
        help="The name of the test set"
    )
    parser.add_argument(
        "--credentials",
        default=os.path.join(os.path.dirname(__file__), ".credentials"),
        help="The credentials file for the Google API"
    )
    parser.add_argument(
        "--spreadsheet",
        default="Sheet1",
        help="The spreadsheet to save the results"
    )

def add_hartmann_related_arguments(parser):
    parser.add_argument(
        "--step_depth",
        default=15,
        type=int,
        help="Number of depth planes to skip before and after the gt"
    )

def add_metrics_related_arguments(parser):
    parser.add_argument(
        "--borders",
        default=40,
        type=int,
        help="The number of pixels to drop from the borders of the image"
    )
    parser.add_argument(
        "--truncate",
        default=float("inf"),
        type=float,
        help="Truncate all distances to this number if their larger"
    )
    parser.add_argument(
        "--min_distance",
        default=-1,
        type=float,
        help="Reduce the density of the points based on this distance"
    )
    parser.add_argument(
        "--consistency_threshold",
        default=0.75,
        type=float,
        help=("The value of the consistency level imposed when converting "
              " depth maps to point-clouds (default=0.75)")
    )
    parser.add_argument(
        "--n_neighbors",
        default=5,
        type=int,
        help=("Number of views considered during the consistency check "
              " (default=5)")
    )
    parser.add_argument(
        "--with_consistency_check",
        action="store_true",
        help=("When set transform depth maps to point cloud using a "
              " consistency check")
    )

def add_dataset_related_arguments(parser):
    parser.add_argument(
        "--select_neighbors_based_on",
        choices=[
            "filesystem",
            "distance"
        ],
        default="filesystem",
        help="Illumination condition used only for DTU dataset"
    )
    parser.add_argument(
        "--illumination_condition",
        choices=[
            "max",
            "0_r5000",
            "1_r5000",
            "2_r5000",
            "3_r5000",
            "4_r5000",
            "5_r5000",
            "6_r5000"
        ],
        default="max",
        help="Illumination condition used only for DTU dataset"
    )
    parser.add_argument(
        "--dataset_type",
        choices=["restrepo", "dtu"],
        default="restrepo",
        help="Choose the type of the dataset"
    )

def add_mrf_related_arguments(parser):
    parser.add_argument(
        "--initial_gamma_prior",
        type=float,
        default=0.05,
        help="The occupancy prior probabality per voxel (default=0.05)"
    )
    parser.add_argument(
        "--bp_iterations",
        type=int,
        default=3,
        help="Number of BP iterations to be used (default=3)"
    )

def add_indexing_related_arguments(parser):
    parser.add_argument(
        "--start_end",
        type=lambda x: tuple(map(int, x.split(","))),
        default="0,5",
        help="The starting and ending frame to be used (default=(0,5)"
    )
    parser.add_argument(
        "--skip_every",
        type=int,
        default=0,
        help="The number of frames to be skipped"
    )

def add_forward_pass_factory_related_arguments(parser):
    parser.add_argument(
        "--forward_pass_factory",
        choices=[
            "multi_view_cnn",
            "multi_view_cnn_voxel_space",
            "hartmann_fp",
            "raynet"
        ],
        default="full_multi_view_cnn_cuda_fp",
        help="Choose the forward pass factory"
    )
    parser.add_argument(
        "--rays_batch",
        type=int,
        default=130000,
        help="Number of rays per batch (default=130000)"
    )

def get_actual_sampling_policy(name):
    if "sample_in_bbox" in name:
        return "sample_in_bbox"
    elif "sample_in_range" in name:
        return "sample_in_range"
    else:
        raise NotImplementedError("Not implemented yet")

def has_valid_argument(argument, valid_options):
    if argument not in valid_options:
        raise ValueError(
            "Expected argument %r but received %r" % (valid_options, argument)
        )
    else:
        pass

def get_input_output_shapes(name):
    return {
        "default": default_input_output_shape,
        "hartmann": hartmann_input_output_shape,
        "reference_wrt_others": reference_wrt_others_input_output_shape
    }[name]

def get_sample_generator(name):
    return {
        "default": DefaultSampleGenerator,
        "hartmann": HartmannSampleGenerator,
        "reference_wrt_others": CompareWithReferenceSampleGenerator
    }[name]

def default_input_output_shape(generation_params):
    neighbors = generation_params.neighbors
    depth_planes = generation_params.depth_planes
    patch_shape = generation_params.patch_shape

    # Find the number of image pairs given the number of the
    # adjacent images
    N = neighbors * (neighbors + 1) / 2
    dims = (depth_planes, N) + patch_shape

    return [dims]*2, [(depth_planes,)]

def hartmann_input_output_shape(generation_params):
    neighbors = generation_params.neighbors
    patch_shape = generation_params.patch_shape

    return [patch_shape]*(neighbors + 1), [(1, 1, 2)]

def reference_wrt_others_input_output_shape(generation_params):

def build_dataset(
    type,
    dir,
    illumination_condition,
    select_neighbors_based_on="filesystem"
):
    if type.lower() == "dtu":
        return DTUDataset(
            dir,
            illumination_condition,
            select_neighbors_based_on=select_neighbors_based_on
        )
    else:
        return RestrepoDataset(
            dir,
            select_neighbors_based_on=select_neighbors_based_on
        )


import os

from ..common.dataset import RestrepoDataset, DTUDataset
from ..train_network.sample import DefaultSampleGenerator,\
    HartmannSampleGenerator, CompareWithReferenceSampleGenerator

def add_nn_arguments(parser):
    """Add arguments to a parser that are related with the NN computation.
    """
    parser.add_argument(
        "--lr",
        type=float,
        default=1e-3,
        help="Learning rate (default 1e-3)"
    )
    parser.add_argument(
        "--reducer",
        choices=["max", "average", "topK"],
        default="average",
        help=("The reducer to be used in order to compute the final "
              "similarity scores for each depth plane (defaut=average)")
    )
    parser.add_argument(
        "--merge_layer",
        choices=["dot-product", "cosine-similarity"],
        default="dot-product",
        help=("Merge layer used to compute the similarity between the features"
              " (default=dot-product)")
    )
    parser.add_argument(
        "--k",
        type=int,
        default=5,
        help="Parameter used only for the topK reducer (default=5)"
    )
    parser.add_argument(
        "--optimizer",
        choices=["Adam", "SGD"],
        default="Adam",
        help="The optimizer to be used (default=Adam)"
    )
    parser.add_argument(
        "--momentum",
        type=float,
        default=0.9,
        help=("Parameter used to update momentum in case of SGD optimizer"
              " (default=0.9)")
    )
    parser.add_argument(
        "--network_architecture",
        choices=[
            "simple_cnn",
            "simple_nn_for_training",
            "simple_nn_for_training_voxel_space",
            "hartmann"
        ],
        default="simple_nn_for_training",
        help="The network architecture to be used"
    )
    parser.add_argument(
        "--cnn_factory",
        choices=[
            "simple_cnn",
            "simple_cnn_ln",
            "dilated_cnn_receptive_field_25",
            "dilated_cnn_receptive_field_25_with_tanh",
            "hartmann_cnn"
        ],
        default="simple_cnn",
        help="NN architecture to be used for the Multi-View CNN"
    )
    parser.add_argument(
        "--loss",
        choices=[
            "categorical_crossentropy",
            "emd",
            "squared_emd",
            "expected_squared_error"
        ],
        default="emd",
        help="Type of loss used for training (default emd)"
    )
    parser.add_argument(
        "--padding",
        default=None,
        type=int,
        help="Zero padding around images"
    )
    parser.add_argument(
        "--weight_decay",
        type=float,
        default=0.0,
        help="L2 regularizer factor used for weight decay"
    )

def add_training_arguments(parser):
    """Add arguments to a parser that are related with the training process.
    """
    parser.add_argument(
        "--epochs",
        type=int,
        default=500,
        help="Number of epochs (default=150)"
    )
    parser.add_argument(
        "--steps_per_epoch",
        type=int,
        default=500,
        help="Total number of batches of samples (default=500)"
    )
    parser.add_argument(
        "--training_cached_samples",
        type=int,
        default=500,
        help="Number of samples kept in cache (default=500)"
    )
    parser.add_argument(
        "--n_test_samples",
        type=int,
        default=500,
        help="Number of samples used in the validation set (default=500)"
    )

    parser.add_argument(
        "--lr_epochs",
        type=lambda x: map(int, x.split(",")),
        default="50,80,100,120",
        help="Training epochs with diminishing learning rate"
    )
    parser.add_argument(
        "--lr_factor",
        type=float,
        default=None,
        help=("Factor according to which the learning rate will be diminished"
              " (default=None)")
    )

def add_generation_arguments(parser):
    """Add arguments to a parser that are related with the sample generation
    process.
    """
    parser.add_argument(
        "--patch_shape",
        type=lambda x: tuple(map(int, x.split(","))),
        default="11,11,3",
        help="The shape of a patch (default=(11,11,3)"
    )
    parser.add_argument(
        "--depth_planes",
        type=int,
        default=32,
        help="Number of depth planes to be used (default=32)"
    )
    parser.add_argument(
        "--neighbors",
        type=int,
        default=4,
        help=("Number of consecutive frames used to compute the pairs"
              " (default=4)")
    )
    parser.add_argument(
        "--target_distribution_factory",
        choices=["dirac", "guassian"],
        default="dirac",
        help="The type of the final depth distribution (default=dirac)"
    )
    parser.add_argument(
        "--stddev_factor",
        type=float,
        default=1.0,
        help=("Initial standard deviation for the gaussian depth distribution"
              " (default=1.0)")
    )
    parser.add_argument(
        "--std_is_distance",
        action="store_true",
        help=("When set the std is semantically equal to the distance between"
              " the minimum and maximum point in the ray, while otherwise it "
              "is the square of their corresponding distances")
    )
    parser.add_argument(
        "--expand_patch",
        action="store_true",
        help=("When set return also patches that are partially or entirely"
              " black if the point is outside the image boundaries")
    )
    parser.add_argument(
        "--sampling_policy",
        choices=[
            "sample_in_disparity",
            "sample_in_bbox",
            "sample_in_range",
            "sample_in_voxel_space",
            "tf_sample_in_bbox",
            "tf_sample_in_range",
            "full_tf_sample_in_bbox",
            "full_tf_sample_in_range"
        ],
        default="sample_in_bbox",
        help=("The sampling generation policy to compute the depth planes"
              " (default=sample_in_bbox)")
    )
    parser.add_argument(
        "--depth_range",
        type=lambda x: tuple(map(float, x.split(","))),
        default="3.0,7.0",
        help="The depth range used when sampling planes in range"
    )
    parser.add_argument(
        "--grid_shape",
        type=lambda x: tuple(map(int, x.split(","))),
        default="256,256,128",
        help="The number of voxels per axis (default=(256, 256, 128)"
    )
    parser.add_argument(
        "--maximum_number_of_marched_voxels",
        type=int,
        default=650,
        help=("The maximum number of voxels through which a ray can intersect"
              "(default=650)")
    )

def add_experiments_related_arguments(parser):
    parser.add_argument(
        "--training_set_name",
        default="BH",
        help="The name of the training set"
    )
    parser.add_argument(
        "--test_set_name",
        default="Downtown",
        help="The name of the test set"
    )
    parser.add_argument(
        "--credentials",
        default=os.path.join(os.path.dirname(__file__), ".credentials"),
        help="The credentials file for the Google API"
    )
    parser.add_argument(
        "--spreadsheet",
        default="Sheet1",
        help="The spreadsheet to save the results"
    )

def add_hartmann_related_arguments(parser):
    parser.add_argument(
        "--step_depth",
        default=15,
        type=int,
        help="Number of depth planes to skip before and after the gt"
    )

def add_metrics_related_arguments(parser):
    parser.add_argument(
        "--borders",
        default=40,
        type=int,
        help="The number of pixels to drop from the borders of the image"
    )
    parser.add_argument(
        "--truncate",
        default=float("inf"),
        type=float,
        help="Truncate all distances to this number if their larger"
    )
    parser.add_argument(
        "--min_distance",
        default=-1,
        type=float,
        help="Reduce the density of the points based on this distance"
    )
    parser.add_argument(
        "--consistency_threshold",
        default=0.75,
        type=float,
        help=("The value of the consistency level imposed when converting "
              " depth maps to point-clouds (default=0.75)")
    )
    parser.add_argument(
        "--n_neighbors",
        default=5,
        type=int,
        help=("Number of views considered during the consistency check "
              " (default=5)")
    )
    parser.add_argument(
        "--with_consistency_check",
        action="store_true",
        help=("When set transform depth maps to point cloud using a "
              " consistency check")
    )

def add_dataset_related_arguments(parser):
    parser.add_argument(
        "--select_neighbors_based_on",
        choices=[
            "filesystem",
            "distance"
        ],
        default="filesystem",
        help="Illumination condition used only for DTU dataset"
    )
    parser.add_argument(
        "--illumination_condition",
        choices=[
            "max",
            "0_r5000",
            "1_r5000",
            "2_r5000",
            "3_r5000",
            "4_r5000",
            "5_r5000",
            "6_r5000"
        ],
        default="max",
        help="Illumination condition used only for DTU dataset"
    )
    parser.add_argument(
        "--dataset_type",
        choices=["restrepo", "dtu"],
        default="restrepo",
        help="Choose the type of the dataset"
    )

def add_mrf_related_arguments(parser):
    parser.add_argument(
        "--initial_gamma_prior",
        type=float,
        default=0.05,
        help="The occupancy prior probabality per voxel (default=0.05)"
    )
    parser.add_argument(
        "--bp_iterations",
        type=int,
        default=3,
        help="Number of BP iterations to be used (default=3)"
    )

def add_indexing_related_arguments(parser):
    parser.add_argument(
        "--start_end",
        type=lambda x: tuple(map(int, x.split(","))),
        default="0,5",
        help="The starting and ending frame to be used (default=(0,5)"
    )
    parser.add_argument(
        "--skip_every",
        type=int,
        default=0,
        help="The number of frames to be skipped"
    )

def add_forward_pass_factory_related_arguments(parser):
    parser.add_argument(
        "--forward_pass_factory",
        choices=[
            "multi_view_cnn",
            "multi_view_cnn_voxel_space",
            "hartmann_fp",
            "raynet"
        ],
        default="full_multi_view_cnn_cuda_fp",
        help="Choose the forward pass factory"
    )
    parser.add_argument(
        "--rays_batch",
        type=int,
        default=130000,
        help="Number of rays per batch (default=130000)"
    )

def get_actual_sampling_policy(name):
    if "sample_in_bbox" in name:
        return "sample_in_bbox"
    elif "sample_in_range" in name:
        return "sample_in_range"
    else:
        raise NotImplementedError("Not implemented yet")

def has_valid_argument(argument, valid_options):
    if argument not in valid_options:
        raise ValueError(
            "Expected argument %r but received %r" % (valid_options, argument)
        )
    else:
        pass

def get_input_output_shapes(name):
    return {
        "default": default_input_output_shape,
        "hartmann": hartmann_input_output_shape,
        "reference_wrt_others": reference_wrt_others_input_output_shape
    }[name]

def get_sample_generator(name):
    return {
        "default": DefaultSampleGenerator,
        "hartmann": HartmannSampleGenerator,
        "reference_wrt_others": CompareWithReferenceSampleGenerator
    }[name]

def default_input_output_shape(generation_params):
    neighbors = generation_params.neighbors
    depth_planes = generation_params.depth_planes
    patch_shape = generation_params.patch_shape

    # Find the number of image pairs given the number of the
    # adjacent images
    N = neighbors * (neighbors + 1) / 2
    dims = (depth_planes, N) + patch_shape

    return [dims]*2, [(depth_planes,)]

def hartmann_input_output_shape(generation_params):
    neighbors = generation_params.neighbors
    patch_shape = generation_params.patch_shape

    return [patch_shape]*(neighbors + 1), [(1, 1, 2)]

def reference_wrt_others_input_output_shape(generation_params):
    views = generation_params.neighbors
    depth_planes = generation_

    patch_shape = generation_params.patch_shape

    # Find the number of image pairs given the number of the
    # adjacent images
    dims = (depth_planes, views) + patch_shape
    return [dims]*2, [(depth_planes,)]

def build_dataset(
    type,
    dir,
    illumination_condition,
    select_neighbors_based_on="filesystem"
):
    if type.lower() == "dtu":
        return DTUDataset(
            dir,
            illumination_condition,
            select_neighbors_based_on=select_neighbors_based_on
        )
    else:
        return RestrepoDataset(
            dir,
            select_neighbors_based_on=select_neighbors_based_on
        )


self.real=r
  self.imag=i
 def __iadd__(self,c):
  return Indexer(self.real+c.real,self.imag+c.imag)
 def __str__(self): #repr or str
  return '{} i{}'.format(self.real,self.imag)
c1=Indexer(10,20)
c2=Indexer(40,80)
print('c1 : ',c1)
print('c2 : ',c2)

c1+=c2

print('c3 : ',c1)
'''
output:
c1 :  10 i20
c2 :  40 i80
c3 :  50 i100
'''


class Indexer:
 def __init__(self,r,i):
  self.real=r
  self.imag=i
 def __iadd__(self,c):

 def __str__(self): #repr or str
  return '{} i{}'.format(self.real,self.imag)
c1=Indexer(10,20)
c2=Indexer(40,80)
print('c1 : ',c1)
print('c2 : ',c2)

c1+=c2

print('c3 : ',c1)
'''
output:
c1 :  10 i20
c2 :  40 i80
c3 :  50 i100
'''


class Indexer:
 def __init__(self,r,i):
  self

  self.imag=i
 def __iadd__(self,c):
  return Indexer(self.real+c.real,self.imag+c.imag)
 def __str__(self): #repr or str
  return '{} i{}'.format(self.real,self.imag)
c1=Indexer(10,20)
c2=Indexer(40,80)
print('c1 : ',c1)
print('c2 : ',c2)

c1+=c2

print('c3 : ',c1)
'''
output:
c1 :  10 i20
c2 :  40 i80
c3 :  50 i100
'''