# coding: utf-8
# -------------------------------------------------------------------
# YakPanel
# -------------------------------------------------------------------
# Copyright (c) 2015-2099 YakPanel(www.yakpanel.com) All rights reserved.
# -------------------------------------------------------------------
# Author: hwliang <hwl@yakpanel.com>
# -------------------------------------------------------------------

# 系统防火墙
# ------------------------------
import sys, os, json, re, time, sqlite3
import contextlib
import traceback
from types import coroutine
from xml.etree.ElementTree import ElementTree, Element
from safeModel.base import safeBase
from flask import send_file, abort

os.chdir("/www/server/panel")
sys.path.append("class/")
import public


class main(safeBase):
    __isFirewalld = False
    __isUfw = False
    __firewall_obj = None
    _add_sid = 0
    _ip_list = []
    _port_list = []
    _ufw_default = '/etc/default/ufw'
    _ufw_sysctl = '/etc/ufw/sysctl.conf'
    _ufw_before = '/etc/ufw/before.rules'

    _trans_status = "/www/server/panel/plugin/firewall/status.json"
    _rule_path = "/www/server/panel/plugin/firewall/"
    _ips_path = "/www/server/panel/plugin/firewall/ips.txt"
    _country_path = "/www/server/panel/plugin/firewall/country.txt"
    _white_list_file = "/www/server/panel/plugin/firewall/whitelist.txt"  # 证书验证IP

    _white_list = []
    _firewall_create_tip = '{}/data/firewall_sqlite.pl'.format(
        public.get_panel_path())

    def __init__(self):
        self.__firewall_obj = firewalld()
        if os.path.exists('/usr/sbin/firewalld') and os.path.exists('/usr/bin/yum'):
            self.__isFirewalld = True
        if os.path.exists('/usr/sbin/ufw') and os.path.exists('/usr/bin/apt-get'):
            self.__isUfw = True
            # self.__ufw = '/usr/sbin/ufw'
        self.get_old_rule()

        if not os.path.exists(self._trans_status):
            ret = {"status": "close"}
            public.writeFile(self._trans_status, json.dumps(ret))

        if not os.path.exists(self._firewall_create_tip):
            Sqlite()
            public.writeFile(self._firewall_create_tip, '')

    def get_old_rule(self):
        """
        @兼容防火墙插件规则
        """

        self._rule_path = self._rule_path.replace('plugin', 'data')
        if not os.path.exists(self._rule_path): os.makedirs(self._rule_path)

        if os.path.exists(self._trans_status):
            n_path = self._trans_status.replace('plugin', 'data')
            if not os.path.exists(n_path):
                public.writeFile(n_path, public.readFile(self._trans_status))

        if os.path.exists(self._ips_path):
            n_path = self._ips_path.replace('plugin', 'data')
            if not os.path.exists(n_path):
                public.writeFile(n_path, public.readFile(self._ips_path))

        if os.path.exists(self._white_list_file):
            n_path = self._white_list_file.replace('plugin', 'data')
            if not os.path.exists(n_path):
                public.writeFile(n_path,
                                 public.readFile(self._white_list_file))

        if os.path.exists(self._country_path):
            n_path = self._country_path.replace('plugin', 'data')
            if not os.path.exists(n_path):
                public.writeFile(n_path, public.readFile(self._country_path))

        self._white_list_file = self._white_list_file.replace('plugin', 'data')
        self._country_path = self._country_path.replace('plugin', 'data')
        self._ips_path = self._ips_path.replace('plugin', 'data')
        self._trans_status = self._trans_status.replace('plugin', 'data')

    def install_sys_firewall(self, get):
        """
        @安装系统防火墙
        """

        res = public.install_sys_firewall()
        if res:
            return public.returnMsg(True, public.lang("Successful installation"))
        return public.returnMsg(False, public.lang("installation failed"))

    def get_firewall_info(self, get):
        """
        @name 获取防火墙统计
        """
        data = {}
        data['port'] = public.M('firewall_new').count()
        data['ip'] = public.M('firewall_ip').count()
        data['trans'] = public.M('firewall_trans').count()
        data['country'] = public.M('firewall_country').count()

        isPing = True
        try:
            file = '/etc/sysctl.conf'
            conf = public.readFile(file)
            rep = r"#*net\.ipv4\.icmp_echo_ignore_all\s*=\s*([0-9]+)\n"
            tmp = re.search(rep, conf).groups(0)[0]
            if tmp == '1': isPing = False
        except:
            isPing = True

        data['ping'] = isPing
        data['status'] = self.get_firewall_status()
        return data

    # 服务状态获取
    def get_firewall_status(self):
        if self.__isUfw:
            res = public.ExecShell("systemctl is-active ufw")[0]
            if res == "active":
                return True

            res = public.ExecShell("systemctl list-units | grep ufw")[0]
            if res.find('active running') != -1:
                return True

            res = public.ExecShell('/lib/ufw/ufw-init status')[0]
            if res.find("Firewall is not running") != -1:
                return False

            res = public.ExecShell('ufw status verbose')[0]
            if res.find('inactive') != -1:
                return False

            return True

        if self.__isFirewalld:
            res = public.ExecShell("ps -ef|grep firewalld|grep -v grep")[0]
            if res:
                return True

            res = public.ExecShell("systemctl is-active firewalld")[0]
            if res == "active":
                return True

            res = public.ExecShell("systemctl list-units | grep firewalld")[0]
            if res.find('active running') != -1:
                return True
            return False

        else:
            res = public.ExecShell("/etc/init.d/iptables status")[0]
            if res.find('not running') != -1:
                return False

            res = public.ExecShell("systemctl is-active iptables")[0]
            if res == "active": return True
            return True


    def SetPing(self, get):
        if get.status == '1':
            get.status = '0'
        else:
            get.status = '1'
        filename = '/etc/sysctl.conf'
        conf = public.readFile(filename)
        if conf.find('net.ipv4.icmp_echo') != -1:
            rep = r"net\.ipv4\.icmp_echo.*"
            conf = re.sub(rep, 'net.ipv4.icmp_echo_ignore_all=' + get.status + "\n", conf)
        else:
            conf += "\nnet.ipv4.icmp_echo_ignore_all=" + get.status + "\n"

        if public.writeFile(filename, conf):
            public.ExecShell('sysctl -p')
            return public.returnMsg(True, public.lang("SUCCESS"))
        else:
            return public.returnMsg(
                False,
                # '<a style="color:red;">错误：设置失败，sysctl.conf不可写!</a><br>'
                # '1、如果安装了[YakPanel 系统加固]，请先关闭<br>'
                # '2、如果安装了云锁，请关闭[系统加固]功能<br>'
                # '3、如果安装了安全狗，请关闭[系统防护]功能<br>'
                # '4、如果使用了其它安全软件，请先卸载<br>'
                '<a style="color:red;">Error: Setting failed, sysctl.conf is not writable!</a><br>'
                '1. If [System Hardening] is installed, please close it first<br>'
                '2. If Cloud Lock is installed, please turn off the [System Hardening] function<br>'
                '3. If a security dog is installed, please turn off the [System Protection] function<br>'
                '4. If you use other security software, please uninstall it first<br>'
            )


    # 服务状态控制
    def firewall_admin(self, get):
        order = ['reload', 'restart', 'stop', 'start']
        if not get.status in order:
            return public.returnMsg(False, public.lang("unknown control command!"))
        names = ["reload", "restart", "stop", "start"]
        result = dict(zip(order, names))
        if self.__isUfw:
            if get.status == "stop":
                public.ExecShell('/usr/sbin/ufw disable')
            elif get.status == "start":
                public.ExecShell('echo y|/usr/sbin/ufw enable')
            elif get.status == "reload":
                public.ExecShell('/usr/sbin/ufw reload')
            elif get.status == "restart":
                public.ExecShell('/usr/sbin/ufw disable && /usr/sbin/ufw enable')

            # ufw防火墙启动时重载一次sysctl
            # 解决deian系统启动ufw后禁ping失1效 by wzz/2023-06-14
            filename = '/etc/sysctl.conf'
            conf = public.readFile(filename)
            if conf.find('net.ipv4.icmp_echo') != -1:
                public.ExecShell("sysctl -p")
            public.WriteLog("system firewall", "firewall {}".format(result[get.status]))
            return public.returnMsg(True, public.lang("firewall has {}", result[get.status]))
        if self.__isFirewalld:
            public.ExecShell('systemctl {} firewalld'.format(get.status))
            public.WriteLog("system firewall", "firewall {}".format(result[get.status]))
            return public.returnMsg(True, public.lang("firewall has {}", result[get.status]))
        else:
            public.ExecShell('service iptables {}'.format(get.status))
        public.WriteLog("system firewall", "firewall {}".format(result[get.status]))
        return public.returnMsg(True, public.lang("firewall has {}", result[get.status]))


    # 重载防火墙配置
    def FirewallReload(self):
        if self.__isUfw:
            public.ExecShell('/usr/sbin/ufw reload')  # 兼容安装了多个防火墙的情况 hezhihong  # return
            # 解决deian系统启动ufw后禁ping失1效 by wzz/2023-06-14
            filename = '/etc/sysctl.conf'
            conf = public.readFile(filename)
            if conf.find('net.ipv4.icmp_echo') != -1:
                public.ExecShell("sysctl -p")
        if self.__isFirewalld:
            public.ExecShell('firewall-cmd --reload')
        else:
            public.ExecShell('/etc/init.d/iptables save')
            public.ExecShell('/etc/init.d/iptables restart')


    # 端口扫描
    def CheckPort(self, port, protocol):
        import socket
        localIP = '127.0.0.1'
        temp = {}
        temp['port'] = port
        temp['local'] = True
        try:
            if 'tcp' in protocol.lower():
                s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                s.settimeout(0.01)
                s.connect((localIP, port))
                s.close()
            if 'udp' in protocol.lower():
                s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
                s.settimeout(0.01)
                s.sendto(b'', (localIP, port))
                s.close()
        except:
            temp['local'] = False

        result = 0
        if temp['local']: result += 2
        return result


    # 查询入栈规则
    def get_rules_list(self, args):

        if self.__isFirewalld:
            self.__firewall_obj = firewalld()
            self.GetList()
        else:
            self.get_ufw_list()
        try:
            p = 1
            limit = 15
            if 'p' in args: p = args.p
            if 'limit' in args: limit = args.limit

            where = '1=1'
            sql = public.M('firewall_new')

            if hasattr(args, 'query'):
                where = " ports like '%{search}%' or brief like '%{search}%' or address like '%{search}%'".format(
                    search=args.query)

            count = sql.where(where, ()).count()
            data = public.get_page(count, int(p), int(limit))

            data['data'] = sql.where(where, ()).limit('{},{}'.format(data['shift'], data['row'])).order('addtime desc').select()
            res_data = data['data']
            for i in range(len(res_data)):
                if not 'ports' in res_data[i]:
                    res_data[i]['status'] = -1
                    continue
                d = res_data[i]
                _port = d['ports']
                _protocol = d['protocol']
                if _port.find(':') != -1 or _port.find('.') != -1 or _port.find(
                        '-') != -1:
                    d['status'] = -1
                else:
                    d['status'] = self.CheckPort(int(_port), _protocol)
            for i in res_data:
                if 'brief' in i:
                    i['brief'] = public.xsssec(i['brief'])
            return res_data
        except:
            return []


    def check_firewall_rule(self, args):
        """
            @检测防火墙规则
            """
        port = args['port']
        find = public.M('firewall_new').where('ports=?', (str(port),)).find()
        if find:
            return True
        return False


    # 端口检查
    def check_port(self, port_list):
        rep1 = r"^\d{1,5}(:\d{1,5})?$"
        # rep1 = r'^[0-9]|[1-9]\d{1,3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5]$'
        for port in port_list:
            if port.find('-') != -1:
                ports = port.split('-')
                if not re.search(rep1, ports[0]):
                    return public.returnMsg(False, public.lang("Port range is incorrect!"))
                if not re.search(rep1, ports[1]):
                    return public.returnMsg(False, public.lang("Port range is incorrect!"))
            elif port.find(':') != -1:
                ports = port.split(':')
                if not re.search(rep1, ports[0]):
                    return public.returnMsg(False, public.lang("Port range is incorrect!"))
                if not re.search(rep1, ports[1]):
                    return public.returnMsg(False, public.lang("Port range is incorrect!"))
            else:
                if not re.search(rep1, port):
                    return public.returnMsg(False, public.lang("Port range is incorrect!"))


    def parse_ip_interval(self, ip_str):
        """解析区间IP

            author: lx
            date: 2022/10/25

            Returns:
                list : IP列表
            """
        ips = []
        try:
            rep2 = r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(\/\d{1,2})?$"
            searchor = re.compile(rep2)
            if ip_str.find("-") != -1:
                pre_ip, end_ip = ip_str.split("-")
                if searchor.search(pre_ip) and searchor.search(end_ip):
                    ips.append(pre_ip)
                    pinx = pre_ip.rfind(".") + 1
                    einx = end_ip.rfind(".") + 1
                    pre = pre_ip[0:pinx]
                    end = end_ip[0:einx]
                    if pre == end:
                        start_num = int(pre_ip[pinx:])
                        end_num = min(int(end_ip[einx:]), 255)
                        for i in range(start_num + 1, end_num):
                            new_ip = pre + str(i)
                            if searchor.search(new_ip):
                                ips.append(new_ip)
                        end_ip = end + str(end_num)
                    ips.append(end_ip)
        except:
            pass
        return ips


    # 判断是否为ipv6网段
    @staticmethod
    def is_ipv6_network_segment_or_ipv6_address(ip_datas: str) -> bool:
        from ipaddress import IPv6Network, IPv6Address
        try:
            tmp_data = IPv6Network(ip_datas)
        except:
            try:
                tmp_data = IPv6Address(ip_datas)
            except:
                return False
        return True


    # 添加入栈规则
    def create_rules2(self, get):
        '''
            get 里面 有  protocol port type address brief   五个参数
            protocol == ['tcp','udp']
            port = 端口
            types == [accept、drop] # 放行和禁止
            address  地址，允许放行的ip，如果全部就是：0.0.0.0/0;另外可以包含“,"或者"-"
            表示区间IP
            brief   备注说明
            '''
        protocol = get.protocol
        ports = get.ports.strip()
        types = get.types
        address = get.source.strip()
        port_list = ports.split(',')
        result = self.check_port(port_list)  # 检测端口
        if result: return result

        allow_ips = []
        if address:
            sources = [
                sip.strip() for sip in address.split(",") if sip.strip()
            ]
            rep2 = r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(\/\d{1,2})?$"
            _ips = []
            for source_ip in sources:
                if source_ip.find("-") != -1:
                    _ips += self.parse_ip_interval(source_ip)
                else:
                    _ips.append(source_ip)

            for source_ip in _ips:
                if not re.search(rep2, source_ip) and not self.is_ipv6_network_segment_or_ipv6_address(source_ip):
                    return public.returnMsg(False, public.lang("IP address you've input is illegal!"))
                allow_ips.append(source_ip)
        if not allow_ips:
            allow_ips.append("")
        for source_ip in allow_ips:
            if self.__isUfw:
                for port in port_list:
                    if port.find('-') != -1:
                        port = port.replace('-', ':')
                    self.add_ufw_rule(source_ip, protocol, port, types)
            else:
                if self.__isFirewalld:
                    for port in port_list:
                        if port.find(':') != -1:
                            port = port.replace(':', '-')
                        self.add_firewall_rule(source_ip, protocol, port,
                                               types)
                else:
                    for port in port_list:
                        self.add_iptables_rule(source_ip, protocol, port, types)


    # 添加入栈规则
    def create_rules(self, get):
        '''
            get 里面 有  protocol port type address brief   五个参数
            protocol == ['tcp','udp']
            port = 端口
            types == [accept、drop] # 放行和禁止
            address  地址，允许放行的ip，如果全部就是：0.0.0.0/0;另外可以包含“,"或者"-"
            表示区间IP
            brief   备注说明
            '''
        protocol = get.protocol
        ports = get.ports.strip()
        types = get.types
        address = get.source.strip()
        brief = get.brief.strip()
        port_list = ports.split(',')
        is_add = 2 if 'add' not in get else get.add
        domain_total = '' if ("domain" not in get or not get.domain) else get.domain.strip()
        domain = '' if ("domain" not in get or not get.domain) else get.domain.strip() + '|' + address
        result = self.check_port(port_list)  # 检测端口
        if result: return result

        allow_ips = []
        if address:
            sources = [sip.strip() for sip in address.split(",") if sip.strip()]
            rep2 = r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(\/\d{1,2})?$"
            _ips = []
            for source_ip in sources:
                if source_ip.find("-") != -1:
                    _ips += self.parse_ip_interval(source_ip)
                else:
                    _ips.append(source_ip)

            for source_ip in _ips:
                if not re.search(rep2, source_ip) and not self.is_ipv6_network_segment_or_ipv6_address(source_ip):
                    return public.returnMsg(False, public.lang("IP address you've input is illegal!"))
                query_result = public.M('firewall_new').where(
                    'ports=? and address=? and protocol=? and types=?',
                    (ports, source_ip, protocol, types,)
                ).count()
                if query_result > 0:
                    continue
                allow_ips.append(source_ip)
        if not allow_ips:
            allow_ips.append("")
        # 忽略的列表
        ignore_list = []
        addtime = time.strftime('%Y-%m-%d %X', time.localtime())
        for source_ip in allow_ips:
            for port in port_list:
                if is_add == 1: continue
                # 检测端口是否已经添加过
                query_result = public.M('firewall_new').where(
                    'ports=? and address=? and protocol=? and types=?',
                    (port, source_ip, protocol, types,)
                ).find()
                firewall_rules = self.get_sys_firewall_rules()
                if query_result:
                    new_query_result = {"ports": query_result["ports"], "address": query_result["address"],
                                        "protocol": query_result["protocol"], "types": query_result["types"]}
                    for firewall_rule in firewall_rules:
                        if new_query_result == firewall_rule:
                            ignore_list.append(port)
                            break

                self._add_firewall_rules(source_ip, protocol, port, types)

                if not query_result:
                    self._add_sid = public.M('firewall_new').add(
                        'ports,brief,protocol,address,types,addtime,domain,sid',
                        (port, public.xsssec(brief), protocol, source_ip, types, addtime, domain, 0)
                    )

                if domain:
                    domain_sid = public.M('firewall_domain').add(
                        'types,domain,port,address,brief,addtime,sid,protocol,domain_total',
                        (types, domain, ports, address, public.xsssec(brief), addtime, self._add_sid, protocol,
                         domain_total)
                    )
                    public.M('firewall_new').where("id=?", (self._add_sid,)).save('sid', domain_sid)
        if len(allow_ips) > 0:
            self.FirewallReload()
        if not get.source.strip():
            log_ip = "All IPs"
        else:
            log_ip = get.source.strip()
        strategy = ''
        if types == 'accept':
            strategy = "accept"
        elif types == 'drop':
            strategy = "drop"
        public.WriteLog("system firewall",
                        "Add port rules: Protocol:{}, Port:{}, Policy:{}, IP:{}".format(protocol, ports, strategy, log_ip))
        # 如果有忽略的端口，返回忽略的端口
        if ignore_list:
            return public.returnMsg(True,
                                    'Added successfully, {} The same rule exists for the port and has been skipped'.format(
                                        ', '.join(ignore_list)))
        return public.returnMsg(True, public.lang("Added successfully!"))


    # 删除入栈规则
    def remove_rules(self, get):
        '''
            get 里面有  id protocol port type address    五个参数
            protocol == ['tcp','udp']
            port = 端口
            types == [accept、drop] # 放行和禁止
            address  地址，允许放行的ip
            '''
        # 检测是否开启防火墙 hezhihong
        if not self.get_firewall_status():
            return public.returnMsg(False, public.lang("Please enable the firewall before proceeding."))

        id = get.id
        address = get.address
        protocol = get.protocol
        ports = get.ports
        types = get.types
        self._del_firewall_rules(address, protocol, ports, types)
        public.M('firewall_new').where("id=?", (id,)).delete()
        self.FirewallReload()
        if not get.address:
            log_ip = "All IPs"
        else:
            log_ip = get.address
        if types == 'accept':
            strategy = "accept"
        elif types == 'drop':
            strategy = "drop"
        public.WriteLog("system firewall",
                        "Delete port rules: Protocol:{}, Port:{}, Policy:{}, IP:{}".format(get.protocol, get.ports, types,
                                                                                           log_ip))
        return public.returnMsg(True, public.lang("Delete successfully!"))


    # 修改入栈规则
    def modify_rules(self, get, addtime=None):
        '''
            get 里面有  id protocol port type address    五个参数
            protocol == ['tcp','udp']
            port = 端口
            types==['reject','accept'] # 放行和禁止
            address  地址，允许放行的ip，如果全部就是：0.0.0.0/0
            '''
        # 检测是否开启防火墙 hezhihong
        if not self.get_firewall_status():
            return public.returnMsg(False, public.lang("Please enable the firewall before proceeding."))
        id = get.id
        protocol = get.protocol
        ports = get.ports.strip()
        types = get.types
        address = get.source.strip()
        brief = get.brief.strip()
        domain = '' if 'domain' not in get else get.domain
        domain_total = domain.split('|')[0]
        sid = 0 if 'sid' not in get else get.sid
        if address:
            rep = r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(\/\d{1,2})?$"
            if not re.search(rep, get.source) and self.is_ipv6_network_segment_or_ipv6_address(get.source):
                return public.returnMsg(False, public.lang("IP address you've input is illegal!"))
        data = public.M('firewall_new').where('id=?', (id,)).field(
            'id,address,protocol,ports,types,brief,addtime,domain'
        ).find()
        if data:
            _address = data.get("address", "")
            _protocol = data.get("protocol", "")
            _port = data.get("ports", "")
            _type = data.get("types", "")
        else:
            _address = _protocol = _port = _type = ""
        self._modify_firewall_rules(_address, _protocol, _port, _type, address, protocol, ports, types)
        addtime = time.strftime('%Y-%m-%d %X', time.localtime())
        public.M('firewall_new').where('id=?', id).update({
            'address': address,
            'protocol': protocol,
            'ports': ports,
            'types': types,
            'brief': brief,
            'addtime': addtime,
            'sid': sid,
            'domain': domain
        }
        )
        if domain:
            public.M('firewall_domain').where("id=?", (sid,)).save(
                'sid,types,brief,protocol,domain_total',
                (id, types, brief, protocol, domain_total)
            )

        with contextlib.suppress(Exception):
            if int(ports) == 22: self.delete_service()
        self.FirewallReload()
        if not address:
            log_ip = "All IPs"
        else:
            log_ip = address
        if get.types == 'accept':
            strategy = "accept"
        elif get.types == 'drop':
            strategy = "drop"
        public.WriteLog("system firewall",
                        "Modify port rules： Protocol:{}, Port:{}, Policy:{}, IP:{}".format(get.protocol, get.ports.strip(), get.types,
                                                                                log_ip))
        return public.returnMsg(True, public.lang("Added successfully!"))


    # firewall端口规则添加
    def add_firewall_rule(self, address, protocol, ports, types):
        if not address:
            if protocol.find('/') != -1:
                if types == "accept":
                    public.ExecShell(
                        'firewall-cmd --permanent --zone=public --add-port=' +
                        ports + '/tcp')
                    public.ExecShell(
                        'firewall-cmd --permanent --zone=public --add-port=' +
                        ports + '/udp')
                else:
                    public.ExecShell(
                        'firewall-cmd --permanent --add-rich-rule="rule family=ipv4 port protocol="tcp" port="%s" drop"'
                        % ports
                    )
                    public.ExecShell(
                        'firewall-cmd --permanent --add-rich-rule="rule family=ipv4 port protocol="udp" port="%s" drop"'
                        % ports
                    )
            else:
                if types == "accept":
                    public.ExecShell(
                        'firewall-cmd --permanent --zone=public --add-port=' +
                        ports + '/' + protocol + '')
                else:
                    public.ExecShell(
                        'firewall-cmd --permanent --add-rich-rule="rule family=ipv4 port protocol="%s" port="%s" drop"'
                        % (protocol, ports))
            return True
        if self.is_ipv6_network_segment_or_ipv6_address(address):
            if protocol.find('/') != -1:
                public.ExecShell(
                    'firewall-cmd --permanent '
                    '--add-rich-rule="rule family=ipv6 source address="%s" port protocol="tcp" port="%s" %s"'
                    % (address, ports, types))
                public.ExecShell(
                    'firewall-cmd --permanent '
                    '--add-rich-rule="rule family=ipv6 source address="%s" port protocol="udp" port="%s" %s"'
                    % (address, ports, types))
            else:
                public.ExecShell(
                    'firewall-cmd --permanent '
                    '--add-rich-rule="rule family=ipv6 source address="%s" port protocol="%s" port="%s" %s"'
                    % (address, protocol, ports, types))
        else:
            if protocol.find('/') != -1:
                public.ExecShell(
                    'firewall-cmd --permanent '
                    '--add-rich-rule="rule family=ipv4 source address="%s" port protocol="tcp" port="%s" %s"'
                    % (address, ports, types))
                public.ExecShell(
                    'firewall-cmd --permanent '
                    '--add-rich-rule="rule family=ipv4 source address="%s" port protocol="udp" port="%s" %s"'
                    % (address, ports, types))
            else:
                public.ExecShell(
                    'firewall-cmd --permanent '
                    '--add-rich-rule="rule family=ipv4 source address="%s" port protocol="%s" port="%s" %s"'
                    % (address, protocol, ports, types))
        return True


    # firewall端口规则删除
    def del_firewall_rule(self, address, protocol, ports, types):
        if not address:
            if protocol.find('/') != -1:
                if types == "accept":
                    public.ExecShell(
                        'firewall-cmd --permanent --zone=public --remove-port='
                        + ports + '/tcp')
                    public.ExecShell(
                        'firewall-cmd --permanent --zone=public --remove-port='
                        + ports + '/udp')
                else:
                    public.ExecShell(
                        'firewall-cmd --permanent '
                        '--remove-rich-rule="rule family=ipv4 port protocol="tcp" port="%s" drop"'
                        % ports
                    )
                    public.ExecShell(
                        'firewall-cmd --permanent '
                        '--remove-rich-rule="rule family=ipv4 port protocol="udp" port="%s" drop"'
                        % ports
                    )
            else:
                if types == "accept":
                    public.ExecShell(
                        'firewall-cmd --permanent --zone=public --remove-port='
                        + ports + '/' + protocol + '')
                else:
                    public.ExecShell(
                        'firewall-cmd --permanent '
                        '--remove-rich-rule="rule family=ipv4 port protocol="%s" port="%s" drop"'
                        % (protocol, ports))
            self.update_panel_data(ports)
            return True
        if self.is_ipv6_network_segment_or_ipv6_address(address):
            if protocol.find('/') != -1:
                public.ExecShell(
                    'firewall-cmd --permanent '
                    '--remove-rich-rule="rule family="ipv6" source address="%s" port protocol="tcp" port="%s" %s"'
                    % (address, ports, types))
                public.ExecShell(
                    'firewall-cmd --permanent '
                    '--remove-rich-rule="rule family="ipv6" source address="%s" port protocol="udp" port="%s" %s"'
                    % (address, ports, types))
            else:
                public.ExecShell(
                    'firewall-cmd --permanent '
                    '--remove-rich-rule="rule family="ipv6" source address="%s" port protocol="%s" port="%s" %s"'
                    % (address, protocol, ports, types))
        else:
            if protocol.find('/') != -1:
                public.ExecShell(
                    'firewall-cmd --permanent '
                    '--remove-rich-rule="rule family="ipv4" source address="%s" port protocol="tcp" port="%s" %s"'
                    % (address, ports, types))
                public.ExecShell(
                    'firewall-cmd --permanent '
                    '--remove-rich-rule="rule family="ipv4" source address="%s" port protocol="udp" port="%s" %s"'
                    % (address, ports, types))
            else:
                public.ExecShell(
                    'firewall-cmd --permanent '
                    '--remove-rich-rule="rule family="ipv4" source address="%s" port protocol="%s" port="%s" %s"'
                    % (address, protocol, ports, types))
        return True


    # firewall端口规则编辑
    def edit_firewall_rule(self, _address, _protocol, _port, _type, address,
                           protocol, ports, types):
        if not _address:
            if _protocol.find('/') != -1:
                if _type == "accept":
                    public.ExecShell(
                        'firewall-cmd --permanent --zone=public --remove-port='
                        + _port + '/tcp')
                    public.ExecShell(
                        'firewall-cmd --permanent --zone=public --remove-port='
                        + _port + '/udp')
                else:
                    public.ExecShell(
                        'firewall-cmd --permanent '
                        '--remove-rich-rule="rule family=ipv4 port protocol="tcp" port="%s" drop"'
                        % ports
                    )
                    public.ExecShell(
                        'firewall-cmd --permanent '
                        '--remove-rich-rule="rule family=ipv4 port protocol="udp" port="%s" drop"'
                        % ports
                    )
            else:
                if _type == "accept":
                    public.ExecShell(
                        'firewall-cmd --permanent --zone=public --remove-port='
                        + _port + '/' + _protocol + '')
                else:
                    public.ExecShell(
                        'firewall-cmd --permanent '
                        '--remove-rich-rule="rule family=ipv4 port protocol="%s" port="%s" drop"'
                        % (protocol, ports))
        else:
            if self.is_ipv6_network_segment_or_ipv6_address(address):
                if _protocol.find('/') != -1:
                    public.ExecShell(
                        'firewall-cmd --permanent '
                        '--remove-rich-rule="rule family="ipv6" source address="%s" port protocol="tcp" port="%s" %s"'
                        % (_address, _port, _type))
                    public.ExecShell(
                        'firewall-cmd --permanent '
                        '--remove-rich-rule="rule family="ipv6" source address="%s" port protocol="udp" port="%s" %s"'
                        % (_address, _port, _type))
                else:
                    public.ExecShell(
                        'firewall-cmd --permanent '
                        '--remove-rich-rule="rule family="ipv6" source address="%s" port protocol="%s" port="%s" %s"'
                        % (_address, _protocol, _port, _type))
            else:
                if _protocol.find('/') != -1:
                    public.ExecShell(
                        'firewall-cmd --permanent '
                        '--remove-rich-rule="rule family="ipv4" source address="%s" port protocol="tcp" port="%s" %s"'
                        % (_address, _port, _type))
                    public.ExecShell(
                        'firewall-cmd --permanent '
                        '--remove-rich-rule="rule family="ipv4" source address="%s" port protocol="udp" port="%s" %s"'
                        % (_address, _port, _type))
                else:
                    public.ExecShell(
                        'firewall-cmd --permanent '
                        '--remove-rich-rule="rule family="ipv4" source address="%s" port protocol="%s" port="%s" %s"'
                        % (_address, _protocol, _port, _type))
        if not address:
            if protocol.find('/') != -1:
                if types == "accept":
                    public.ExecShell(
                        'firewall-cmd --permanent --zone=public --add-port=' +
                        ports + '/tcp')
                    public.ExecShell(
                        'firewall-cmd --permanent --zone=public --add-port=' +
                        ports + '/udp')
                else:
                    public.ExecShell(
                        'firewall-cmd --permanent '
                        '--add-rich-rule="rule family=ipv4 port protocol="tcp" port="%s" drop"'
                        % ports
                    )
                    public.ExecShell(
                        'firewall-cmd --permanent '
                        '--add-rich-rule="rule family=ipv4 port protocol="udp" port="%s" drop"'
                        % ports
                    )
            else:
                if types == "accept":
                    public.ExecShell(
                        'firewall-cmd --permanent --zone=public --add-port=' +
                        ports + '/' + protocol + '')
                else:
                    public.ExecShell(
                        'firewall-cmd --permanent '
                        '--add-rich-rule="rule family=ipv4 port protocol="%s" port="%s" drop"'
                        % (protocol, ports))
        else:
            if self.is_ipv6_network_segment_or_ipv6_address(address):
                if protocol.find('/') != -1:
                    public.ExecShell(
                        'firewall-cmd --permanent '
                        '--add-rich-rule="rule family=ipv6 source address="%s" port protocol="tcp" port="%s" %s"'
                        % (address, ports, types))
                    public.ExecShell(
                        'firewall-cmd --permanent '
                        '--add-rich-rule="rule family=ipv6 source address="%s" port protocol="udp" port="%s" %s"'
                        % (address, ports, types))
                else:
                    public.ExecShell(
                        'firewall-cmd --permanent '
                        '--add-rich-rule="rule family=ipv6 source address="%s" port protocol="%s" port="%s" %s"'
                        % (address, protocol, ports, types))
            else:
                if protocol.find('/') != -1:
                    public.ExecShell(
                        'firewall-cmd --permanent '
                        '--add-rich-rule="rule family=ipv4 source address="%s" port protocol="tcp" port="%s" %s"'
                        % (address, ports, types))
                    public.ExecShell(
                        'firewall-cmd --permanent '
                        '--add-rich-rule="rule family=ipv4 source address="%s" port protocol="udp" port="%s" %s"'
                        % (address, ports, types))
                else:
                    public.ExecShell(
                        'firewall-cmd --permanent '
                        '--add-rich-rule="rule family=ipv4 source address="%s" port protocol="%s" port="%s" %s"'
                        % (address, protocol, ports, types))
        return True


    # ufw 端口规则添加
    def add_ufw_rule(self, address, protocol, ports, types):
        rule = "allow" if types == "accept" else "deny"
        if address == "":
            if protocol.find('/') != -1:
                # public.ExecShell('ufw ' + rule + ' ' + ports + '/tcp')
                # public.ExecShell('ufw ' + rule + ' ' + ports + '/udp')
                public.ExecShell('ufw ' + rule + ' ' + ports)
            else:
                public.ExecShell('ufw ' + rule + ' ' + ports + '/' + protocol + '')
        else:
            if protocol.find('/') != -1:
                # public.ExecShell('ufw ' + rule + ' proto tcp from ' + address + ' to any port ' + ports + '')
                # public.ExecShell('ufw ' + rule + ' proto udp from ' + address + ' to any port ' + ports + '')
                public.ExecShell('ufw ' + rule + ' from ' + address + ' to any port ' + ports + '')
            else:
                public.ExecShell(
                    'ufw ' + rule + ' proto ' + protocol + ' from ' + address + ' to any port ' + ports + '')


    # ufw 端口规则删除
    def del_ufw_rule(self, address, protocol, ports, types):
        rule = "allow" if types == "accept" else "deny"
        if address == "":
            if protocol.find('/') != -1:
                public.ExecShell('ufw delete ' + rule + ' ' + ports + '/tcp')
                public.ExecShell('ufw delete ' + rule + ' ' + ports + '/udp')
                public.ExecShell('ufw delete ' + rule + ' ' + ports)
            else:
                public.ExecShell('ufw delete ' + rule + ' ' + ports + '/' + protocol + '')
        else:
            if protocol.find('/') != -1:
                public.ExecShell('ufw delete ' + rule + ' proto tcp from ' + address + ' to any port ' + ports + '')
                public.ExecShell('ufw delete ' + rule + ' proto udp from ' + address + ' to any port ' + ports + '')
                public.ExecShell('ufw delete ' + rule + ' from ' + address + ' to any port ' + ports)
            else:
                public.ExecShell(
                    'ufw delete ' + rule + ' proto ' + protocol + ' from ' + address + ' to any port ' + ports + ''
                )
        self.update_panel_data(ports)


    # ufw 端口规则修改
    def edit_ufw_rule(self, _address, _protocol, _port, _type, address,
                      protocol, ports, types):
        _rule = "allow" if _type == "accept" else "deny"
        rules = "allow" if types == "accept" else "deny"
        if _address == "":
            if _protocol.find('/') != -1:
                public.ExecShell('ufw delete ' + _rule + ' ' + _port + '/tcp')
                public.ExecShell('ufw delete ' + _rule + ' ' + _port + '/udp')
                public.ExecShell('ufw delete ' + _rule + ' ' + _port)
            else:
                public.ExecShell('ufw delete ' + _rule + ' ' + _port + '/' + _protocol + '')
        else:
            if _protocol.find('/') != -1:
                public.ExecShell('ufw delete ' + _rule + ' proto tcp from ' + _address + ' to any port ' + _port + '')
                public.ExecShell('ufw delete ' + _rule + ' proto udp from ' + _address + ' to any port ' + _port + '')
                public.ExecShell('ufw delete ' + _rule + ' from ' + _address + ' to any port ' + _port)
            else:
                public.ExecShell(
                    'ufw delete ' + _rule + ' proto ' + _protocol + ' from ' + _address + ' to any port ' + _port + ''
                )
        if address == "":
            if protocol.find('/') != -1:
                # public.ExecShell('ufw ' + rules + ' ' + ports + '/tcp')
                # public.ExecShell('ufw ' + rules + ' ' + ports + '/udp')
                public.ExecShell('ufw ' + rules + ' ' + ports)
            else:
                public.ExecShell('ufw ' + rules + ' ' + ports + '/' + protocol + '')
        else:
            if protocol.find('/') != -1:
                # public.ExecShell('ufw ' + rules + ' proto tcp from ' + address + ' to any port ' + ports + '')
                # public.ExecShell('ufw ' + rules + ' proto udp from ' + address + ' to any port ' + ports + '')
                public.ExecShell('ufw ' + rules + ' from ' + address + ' to any port ' + ports)
            else:
                public.ExecShell(
                    'ufw ' + rules + ' proto ' + protocol + ' from ' + address + ' to any port ' + ports + ''
                )


    # iptables端口规则添加
    def add_iptables_rule(self, address, protocol, ports, types):
        rule = "ACCEPT" if types == "accept" else "DROP"
        if not address:
            if protocol.find('/') != -1:
                public.ExecShell(
                    'iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport '
                    + ports + ' -j ' + rule + '')
                public.ExecShell(
                    'iptables -I INPUT -p tcp -m state --state NEW -m udp --dport '
                    + ports + ' -j ' + rule + '')
            else:
                public.ExecShell(
                    'iptables -I INPUT -p tcp -m state --state NEW -m ' +
                    protocol + ' --dport ' + ports + ' -j ' + rule + '')
        else:
            if protocol.find('/') != -1:
                public.ExecShell('iptables -I INPUT -s ' + address +
                                 ' -p tcp --dport ' + ports + ' -j ' + rule +
                                 '')
                public.ExecShell('iptables -I INPUT -s ' + address +
                                 ' -p udp --dport ' + ports + ' -j ' + rule +
                                 '')
            else:
                public.ExecShell('iptables -I INPUT -s ' + address + ' -p ' +
                                 protocol + ' --dport ' + ports + ' -j ' +
                                 rule + '')
        return True


    # iptables端口规则删除
    def del_iptables_rule(self, address, protocol, ports, types):
        rule = "ACCEPT" if types == "accept" else "DROP"
        if not address:
            if protocol.find('/') != -1:
                public.ExecShell(
                    'iptables -D INPUT -p tcp -m state --state NEW -m tcp --dport '
                    + ports + ' -j ' + rule + '')
                public.ExecShell(
                    'iptables -D INPUT -p tcp -m state --state NEW -m udp --dport '
                    + ports + ' -j ' + rule + '')
            else:
                public.ExecShell(
                    'iptables -D INPUT -p tcp -m state --state NEW -m ' +
                    protocol + ' --dport ' + ports + ' -j ' + rule + '')
        else:
            if protocol.find('/') != -1:
                public.ExecShell('iptables -D INPUT -s ' + address +
                                 ' -p tcp --dport ' + ports + ' -j ' + rule +
                                 '')
                public.ExecShell('iptables -D INPUT -s ' + address +
                                 ' -p udp --dport ' + ports + ' -j ' + rule +
                                 '')
            else:
                public.ExecShell('iptables -D INPUT -s ' + address + ' -p ' +
                                 protocol + ' --dport ' + ports + ' -j ' +
                                 rule + '')
        return True


    # iptables端口规则编辑
    def edit_iptables_rule(self, _address, _protocol, _port, _type, address,
                           protocol, ports, types):
        rule1 = "ACCEPT" if _type == "accept" else "DROP"
        rule2 = "ACCEPT" if types == "accept" else "DROP"
        if not _address:
            if _protocol.find('/') != -1:
                public.ExecShell(
                    'iptables -D INPUT -p tcp -m state --state NEW -m tcp --dport '
                    + _port + ' -j ' + rule1 + '')
                public.ExecShell(
                    'iptables -D INPUT -p tcp -m state --state NEW -m udp --dport '
                    + _port + ' -j ' + rule1 + '')
            else:
                public.ExecShell(
                    'iptables -D INPUT -p tcp -m state --state NEW -m ' +
                    _protocol + ' --dport ' + _port + ' -j ' + rule1 + '')
        else:
            if _protocol.find('/') != -1:
                public.ExecShell('iptables -D INPUT -s ' + _address +
                                 ' -p tcp --dport ' + _port + ' -j ' + rule1 +
                                 '')
                public.ExecShell('iptables -D INPUT -s ' + _address +
                                 ' -p udp --dport ' + _port + ' -j ' + rule1 +
                                 '')
            else:
                public.ExecShell('iptables -D INPUT -s ' + _address + ' -p ' +
                                 _protocol + ' --dport ' + _port + ' -j ' +
                                 rule1 + '')
        if not address:
            if protocol.find('/') != -1:
                public.ExecShell(
                    'iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport '
                    + ports + ' -j ' + rule2 + '')
                public.ExecShell(
                    'iptables -I INPUT -p tcp -m state --state NEW -m udp --dport '
                    + ports + ' -j ' + rule2 + '')
            else:
                public.ExecShell(
                    'iptables -I INPUT -p tcp -m state --state NEW -m ' +
                    protocol + ' --dport ' + ports + ' -j ' + rule2 + '')
        else:
            if protocol.find('/') != -1:
                public.ExecShell('iptables -I INPUT -s ' + address +
                                 ' -p tcp --dport ' + ports + ' -j ' + rule2 +
                                 '')
                public.ExecShell('iptables -I INPUT -s ' + address +
                                 ' -p udp --dport ' + ports + ' -j ' + rule2 +
                                 '')
            else:
                public.ExecShell('iptables -I INPUT -s ' + address + ' -p ' +
                                 protocol + ' --dport ' + ports + ' -j ' +
                                 rule2 + '')
        return True


    # 修改面板数据
    def update_panel_data(self, ports):
        res = public.M('firewall').where("port=?", (ports,)).delete()


    # 查询IP规则
    def get_ip_rules_list(self, args):
        p = 1
        limit = 15
        if 'p' in args: p = args.p
        if 'limit' in args: limit = args.limit

        where = '1=1'
        sql = public.M('firewall_ip')

        if hasattr(args, 'query'):
            where = " address like '%{search}%' or brief like '%{search}%' ".format(
                search=args.query)

        count = sql.where(where, ()).count()
        data = public.get_page(count, int(p), int(limit))
        data['data'] = sql.where(where, ()).limit('{},{}'.format(
            data['shift'], data['row'])).order('addtime desc').select()

        data['data'] = public.return_area(data['data'], 'address')
        return data


    def check_a_ip(self, address):
        """
            @name 检测A记录是否为域名
            @author hezhihong
            """
        if address:
            if public.is_ipv4(address) or public.is_ipv6(address):
                return address
            if address[-1] == '.':
                address = address[:-1]
            if public.is_domain(address): return self.get_a_ip(address)
        return address


    def get_a_ip(self, hostname):
        '''
            @name 检测主机名是否有A记录
            @author hezhihong
            :param hostname:
            :return:
            '''
        if not self.install_dnspython():
            return public.returnMsg(False, public.lang("Please install dnspython module first: btpip install dnspython"))
        import dns.resolver
        # 尝试3次
        a_ip = []
        for i in range(3):
            try:
                resolver = dns.resolver.Resolver()
                resolver.timeout = 1
                try:
                    result = resolver.query(hostname, 'A')
                except:
                    result = resolver.resolve(hostname, 'A')
                for i in result.response.answer:
                    for j in i.items:
                        try:
                            A_ip = str(j).strip()
                            if A_ip[-1] == '.':
                                A_ip = A_ip[:-1]
                        except:
                            pass

                        if A_ip not in a_ip:
                            a_ip.append(A_ip)

            except:
                pass

        # 去除域名
        if len(a_ip) > 1:
            for i2 in a_ip:
                if public.is_ipv4(i2) or public.is_ipv6(i2):
                    continue
                if public.is_domain(i2):
                    a_ip.remove(i2)
        return a_ip


    def install_dnspython(self):
        """
            @name 安装dnspython模块
            @author hezhihong
            """
        # 检测dns解析
        try:
            import dns.resolver
            return True
        except:
            if os.path.exists('/www/server/panel/pyenv'):
                public.ExecShell('/www/server/panel/pyenv/bin/pip install dnspython')
            else:
                public.ExecShell('pip3 install dnspython')
            try:
                import dns.resolver
                return True
            except:
                return False


    def del_domain_ip(self, args):
        """
            @name 删除域名设置
            @author hezhihong
            """

        if 'id' not in args or not args.id or 'sid' not in args:
            return public.returnMsg(False, public.lang("Parameter error"))
        domain_id = int(args.sid)

        # 删除IP规则
        if domain_id > 0:
            # 删除域名解析
            public.M('firewall_domain').where('id=?', (str(domain_id),)).delete()
        # 删除端口规则
        if 'ports' in args:
            self.remove_rules(args)
        # 删除IP规则
        else:
            self.remove_ip_rules(args)

        # 当没有域名解析时，删除计划任务
        if not public.M('firewall_domain').count():
            pdata = public.M('crontab').where('name=?',
                                              '[Do not delete] System firewall domain name resolution detection task').select()
            if pdata:
                for i in pdata:
                    args = {"id": i['id']}
                    import crontab
                    crontab.crontab().DelCrontab(args)

        return public.returnMsg(True, public.lang("successfully deleted"))


    def add_crontab(self):
        """
            @name 构造日志切割任务
            @author hezhihong
            """
        python_path = ''
        try:
            python_path = public.ExecShell('which btpython')[0].strip("\n")
        except:
            try:
                python_path = public.ExecShell('which python')[0].strip("\n")
            except:
                pass
        if not python_path: return False
        if not public.M('crontab').where('name=?', (
        '[Do not delete] System firewall domain name resolution detection task',)).count():
            cmd = '{} {}'.format(python_path, '/www/server/panel/script/firewall_domain.py')
            args = {"name": "[Do not delete] System firewall domain name resolution detection task", "type": 'minute-n',
                    "where1": '5', "hour": '',
                    "minute": '', "sName": "",
                    "sType": 'toShell', "notice": '', "notice_channel": '', "save": '', "save_local": '1',
                    "backupTo": '', "sBody": cmd,
                    "urladdress": ''}
            import crontab
            res = crontab.crontab().AddCrontab(args)
            if res and "id" in res.keys():
                return True
            return False
        return True


    def __check_auth(self):
        try:
            from pluginAuth import Plugin
            plugin_obj = Plugin(False)
            plugin_list = plugin_obj.get_plugin_list()
            if int(plugin_list['ltd']) > time.time():
                return True
            return False
        except:
            return False


    def set_domain_ip2(self, args):
        """
            @name 设置域名规则
            @author hezhihong
            """
        pay = self.__check_auth()
        if not pay: return public.returnMsg(False, public.lang("Current features are exclusive to the professional version"))
        if not args.domain: return public.returnMsg(False, public.lang("Please enter domain name"))
        ports = ''
        if 'ports' in args and args.ports: ports = args.ports
        ip = args.source

        # 添加计划任务
        self.add_crontab()
        # 添加端口规则
        # {"protocol":"tcp","ports":"819","choose":"point","address":"125.93.252.236","types":"accept","brief":"","source":"125.93.252.236"}
        args.address = ip
        args.source = ip
        if ports:
            if public.is_ipv6(ip):
                return public.returnMsg(False, public.lang("The domain name is resolved to an IPv6 address and port rules are not supported."))
            self.create_rules2(args)
        # 添加IP规则
        else:
            # return 333
            self.create_ip_rules(args)

        return public.returnMsg(True, public.lang("Domain name {} resolution added successfully", args.domain))


    def set_domain_ip(self, args):
        """
            @name 设置域名规则
            @author hezhihong
            """
        pay = self.__check_auth()
        if not pay: return public.returnMsg(False, public.lang("Current features are exclusive to the professional version"))
        if not args.domain: return public.returnMsg(False, public.lang("Please enter domain name"))
        ports = ''
        if 'ports' in args and args.ports: ports = args.ports
        protocol = '' if 'protocol' not in args else args.protocol
        a_ip = self.get_a_ip(args.domain)
        # return a_ip
        if a_ip and len(a_ip) < 2 and public.is_domain(a_ip[0]):
            # return 111
            a_ip = [self.check_a_ip(a_ip[0])]
        # return a_ip
        if not a_ip:
            return public.returnMsg(False, public.lang("The domain name resolution has not been resolved or the resolution has not taken effect. If it has been resolved, please try again after 10 minutes."))
        if public.M('firewall_domain').where("domain=? and types=? and port=? and protocol=?",
                                             (args.domain, args.types, ports, protocol,)).count():
            return public.returnMsg(False, public.lang("Domain name {} already exists", args.domain))

        # 添加计划任务
        self.add_crontab()
        # 添加端口规则
        # {"protocol":"tcp","ports":"819","choose":"point","address":"125.93.252.236","types":"accept","brief":"","source":"125.93.252.236"}
        for ip in a_ip:
            args.address = ip
            args.source = ip
            if ports:
                if public.is_ipv6(ip):
                    return public.returnMsg(False, public.lang("The domain name is resolved to an IPv6 address and port rules are not supported."))
                self.create_rules(args)
            # 添加IP规则
            else:
                # return 333
                self.create_ip_rules(args)

        return public.returnMsg(True, public.lang("Domain name {} resolution added successfully", args.domain))


    def modify_domain_ip(self, args):
        """
            @name 修改域名规则(当修改为指定域名或从指定域名修改为其他时，需要调用此方法)
            @name hezhihong
            """
        pay = self.__check_auth()
        if not pay: return public.returnMsg(False, public.lang("Current features are exclusive to the professional version"))

        # 检测是否开启防火墙 hezhihong
        if not self.get_firewall_status():
            return public.returnMsg(False, public.lang("Please enable the firewall before proceeding."))

        modify_args = public.dict_obj()
        modify_args.id = args.id
        modify_args.types = args.types
        modify_args.brief = args.brief
        modify_args.address = args.address
        modify_args.sid = 0 if 'sid' not in args else args.sid
        ports = '' if 'ports' not in args else args.ports
        domain = '' if 'domain' not in args else args.domain
        if ports: modify_args.ports = ports
        choose = '' if 'choose' not in args else args.choose
        pdata = {}
        if int(args.sid) > 0:
            pdata = public.M('firewall_domain').where('id=?', (args.sid,)).find()
        # 修改端口规则
        if ports:
            modify_args.protocol = args.protocol
            # 已经指定域名
            if int(args.sid) > 0:
                # 当修改为指定域名时
                if choose == 'domain':
                    # 当修改为不同域名时
                    if domain != pdata['domain']:
                        self.del_domain_ip(args)
                        self.set_domain_ip(args)
                    # 当修改为相同域名时
                    else:
                        pdata['protocol'] = args.protocol
                        pdata['types'] = args.types
                        pdata['brief'] = public.xsssec(args.brief)
                        addtime = time.strftime('%Y-%m-%d %X', time.localtime())
                        pdata['addtime'] = addtime
                        public.M('firewall_domain').where('id=?', pdata['id']).update(pdata)
                        self.modify_rules(args)
                    return public.returnMsg(True, public.lang("Successfully modified"))
                else:
                    args.domain = ''
                    self.del_domain_ip(args)
                    self.create_rules(args)
                    return public.returnMsg(True, public.lang("Successfully modified"))
            # 当未指定域名时
            else:
                # 修改为指定域名
                if domain:
                    self.remove_rules(args)
                    self.set_domain_ip(args)
                    return public.returnMsg(True, public.lang("Successfully modified"))
        # 修改IP规则
        else:
            if int(args.sid) > 0:
                modify_args.address = pdata['address']
                modify_args.domain = pdata['domain']
            return self.modify_ip_rules(modify_args)


    # IP地址检测
    def check_ip(self, address_list):
        rep = r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(\/\d{1,2})?$"
        for address in address_list:
            address = address.split('/')[0]
            if address.find('-') != -1:
                addresses = address.split('-')
                if addresses[0] >= addresses[1]:
                    return public.returnMsg(False, public.lang("IP address you've input is illegal!"))
                s_ips = addresses[0].split(".")
                e_ips = addresses[1].split(".")
                head_s_ip = s_ips[0] + "." + s_ips[1] + "." + s_ips[2] + "."
                head_e_ip = e_ips[0] + "." + e_ips[1] + "." + e_ips[2] + "."
                if head_s_ip != head_e_ip:
                    return public.returnMsg(False, public.lang("IP address you've input is illegal!"))
                if not re.search(rep, addresses[0]):
                    return public.returnMsg(False, public.lang("IP address you've input is illegal!"))
                if not re.search(rep, addresses[1]):
                    return public.returnMsg(False, public.lang("IP address you've input is illegal!"))
            else:
                if not re.search(rep, address) and not public.is_ipv6(address):
                    return public.returnMsg(False, public.lang("IP address you've input is illegal!"))


    # 获取IP范围
    def get_ip(self, address):
        result = []
        arrys = address.split("-")
        s_ips = arrys[0].split(".")
        e_ips = arrys[1].split(".")
        head_s_ip = s_ips[0] + "." + s_ips[1] + "." + s_ips[2] + "."
        region = int(e_ips[-1]) - int(s_ips[-1])
        for num in range(0, region + 1):
            result.append(head_s_ip + str(num + int(s_ips[-1])))
        return result


    def handle_firewall_ip(self, address, types):
        ip_list = self.get_ip(address)
        if isinstance(ip_list, dict):
            return
        public.ExecShell(
            'firewall-cmd --permanent --zone=public --new-ipset=' + address +
            ' --type=hash:net')
        xml_path = "/etc/firewalld/ipsets/%s.xml" % address
        tree = ElementTree()
        tree.parse(xml_path)
        root = tree.getroot()
        for ip in ip_list:
            entry = Element("entry")
            entry.text = ip
            root.append(entry)
        self.format(root)
        tree.write(xml_path, 'utf-8', xml_declaration=True)
        # public.ExecShell('firewall-cmd --permanent --zone=public --add-rich-rule=\'rule source ipset="'+ address +'" accept\'')
        public.ExecShell(
            'firewall-cmd --permanent --zone=public --add-rich-rule=\'rule source ipset="'
            + address + '" ' + types + '\'')


    def handle_ufw_ip(self, address, types):
        ip_list = self.get_ip(address)
        if isinstance(ip_list, dict):
            return
        public.ExecShell('ipset create ' + address + ' hash:net')
        for ip in ip_list:
            public.ExecShell('ipset add ' + address + ' ' + ip)
        public.ExecShell('iptables -I INPUT -m set --match-set ' + address +
                         ' src -j ' + types.upper())


    # 检查IP地址是否在范围内
    def ip_in_range(self, ip, ip_range):
        import ipaddress
        # 2024/1/3 下午 7:59 兼容192.168.0.0/24这种形式
        if ip_range.find('/') != -1:
            return ipaddress.ip_address(ip) in ipaddress.ip_network(ip_range)

        ip_range = ip_range.split('-')
        if len(ip_range) == 1:  # 如果只有一个IP地址
            return ipaddress.ip_address(ip) == ipaddress.ip_address(ip_range[0])
        else:  # 如果是一个IP范围
            start_ip, end_ip = ip_range
            ip_networks = ipaddress.summarize_address_range(ipaddress.ip_address(start_ip), ipaddress.ip_address(end_ip))
            return any(ipaddress.ip_address(ip) in net for net in ip_networks)


    # 添加IP规则
    def create_ip_rules(self, get):
        from flask import request
        user_ip = request.remote_addr
        _address = get.address.strip()
        original_types = get.types
        brief = get.brief
        domain_total = '' if ('domain' not in get or not get.domain) else get.domain.strip()
        domain = '' if ('domain' not in get or not get.domain) else get.domain.strip() + '|' + _address
        address_list = _address.split(',')
        # public.print_log('############ ip接口 {}'.format(address_list))
        result = self.check_ip(address_list)

        if result:
            return result

        # 先处理用户的IP地址
        old_login_ip = public.M('firewall_ip').where("brief=?", ("IP that allows users to log in",)).field(
            'id, address').select()
        # public.print_log('############  ip接口user_ip {}'.format(user_ip))
        for ip_range in address_list:

            if self.ip_in_range(user_ip, ip_range):

                if old_login_ip and old_login_ip[0][' address'] != user_ip:
                    address = old_login_ip[0][' address']
                    public.M('firewall_ip').where("address=?", (address,)).delete()

                    self.update_panel_data(address)  # 删除面板自带防火墙的表数据

                self.add_rule(user_ip, "accept", "IP that allows users to log in", domain, domain_total)

                break

        # 然后处理其他的IP地址
        for address in address_list:
            self.add_rule(address, original_types, brief, domain, domain_total)

        self.FirewallReload()

        public.WriteLog("system firewall", "Add IP rules: IP: {}, policy: {}".format(_address, original_types))

        return public.returnMsg(True, public.lang("Added successfully!"))


    # 添加单个IP规则
    def add_rule(self, address, types, brief, domain, domain_total):
        if public.M('firewall_ip').where("address=? and types=? and domain=?", (address, types, domain)).count() > 0:
            return
        if self.__isUfw:
            # public.print_log('############ ip接口 1')
            _rule = "allow" if types == "accept" else "deny"
            if address.find('-') != -1:
                # public.print_log('############!!! ip接口 2')
                self.handle_ufw_ip(address, types)
            else:
                # public.print_log('############!!! ip接口 3')
                is_debian = True if public.get_os_version().lower().find("debian") != -1 else False
                if not is_debian:
                    if _rule == "allow":
                        if public.is_ipv6(address):
                            public.ExecShell('ufw ' + _rule + ' from ' + address + ' to any')
                        else:
                            public.ExecShell('ufw insert 1 ' + _rule + ' from ' + address + ' to any')
                    else:
                        public.ExecShell('ufw ' + _rule + ' from ' + address + ' to any')
                else:
                    public.ExecShell('iptables -I INPUT -s ' + address + ' -j ' + types.upper())
        else:
            # public.print_log('############!!! ip接口 4')
            if self.__isFirewalld:
                # public.print_log('############ ip接口 6')
                if address.find('-') != -1:
                    self.handle_firewall_ip(address, types)
                else:
                    if types == "accept":
                        public.ExecShell('firewall-cmd --permanent --add-source=' + address + ' --zone=trusted')
                    else:
                        if public.is_ipv6(address):
                            public.ExecShell(
                                'firewall-cmd --permanent --add-rich-rule=\'rule family=ipv6 source address="' + address + '" ' + types + '\'')
                        else:
                            public.ExecShell(
                                'firewall-cmd --permanent --add-rich-rule=\'rule family=ipv4 source address="' + address + '" ' + types + '\'')
            else:
                # public.print_log('############ ip接口 7')
                if address.find('-') != -1:
                    self.handle_ufw_ip(address, types)
                else:
                    public.ExecShell('iptables -I INPUT -s ' + address + ' -j ' + types.upper())
        addtime = time.strftime('%Y-%m-%d %X', time.localtime())
        self._add_sid = public.M('firewall_ip').add('address,types,brief,addtime,domain,sid',
                                                    (address, types, public.xsssec(brief), addtime, domain, 0,))
        # public.print_log('############ ip接口 8{}'.format(self._add_sid))
        if domain:
            # public.print_log('############ ip接口9{}'.format(domain))
            domain_sid = public.M('firewall_domain').add(
                'types,domain,port,address,brief,addtime,sid,protocol,domain_total', (
                    types, domain, '', address, public.xsssec(brief), addtime, self._add_sid, '', domain_total))

            public.M('firewall_ip').where("id=?", (self._add_sid,)).save('sid', domain_sid)


    # 删除All IPs规则
    def remove_all_ip_rules(self, get):
        ip_list = public.M('firewall_ip').select()
        for ip in ip_list:
            id = ip["id"]
            address = ip["address"]
            types = ip["types"]
            if self.__isUfw:
                _rule = "allow" if types == "accept" else "deny"
                if address.find('-') != -1:
                    public.ExecShell('iptables -D INPUT -m set --match-set ' +
                                     address + ' src -j ' + types.upper())
                    public.ExecShell('ipset destroy ' + address)
                else:
                    is_debian = True if public.get_os_version().lower().find(
                        "debian") != -1 else False
                    if not is_debian:
                        public.ExecShell('ufw delete ' + _rule + ' from ' + address + ' to any')
                    else:
                        public.ExecShell("iptables -D INPUT -s " + address +
                                         " -j " + types.upper())
            else:
                if self.__isFirewalld:
                    if address.find('-') != -1:
                        public.ExecShell(
                            'firewall-cmd --permanent --zone=public --remove-rich-rule=\'rule source ipset="'
                            + address + '" ' + types + '\'')
                        public.ExecShell(
                            'firewall-cmd --permanent --zone=public --delete-ipset='
                            + address)
                    else:
                        public.ExecShell(
                            'firewall-cmd --permanent --remove-source=' +
                            address + ' --zone=trusted')
                        if public.is_ipv6(address):
                            public.ExecShell(
                                'firewall-cmd --permanent --remove-rich-rule=\'rule family=ipv6 source address="'
                                + address + '" ' + types + '\'')
                        else:
                            public.ExecShell(
                                'firewall-cmd --permanent --remove-rich-rule=\'rule family=ipv4 source address="'
                                + address + '" ' + types + '\'')
                else:
                    if address.find('-') != -1:
                        public.ExecShell(
                            'iptables -D INPUT -m set --match-set ' + address +
                            ' src -j ' + types.upper())
                        public.ExecShell('ipset destroy ' + address)
                    else:
                        public.ExecShell('iptables -D INPUT -s ' + address +
                                         ' -j ' + types.upper())
            public.M('firewall_ip').where("id=?", (id,)).delete()
            self.update_panel_data(address)  # 删除面板自带防火墙的表数据
        self.FirewallReload()
        return public.returnMsg(True, public.lang("All IP rules have been removed."))


    # 删除IP规则
    def remove_ip_rules(self, get):
        id = get.id
        address = get.address
        types = get.types
        if self.__isUfw:
            _rule = "allow" if types == "accept" else "deny"
            if address.find('-') != -1:
                public.ExecShell('iptables -D INPUT -m set --match-set ' +
                                 address + ' src -j ' + types.upper())
                public.ExecShell('ipset destroy ' + address)
            else:
                is_debian = True if public.get_os_version().lower().find(
                    "debian") != -1 else False
                if not is_debian:
                    public.ExecShell('ufw delete ' + _rule + ' from ' + address + ' to any')
                else:
                    public.ExecShell('ufw delete ' + _rule + ' from ' + address + ' to any')
                    public.ExecShell("iptables -D INPUT -s " + address +
                                     " -j " + types.upper())
        else:
            if self.__isFirewalld:
                if address.find('-') != -1:
                    public.ExecShell(
                        'firewall-cmd --permanent --zone=public --remove-rich-rule=\'rule source ipset="'
                        + address + '" ' + types + '\'')
                    public.ExecShell(
                        'firewall-cmd --permanent --zone=public --delete-ipset='
                        + address)
                else:
                    public.ExecShell(
                        'firewall-cmd --permanent --remove-source=' + address +
                        ' --zone=trusted')
                    if public.is_ipv6(address):
                        public.ExecShell(
                            'firewall-cmd --permanent --remove-rich-rule=\'rule family=ipv6 source address="'
                            + address + '" ' + types + '\'')
                    else:
                        public.ExecShell(
                            'firewall-cmd --permanent --remove-rich-rule=\'rule family=ipv4 source address="'
                            + address + '" ' + types + '\'')
            else:
                if address.find('-') != -1:
                    public.ExecShell('iptables -D INPUT -m set --match-set ' +
                                     address + ' src -j ' + types.upper())
                    public.ExecShell('ipset destroy ' + address)
                else:
                    public.ExecShell('iptables -D INPUT -s ' + address +
                                     ' -j ' + types.upper())
        public.M('firewall_ip').where("id=?", (id,)).delete()
        self.update_panel_data(address)  # 删除面板自带防火墙的表数据
        self.FirewallReload()
        strategy = ''
        if get.types == 'accept':
            strategy = "accept"
        elif get.types == 'drop':
            strategy = "drop"
        public.WriteLog("system firewall", "Delete IP rules: IP:{}, policy:{}".format(get.address, strategy))
        return public.returnMsg(True, public.lang("Delete successfully!"))


    # 修改IP规则
    def modify_ip_rules(self, get):
        id = get.id
        address = get.address.strip()
        types = get.types
        brief = get.brief
        result = self.check_ip([address])
        sid = 0 if 'sid' not in get else get.sid
        domain = '' if 'domain' not in get else get.domain
        domain_total = domain.split('|')[0]
        # return 22
        if result:
            return result
        data = public.M('firewall_ip').where(
            'id=?', (id,)).field('id,address,types,brief,addtime').find()
        _address = data.get("address", "")
        _type = data.get("types", "")
        if self.__isUfw:
            rule1 = "allow" if _type == "accept" else "deny"
            if _address.find('-') != -1:
                public.ExecShell('iptables -D INPUT -m set --match-set ' +
                                 _address + ' src -j ' + _type.upper())
                public.ExecShell('ipset destroy ' + _address)
            else:
                is_debian = True if public.get_os_version().lower().find(
                    "debian") != -1 else False
                if not is_debian:
                    public.ExecShell('ufw delete ' + rule1 + ' from ' + address + ' to any')
                else:
                    cmd = "iptables -D INPUT -s " + address + " -j " + _type.upper(
                    )
                    public.ExecShell(cmd)
                # public.ExecShell('ufw delete ' + rule1 + ' from ' + _address + ' to any')
            rule2 = "allow" if types == "accept" else "deny"
            if address.find('-') != -1:
                self.handle_ufw_ip(address, types)
            else:
                is_debian = True if public.get_os_version().lower().find(
                    "debian") != -1 else False
                if not is_debian:
                    if rule2 == "allow":
                        if public.is_ipv6(address):
                            public.ExecShell('ufw ' + rule2 + ' from ' + address + ' to any')
                        else:
                            public.ExecShell('ufw insert 1 ' + rule2 + ' from ' + address + ' to any')
                    else:
                        public.ExecShell('ufw ' + rule2 + ' from ' + address + ' to any')
                else:
                    public.ExecShell('iptables -I INPUT -s ' + address +
                                     ' -j ' + types.upper())
        else:
            if self.__isFirewalld:
                if _address.find('-') != -1:
                    public.ExecShell(
                        'firewall-cmd --permanent --zone=public --remove-rich-rule=\'rule source ipset="'
                        + _address + '" ' + _type + '\'')
                    public.ExecShell(
                        'firewall-cmd --permanent --zone=public --delete-ipset='
                        + _address)
                else:
                    public.ExecShell(
                        'firewall-cmd --permanent --remove-source=' +
                        _address + ' --zone=trusted')
                    if public.is_ipv6(address):
                        public.ExecShell(
                            'firewall-cmd --permanent --remove-rich-rule=\'rule family=ipv6 source address="'
                            + _address + '" ' + _type + '\'')
                    else:
                        public.ExecShell(
                            'firewall-cmd --permanent --remove-rich-rule=\'rule family=ipv4 source address="'
                            + _address + '" ' + _type + '\'')
                if address.find('-') != -1:
                    brief = address
                    self.handle_firewall_ip(address, types)
                else:
                    if types == "accept":
                        public.ExecShell(
                            'firewall-cmd --permanent --add-source=' +
                            address + ' --zone=trusted')
                    else:
                        if public.is_ipv6(address):
                            public.ExecShell(
                                'firewall-cmd --permanent --add-rich-rule=\'rule family=ipv6 source address="'
                                + address + '" ' + types + '\'')
                        else:
                            public.ExecShell(
                                'firewall-cmd --permanent --add-rich-rule=\'rule family=ipv4 source address="'
                                + address + '" ' + types + '\'')
            else:
                if _address.find('-') != -1:
                    public.ExecShell('iptables -D INPUT -m set --match-set ' +
                                     _address + ' src -j ' + types.upper())
                    public.ExecShell('ipset destroy ' + _address)
                else:
                    public.ExecShell('iptables -D INPUT -s ' + _address +
                                     ' -j ' + _type.upper())
                if address.find('-') != -1:
                    self.handle_ufw_ip(address, types)
                else:
                    public.ExecShell('iptables -I INPUT -s ' + address +
                                     ' -j ' + types.upper())
        addtime = time.strftime('%Y-%m-%d %X', time.localtime())
        public.M('firewall_ip').where('id=?', id).update(
            {'address': address, 'types': types, 'brief': brief, 'addtime': addtime, 'sid': sid, 'domain': domain})
        if domain:
            public.M('firewall_domain').where('id=?', (sid,)).save('sid,types,brief,domain_total',
                                                                   (id, types, brief, domain_total))
        self.FirewallReload()
        old_strategy = ''
        if types == 'accept':
            old_strategy = "accept"
        elif types == 'drop':
            old_strategy = "drop"
        if get.types == 'accept':
            strategy = "accept"
        elif get.types == 'drop':
            strategy = "drop"
        public.WriteLog("system firewall",
                        "修改规则, IP:{}, 策略:{} -> IP:{}, 策略:{}".format(_address, old_strategy, get.address.strip(),
                                                                            get.types))
        return public.returnMsg(True, public.lang("Successful operation"))


    # 查看端口转发状态
    def trans_status(self):
        content = dict()
        with open(self._trans_status, 'r') as fr:
            content = json.loads(fr.read())
            if content["status"] == "open":
                return True
            self.open_forward()
            content["status"] = "open"
            with open(self._trans_status, 'w') as fw:
                fw.write(json.dumps(content))
        return True


    # 查询端口转发
    def get_forward_list(self, args):
        result = self.trans_status()

        p = 1
        limit = 15
        if 'p' in args: p = args.p
        if 'limit' in args: limit = args.limit

        where = '1=1'
        sql = public.M('firewall_trans')

        if hasattr(args, 'query'):
            where = " start_port like '%{search}%'".format(search=args.query)

        count = sql.where(where, ()).count()
        data = public.get_page(count, int(p), int(limit))
        data['data'] = sql.where(where, ()).limit('{},{}'.format(
            data['shift'], data['row'])).order('addtime desc').select()
        return data


    # 添加端口转发
    def create_forward(self, get):
        s_port = get.s_ports.strip()  # 起始端口
        d_port = get.d_ports.strip()  # 目的端口
        d_ip = get.d_address.strip()  # 目的ip
        protocol = get.protocol
        rep1 = r"^\d{1,5}(:\d{1,5})?$"
        if not re.search(rep1, s_port):
            return public.returnMsg(False, public.lang("Port range is incorrect!"))
        if not re.search(rep1, d_port):
            return public.returnMsg(False, public.lang("Port range is incorrect!"))
        rep = r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(\/\d{1,2})?$"
        if d_ip:
            if not re.search(rep, get.d_address) and not public.is_ipv6(
                    get.d_address):
                return public.returnMsg(False, public.lang("IP address you've input is illegal!"))
            if d_ip in ["127.0.0.1", "localhost"]:
                d_ip = ""
        if public.M('firewall_trans').where("start_port=?",
                                            (s_port,)).count() > 0:
            return public.returnMsg(False, public.lang("This port already exists, please do not add it again!"))
        if self.__isUfw:
            content = self.ufw_handle_add(s_port, d_port, d_ip, protocol)
            self.save_profile(self._ufw_before, content)
        else:
            if self.__isFirewalld:
                self.firewall_handle_add(s_port, d_port, d_ip, protocol)
            else:
                self.iptables_handle_add(s_port, d_port, d_ip, protocol)
        addtime = time.strftime('%Y-%m-%d %X', time.localtime())
        public.M('firewall_trans').add(
            'start_port, ended_ip, ended_port, protocol, addtime',
            (s_port, d_ip, d_port, protocol, addtime))
        self.FirewallReload()
        public.WriteLog("system firewall",
                        "Add port forwarding rules: Start port: {}, Destination port: {}, Destination IP: {}".format(s_port,
                                                                                                                     d_port,
                                                                                                                     d_ip))
        return public.returnMsg(True, public.lang("Added successfully!"))


    # 删除端口转发
    def remove_forward(self, get):
        id = get.id
        s_port = get.s_port
        d_port = get.d_port
        d_ip = get.d_ip
        protocol = get.protocol
        if self.__isUfw:
            content = self.ufw_handle_del(s_port, d_port, d_ip, protocol)
            self.save_profile(self._ufw_before, content)
        else:
            if self.__isFirewalld:
                self.firewall_handle_del(s_port, d_port, d_ip, protocol)
            else:
                self.iptables_handle_del(s_port, d_port, d_ip, protocol)
        public.M('firewall_trans').where("id=?", (id,)).delete()
        self.FirewallReload()
        public.WriteLog("system firewall",
                        "Delete port forwarding rules: Start port: {}, Destination port: {}, Destination IP: {}".format(
                            s_port, d_port, d_ip))
        return public.returnMsg(True, public.lang("Delete successfully!"))


    # 修改端口转发
    def modify_forward(self, get):
        id = get.id
        s_port = get.s_ports.strip()
        d_port = get.d_ports.strip()
        d_ip = get.d_address.strip()
        pool = get.protocol
        rep1 = r"^\d{1,5}(:\d{1,5})?$"
        if not re.search(rep1, s_port):
            return public.returnMsg(False, public.lang("Port range is incorrect!"))
        if not re.search(rep1, d_port):
            return public.returnMsg(False, public.lang("Port range is incorrect!"))
        data = public.M('firewall_trans').where('id=?', (id,)).field(
            'id,start_port,ended_ip,ended_port,protocol,addtime').find()
        start_port = data.get("start_port", "")
        ended_ip = data.get("ended_ip", "")
        ended_port = data.get("ended_port", "")
        protocol = data.get("protocol", "")
        if d_ip:
            rep = r"^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(\/\d{1,2})?$"
            if not re.search(rep, get.d_address) and not public.is_ipv6(
                    get.d_address):
                return public.returnMsg(False, public.lang("IP address you've input is illegal!"))
            if d_ip in ["127.0.0.1", "localhost"]:
                d_ip = ""
        if self.__isUfw:
            content = self.ufw_handle_update(start_port, ended_ip, ended_port,
                                             protocol, s_port, d_ip, d_port,
                                             pool)
            self.save_profile(self._ufw_before, content)
        else:
            if self.__isFirewalld:
                self.firewall_handle_update(start_port, ended_ip, ended_port,
                                            protocol, s_port, d_ip, d_port,
                                            pool)
            else:
                self.iptables_handle_update(start_port, ended_ip, ended_port,
                                            protocol, s_port, d_ip, d_port,
                                            pool)
        addtime = time.strftime('%Y-%m-%d %X', time.localtime())
        public.M('firewall_trans').where('id=?', id).update(
            {'start_port': s_port, "ended_ip": d_ip, "ended_port": d_port, "protocol": pool})
        self.FirewallReload()
        public.WriteLog("system firewall",
                        "Modify port forwarding rules: Start port: {}, Destination port: {}, Destination IP: {} -> Start port: {}, Destination port: {}, Destination IP: {}".format(
                            start_port, ended_port, ended_ip, s_port, d_port, d_ip))
        return public.returnMsg(True, public.lang("Successful operation."))


    # 处理ufw的端口转发添加
    def ufw_handle_add(self, s_port, d_port, d_ip, protocol):
        content = self.get_profile(self._ufw_before)
        if content.find('*nat') == -1:
            content = "*nat\n" + ":PREROUTING ACCEPT [0:0]\n" + ":POSTROUTING ACCEPT [0:0]\n" + "COMMIT\n" + content
        array = content.split('\n')
        result = array.index(":POSTROUTING ACCEPT [0:0]")
        if d_ip == "":
            if protocol.find('/') != -1:
                _string = "-A PREROUTING -p tcp --dport {1} -j REDIRECT --to-port {2}\n".format(
                    s_port, d_port)
                _string = _string + "-A PREROUTING -p udp --dport {1} -j REDIRECT --to-port {2}".format(
                    s_port, d_port)
            else:
                _string = "-A PREROUTING -p {0} --dport {1} -j REDIRECT --to-port {2}".format(
                    protocol, s_port, d_port)
        else:
            _string = "-A PREROUTING -p {0} --dport {1} -j DNAT --to-destination {2}:{3}\n".format(
                protocol, s_port, d_ip,
                d_port) + "-A POSTROUTING -d {0} -j MASQUERADE".format(d_ip)
        array.insert(result + 1, _string)
        return '\n'.join(array)


    # 处理ufw的端口转发删除
    def ufw_handle_del(self, s_port, d_port, d_ip, protocol):
        content = self.get_profile(self._ufw_before)
        if d_ip == "":
            _string = "-A PREROUTING -p {0} --dport {1} -j REDIRECT --to-port {2}\n".format(
                protocol, s_port, d_port)
        else:
            _string = "-A PREROUTING -p {0} --dport {1} -j DNAT --to-destination {2}:{3}\n".format(
                protocol, s_port, d_ip,
                d_port) + "-A POSTROUTING -d {0} -j MASQUERADE\n".format(d_ip)
        content = content.replace(_string, "")
        return content


    # 处理ufw的端口转发修改
    def ufw_handle_update(self, start_port, ended_ip, ended_port, protocol,
                          s_port, d_ip, d_port, pool):
        content = self.get_profile(self._ufw_before)
        if ended_ip == "":
            s_string = "-A PREROUTING -p {0} --dport {1} -j REDIRECT --to-port {2}\n".format(
                protocol, start_port, ended_port)
        else:
            s_string = "-A PREROUTING -p {0} --dport {1} -j DNAT --to-destination {2}:{3}\n".format(
                protocol, start_port, ended_ip, ended_port
            ) + "-A POSTROUTING -d {0} -j MASQUERADE\n".format(ended_ip)
        if d_ip == "":
            d_string = "-A PREROUTING -p {0} --dport {1} -j REDIRECT --to-port {2}\n".format(
                pool, s_port, d_port)
        else:
            d_string = "-A PREROUTING -p {0} --dport {1} -j DNAT --to-destination {2}:{3}\n".format(
                pool, s_port, d_ip,
                d_port) + "-A POSTROUTING -d {0} -j MASQUERADE\n".format(d_ip)
        content = content.replace(s_string, d_string)
        return content


    # 处理firewall的端口转发添加
    def firewall_handle_add(self, s_port, d_port, d_ip, protocol):
        if protocol.find('/') != -1:
            public.ExecShell(
                "firewall-cmd --permanent --zone=public --add-forward-port=port="
                + s_port + ":proto=tcp:toaddr=" + d_ip + ":toport=" + d_port +
                "")
            public.ExecShell(
                "firewall-cmd --permanent --zone=public --add-forward-port=port="
                + s_port + ":proto=udp:toaddr=" + d_ip + ":toport=" + d_port +
                "")
        else:
            cmd = "firewall-cmd --permanent --zone=public --add-forward-port=port=" + s_port + ":proto=" + protocol + ":toaddr=" + d_ip + ":toport=" + d_port + ""
            public.ExecShell(cmd)


    # 处理firewall的端口转发删除
    def firewall_handle_del(self, s_port, d_port, d_ip, protocol):
        if protocol.find('/') != -1:
            public.ExecShell(
                "firewall-cmd --permanent --zone=public --remove-forward-port=port="
                + s_port + ":proto=tcp:toaddr=" + d_ip + ":toport=" + d_port +
                "")
            public.ExecShell(
                "firewall-cmd --permanent --zone=public --remove-forward-port=port="
                + s_port + ":proto=udp:toaddr=" + d_ip + ":toport=" + d_port +
                "")
        else:
            public.ExecShell(
                "firewall-cmd --permanent --zone=public --remove-forward-port=port="
                + s_port + ":proto=" + protocol + ":toaddr=" + d_ip +
                ":toport=" + d_port + "")


    # 处理firewall的端口转发修改
    def firewall_handle_update(self, start_port, ended_ip, ended_port,
                               protocol, s_port, d_ip, d_port, pool):
        if protocol.find('/') != -1:
            public.ExecShell(
                "firewall-cmd --permanent --zone=public --remove-forward-port=port="
                + start_port + ":proto=tcp:toaddr=" + ended_ip + ":toport=" +
                ended_port + "")
            public.ExecShell(
                "firewall-cmd --permanent --zone=public --remove-forward-port=port="
                + start_port + ":proto=udp:toaddr=" + ended_ip + ":toport=" +
                ended_port + "")
        else:
            public.ExecShell(
                "firewall-cmd --permanent --zone=public --remove-forward-port=port="
                + start_port + ":proto=" + protocol + ":toaddr=" + ended_ip +
                ":toport=" + ended_port + "")
        if pool.find('/') != -1:
            public.ExecShell(
                "firewall-cmd --permanent --zone=public --add-forward-port=port="
                + s_port + ":proto=tcp:toaddr=" + d_ip + ":toport=" + d_port +
                "")
            public.ExecShell(
                "firewall-cmd --permanent --zone=public --add-forward-port=port="
                + s_port + ":proto=udp:toaddr=" + d_ip + ":toport=" + d_port +
                "")
        else:
            public.ExecShell(
                "firewall-cmd --permanent --zone=public --add-forward-port=port="
                + s_port + ":proto=" + pool + ":toaddr=" + d_ip + ":toport=" +
                d_port + "")


    # 处理iptables的端口转发添加
    def iptables_handle_add(self, s_port, d_port, d_ip, protocol):
        if d_ip == "":
            if protocol.find('/') != -1:
                public.ExecShell(
                    "iptables -t nat -A PREROUTING -p tcp --dport " + s_port +
                    " -j REDIRECT --to-port " + d_port + '')
                public.ExecShell(
                    "iptables -t nat -A PREROUTING -p udp --dport " + s_port +
                    " -j REDIRECT --to-port " + d_port + '')
            else:
                public.ExecShell("iptables -t nat -A PREROUTING -p " +
                                 protocol + " --dport " + s_port +
                                 " -j REDIRECT --to-port " + d_port + '')
        else:
            if protocol.find('/') != -1:
                public.ExecShell(
                    "iptables -t nat -A PREROUTING -p tcp --dport " + s_port +
                    " -j DNAT --to-destination " + d_ip + ":" + d_port + '')
                public.ExecShell(
                    "iptables -t nat -A PREROUTING -p udp --dport " + s_port +
                    " -j DNAT --to-destination " + d_ip + ":" + d_port + '')
                public.ExecShell(
                    "iptables -t nat -A POSTROUTING -j MASQUERADE")
            else:
                public.ExecShell(
                    "iptables -t nat -A PREROUTING -p " + protocol + " --dport " + s_port + " -j DNAT --to-destination " + d_ip + ":" + d_port + '')
                public.ExecShell("iptables -t nat -A POSTROUTING -j MASQUERADE")
        return True


    # 处理iptables的端口转发删除
    def iptables_handle_del(self, s_port, d_port, d_ip, protocol):
        if d_ip == "":
            if protocol.find('/') != -1:
                public.ExecShell(
                    "iptables -t nat -D PREROUTING -p tcp --dport " + s_port +
                    " -j REDIRECT --to-port " + d_port + '')
                public.ExecShell(
                    "iptables -t nat -D PREROUTING -p udp --dport " + s_port +
                    " -j REDIRECT --to-port " + d_port + '')
            else:
                public.ExecShell("iptables -t nat -D PREROUTING -p " +
                                 protocol + " --dport " + s_port +
                                 " -j REDIRECT --to-port " + d_port + '')
        else:
            if protocol.find('/') != -1:
                public.ExecShell(
                    "iptables -t nat -D PREROUTING -p tcp --dport " + s_port + " -j DNAT --to-destination " + d_ip + ":" + d_port + '')
                public.ExecShell(
                    "iptables -t nat -D PREROUTING -p udp --dport " + s_port + " -j DNAT --to-destination " + d_ip + ":" + d_port + '')
                public.ExecShell("iptables -t nat -D POSTROUTING -j MASQUERADE")
            else:
                public.ExecShell(
                    "iptables -t nat -D PREROUTING -p " + protocol + " --dport " + s_port + " -j DNAT --to-destination " + d_ip + ":" + d_port + '')
                public.ExecShell("iptables -t nat -D POSTROUTING -j MASQUERADE")
        return True


    # 处理iptables的端口转发删除
    def iptables_handle_update(self, start_port, ended_ip, ended_port,
                               protocol, s_port, d_ip, d_port, pool):
        if ended_ip == "":
            if protocol.find('/') != -1:
                public.ExecShell(
                    "iptables -t nat -D PREROUTING -p tcp --dport " + s_port +
                    " -j REDIRECT --to-port " + d_port + '')
                public.ExecShell(
                    "iptables -t nat -D PREROUTING -p udp --dport " + s_port +
                    " -j REDIRECT --to-port " + d_port + '')
            else:
                public.ExecShell("iptables -t nat -D PREROUTING -p " +
                                 protocol + " --dport " + s_port +
                                 " -j REDIRECT --to-port " + d_port + '')
        else:
            if protocol.find('/') != -1:
                public.ExecShell(
                    "iptables -t nat -D PREROUTING -p tcp --dport " + s_port + " -j DNAT --to-destination " + d_ip + ":" + d_port + '')
                public.ExecShell(
                    "iptables -t nat -D PREROUTING -p udp --dport " + s_port + " -j DNAT --to-destination " + d_ip + ":" + d_port + '')
                public.ExecShell("iptables -t nat -D POSTROUTING -j MASQUERADE")
            else:
                public.ExecShell(
                    "iptables -t nat -D PREROUTING -p " + protocol + " --dport " + s_port + " -j DNAT --to-destination " + d_ip + ":" + d_port + '')
                public.ExecShell("iptables -t nat -D POSTROUTING -j MASQUERADE")
        if d_ip == "":
            if pool.find('/') != -1:
                public.ExecShell(
                    "iptables -t nat -A PREROUTING -p tcp --dport " + s_port +
                    " -j REDIRECT --to-port " + d_port + '')
                public.ExecShell(
                    "iptables -t nat -A PREROUTING -p udp --dport " + s_port +
                    " -j REDIRECT --to-port " + d_port + '')
            else:
                public.ExecShell("iptables -t nat -A PREROUTING -p " +
                                 protocol + " --dport " + s_port +
                                 " -j REDIRECT --to-port " + d_port + '')
        else:
            if pool.find('/') != -1:
                public.ExecShell(
                    "iptables -t nat -A PREROUTING -p tcp --dport " + s_port + " -j DNAT --to-destination " + d_ip + ":" + d_port + '')
                public.ExecShell(
                    "iptables -t nat -A PREROUTING -p udp --dport " + s_port + " -j DNAT --to-destination " + d_ip + ":" + d_port + '')
                public.ExecShell("iptables -t nat -A POSTROUTING -j MASQUERADE")
            else:
                public.ExecShell(
                    "iptables -t nat -A PREROUTING -p " + protocol + " --dport " + s_port + " -j DNAT --to-destination " + d_ip + ":" + d_port + '')
                public.ExecShell("iptables -t nat -A POSTROUTING -j MASQUERADE")
        return True


    # 开启端口转发
    def open_forward(self):
        if self.__isUfw:
            content1 = self.get_profile(self._ufw_default)
            content2 = self.get_profile(self._ufw_sysctl)
            content1 = content1.replace('DEFAULT_FORWARD_POLICY="DROP"',
                                        'DEFAULT_FORWARD_POLICY="ACCEPT"')
            content2 = content2.replace('#net/ipv4/ip_forward=1',
                                        'net/ipv4/ip_forward=1')
            self.save_profile(self._ufw_default, content1)
            self.save_profile(self._ufw_sysctl, content2)
            self.FirewallReload()
            return True
        if self.__isFirewalld:
            public.ExecShell(
                'echo "\nnet.ipv4.ip_forward=1" >> /etc/sysctl.conf')
            public.ExecShell('firewall-cmd --add-masquerade --permanent')
            self.FirewallReload()
        else:
            public.ExecShell(
                'echo "\nnet.ipv4.ip_forward=1" >> /etc/sysctl.conf')
            public.ExecShell('sysctl -p /etc/sysctl.conf')
            self.FirewallReload()
        return True


    # 开启或关闭端口转发
    def open_close_forward(self, get):
        if not get.status in ["open", "close"]:
            return public.returnMsg(False, public.lang("Unknown control command!"))
        if self.__isUfw:
            content1 = self.get_profile(self._ufw_default)
            content2 = self.get_profile(self._ufw_sysctl)
            if get.status == 'open':
                content1 = content1.replace('DEFAULT_FORWARD_POLICY="DROP"',
                                            'DEFAULT_FORWARD_POLICY="ACCEPT"')
                content2 = content2.replace('#net/ipv4/ip_forward=1',
                                            'net/ipv4/ip_forward=1')
            else:
                content1 = content1.replace('DEFAULT_FORWARD_POLICY="ACCEPT"',
                                            'DEFAULT_FORWARD_POLICY="DROP"')
                content2 = content2.replace('net/ipv4/ip_forward=1',
                                            '#net/ipv4/ip_forward=1')
            self.save_profile(self._ufw_default, content1)
            self.save_profile(self._ufw_sysctl, content2)
            self.FirewallReload()
            return public.returnMsg(True,
                                    public.lang('Enable' if get.status == "open" else "Disable"))
        if self.__isFirewalld:
            if get.status == 'open':
                public.ExecShell('firewall-cmd --add-masquerade --permanent')
            else:
                public.ExecShell(
                    'firewall-cmd --remove-masquerade --permanent')
            self.FirewallReload()
        else:
            public.ExecShell(
                'echo "net.ipv4.ip_forward=1" >> /etc/sysctl.conf')
            public.ExecShell('sysctl -p /etc/sysctl.conf')
        return public.returnMsg(True, public.lang("Turn off port forwarding"))


    def get_host_ip(self):
        """
            查询本机ip地址
            :return:
            """
        try:
            import socket
            s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            s.connect(('8.8.8.8', 80))
            ip = s.getsockname()[0]
        finally:
            s.close()

        return ip


    def load_white_list(self):
        try:
            if not self._white_list:
                ip_data = self.get_profile(self._white_list_file)
                white_list_ips = json.loads(ip_data)
                white_list = []
                for ip_obj in white_list_ips:
                    white_list += ip_obj["ips"]
                self._white_list = white_list
                # public.WriteLog("firewall_debug", str(white_list))
            return self._white_list
        except Exception as e:
            public.WriteLog("firewall", "Failed to load whitelist！")
        return []


    def verify_ip(self, ip_entry):
        """检查规则IP是否和内网IP重叠"""
        try:
            try:
                import IPy
            except:
                ipy_tips = '/tmp/bt_ipy.pl'
                if not os.path.exists(ipy_tips):
                    os.system("nohup btpip install IPy &>/dev/null &")
                    public.WriteFile(ipy_tips, 'True')

            release_ips = [
                IPy.IP("127.0.0.1"),
                IPy.IP("172.16.1.1"),
                IPy.IP("10.0.0.1"),
                IPy.IP("192.168.0.0"),
                IPy.IP(self.get_host_ip())
            ]

            white_list = self.load_white_list()

            release_ips += white_list

            ip = IPy.IP(ip_entry, make_net=True)
            for rip_obj in release_ips:
                overlap = ip.overlaps(rip_obj)
                if overlap > 0:
                    return False
            return True
        except:
            return False


    def handle_firewall_country(self, brief, ip_list, types, port_list):
        try:
            public.ExecShell(
                'firewall-cmd --permanent --zone=public --new-ipset=' + brief +
                ' --type=hash:net')
            xml_path = "/etc/firewalld/ipsets/%s.xml" % brief
            tree = ElementTree()
            tree.parse(xml_path)
            root = tree.getroot()
            for ip in ip_list:
                if self.verify_ip(ip):
                    entry = Element("entry")
                    entry.text = ip
                    root.append(entry)
            self.format(root)
            tree.write(xml_path, 'utf-8', xml_declaration=True)
            if port_list:
                for port in port_list:
                    public.ExecShell(
                        'firewall-cmd --permanent --zone=public --add-rich-rule=\'rule source ipset="'
                        + brief + '" port port="' + port + '" protocol=tcp ' +
                        types + '\'')
            else:
                public.ExecShell(
                    'firewall-cmd --permanent --zone=public --add-rich-rule=\'rule source ipset="'
                    + brief + '" ' + types + '\'')
        except Exception as e:
            return {"status": "error", "msg": e}


    def handle_ufw_country(self, brief, ip_list, types, port_list):
        tmp_path = '/tmp/firewall_tmp.sh'
        tmp_file = open(tmp_path, 'w')
        _string = "#!/bin/bash\n"
        for ip in ip_list:
            if self.verify_ip(ip):
                _string = _string + 'ipset add ' + brief + ' ' + ip + '\n'
        tmp_file.write(_string)
        tmp_file.close()
        public.ExecShell('ipset create ' + brief +
                         ' hash:net; /bin/bash /tmp/firewall_tmp.sh')
        if port_list:
            for port in port_list:
                public.ExecShell('iptables -I INPUT -m set --match-set ' +
                                 brief + ' src -p tcp --destination-port ' +
                                 port + ' -j ' + types.upper())
        else:
            public.ExecShell('iptables -I INPUT -m set --match-set ' + brief +
                             ' src -j ' + types.upper())


    # 查询区域规则
    def get_country_list(self, args):
        p = 1
        limit = 15
        if 'p' in args: p = args.p
        if 'limit' in args: limit = args.limit

        where = '1=1'
        sql = public.M('firewall_country')

        if hasattr(args, 'query'):
            where = " country like '%{search}%' or brief like '%{search}%'".format(
                search=args.query)

        count = sql.where(where, ()).count()
        data = public.get_page(count, int(p), int(limit))
        data['data'] = sql.where(where, ()).limit('{},{}'.format(
            data['shift'], data['row'])).order('addtime desc').select()
        return data


    def create_countrys(self, get):
        try:
            if not hasattr(get, 'country'):
                return public.returnMsg(False, public.lang("Please enter the country name!"))
            input_country = get.country
            countrys = self.get_countrys(None)
            countrys = countrys[1:]

            # 2024/1/6 下午 5:00 获取防火墙状态，如果没有启动则启动防火墙
            if not self.get_firewall_status():
                get.status = "start"
                self.firewall_admin(get)

            if "Except China" in input_country:
                input_country = [i['CH'] for i in countrys if not "China" in i['CH']]
                countrys_dict = {i['CH']: i['brief'] for i in countrys}
                content = self.get_profile(self._ips_path)

                for i in input_country:
                    get.brief = countrys_dict.get(i, None)
                    get.country = i
                    self.create_country(get, True, content)
            else:
                countrys_dict = {i['CH']: i['brief'] for i in countrys}
                if isinstance(input_country, str):
                    input_country = [input_country]
                for i in input_country:
                    get.brief = countrys_dict.get(i, None)
                    get.country = i
                    self.create_country(get, True)
            get.status = "restart"
            self.firewall_admin(get)
            return public.returnMsg(True, public.lang("Added successfully"))
        except:
            print(traceback.format_exc())
            return public.returnMsg(False, public.lang("Add failed"))


    # 添加区域规则
    def create_country(self, get, is_mutil=False, _ips_paths=None):
        brief = get.brief
        types = get.types  # types in [accept, drop]
        ports = get.ports
        country = get.country
        rep = r"^\d{1,5}(:\d{1,5})?$"
        port_list = []

        # 检测该区域是否已添加过全部端口规则 hezhihong
        add_list = public.M('firewall_country').where("country=?", (country,)).field('ports').select()

        for add in add_list:
            if not add['ports']:
                return public.returnMsg(False, public.lang("This area has already been added, please do not add it again!"))

        if ports:
            port_list = ports.split(',')
            for port in port_list:
                if not re.search(rep, port):
                    return public.returnMsg(False, public.lang("Port range is incorrect!"))
                if public.M('firewall_country').where(
                        "country=? and ports=?", (country, port)).count() > 0:
                    public.print_log('############ 地区接口8{}'.format(port))
                    return public.returnMsg(False, public.lang("This area has already been added, please do not add it again!"))
        self.get_os_info()
        if _ips_paths is None:
            content = self.get_profile(self._ips_path)
        else:
            content = _ips_paths
        result = json.loads(content)
        ip_list = []
        for r in result:
            if brief == r["brief"]:
                ip_list = r["ips"]
                break
        if not ip_list:
            public.print_log('############ 地区接口7'.format())
            return public.returnMsg(True, public.lang("Please enter the correct area name!"))
        if self.__isUfw:
            self.handle_ufw_country(brief, ip_list, types, port_list)
        else:
            if self.__isFirewalld:
                result = self.handle_firewall_country(brief, ip_list, types,
                                                      port_list)
                if result:
                    public.print_log('############ 地区接口6{}'.format(result))
                    return result
            else:
                self.handle_ufw_country(brief, ip_list, types, port_list)
        addtime = time.strftime('%Y-%m-%d %X', time.localtime())
        if port_list:
            for port in port_list:
                public.M('firewall_country').add(
                    'country,types,brief,ports,addtime',
                    (country, types, brief, port, addtime))
        else:
            public.M('firewall_country').add(
                'country,types,brief,ports,addtime',
                (country, types, brief, '', addtime))
        if is_mutil is False:
            # self.FirewallReload()
            get.status = "restart"
            self.firewall_admin(get)
        if not get.ports:
            log_port = "All ports"
        else:
            log_port = get.ports
        # strategy = ''
        # if get.types == 'accept':
        #     strategy = "accept"
        # elif get.types == 'drop':
        #     strategy = "drop"
        public.print_log('############ 地区接口5'.format())
        public.WriteLog("system firewall",
                        "Add regional rules: Region:{}, Policy:{}, Port:{}".format(get.country, get.types, log_port))
        return public.returnMsg(True, public.lang("Added successfully!"))


    # 删除区域规则
    def remove_country(self, get):
        id = get.id
        types = get.types
        brief = get.brief
        ports = get.ports
        country = get.country
        reload = True
        if "not_reload" in get:
            reload = get.not_reload.lower() == "true"
        public.M('firewall_country').where("id=?", (id,)).delete()
        if self.__isUfw:
            if not ports:
                public.ExecShell('iptables -D INPUT -m set --match-set ' +
                                 brief + ' src -j ' + types.upper())
            else:
                public.ExecShell('iptables -D INPUT -m set --match-set ' +
                                 brief + ' src -p tcp --destination-port ' +
                                 ports + ' -j ' + types.upper())
            if not public.M('firewall_country').where("country=?",
                                                      (country,)).count() > 0:
                public.ExecShell('ipset destroy ' + brief)
        else:
            if self.__isFirewalld:
                if not ports:
                    public.ExecShell(
                        'firewall-cmd --permanent --zone=public --remove-rich-rule=\'rule source ipset="'
                        + brief + '" ' + types + '\'')
                else:
                    public.ExecShell(
                        'firewall-cmd --permanent --zone=public --remove-rich-rule=\'rule source ipset="'
                        + brief + '" port port="' + ports + '" protocol=tcp ' +
                        types + '\'')
                if not public.M('firewall_country').where(
                        "country=?", (country,)).count() > 0:
                    public.ExecShell(
                        'firewall-cmd --permanent --zone=public --delete-ipset='
                        + brief)
            else:
                if not ports:
                    public.ExecShell('iptables -D INPUT -m set --match-set ' +
                                     brief + ' src -j ' + types.upper())
                else:
                    public.ExecShell('iptables -D INPUT -m set --match-set ' +
                                     brief +
                                     ' src -p tcp --destination-port ' +
                                     ports + ' -j ' + types.upper())
                if not public.M('firewall_country').where(
                        "country=?", (country,)).count() > 0:
                    public.ExecShell('ipset destroy ' + brief)
        if reload:
            get.status = "restart"
            self.firewall_admin(get)
        if not get.ports:
            log_port = "All ports"
        else:
            log_port = get.ports
        strategy = ''
        if get.types == 'accept':
            strategy = "accept"
        elif get.types == 'drop':
            strategy = 'drop'
        public.WriteLog("system firewall",
                        "Delete zone rules: Region:{}, Policy:{}, Port:{}".format(get.country, strategy, log_port))
        return public.returnMsg(True, public.lang("Delete successfully!"))


    # 编辑区域规则
    def modify_country(self, get):
        # 2022/11/24 修复编辑地区端口规则问题 lx
        id = get.id
        # types = get.types
        # brief = get.brief
        # country = get.country
        data = public.M('firewall_country').where(
            'id=?',
            (id,)).field('id,country,types,brief,ports,addtime').find()
        ori_get = public.dict_obj()
        ori_get.id = id
        ori_get.types = data.get("types", "")
        ori_get.brief = data.get("brief", "")
        ori_get.country = data.get("country", "")
        ori_get.ports = data.get("ports", "")
        ori_get.not_reload = "true"
        rm_res = self.remove_country(ori_get)
        if rm_res["status"]:
            create_res = self.create_country(get)
            if create_res["status"]:
                return public.returnMsg(True, public.lang("Successful operation"))
        return public.returnMsg(False, public.lang("operation failed"))


    # 获取服务端列表：centos
    def GetList(self):
        try:
            result, arry = self.__Obj.GetAcceptPortList()
            addtime = time.strftime('%Y-%m-%d %X', time.localtime())
            for i in range(len(result)):
                if "address" not in result[i].keys(): continue
                tmp = self.check_db_exists(result[i]['ports'],
                                           result[i]['address'],
                                           result[i]['types'])
                protocol = result[i]['protocol']
                ports = result[i]['ports']
                types = result[i]['types']
                address = result[i]['address']
                if not tmp:
                    if ports:
                        public.M('firewall_new').add(
                            'ports,protocol,address,types,brief,addtime',
                            (ports, protocol, address, types, '', addtime))
                    else:
                        public.M('firewall_ip').add(
                            'address,types,brief,addtime',
                            (address, types, '', addtime))
            for i in range(len(arry)):
                if arry[i]['port']:
                    tmp = self.check_trans_data(arry[i]['port'])
                    protocol = arry[i]['protocol']
                    s_port = arry[i]['port']
                    d_port = arry[i]['to-port']
                    address = arry[i]['address']
                    if not tmp:
                        public.M('firewall_trans').add(
                            'start_port,ended_ip,ended_port,protocol,addtime',
                            (s_port, address, d_port, protocol, addtime))
        except Exception as e:
            file = open('error.txt', 'w')
            return public.returnMsg(False, e)


    # 获取服务端列表：ufw
    def get_ufw_list(self):
        data = public.M('firewall').field('id,port,ps,addtime').select()
        if type(data) != list: return
        try:
            for dt in data:
                port = dt['port']
                brief = dt['ps']
                addtime = dt['addtime']
                if port.find('.') != -1:
                    tmp = self.check_db_exists('', port, 'drop')
                    if not tmp:
                        public.M('firewall_ip').add('address,types,brief,addtime',
                                                    (port, 'drop', '', addtime))
                else:
                    tmp = self.check_db_exists(port, '', 'accept')
                    if not tmp:
                        public.M('firewall_new').add(
                            'ports,brief,protocol,address,types,addtime',
                            (port, brief, 'tcp/udp', '', 'accept', addtime))
        except:
            pass


    # 检查数据库是否存在
    def check_db_exists(self, ports, address, types):
        if ports:
            data = public.M('firewall_new').field(
                'id,ports,protocol,address,types,brief,addtime').select()
            for dt in data:
                if dt['ports'] == ports: return dt
            return False
        else:
            data = public.M('firewall_ip').field(
                'id,address,types,brief,addtime').select()
            for dt in data:
                if dt["address"] == address and dt["types"] == types: return dt
            return False


    def check_trans_data(self, ports):
        data = public.M('firewall_trans').field(
            'id,start_port,ended_ip,ended_port,protocol,addtime').select()
        for dt in data:
            if dt['start_port'] == ports: return dt
        return False


    # 规则导出：服务器
    def export_rules(self, get):
        rule_name = get.rule_name
        arry = []
        data_list = None
        filename = ''
        if rule_name == "port_rule":
            filename = self._rule_path + "port.json"
            data_list = public.M('firewall_new').order("id desc").select()
        elif rule_name == "ip_rule":
            filename = self._rule_path + "ip.json"
            data_list = public.M('firewall_ip').order("id desc").select()
        elif rule_name == "trans_rule":
            filename = self._rule_path + "forward.json"
            data_list = public.M('firewall_trans').order("id desc").select()
        elif rule_name == "country_rule":
            filename = self._rule_path + "country.json"
            data_list = public.M('firewall_country').order("id desc").select()
        if not data_list:
            data_list = []
        # 将数据格式换成以|分割的字符串 hezhihong
        write_string = ""
        if data_list:
            for i in data_list:
                for v in i.keys():
                    if v == 'domain': i[v] = i[v].replace('|', '#')
                    write_string += str(i[v]) + "|"
                write_string += '\n'
        public.writeFile(filename, write_string)
        public.WriteLog("system firewall", "Export port rules")
        return public.returnMsg(True, filename)


    # 规则导出：本地
    def get_file(self, args):
        filename = args.filename
        mimetype = "application/octet-stream"
        if not os.path.exists(filename): return abort(404)
        return send_file(filename,
                         mimetype=mimetype,
                         as_attachment=True,
                         attachment_filename=os.path.basename(filename),
                         cache_timeout=0)


    # 规则导入：json
    def import_rules(self, get):
        try:
            rule_name = get.rule_name  # 规则名:[port_rule, ip_rule, trans_rule, country_rule]
            file_name = get.file_name  # 文件命:[port.json, ip.json, trans.json, country.json]
            file_path = "{0}{1}".format(self._rule_path, file_name)
            data_list = self.get_profile(file_path)
            pay = self.__check_auth()
            not_pay_list = []
            tmp_data = []
            # |分隔符格式文件导入 hezhihong
            if data_list and isinstance(data_list, str):

                if data_list.find('|') != -1:
                    data_list = data_list.split('\n')
                    for data in data_list:
                        if not data: continue
                        split_data = data.split('|')
                        data_dict = {}
                        data_dict['id'] = split_data[0]
                        if rule_name == 'port_rule':
                            if not pay and split_data[7].find('#') != -1:
                                not_pay_list.append(data)
                                continue
                            data_dict['protocol'] = split_data[1]
                            data_dict['ports'] = split_data[2]
                            data_dict['types'] = split_data[3]
                            data_dict['address'] = split_data[4]
                            data_dict['brief'] = split_data[5]
                            data_dict['addtime'] = split_data[6]
                            data_dict['domain'] = split_data[7]
                        elif rule_name == 'ip_rule':
                            if not pay and split_data[6].find('#') != -1:
                                not_pay_list.append(data)
                                continue
                            data_dict['types'] = split_data[1]
                            data_dict['address'] = split_data[2]
                            data_dict['brief'] = split_data[3]
                            data_dict['addtime'] = split_data[4]
                            data_dict['domain'] = split_data[6]
                        elif rule_name == 'trans_rule':
                            data_dict['start_port'] = split_data[1]
                            data_dict['ended_ip'] = split_data[2]
                            data_dict['ended_port'] = split_data[3]
                            data_dict['protocol'] = split_data[4]
                            data_dict['addtime'] = split_data[5]
                        elif rule_name == 'country_rule':
                            data_dict['types'] = split_data[1]
                            data_dict['country'] = split_data[2]
                            data_dict['brief'] = split_data[3]
                            data_dict['addtime'] = split_data[4]
                            data_dict['ports'] = split_data[5]
                        tmp_data.append(data_dict)
            data_list = tmp_data
            # 一行一条规则格式文件导入 hezhihong
            if data_list and isinstance(data_list, str):
                data_list = data_list.strip()
            if isinstance(data_list, str) and data_list.find('\n') != -1:
                data_list = data_list.split('\n')
                try:
                    data_list.remove('')
                except:
                    pass
            if isinstance(data_list, str):
                try:
                    data_list = json.loads(data_list)
                except:
                    if os.path.exists(file_path):
                        os.remove(file_path)
                    return public.ReturnMsg(False, "The file content is incorrect!！")
            if data_list:
                if isinstance(data_list, dict):
                    data_list = [data_list]
                if not isinstance(data_list, list):
                    if os.path.exists(file_path):
                        os.remove(file_path)
                    return public.ReturnMsg(False, "The file content is incorrect!！")
                if len(data_list) == 0:
                    return public.ReturnMsg(False, "The file is empty!")
                result = self.hand_import_rules(rule_name, data_list)
            os.remove(file_path)
            if not_pay_list:
                not_pay_list = ("<br/>" + "-" * 20 + "<br/>").join(not_pay_list)
                return public.ReturnMsg(
                    result["status"],
                    "{}<br/>The designated domain name function is exclusive to the Enterprise Edition, and the following rules are not imported:<br/>{}".format(
                        result["msg"], not_pay_list)
                )
            public.WriteLog("system firewall", "Import port rules")
            return public.ReturnMsg(result["status"], result["msg"])
        except Exception:
            return public.ReturnMsg(False,
                                    "The import failed. The format of the rules is wrong. Please try again according to the format of the export rules!")


    # 处理规则导入，读取json文件内容
    def hand_import_rules(self, rule_name, data_list):
        table_head = []
        try:
            if rule_name == "port_rule":
                table_head = ["id", "protocol", "ports", "types", "address", "brief", "addtime", "domain", ]
                for data in data_list:
                    # 兼容一行一条规则格式文件导入 hezhihong
                    try:
                        data = json.loads(data)
                    except:
                        pass

                    res = all([field in data.keys() for field in table_head])
                    if not res or len(table_head) != len(data.keys()):
                        return {"status": False, "msg": "The data format is incorrect!"}
                    get = public.dict_obj()
                    get.protocol = data["protocol"]
                    get.ports = data["ports"]
                    get.types = data["types"]
                    get.source = data["address"]
                    get.brief = data["brief"]
                    # 兼容域名导入 hezhihong
                    if 'domain' in data.keys() and data['domain']:
                        get.domain = data['domain'].split('#')[0]
                        get.source = data['domain'].split('#')[1]
                    result = self.create_rules(get)
                    if not result["status"]:
                        continue
            elif rule_name == "ip_rule":
                table_head = ["id", "types", "address", "brief", "addtime"]
                for data in data_list:
                    # 兼容一行一条规则格式文件导入 hezhihong
                    try:
                        data = json.loads(data)
                    except:
                        pass
                    res = all([field in data.keys() for field in table_head])
                    if not res:
                        return {"status": False, "msg": "The data format is incorrect!"}
                    get = public.dict_obj()
                    get.types = data["types"]
                    get.address = data["address"]
                    get.brief = data["brief"]
                    # 兼容域名导入 hezhihong
                    if 'domain' in data.keys() and data['domain']:
                        get.domain = data['domain'].split('#')[0]
                        get.source = data['domain'].split('#')[1]
                    result = self.create_ip_rules(get)
                    if not result["status"]:
                        continue
            elif rule_name == "trans_rule":
                table_head = [
                    "id", "start_port", "ended_ip", "ended_port", "protocol",
                    "addtime"
                ]
                for data in data_list:
                    # 兼容一行一条规则格式文件导入 hezhihong
                    try:
                        data = json.loads(data)
                    except:
                        pass
                    res = all([field in data.keys() for field in table_head])
                    if not res:
                        return {"status": False, "msg": "The data format is incorrect!"}
                    get = public.dict_obj()
                    get.s_ports = data["start_port"]
                    get.d_address = data["ended_ip"]
                    get.d_ports = data["ended_port"]
                    get.protocol = data["protocol"]
                    result = self.create_forward(get)
                    if not result["status"]:
                        continue
            elif rule_name == "country_rule":
                table_head = ["id", "types", "country", "brief", "addtime", "ports"]
                for data in data_list:
                    # 兼容一行一条规则格式文件导入 hezhihong
                    try:
                        data = json.loads(data)
                    except:
                        pass
                    res = all([field in data.keys() for field in table_head])
                    if not res:
                        return {"status": False, "msg": "The data format is incorrect!"}
                    get = public.dict_obj()
                    get.types = data["types"]
                    get.ports = data["ports"]
                    get.brief = data["brief"]
                    get.country = data["country"]
                    result = self.create_country(get)
                    if not result["status"]:
                        continue
        except:
            return {"status": False, "msg": "Import failed!"}
        return {"status": True, "msg": "Imported successfully!"}


    def get_countrys(self, get):
        result = []
        content = self.get_profile(self._country_path)
        result = json.loads(content)
        result = sorted(result, key=lambda x: x['CH'], reverse=True);

        if isinstance(result, list):
            result.insert(0, {"CH": "Except China", "brief": "OTHER"})
        return result


    # 读取配置文件
    def get_profile(self, path):
        if not os.path.exists(path):
            b_path = os.path.dirname(path)
            if not os.path.exists(b_path): os.makedirs(b_path)

            if path in [
                self._ips_path, self._country_path, self._white_list_file
            ]:
                public.downloadFile(
                    'https://www.yakpanel.com/install/lib/{}'.format(
                        os.path.basename(path)), path)

        content = ""
        with open(path, "r") as fr:
            content = fr.read()
        return content


    # 保存配置文件
    def save_profile(self, path, data):
        with open(path, "w") as fw:
            fw.write(data)


    # 读取配置文件
    def update_profile(self, path):
        import files
        f = files.files()
        return f.GetFileBody(path)


    # 获取端口规则列表
    def get_port_rules(self, get):
        rule_list = public.M('firewall_new').order("id desc").select()
        return public.returnMsg(True, rule_list)


    # 整理配置文件格式
    def format(self, em, level=0):
        i = "\n" + level * "  "
        if len(em):
            if not em.text or not em.text.strip():
                em.text = i + "  "
            for e in em:
                self.format(e, level + 1)
            if not e.tail or not e.tail.strip():
                e.tail = i
        if level and (not em.tail or not em.tail.strip()):
            em.tail = i


    def check_table(self):
        if public.M('sqlite_master').where('type=? AND name=?',
                                           ('table', 'firewall_new')).count():
            if public.M('sqlite_master').where(
                    'type=? AND name=?', ('table', 'firewall_ip')).count():
                if public.M('sqlite_master').where(
                        'type=? AND name=?',
                        ('table', 'firewall_trans')).count():
                    if public.M('sqlite_master').where(
                            'type=? AND name=?',
                            ('table', 'firewall_country')).count():
                        return True
        return Sqlite()


    def delete_service(self):
        if self.__isUfw:
            public.ExecShell('ufw delete allow ssh')
        else:
            if self.__isFirewalld:
                public.ExecShell(
                    'firewall-cmd --zone=public --remove-service=ssh --permanent'
                )
            else:
                pass
        return True


    # 获取系统类型(具体到哪个版本)
    def get_os_info(self):
        tmp = {"osname": "", "version": ""}
        if os.path.exists('/etc/redhat-release'):
            sys_info = public.ReadFile('/etc/redhat-release')
        elif os.path.exists('/usr/bin/yum'):
            sys_info = public.ReadFile('/etc/issue')
        elif os.path.exists('/etc/issue'):
            sys_info = public.ReadFile('/etc/issue')
        try:
            tmp['osname'] = sys_info.split()[0]
            tmp['version'] = re.search(r'\d+(\.\d*)*', sys_info).group()
        except:
            os_result = public.ExecShell(". /etc/os-release && echo $ID")[0]
            if "amzn" == os_result:
                tmp['osname'] = 'CentOS'
                tmp['version'] = '8'
        if tmp["osname"] == "CentOS":
            if tmp["version"].startswith("8"):
                content = self.get_profile("/etc/firewalld/firewalld.conf")
                content = content.replace("FirewallBackend=nftables", "FirewallBackend=iptables")
                self.save_profile("/etc/firewalld/firewalld.conf", content)
                public.ExecShell("systemctl restart firewalld")
        return True


    # 新加代码----- start

    def sync_must_ports(self, get):
        '''
            同步必须放行的端口
            @param get:
            @return:
            '''
        protocol = "tcp"
        ports = get.ports.strip()
        print(ports)
        if not ports: return public.returnMsg(False, public.lang("Port cannot be empty!"))
        port_list = ports.split(",") if ports.find(",") != -1 else [ports]
        types = "accept"
        check_result = self.check_port(port_list)
        if check_result: return check_result

        firewall_type = 'iptables'
        if self.__isFirewalld: firewall_type = 'firewalld'
        if self.__isUfw: firewall_type = 'ufw'

        try:
            # 2024/1/6 下午 5:00 获取防火墙状态，如果没有启动则启动防火墙
            if not self.get_firewall_status():
                get = public.dict_obj()
                get.status = 1
                self.firewall_admin(get)

            for port in port_list:
                if firewall_type == 'firewalld':
                    if port.find(':') != -1: port = port.replace(':', '-')
                    self.add_firewall_rule("", protocol, port, types)
                elif firewall_type == 'ufw':
                    if port.find('-') != -1: port = port.replace('-', ':')
                    self.add_ufw_rule("", protocol, port, types)
                else:
                    self.add_iptables_rule("", protocol, port, types)

                query_result = public.M('firewall_new').where(
                    'ports=? and address=? and protocol=? and types=?',
                    (port, "", protocol, types)
                ).find()
                print(query_result)
                if query_result: continue

                addtime = time.strftime('%Y-%m-%d %X', time.localtime())
                self._add_sid = public.M('firewall_new').add(
                    'ports,brief,protocol,address,types,addtime,domain,sid',
                    (port, "", protocol, "", types, addtime, "", 0)
                )
            return public.returnMsg(True, public.lang("Added successfully!"))
        except Exception:
            print(traceback.format_exc())
            return public.returnMsg(False, public.lang("Failed to add"))


    def _get_webserver(self):
        '''
            获取web服务器类型
            @return:
            '''
        webserver = ''
        if os.path.exists('/www/server/nginx/sbin/nginx'):
            webserver = 'nginx'
        elif os.path.exists('/www/server/apache/bin/httpd'):
            webserver = 'apache'
        elif os.path.exists('/usr/local/lsws/bin/lswsctrl'):
            webserver = 'lswsctrl'
        return webserver


    def get_port_info(self, get):
        '''
            获取面板防火墙关键服务端口放行状态信息
            判断服务是否存在,能读取文件就读取文件,配置文件不大不会影响性能,这种方式能最大缩短接口响应时间,公网测试80ms
            @param get:
            @return:
            '''
        ports_list = []
        result_list = [{"name": "FTP passive port", "status": 0, "port": "39000-40000"}]

        webserver = self._get_webserver()
        if webserver in ['nginx', 'apache', 'lswsctrl']:
            result_list.append({"name": "website port", "status": 0, "port": "80"})
            ports_list.append("80")

        port_443, _ = public.ExecShell("fuser -n tcp 443")
        if port_443:
            result_list.append({"name": "HTTPS port", "status": 0, "port": "443"})
            ports_list.append("443")

        _panel_port_file = '/www/server/panel/data/port.pl'
        panel_port = public.readFile(_panel_port_file).strip(" ").strip("\n")

        cmd = "cat /www/server/pure-ftpd/etc/pure-ftpd.conf |grep Bind|awk -F ',' '{print $2}'"
        ftp_port = public.ExecShell(cmd)[0].strip(" ").strip("\n").strip("\r")

        cmd = "cat /etc/ssh/sshd_config |grep -E '^Port'|awk '{print $2}'|awk 'NR == 1'"
        ssh_port = public.ExecShell(cmd)[0].strip(" ").strip("\n")

        if ssh_port == "": ssh_port = "22"
        ports_list.append(panel_port)
        result_list.append({"name": "panel", "status": 0, "port": panel_port})
        ports_list.append(ftp_port)
        result_list.append({"name": "FTP active port", "status": 0, "port": ftp_port})
        ports_list.append(ssh_port)
        result_list.append({"name": "SSH", "status": 0, "port": ssh_port})
        ports_list.append("39000-40000")

        if self.__isUfw:
            return self._get_ufw_port_status(ports_list, result_list)
        if self.__isFirewalld:
            return self._get_firewall_port_status(ports_list, result_list)
        return {}


    def _get_firewall_port_status(self, ports_list, result_list):
        '''
            获取firewalld防火墙端口状态
            @param ports_list:
            @param result_list:
            @return:
            '''
        with contextlib.suppress(Exception):
            _firewalld_ports, _ = self.__firewall_obj.GetAcceptPortList()
            # print("_firewalld_ports: ", _firewalld_ports)
            for firewalld_port in _firewalld_ports:
                if firewalld_port['ports'] in ports_list:
                    for result in result_list:
                        if result['port'] == firewalld_port['ports']:
                            result['status'] = 1
                            break
        return result_list


    def _get_ufw_port_status(self, ports_list, result_list):
        '''
            获取ufw防火墙端口状态
            @param ports_list:
            @param result_list:
            @return:
            '''
        with contextlib.suppress(Exception):
            rules_result = self._get_ufw_port_info()
            # print("rules_result: ", rules_result)
            ports_set = set(ports_list)  # 将要查找的端口列表转换成集合，以便进行高效查找

            for rule in rules_result:
                if 'tcp' in rule['protocol'] and rule['ports'] in ports_set:
                    for result in result_list:
                        if result['port'] == rule['ports']:
                            result['status'] = 1
                            ports_set.remove(rule['ports'])
                            break

                if not ports_set: break
        return result_list


    def _get_ufw_port_info(self):
        '''
            获取ufw防火墙端口信息
            @return:
            '''
        with open('/etc/ufw/user.rules', 'r') as f:
            content = f.read()
            start_index = content.find('### RULES ###')
            end_index = content.find('### END RULES ###')
            result = content[start_index + 15:end_index]
        sys_rules = [rule for rule in result.split('\n') if rule != '' and '###' in rule]
        # 将sys_rules列表中的每个元素拆分出来,并且去掉空格,元素为字符串,例如:'### tuple ### allow tcp 20 0.0.0.0/0 any 0.0.0.0/0 in'
        # 拆分后的列表元素为:{'protocol': 'tcp', 'ports': '20', 'types': 'allow', 'address': '0.0.0.0'}
        rules = []
        for rule in sys_rules:
            rule = rule.split(' ')
            rule = [i for i in rule if i != '']
            rules.append({
                'protocol': rule[4] if rule[4] != 'any' else 'tcp/udp',
                'ports': rule[5] if rule[5].find(':') == -1 else rule[5].replace(':', '-'),
                'types': 'accept' if rule[3] == 'allow' else 'drop',
                'address': rule[8] if rule[8] != '0.0.0.0/0' else ''
            })

        unique_set = set(tuple(sorted(item.items())) for item in rules)
        rules = [dict(item) for item in unique_set]
        return rules


    @staticmethod
    def get_listening_processes(get: public.dict_obj):
        '''
            获取指定端口的进程信息
            @param get:
            @return:
            '''
        if 'port' not in get.get_items().keys(): return public.returnMsg(False, public.lang("Parameter passing error, please pass the port field"))
        if len(get.port) == 0: return public.returnMsg(False, public.lang("Port cannot be empty"))
        if get.port.find('-') != -1 or get.port.find(':') != -1: return public.returnMsg(False, public.lang("Range ports not supported"))
        if not get.port.isdigit(): return public.returnMsg(False, public.lang("Port must be numeric"))
        if int(get.port) < 1 or int(get.port) > 65535: return public.returnMsg(False, public.lang("The port range is 1-65535"))

        process_name = ''
        process_pid = ''
        process_cmd = ''

        cmd = "lsof -i:{}|grep LISTEN|grep -v COMMAND".format(get.port) + "|awk '{print $1,$2}'"
        info_list = public.ExecShell(cmd)[0].split("\n")[0].split(" ")
        if len(info_list) == 2:
            process_name = info_list[0]
            process_pid = info_list[1]
            cmd_ps = "ps aux|grep {}|grep -v grep".format(process_pid)
            cmd_awk = "|awk '{print $11,$12,$13,$14}'"
            process_cmd = public.ExecShell(cmd_ps + cmd_awk)[0].split("\n")[0].strip(" ")

        return {
            "process_name": process_name,
            "process_pid": process_pid,
            "process_cmd": process_cmd
        }


    def get_diff_panel_firewall_rules(self, get):
        '''
            对比面板防火墙规则数据库和防火墙配置文件,取出差异的规则
            @return:
            '''
        # 获取面板防火墙规则数据库
        panel_firewall_rules = self.get_panel_firewall_rules()
        # 获取防火墙配置文件
        firewall_rules = self.get_sys_firewall_rules()
        # 取出差异的规则
        diff_rules = self._get_diff_rules(panel_firewall_rules, firewall_rules)
        return diff_rules


    def get_panel_firewall_rules(self):
        '''
            获取面板防火墙规则数据库
            @return:
            '''
        all_ports = public.M('firewall_new').field('protocol,ports,types,address').order('addtime desc').select()
        unique_set = set(tuple(sorted(item.items())) for item in all_ports)
        new_ports = [dict(item) for item in unique_set]
        return new_ports


    def get_sys_firewall_rules(self):
        '''
            获取防火墙配置文件
            @return:
            '''
        if self.__isUfw: return self._get_ufw_port_info()
        if self.__isFirewalld: return self.__firewall_obj.recombine_rules()
        return []


    def _diff_dict_list(self, list1, list2):
        '''
            比较两个dict类型的list,返回list1中有,而list2中没有的元素
            @param list1:
            @param list2:
            @return:
            '''
        list1_not_in_list2 = []
        for item1 in list1:
            found = False
            try:
                for item2 in list2:
                    # 对比'protocol,ports,types,address'是否相同
                    if all(item1[key] == item2[key] for key in ('protocol', 'ports', 'types', 'address')):
                        found = True
                        break
            except KeyError:
                continue
            if not found: list1_not_in_list2.append(item1)
        return list1_not_in_list2


    def _get_diff_rules(self, panel_firewall_rules, firewall_rules):
        '''
            取出差异的规则
            @param panel_firewall_rules:
            @param firewall_rules:
            @return:
            '''
        firewall_diff_rules_name = 'firewall_diff_rules'
        firewall_diff_rules = {}
        if os.path.isfile("config/{}.json".format(firewall_diff_rules_name)):
            firewall_diff_rules = public.read_config(firewall_diff_rules_name)
        if not firewall_diff_rules:
            data = {
                "panel_not_in_sys_fw_diff_list": self._diff_dict_list(panel_firewall_rules, firewall_rules),
                "sys_not_in_panel_fw_diff_list": self._diff_dict_list(firewall_rules, panel_firewall_rules),
                "panel_exclude": [],
                "sys_exclude": []
            }
            public.save_config(firewall_diff_rules_name, data)
            return data

        panel_not_in_sys_fw_diff_list = self._diff_dict_list(panel_firewall_rules, firewall_rules)
        sys_not_in_panel_fw_diff_list = self._diff_dict_list(firewall_rules, panel_firewall_rules)

        # 如果firewall_diff_rules的panel_exclude和sys_exclude中有值,则将其从panel_not_in_sys_fw_diff_list和sys_not_in_panel_fw_diff_list中去掉
        if firewall_diff_rules['panel_exclude']:
            for key in firewall_diff_rules['panel_exclude']:
                if key in panel_not_in_sys_fw_diff_list:
                    panel_not_in_sys_fw_diff_list.remove(key)
        if firewall_diff_rules['sys_exclude']:
            for key in firewall_diff_rules['sys_exclude']:
                # panel_not_in_sys_fw_diff_list是list，如果key在panel_not_in_sys_fw_diff_list中,则将其从panel_not_in_sys_fw_diff_list中去掉
                if key in sys_not_in_panel_fw_diff_list:
                    sys_not_in_panel_fw_diff_list.remove(key)

        # 将差异规则写入文件并返回
        firewall_diff_rules['panel_not_in_sys_fw_diff_list'] = panel_not_in_sys_fw_diff_list
        firewall_diff_rules['sys_not_in_panel_fw_diff_list'] = sys_not_in_panel_fw_diff_list
        public.save_config(firewall_diff_rules_name, firewall_diff_rules)
        return firewall_diff_rules


    def exclude_diff_rules(self, get):
        '''
            排除firewall_diff_rules的规则，并写入配置文件
            @param get:
            @return:
            '''
        try:
            panel_excludes = get.panel_exclude if "panel_exclude" in get.get_items().keys() else {}
            sys_excludes = get.sys_exclude if "sys_exclude" in get.get_items().keys() else {}
            status = get.status if "status" in get.get_items().keys() else {}
            # print("panel_excludes: ", panel_excludes)

            if status == 'add':
                return self._add_exclude(panel_excludes, sys_excludes)
            elif status == 'del':
                return self._del_exclude(panel_excludes, sys_excludes)
        except Exception as e:
            # print(e)
            return public.returnMsg(False, public.lang("Ignore rule failed,{}!", e))


    def _add_exclude(self, panel_excludes, sys_excludes):
        '''
            添加排除规则
            @param panel_exclude:
            @param sys_exclude:
            @return:
            '''
        firewall_diff_rules_name = 'firewall_diff_rules'
        firewall_diff_rules = {}
        if os.path.isfile("config/{}.json".format(firewall_diff_rules_name)):
            firewall_diff_rules = public.read_config(firewall_diff_rules_name)

        if not firewall_diff_rules['panel_exclude']:
            firewall_diff_rules['panel_exclude'] = panel_excludes
        else:
            for exclude in panel_excludes:
                if exclude not in firewall_diff_rules['panel_exclude']:
                    firewall_diff_rules['panel_exclude'].append(exclude)

        if not firewall_diff_rules['sys_exclude']:
            firewall_diff_rules['sys_exclude'] = sys_excludes
        else:
            for exclude in sys_excludes:
                if exclude not in firewall_diff_rules['sys_exclude']:
                    firewall_diff_rules['sys_exclude'].append(exclude)
        public.save_config(firewall_diff_rules_name, firewall_diff_rules)
        return public.returnMsg(True, public.lang("Ignore rules successfully!"))


    def _del_exclude(self, panel_excludes, sys_excludes):
        '''
            删除排除规则
            @param panel_excludes:
            @param sys_excludes:
            @return:
            '''
        firewall_diff_rules_name = 'firewall_diff_rules'
        firewall_diff_rules = {}
        if os.path.isfile("config/{}.json".format(firewall_diff_rules_name)):
            firewall_diff_rules = public.read_config(firewall_diff_rules_name)

        new_panel_exclude = []
        new_sys_exclude = []

        for exclude in firewall_diff_rules['panel_exclude']:
            if exclude not in panel_excludes:
                new_panel_exclude.append(exclude)
        for exclude in firewall_diff_rules['sys_exclude']:
            if exclude not in sys_excludes:
                new_sys_exclude.append(exclude)

        firewall_diff_rules['panel_exclude'] = new_panel_exclude
        firewall_diff_rules['sys_exclude'] = new_sys_exclude
        public.save_config(firewall_diff_rules_name, firewall_diff_rules)
        return public.returnMsg(True, public.lang("Cancel ignore rule successfully!"))


    def _add_firewall_rules(self, source_ip, protocol, port, types):
        '''
            添加防火墙规则
            @param source_ip:
            @param protocol:
            @param port:
            @param types:
            @return:
            '''
        if self.__isUfw:
            if port.find('-') != -1:
                port = port.replace('-', ':')
            self.add_ufw_rule(source_ip, protocol, port, types)
        elif self.__isFirewalld:
            if port.find(':') != -1:
                port = port.replace(':', '-')
            self.add_firewall_rule(source_ip, protocol, port, types)
        else:
            self.add_iptables_rule(source_ip, protocol, port, types)


    def _del_firewall_rules(self, source_ip, protocol, port, types):
        '''
            删除防火墙规则
            @param source_ip:
            @param protocol:
            @param port:
            @param types:
            @return:
            '''
        if self.__isUfw:
            self.del_ufw_rule(source_ip, protocol, port, types)
        elif self.__isFirewalld:
            self.del_firewall_rule(source_ip, protocol, port, types)
        else:
            self.del_iptables_rule(source_ip, protocol, port, types)


    def _modify_firewall_rules(self, address, protocol, port, type, source_ip, source_protocol, ports, types):
        '''
            修改防火墙规则1
            @param address:
            @param protocol:
            @param port:
            @param type:
            @param source_ip:
            @param source_protocol:
            @param ports:
            @param types:
            @return:
            '''
        if self.__isUfw:
            self.edit_ufw_rule(address, protocol, port, type, source_ip, source_protocol, ports, types)
        elif self.__isFirewalld:
            self.edit_firewall_rule(address, protocol, port, type, source_ip, source_protocol, ports, types)
        else:
            self.edit_iptables_rule(address, protocol, port, type, source_ip, source_protocol, ports, types)


    # 新加代码----- end

    # 端口防扫描 --- start
    def _get_server_lists_scan(self):
        """
            @name 获取服务器常用端口
            @return:
            """
        return {
            "sshd": "{}".format(public.get_sshd_port()),
            "mysql": "{}".format(public.get_mysql_info()["port"]),
            "ftpd": "21",
            "dovecot": "110,143",
            "postfix": "25,465,587",
        }


    def get_anti_scan_logs(self, get):
        """
            @name 获取防扫描日志
            @param get:
            @return:
            """
        get = public.dict_obj()
        server_lists = self._get_server_lists_scan()
        result_dict = {
            "currently_failed": 0,
            "total_failed": 0,
            "currently_banned": 0,
            "total_banned": 0,
            "banned_ip_list": []
        }

        import PluginLoader
        for key in server_lists:
            get.mode = key

            logs_result = PluginLoader.plugin_run('fail2ban', 'get_status', get)
            if type(logs_result['msg']) is dict:
                result_dict["currently_failed"] += int(logs_result["msg"]["currently_failed"])
                result_dict["total_failed"] += int(logs_result["msg"]["total_failed"])
                result_dict["currently_banned"] += int(logs_result["msg"]["currently_banned"])
                result_dict["total_banned"] += int(logs_result["msg"]["total_banned"])
                result_dict["banned_ip_list"] += logs_result["msg"]["banned_ip_list"]

        return result_dict


    def get_anti_scan_status(self, get):
        """
            @name 获取端口防扫描
            @return:
            """
        plugin_path = "/www/server/panel/plugin/fail2ban"
        result_data = {"status": 0, "installed": 1}
        if not os.path.exists("{}".format(plugin_path)):
            result_data['installed'] = 0
            return result_data

        sock = "{}/fail2ban.sock".format(plugin_path)
        if not os.path.exists(sock):
            return result_data

        server_lists = self._get_server_lists_scan()
        s_file = '{}/plugin/fail2ban/config.json'.format(public.get_panel_path())
        if os.path.exists(s_file):
            try:
                data = json.loads(public.readFile(s_file))
                if len(data) == 0:
                    return result_data

                for key in server_lists:
                    if key in data:
                        if data[key]['act'] != 'true':
                            result_data['status'] = 0
                            return result_data

                result_data['status'] = 1
                return result_data
            except:
                pass

        return result_data


    def set_anti_scan_status(self, get):
        """
        @name 设置常用端口防扫描
        @param get:
        @return:
        """
        scan_status = get.status if "status" in get else 0
        param_dict = {
            'type': 'edit',
            'act': 'true' if scan_status == 1 else 'false',
            'maxretry': '30',
            'findtime': '300',
            'bantime': '600',
            'port': '',
            'mode': ''
        }
        server_lists = self._get_server_lists_scan()
        _set_up_path = "/www/server/panel/plugin/fail2ban"
        _config = _set_up_path + "/config.json"
        if not os.path.exists(_set_up_path + "/fail2ban_main.py"):
            return public.returnMsg(False, public.lang("fail2ban plugin is not installed"))

        if os.path.exists(_config):
            try:
                _conf_data = json.loads(public.ReadFile(_config))
            except:
                _conf_data = {}
        else:
            _conf_data = {}

        import PluginLoader

        # if scan_status == "1" and PluginLoader.plugin_run('fail2ban', 'get_fail2ban_status', get) is False:
        #     get.type = "start"
        #     PluginLoader.plugin_run('fail2ban', 'set_fail2ban_status', get)

        for key in server_lists:
            tmp = param_dict.copy()
            tmp["port"] = server_lists[key]
            tmp["mode"] = key

            if key not in _conf_data:
                tmp["type"] = "add"
            else:
                tmp["maxretry"] = _conf_data[key]["maxretry"]
                tmp["findtime"] = _conf_data[key]["findtime"]
                tmp["bantime"] = _conf_data[key]["bantime"]

            tmp = public.to_dict_obj(tmp)

            PluginLoader.plugin_run('fail2ban', 'set_anti', tmp)
            del tmp

        # if scan_status == "0":
        #     get.type = "stop"
        #     PluginLoader.plugin_run('fail2ban', 'set_fail2ban_status', get)

        public.WriteLog("Port Scanning Prevention", "[Security]-[System Firewall]-[Set Port Scanning Prevention]")
        return public.returnMsg(True, public.lang("Setup successful!"))


    def del_ban_ip(self, get):
        """
        删除封锁IP
        @param get:
        @return:
        """
        get.ip = get.ip
        import PluginLoader
        server_lists = self._get_server_lists_scan()
        for key in server_lists:
            get.mode = key
            PluginLoader.plugin_run('fail2ban', 'ban_ip_release', get)

        return public.returnMsg(True, public.lang("Unlocked successfully"))


# 端口防扫描 --- end111


class firewalld:
    __TREE = None
    __ROOT = None
    __CONF_FILE = '/etc/firewalld/zones/public.xml'

    # 初始化配置文件XML对象
    def __init__(self):
        if self.__TREE: return
        if not os.path.exists(self.__CONF_FILE): return
        self.__TREE = ElementTree()
        self.__TREE.parse(self.__CONF_FILE)
        self.__ROOT = self.__TREE.getroot()

    # 获取规则列表
    def GetAcceptPortList(self):
        try:
            mlist = self.__ROOT.getchildren()
        except:
            mlist = []
        data, arry = [], []

        if len(mlist) < 1:
            return data, arry

        data, arry = [], []
        for p in mlist:
            tmp = {}
            if p.tag == 'port':
                tmp["protocol"] = p.attrib['protocol']
                tmp['ports'] = p.attrib['port']
                tmp['types'] = 'accept'
                tmp['address'] = ''
            elif p.tag == 'forward-port':
                tmp["protocol"] = p.attrib['protocol']
                tmp["port"] = p.attrib['port']
                tmp["address"] = p.attrib.get('to-addr', '')
                tmp["to-port"] = p.attrib['to-port']
                arry.append(tmp)
                continue
            elif p.tag == 'rule':
                tmp["types"] = 'accept'
                tmp['ports'] = ''
                tmp['protocol'] = ''
                ch = p.getchildren()
                for c in ch:
                    if c.tag == 'port':
                        tmp['protocol'] = c.attrib['protocol']
                        tmp['ports'] = c.attrib['port']
                    elif c.tag == 'drop':
                        tmp['types'] = 'drop'
                    elif c.tag == 'reject':
                        tmp['types'] = 'reject'
                    elif c.tag == 'source':
                        if "address" in c.attrib.keys():
                            tmp['address'] = c.attrib['address']
                    if "address" not in tmp:
                        tmp['address'] = ''
            else:
                continue
            if tmp:
                data.append(tmp)
        return data, arry

    def recombine_rules(self):
        '''
        重组防火墙规则,将tcp和udp端口相同的规则合并111111
        @return:
        '''
        firewalld_rules = self.GetAcceptPortList()[0]
        tcp_rules = []
        udp_rules = []
        for rule in firewalld_rules:
            if rule['protocol'] == 'tcp':
                tcp_rules.append(rule)
            elif rule['protocol'] == 'udp':
                udp_rules.append(rule)

        result_rules = []

        for tcp_rule in tcp_rules:
            for udp_rule in udp_rules:
                if tcp_rule['ports'] == udp_rule['ports']:
                    if tcp_rule['types'] == udp_rule['types']:
                        if tcp_rule['address'] == udp_rule['address']:
                            if tcp_rule['protocol'] != udp_rule['protocol']:
                                tcp_rule['protocol'] = 'tcp/udp'
                                udp_rules.remove(udp_rule)
                                break
            result_rules.append(tcp_rule)
        result_rules.extend(udp_rules)
        return result_rules


class Sqlite():
    db_file = None  # 数据库文件
    connection = None  # 数据库连接对象

    def __init__(self):
        self.db_file = "/www/server/panel/data/default.db"
        self.create_table()

    # 获取数据库对象
    def GetConn(self):
        try:
            if self.connection == None:
                self.connection = sqlite3.connect(self.db_file)
                self.connection.text_factory = str
        except Exception as ex:
            import traceback
            traceback.print_exc()
            return "error: " + str(ex)

    def create_table(self):
        # 创建firewall_new表记录端口规则
        if not public.M('sqlite_master').where(
                'type=? AND name=?', ('table', 'firewall_new')).count():
            public.M('').execute('''CREATE TABLE "firewall_new" (
                "id" INTEGER PRIMARY KEY AUTOINCREMENT,
                "protocol" TEXT DEFAULT '',
                "ports" TEXT,
                "types" TEXT,
                "address" TEXT DEFAULT '',
                "brief" TEXT DEFAULT '',
                "addtime" TEXT DEFAULT '');''')
            public.M('').execute(
                'CREATE INDEX firewall_new_port ON firewall_new (ports);')

        if public.M('firewall_new').count() < 1:
            # 写入默认数据
            if not public.M('firewall_new').where('ports=?', ('80',)).count():
                public.M('firewall_new').add(
                    'ports,brief,addtime,protocol,types',
                    ('80', 'Website default port', '0000-00-00 00:00:00', 'tcp', 'accept')
                )
            if not public.M('firewall_new').where('ports=?', ('21',)).count():
                public.M('firewall_new').add(
                    'ports,brief,addtime,protocol,types',
                    ('21', 'FTP service', '0000-00-00 00:00:00', 'tcp', 'accept')
                )
            if not public.M('firewall_new').where('ports=?', ('22',)).count():
                public.M('firewall_new').add(
                    'ports,brief,addtime,protocol,types',
                    ('22', 'SSH remote service', '0000-00-00 00:00:00', 'tcp', 'accept')
                )
            try:
                _panel_port_file = '/www/server/panel/data/port.pl'
                panel_port = public.readFile(_panel_port_file).strip(" ").strip("\n")
            except Exception:
                panel_port = '8888'

            if not public.M('firewall_new').where('ports=?', (panel_port,)).count():
                public.M('firewall_new').add(
                    'ports,brief,addtime,protocol,types',
                    (panel_port, 'panel', '0000-00-00 00:00:00', 'tcp', 'accept')
                )

        # 创建firewall_ip表记录IP规则（屏蔽或放行）
        if not public.M('sqlite_master').where(
                'type=? AND name=?', ('table', 'firewall_ip')).count():
            public.M('').execute('''CREATE TABLE "firewall_ip" (
                "id" INTEGER PRIMARY KEY AUTOINCREMENT,
                "types" TEXT,
                "address" TEXT DEFAULT '',
                "brief" TEXT DEFAULT '',
                "addtime" TEXT DEFAULT '');''')
            public.M('').execute(
                'CREATE INDEX firewall_ip_addr ON firewall_ip (address);')

        # 创建firewall_trans表记录端口转发记录
        if not public.M('sqlite_master').where(
                'type=? AND name=?', ('table', 'firewall_trans')).count():
            public.M('').execute('''CREATE TABLE firewall_trans (
                "id" INTEGER PRIMARY KEY AUTOINCREMENT,
                "start_port" TEXT,
                "ended_ip" TEXT,
                "ended_port" TEXT,
                "protocol" TEXT DEFAULT '',
                "addtime" TEXT DEFAULT '');''')
            public.M('').execute(
                'CREATE INDEX firewall_trans_port ON firewall_trans (start_port);'
            )

        # 创建firewall_country表记录IP规则（屏蔽或放行）
        if not public.M('sqlite_master').where(
                'type=? AND name=?', ('table', 'firewall_country')).count():
            public.M('').execute('''CREATE TABLE "firewall_country" (
                "id" INTEGER PRIMARY KEY AUTOINCREMENT,
                "types" TEXT,
                "country" TEXT DEFAULT '',
                "brief" TEXT DEFAULT '',
                "addtime" TEXT DEFAULT '');''')
            public.M('').execute('CREATE INDEX firewall_country_name ON firewall_country (country);')

        # 创建firewall_domain表记录域名规则（屏蔽或放行）
        if not public.M('sqlite_master').where('type=? AND name=?', ('table', 'firewall_domain')).count():
            public.M('').execute('''CREATE TABLE "firewall_domain" (
                "id" INTEGER PRIMARY KEY AUTOINCREMENT,
                "types" TEXT,
                "domain" TEXT,
                "domain_total" TEXT,
                "port" TEXT,
                "sid" int DEFAULT 0,
                "address" TEXT DEFAULT '',
                "brief" TEXT DEFAULT '',
                "protocol" TEXT DEFAULT '',
                "addtime" TEXT DEFAULT '');''')
            public.M('').execute('CREATE INDEX firewall_domain_addr ON firewall_domain (domain);')

        # 修复之前已经创建的 firewall_domain 表无 domain_total 字段的问题
        create_table_str = public.M('firewall_new').table('sqlite_master').where(
            'type=? AND name=?', ('table', 'firewall_new')).getField('sql')
        if 'domain_total' not in create_table_str:
            public.M('firewall_new').execute('ALTER TABLE "firewall_domain" ADD "domain_total" TEXT DEFAULT ""')
        # 修复之前已经创建的 firewall_new 表无 domain 字段的问题
        create_table_str = public.M('firewall_new').table('sqlite_master').where(
            'type=? AND name=?', ('table', 'firewall_new')).getField('sql')
        if 'domain' not in create_table_str:
            public.M('firewall_new').execute('ALTER TABLE "firewall_new" ADD "domain" TEXT DEFAULT ""')
        if 'sid' not in create_table_str:
            public.M('firewall_new').execute('ALTER TABLE "firewall_new" ADD "sid"  int DEFAULT 0')
        # 修复之前已经创建的 firewall_ip 表无 domain 字段的问题
        create_table_str = public.M('firewall_ip').table('sqlite_master').where(
            'type=? AND name=?', ('table', 'firewall_ip')).getField('sql')
        if 'sid' not in create_table_str:
            public.M('firewall_ip').execute('ALTER TABLE "firewall_ip" ADD "sid"  int DEFAULT 0')
        if 'domain' not in create_table_str:
            public.M('firewall_ip').execute('ALTER TABLE "firewall_ip" ADD "domain" TEXT DEFAULT ""')

        # 修复之前已经创建的 firewall_country 表无 ports 字段的问题
        create_table_str = public.M('firewall_country').table('sqlite_master').where(
            'type=? AND name=?', ('table', 'firewall_country')).getField('sql')
        if 'ports' not in create_table_str:
            public.M('firewall_country').execute('ALTER TABLE "firewall_country" ADD "ports" TEXT DEFAULT ""')


    def create_trigger(self, sql):
        self.GetConn()
        self.connection.text_factory = str
        try:
            result = self.connection.execute(sql)
            id = result.lastrowid
            self.connection.commit()
            self.rm_lock()
            return id
        except Exception as ex:
            return "error: " + str(ex)


sql = """
        CREATE TRIGGER update_port AFTER DELETE ON firewall
        when old.port!=''
        BEGIN
            delete from firewall_new where ports = old.port;
            delete from firewall_ip where address = old.port;
        END;
      """
s = Sqlite()
s.create_trigger(sql)
