Files
kefu/im/server-fix-20260901/panelPlugin.py
T
2026-09-03 08:38:17 +08:00

4309 lines
174 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# coding: utf-8
# -------------------------------------------------------------------
# 宝塔Linux面板
# -------------------------------------------------------------------
# Copyright (c) 2015-2099 宝塔软件(http://bt.cn) All rights reserved.
# -------------------------------------------------------------------
# Author: hwliang <hwl@bt.cn>
# -------------------------------------------------------------------
import datetime
import traceback
import public, os, sys, json, time, psutil, re, shutil
try:
from BTPanel import session, cache, send_file
import requests
except:
pass
from pluginAuth import Plugin
from plugin_tool import SettingMager
if sys.version_info[0] == 3: from importlib import reload
from urllib.parse import urlparse, urlunparse
class mget:
pass
class panelPlugin:
__isTable = None
__install_path = None
__tasks = None
__list = 'data/list.json'
__type = 'data/type.json'
__index = 'config/index.json'
__link = 'config/link.json'
__product_list = None
__plugin_list = None
__exists_names = {}
__plugin_s_list = []
__panel_path = '/www/server/panel'
__plugin_info = None
__plugin_name = None
__plugin_object = None
__plugin_list = None
__panel_path = '/www/server/panel'
__plugin_path = __panel_path + '/plugin/'
__plugin_save_file = __panel_path + '/data/plugin_bin.pl'
__plugin_save_json_file = __panel_path + '/data/plugin_bin.json'
__api_root_url = 'https://api.bt.cn'
__api_url = __api_root_url + '/panel/get_plugin_list'
__download_url = __api_root_url + '/down/download_plugin'
__download_d_main_url = __api_root_url + '/down/download_plugin_main'
_check_url = __api_root_url + '/panel/get_soft_list_status'
_unbinding_url = __api_root_url + '/panel/get_unbinding'
__tmp_path = __panel_path + '/temp/'
__plugin_timeout = 3600
__is_php = False
__install_opt = 'i'
__pid = 0
__path_error = __panel_path + '/data/error_pl.pl'
__error_html = '/www/server/panel/BTPanel/templates/default/block_error.html'
__sub_rules = []
__dict__ = None
__replace_rule = []
pids = None
ROWS = 15
def __init__(self):
self.__install_path = '/www/server/panel/plugin'
self.__replace_rule = public.get_plugin_replace_rules()
if not self.__tasks:
try:
self.__tasks = public.M('tasks').where("status!=?", ('1',)).field('status,name').select()
except:
self.__tasks = []
self.__get_new_url()
def __get_new_url(self):
'''
@name 获取新的下载地址
@param url<string> 旧的下载地址
@return string
'''
self.__api_root_url = public.get_home_node('https://api.bt.cn')
self.__api_url = self.__api_root_url + '/panel/get_plugin_list'
self.__download_url = self.__api_root_url + '/down/download_plugin'
self.__download_d_main_url = self.__api_root_url + '/down/download_plugin_main'
self._check_url = self.__api_root_url + '/panel/get_soft_list_status'
self._unbinding_url = self.__api_root_url + '/panel/get_unbinding'
return True
def input_package(self, get):
'''
@name 导入插件包到面板
@author hwliang<2021-06-23>
@param filename<string> 解包后的文件路径
@param plugin_name<string> 插件名称
@param install_opt<string> 安装选项 i.安装 r.修复 u.升级 默认: i
@return dict
'''
get.exists(['tmp_path', 'plugin_name', 'install_opt'])
if not os.path.exists(get.tmp_path):
return public.returnMsg(False, '安装包临时文件不存在: {}'.format(get.tmp_path))
if not get.install_opt in ['r', 'i', 'u']:
return public.returnMsg(False, '安装选项[install_opt]错误,只能为 i.安装 r.修复 u.升级')
if not get.plugin_name:
return public.returnMsg(False, '插件名称不能为空!')
return self.__input_plugin(get.tmp_path, get.plugin_name, get.install_opt)
def __install_plugin(self, upgrade_plugin_name, upgrade_version=None):
'''
@name 安装指定插件
@author hwliang<2021-06-21>
@param upgrade_plugin_name<string> 插件名称
@param upgrade_version<string> 插件版本 版本号.指定版本号 / tls.最新正式版 / beta.最新测试版
@return dict
'''
self.__plugin_name = upgrade_plugin_name
plugin_info = self.__get_plugin_find(upgrade_plugin_name)
if not plugin_info:
raise public.PanelError('指定插件不存在,无法安装!')
if not plugin_info['versions']:
raise public.PanelError('指定插件当前未发布版本信息,请稍候再安装!')
if not upgrade_version:
upgrade_version = '{}.{}'.format(
plugin_info['versions'][0]['m_version'],
plugin_info['versions'][0]['version'])
filename = self.__download_plugin(upgrade_plugin_name, upgrade_version)
# 如果下载失败
if isinstance(filename, dict):
return filename
return self.__unpackup_plugin(filename)
def __repair_plugin(self, upgrade_plugin_name, upgrade_version=None):
'''
@name 修复指定插件
@author hwliang<2021-06-21>
@param upgrade_plugin_name<string> 插件名称
@param upgrade_version<string> 插件版本 版本号.指定版本号 / tls.最新正式版 / beta.最新测试版
@return dict
'''
self.__install_opt = 'r'
return self.__install_plugin(upgrade_plugin_name, upgrade_version)
def __upgrade_plugin(self, upgrade_plugin_name, upgrade_version=None):
'''
@name 升级到指定版本
@author hwliang<2021-06-21>
@param upgrade_plugin_name<string> 插件名称
@param upgrade_version<string> 插件版本 版本号.指定版本号 / tls.最新正式版 / beta.最新测试版
@return dict
'''
self.__install_opt = 'u'
return self.__install_plugin(upgrade_plugin_name, upgrade_version)
def __check_dependnet(self, upgrade_plugin_name):
'''
@name 检查指定插件的依赖安装情况
@author hwliang<2021-06-21>
@param upgrade_plugin_name<string> 插件名称
@return dict
'''
plugin_info = self.__get_plugin_find(upgrade_plugin_name)
if not plugin_info: return {}
if not plugin_info['dependnet']: return {}
deployment_list = {}
for dependnet_plu_name in plugin_info['dependnet'].split(','):
p_info = self.__get_plugin_find(dependnet_plu_name)
if not p_info: continue
deployment_list[dependnet_plu_name] = os.path.exists(
p_info['install_checks'])
return deployment_list
def __get_plugin_info(self, upgrade_plugin_name):
'''
@name 获取插件信息
@author hwliang<2021-06-15>
@param upgrade_plugin_name<string> 插件名称
@return dict
'''
plugin_info_file = '{}/{}/info.json'.format(self.__plugin_path,
upgrade_plugin_name)
if not os.path.exists(plugin_info_file): return {}
info_body = self.__read_file(plugin_info_file)
if not info_body: return {}
plugin_info = json.loads(info_body)
return plugin_info
def __get_update_msg(self, upgrade_plugin_name, upgrade_version):
'''
@name 检查指定插件版本更新日志
@author hwliang<2021-06-21>
@param upgrade_plugin_name<string> 插件名称
@param upgrade_version<string> 插件版本
@return string
'''
plugin_update_msg = ''
plugin_info = self.__get_plugin_find(upgrade_plugin_name)
if not plugin_info: return plugin_update_msg
for _version_info in plugin_info['versions']:
l_version = '{}.{}'.format(_version_info['m_version'],
_version_info['version'])
if l_version == upgrade_version:
plugin_update_msg = _version_info['update_msg']
break
return plugin_update_msg
def __get_plugin_upgrades(self, upgrade_plugin_name):
'''
@name 检查指定插件最近10条更新日志
@author hwliang<2021-06-21>
@param upgrade_plugin_name<string> 插件名称
@return list
'''
plugin_info = self.__get_plugin_find(upgrade_plugin_name)
if not plugin_info: return []
try:
upgrade_list = public.httpPost(
self.__api_root_url + '/down/get_update_msg',
{'soft_id': plugin_info['id']})
return json.loads(upgrade_list)
except:
return []
def __set_pyenv(self, filename):
'''
@name 设置安全脚本的Python环境变量
@param filename<string> 安装脚本文件名
@return bool
'''
if not os.path.exists(filename): return False
env_py = self.__panel_path + '/pyenv/bin'
if not os.path.exists(env_py): return False
temp_file = public.readFile(filename)
env_path = [
'PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin'
]
rep_path = [
'PATH={}/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin'
.format(env_py + ":")
]
for index_key in range(len(env_path)):
temp_file = temp_file.replace(env_path[index_key],
rep_path[index_key])
public.writeFile(filename, temp_file)
return True
def __copy_path(self, src_path, dst_path, input_not_substituted=[]):
'''
@name 复制文件夹
@author hwliang<2021-06-24>
@param src_path<string> 源路径
@param dst_path<string> 目标路径
@param input_not_substituted<list> 不复盖规则
@return bool
'''
if not os.path.exists(src_path):
raise public.PanelError('指定源目录不存在:{}'.format(src_path))
if not os.path.exists(dst_path):
os.makedirs(dst_path, 384)
for tmp_list_name in os.listdir(src_path):
tmp_src_path = os.path.join(src_path, tmp_list_name)
tmp_dst_path = os.path.join(dst_path, tmp_list_name)
# 目标文件存在,且被不覆盖规则匹配,则跳过此文件
if os.path.exists(tmp_dst_path):
if self.__sub_check(tmp_src_path, input_not_substituted):
continue
# 递归目录
if os.path.isdir(tmp_src_path):
self.__copy_path(tmp_src_path, tmp_dst_path,
input_not_substituted)
continue
# 复制文件
shutil.copyfile(tmp_src_path, tmp_dst_path)
self.__replace_check(tmp_dst_path)
return True
def __replace_check(self, filename):
'''
@name 检查文件内容是否需要替换
@author hwliang<2021-06-28>
@param filename<string> 文件全路径
@return void
'''
# 检查前置替换关系
rkey = 'replace_files'
if not rkey in self.__plugin_info: return
if not self.__plugin_info[rkey]: return
if not self.__replace_rule: return
# 指定文件名是否需要替换
p_file_name = os.path.basename(filename)
if not p_file_name in self.__plugin_info[rkey]: return
# 开始替换文件内容
f_body = public.readFile(filename)
is_write = False
for temp_i_rule in self.__replace_rule:
if f_body.find(temp_i_rule['find']) == -1: continue
f_body = f_body.replace(temp_i_rule['find'],
temp_i_rule['replace'])
is_write = True
# 是否需要写入数据
if is_write: public.writeFile(filename, f_body)
def __sub_check(self, filename, input_not_substituted):
'''
@name 不覆盖规则检查
@author hwliang<2021-06-24>
@param filename<string> 文件或文件夹名称
@param input_not_substituted<list> 不复盖规则
@return bool
'''
is_file = os.path.isfile(filename)
# 不匹配全路径
f_i_name = os.path.basename(filename)
for temp_i_rule in self.__format_sub_rule(input_not_substituted):
if temp_i_rule['fd'] == 'd' and is_file: continue
if temp_i_rule['fd'] == 'f' and not is_file: continue
# 完全匹配?
if temp_i_rule['type'] == 'find':
if f_i_name == temp_i_rule['rule']:
return True
# 正则表达式?
elif temp_i_rule['type'] == 're':
if temp_i_rule['rule'].search(f_i_name):
return True
return False
def __format_sub_rule(self, input_not_substituted):
'''
@name 解析覆盖规则
@author hwliang<2021-06-24>
@param input_not_substituted<list> 不复盖规则
@return list
'''
if self.__sub_rules:
return self.__sub_rules
self.__sub_rules = []
for item_sub_rule in input_not_substituted:
temp_i_rule = {}
f_sub_2 = item_sub_rule[-2:]
_type_fd = '' if f_sub_2[0] != '|' else f_sub_2[1]
temp_i_rule['fd'] = _type_fd
if item_sub_rule[:3] == 're|':
temp_i_rule['type'] = 're'
if _type_fd:
item_re_string = item_sub_rule[3:-2]
else:
item_re_string = item_sub_rule[3:]
temp_i_rule['rule'] = re.compile(item_re_string)
else:
temp_i_rule['type'] = 'find'
if _type_fd:
temp_i_rule['rule'] = item_sub_rule[:-2]
else:
temp_i_rule['rule'] = item_sub_rule
self.__sub_rules.append(temp_i_rule)
return self.__sub_rules
def __read_file(self, filename, open_mode='r'):
'''
@name 读取指定文件
@author hwliang<2021-06-16>
@param filename<string> 文件名
@param mode<string> 打开模式, 默认: r
@return bytes or string
'''
f_object = open(filename, mode=open_mode)
file_body = f_object.read()
f_object.close()
return file_body
def __input_plugin(self,
filename,
input_plugin_name,
input_install_opt='i'):
'''
@name 导入插件包到面板
@author hwliang<2021-06-21>
@param filename<string> 解包后的文件路径
@param input_plugin_name<string> 插件名称
@param input_install_opt<string> 安装选项 i.安装 r.修复 u.升级 默认: i
@return dict
'''
if public.is_debug():
mod_key = input_plugin_name + '_main'
if mod_key in sys.modules:
return public.returnMsg(False, '当前插件正在被使用,请重启面板后重试')
opts = {'i': '安装', 'u': '更新', 'r': '修复'}
i_opts = {
'i': 'install.sh install',
'u': 'upgrade.sh',
'r': 'repair.sh'
}
if not os.path.exists(filename):
return public.returnMsg(False, '文件校验失败,请重新安装此插件!')
plugin_path_panel = self.__plugin_path + input_plugin_name
if input_install_opt == 'r' and os.path.exists(
filename + '/' + i_opts[input_install_opt]):
public.ExecShell(f"echo \"repair\" > {filename}/repair.pl")
i_opts[input_install_opt] = 'install.sh install'
if input_install_opt == 'u' and os.path.exists(
filename + '/' + i_opts[input_install_opt]):
public.ExecShell(f"echo \"upgrade\" > {filename}/upgrade.pl")
i_opts[input_install_opt] = 'install.sh install'
if not os.path.exists(plugin_path_panel):
os.makedirs(plugin_path_panel)
p_info = public.ReadFile(filename + '/info.json')
if not p_info: raise public.PanelError(filename)
p_info = json.loads(p_info)
if not 'not_substituted' in p_info: p_info['not_substituted'] = []
self.__plugin_info = p_info
self.__copy_path(filename, plugin_path_panel,
p_info['not_substituted'])
self.__set_pyenv(plugin_path_panel + '/install.sh')
log_file = '/tmp/panelShell.pl'
if os.path.exists(log_file): os.remove(log_file)
public.stop_syssafe()
public.ExecShell('cd ' + plugin_path_panel + ' && bash {} &> /tmp/panelShell.pl'.format(i_opts[input_install_opt]))
public.start_syssafe()
# 清理临时文件
if os.path.exists(filename): shutil.rmtree(filename)
if p_info and os.path.exists(plugin_path_panel):
# ------ 2023-07-31 cjx 增加svg图标支持
# 复制图标
for ico_file in ['svg', 'png']:
icon_sfile = plugin_path_panel + '/icon.' + ico_file
icon_dfile = self.__panel_path + '/BTPanel/static/img/soft_ico/ico-{}.{}'.format(
input_plugin_name, ico_file)
if os.path.exists(plugin_path_panel + '/icon.' + ico_file):
shutil.copyfile(icon_sfile, icon_dfile)
public.WriteLog('软件管理', '{}插件[{}]'.format(opts[input_install_opt], p_info['title']))
# 标记一次重新加载插件
reload_file = os.path.join(self.__panel_path, 'data/{}.pl'.format(input_plugin_name))
public.writeFile(reload_file, '')
pluginInfo = self.__get_plugin_find(input_plugin_name)
public.run_thread(
public.httpPost,
(public.GetConfigValue('home') + '/api/panel/plugin_total', {
"pid": pluginInfo['id'],
'p_name': input_plugin_name
}, 10)) # 线程
# public.httpPost(public.GetConfigValue('home') + '/api/panel/plugin_total',{"pid":pluginInfo['id'],'p_name':input_plugin_name},3)
if os.path.exists(log_file): os.remove(log_file)
return public.returnMsg(True,
'{}成功!'.format(opts[input_install_opt]))
# 安装失败清理安装文件?
if os.path.exists(plugin_path_panel): shutil.rmtree(plugin_path_panel)
err_msg = public.GetNumLines(log_file, 30)
if os.path.exists(log_file): os.remove(log_file)
return public.returnMsg(
False, '{}失败: <pre>{}</pre>'.format(opts[input_install_opt],
err_msg))
def __unpackup_plugin(self, tmp_file):
'''
@name 解包插件包
@author hwliang<2021-06-21>
@param tmp_file<string> 下载好的保存路径,从self.download_plugin方法中获取
@return dict
'''
if type(tmp_file) == dict:
return tmp_file
if "false" in tmp_file or "错误" in tmp_file:
return json.loads(tmp_file)
s_tmp_path = self.__tmp_path
if not os.path.exists(s_tmp_path):
os.makedirs(s_tmp_path, mode=384)
if tmp_file:
if not os.path.exists(tmp_file):
return public.returnMsg(False, '文件下载失败!')
import panelTask as plu_panelTask
plu_panelTask.bt_task()._unzip(tmp_file, s_tmp_path, '',
'/dev/null')
if os.path.exists(tmp_file):
os.remove(tmp_file)
s_tmp_path = os.path.join(s_tmp_path, self.__plugin_name)
# 替换安装脚本里的下载节点
self.replace_download_url(s_tmp_path)
p_info = os.path.join(s_tmp_path, 'info.json')
if not os.path.exists(p_info):
d_path = None
for plugin_df in os.walk(s_tmp_path):
if len(plugin_df[2]) < 3: continue
if not 'info.json' in plugin_df[2]: continue
if not 'install.sh' in plugin_df[2]: continue
if not os.path.exists(plugin_df[0] + '/info.json'): continue
d_path = plugin_df[0]
if d_path:
s_tmp_path = d_path
p_info = s_tmp_path + '/info.json'
try:
try:
plugin_data_info = json.loads(public.ReadFile(p_info))
except:
plugin_data_info = json.loads(self.__read_file(p_info))
plugin_data_info['size'] = public.get_path_size(s_tmp_path)
if not 'author' in plugin_data_info:
plugin_data_info['author'] = '宝塔'
if not 'home' in plugin_data_info:
plugin_data_info['home'] = 'https://www.bt.cn'
p_info_file = self.__plugin_path + plugin_data_info[
'name'] + '/info.json'
plugin_data_info['old_version'] = '0'
plugin_data_info['tmp_path'] = s_tmp_path
if os.path.exists(p_info_file):
try:
old_info = json.loads(public.ReadFile(p_info_file))
plugin_data_info['old_version'] = old_info['versions']
except:
pass
except:
public.ExecShell("rm -rf " + s_tmp_path + '/*')
return public.get_error_object(plugin_name=self.__plugin_name)
plugin_data_info['install_opt'] = self.__install_opt
plugin_data_info['dependnet'] = self.__check_dependnet(
plugin_data_info['name'])
plugin_data_info['update_msg'] = self.__get_update_msg(
plugin_data_info['name'], plugin_data_info['versions'])
not_check = self.not_cpu_or_bit(plugin_data_info)
if not_check:
if os.path.exists(s_tmp_path): shutil.rmtree(s_tmp_path)
return not_check
return plugin_data_info
def replace_download_url(self, path):
'''
@name 替换下载地址为新的下载地址
@param path<string> 旧的下载地址
@return string
'''
try:
for sfile in ['install.sh', 'upgrade.sh', 'repair.sh']:
if not os.path.exists(path + '/' + sfile): continue
sbody = public.readFile(path + '/' + sfile)
if not sbody: continue
url = 'https://download.bt.cn'
new_domain = urlparse(public.get_home_node(url)).netloc
sbody = sbody.replace(urlparse(url).netloc, new_domain)
public.writeFile(path + '/' + sfile, sbody)
except:
pass
return True
def not_cpu_or_bit(self, plugin_data_info):
'''
@name 检测是否为不支持的平台和系统位数
@author hwliang<2021-07-07>
@param plugin_data_info<dict> 插件信息数据
@return dict or None
'''
if 'not_os_bit' in plugin_data_info:
if public.get_sysbit() == int(plugin_data_info['not_os_bit']):
return public.returnMsg(
False,
'该应用不支持{}位系统'.format(plugin_data_info['not_os_bit']))
if 'not_cpu_type' in plugin_data_info:
if not plugin_data_info['not_cpu_type']: return None
machine = os.uname().machine
for c_type in plugin_data_info['not_cpu_type']:
c_type = c_type.lower()
result = public.returnMsg(
False, '该应用不支持{}平台,{}'.format(c_type, machine))
if c_type in ['arm', 'aarch64', 'aarch']:
if machine in ['aarch64', 'aarch']:
return result
elif c_type in ['mips', 'mips64', 'mips64el']:
if machine.find('mips') != -1:
return result
elif c_type in ['x86', 'x86-64']:
if machine in ['x86', 'x86-64']:
return result
return None
def __download_plugin(self, upgrade_plugin_name, upgrade_version):
'''
@name 下载插件包
@author hwliang<2021-06-21>
@param upgrade_plugin_name<string> 插件名称
@param upgrade_version<string> 插件版本
@return string 保存路径
'''
pkey = '{}_pre'.format(upgrade_plugin_name)
pdata = public.get_user_info()
pdata['name'] = upgrade_plugin_name
pdata['version'] = upgrade_version
pdata['os'] = 'Linux'
filename = '{}/{}.zip'.format(self.__tmp_path, upgrade_plugin_name)
if not os.path.exists(self.__tmp_path):
os.makedirs(self.__tmp_path, 384)
if not cache.get(pkey):
import config, socket
import requests.packages.urllib3.util.connection as urllib3_conn
_ip_type = config.config().get_request_iptype()
# old_family = urllib3_conn.allowed_gai_family
if _ip_type == 'ipv4':
urllib3_conn.allowed_gai_family = lambda: socket.AF_INET
elif _ip_type == 'ipv6':
urllib3_conn.allowed_gai_family = lambda: socket.AF_INET6
try:
download_res = requests.post(
self.__download_url,
pdata,
headers=public.get_requests_headers(),
timeout=(60, 1800),
stream=True)
except Exception as ex:
str_ex = str(ex)
if 'Name or service not known' in str_ex:
return public.returnMsg(False, '下载软件包时DNS解析失败,请检查服务器网络配置是否正常: <p>Error: Name or service not known</p>')
elif 'Failed to establish a new connection' in str_ex:
return public.returnMsg(False, '连接下载节点失败,请尝试到【面板设置】页面切换通讯节点,或检查服务器网络是否正常: <p>Error: Failed to establish a new connection</p>')
elif 'Read timed out' in str_ex:
return public.returnMsg(False, '连接下载节点失败,请尝试到【面板设置】页面切换通讯节点,或检查服务器网络是否正常: <p>Error: Read timed out</p>')
elif 'Connection refused' in str_ex:
return public.returnMsg(False, '连接下载节点失败,请尝试到【面板设置】页面切换通讯节点,或检查服务器网络是否正常: <p>Error: Connection refused</p>')
elif 'Remote end closed connection without response' in str_ex:
return public.returnMsg(False, '连接下载节点失败,请尝试到【面板设置】页面切换通讯节点,或检查服务器网络是否正常: <p>Error: Remote end closed connection without response</p>')
else:
return public.returnMsg(False, '连接下载节点失败,请尝试到【面板设置】页面切换通讯节点,或检查服务器网络是否正常: <p>Error: {}</p>'.format(str_ex))
finally:
public.reset_allowed_gai_family()
try:
headers_total_size = int(download_res.headers['File-size'])
except:
try:
if json.loads(download_res.text):
return download_res.json()
except:
if download_res.text.find('<html>') != -1:
raise public.PanelError(
public.error_conn_cloud(download_res.text))
raise public.PanelError(download_res.text)
res_down_size = 0
res_chunk_size = 8192
last_time = time.time()
with open(filename, 'wb+') as with_res_f:
try:
for download_chunk in download_res.iter_content(
chunk_size=res_chunk_size):
if download_chunk:
with_res_f.write(download_chunk)
speed_last_size = len(download_chunk)
res_down_size += speed_last_size
res_start_time = time.time()
res_timeout = (res_start_time - last_time)
res_sec_speed = int(res_down_size / res_timeout)
pre_text = '{}/{}/{}'.format(res_down_size,
headers_total_size,
res_sec_speed)
cache.set(pkey, pre_text, 3600)
except Exception as ex:
ex_str = str(ex)
if "Read timed out" in ex_str:
return public.returnMsg(False, '软件包下载超时,请重试: {}'.format(ex_str))
if "No space left on device" in ex_str:
return public.returnMsg(False, "磁盘空间不足,请先清理后再试!")
finally:
with_res_f.close()
if cache.get(pkey): cache.delete(pkey)
if public.FileMd5(filename) != download_res.headers['Content-md5']:
return public.returnMsg(False, '软件包校验失败,请更新软件列表,并重试')
else:
while True:
time.sleep(1)
if not cache.get(pkey): break
return ''
return filename
def __get_plugin_find(self, upgrade_plugin_name=None):
'''
@name 获取指定软件信息
@author hwliang<2021-06-15>
@param upgrade_plugin_name<string> 插件名称
@return dict
'''
if not self.__plugin_object: self.__plugin_object = Plugin(False)
if not self.__plugin_list:
self.__plugin_list = self.__plugin_object.get_plugin_list()
for p_data_info in self.__plugin_list['list']:
if p_data_info['name'] == upgrade_plugin_name:
upgrade_plugin_name = p_data_info['name']
return p_data_info
# 如果不在插件列表中
return self.__get_plugin_info(upgrade_plugin_name)
def __download_main(self, upgrade_plugin_name, upgrade_version):
'''
@name 下载插件主程序文件
@author hwliang<2021-06-25>
@param upgrade_plugin_name<string> 插件名称
@param upgrade_version<string> 插件版本
@return void
'''
pdata = public.get_user_info()
pdata['name'] = upgrade_plugin_name
pdata['version'] = upgrade_version
pdata['os'] = 'Linux'
download_res = requests.post(self.__download_d_main_url,
pdata,
timeout=30)
filename = '{}/{}.py'.format(self.__tmp_path, upgrade_plugin_name)
with open(filename, 'wb+') as save_script_f:
save_script_f.write(download_res.content)
save_script_f.close()
if public.md5(
download_res.content) != download_res.headers['Content-md5']:
raise public.PanelError('插件安装包HASH校验失败')
dst_file = '{plugin_path}/{plugin_name}/{plugin_name}_main.py'.format(
plugin_path=self.__plugin_path, plugin_name=upgrade_plugin_name)
shutil.copyfile(filename, dst_file)
if os.path.exists(filename): os.remove(filename)
public.WriteLog(
'软件管理', "检测到插件[{}]程序文件异常,已尝试自动修复!".format(
self.__get_plugin_info(upgrade_plugin_name)['title']))
def __get_download_speed(self, upgrade_plugin_name):
'''
@name 取插件下载进度
@author hwliang<2021-06-21>
@param upgrade_plugin_name<string> 插件名称
@return dict
'''
pkey = '{}_pre'.format(upgrade_plugin_name)
pre_text = cache.get(pkey)
if not pre_text:
return public.returnMsg(False, '指定进度信息不存在!')
result = {"status": True}
pre_tmp = pre_text.split('/')
result['down_size'], result['total_size'] = (int(pre_tmp[0]),
int(pre_tmp[1]))
result['down_pre'] = round(
result['down_size'] / result['total_size'] * 100, 1)
result['sec_speed'] = int(float(pre_tmp[2]))
result['need_time'] = int(
(result['total_size'] - result['down_size']) / result['sec_speed'])
return result
def close_install(self, get):
'''
@name 取消指定插件安装过程
@author hwliang<2021-07-07>
@param plugin_name<string> 插件名称
@return void
'''
plugin_name = get.plugin_name.strip()
tmp_path = '{}/{}'.format(self.__tmp_path, plugin_name)
if os.path.exists(tmp_path): shutil.rmtree(tmp_path)
return public.returnMsg(False, '安装过程已取消!')
# 检查依赖
def check_deps(self, get):
cacheKey = 'plugin_lib_list'
if not 'force' in get:
libList = cache.get(cacheKey)
if libList: return libList
libList = json.loads(public.readFile('config/lib.json'))
centos = os.path.exists('/bin/yum')
for key in libList.keys():
for i in range(len(libList[key])):
checks = libList[key][i]['check'].split(',')
libList[key][i]['status'] = False
for check in checks:
if os.path.exists(check):
libList[key][i]['status'] = True
break
libList[key][i]['version'] = "-"
if libList[key][i]['status']:
shellTmp = libList[key][i]['getv'].split(':D')
shellEx = shellTmp[0]
if len(shellTmp) > 1 and not centos: shellEx = shellTmp[1]
libList[key][i]['version'] = public.ExecShell(
shellEx)[0].strip()
cache.set(cacheKey, libList, 86400)
return libList
# 检查互斥
def check_mutex(self, mutex):
if mutex == -1: return True
mutexs = mutex.split(',')
for name in mutexs:
pluginInfo = self.get_soft_find(name)
if not pluginInfo: continue
if pluginInfo['setup'] == True:
self.mutex_title = pluginInfo['title']
return False
return True
# 检查依赖
def check_dependnet(self, dependnet):
if not dependnet: return True
dependnets = dependnet.split(',')
status = True
for dep in dependnets:
if not dep: continue
if dep.find('|') != -1:
names = dep.split('|')
for name in names:
pluginInfo = self.get_soft_find(name)
if not pluginInfo: return True
if pluginInfo['setup'] == True:
status = True
break
else:
status = False
else:
pluginInfo = self.get_soft_find(dep)
if not pluginInfo: return True
if pluginInfo['setup'] != True:
status = False
break
return status
# 检查CPU限制
def check_cpu_limit(self, cpuLimit):
if psutil.cpu_count() < cpuLimit: return False
return True
# 检查内存限制
def check_mem_limit(self, memLimit):
if psutil.virtual_memory().total / 1024 / 1024 < memLimit: return False
return True
# 检查操作系统限制
def check_os_limit(self, osLimit):
if osLimit == 0: return True
if osLimit == 1:
centos = os.path.exists('/usr/bin/yum')
return centos
elif osLimit == 2:
debian = os.path.exists('/usr/bin/apt-get')
return debian
return True
# 获取插件title
def get_dep_title(self, dependnet):
if not dependnet: return dependnet
dependnets = dependnet.split(',')
title_list = []
for dep in dependnets:
if not dep: continue
if dep.find('|') != -1:
dep_or_list = []
for name in dep.split('|'):
pluginInfo = self.get_soft_find(name)
if not isinstance(pluginInfo, dict) or not 'title' in pluginInfo:
dep_or_list.append(name)
else:
dep_or_list.append(pluginInfo['title'])
title_list.append('|'.join(dep_or_list))
else:
pluginInfo = self.get_soft_find(dep)
if not isinstance(pluginInfo, dict) or not 'title' in pluginInfo:
title_list.append(dep)
else:
title_list.append(pluginInfo['title'])
return ','.join(title_list) or dependnet
# 安装插件
def install_plugin(self, get):
if not public.check_sys_write():
return public.returnCode(
False,
'<a style="color:red;">错误:检测到系统关键目录不可写!</a><br>1、如果安装了[宝塔系统加固],请先关闭<br>2、如果安装了云锁,请关闭[系统加固]功能<br>3、如果安装了安全狗,请关闭[系统防护]功能<br>4、如果使用了其它安全软件,请先卸载<br>'
, -1)
if not 'sName' in get: return public.returnMsg(False, '请指定软件名称!')
pluginInfo = self.get_soft_find(get.sName)
p_node = '/www/server/panel/install/public.sh'
if os.path.exists(p_node):
if len(public.readFile(p_node)) < 100: os.remove(p_node)
if not pluginInfo: return public.returnMsg(False, '指定插件不存在!')
self.mutex_title = pluginInfo['mutex']
if not self.check_mutex(pluginInfo['mutex']):
return public.returnMsg(False, '请到[软件商店]先卸载[%s]' % self.mutex_title)
if not hasattr(get, 'id'):
if not self.check_dependnet(pluginInfo['dependnet']):
return public.returnMsg(
False, '依赖以下软件,请到[软件商店]先安装[%s]' % self.get_dep_title(pluginInfo['dependnet']))
if 'version' in get:
for versionInfo in pluginInfo['versions']:
if versionInfo['m_version'] != get.version or versionInfo['version'] != get.min_version: continue
if not 'type' in get: get.type = '0'
if not 'force' in get: get.force = "false"
get.force = str(get.force).lower()
if int(get.type) > 4: get.type = '0'
if get.type == '0' and get.force == "false":
limit_list = []
if not self.check_cpu_limit(versionInfo['cpu_limit']):
limit_list.append('[{}]个CPU核心'.format(versionInfo['cpu_limit']))
if not self.check_mem_limit(versionInfo['mem_limit']):
limit_list.append('[{}MB]内存'.format(versionInfo['mem_limit']))
if len(limit_list) > 0:
err_msg = '至少需要' + '、'.join(limit_list) + '才能安装'
return public.returnCode(False, err_msg, -1)
if not self.check_os_limit(versionInfo['os_limit']):
m_ps = {0: "所有的", 1: "Centos", 2: "Ubuntu/Debian"}
return public.returnMsg(
False,
'仅支持[%s]系统' % m_ps[int(versionInfo['os_limit'])])
if not hasattr(get, 'id'):
if not self.check_dependnet(versionInfo['dependnet']):
return public.returnMsg(
False,
'依赖以下软件,请到[软件商店]先安装[%s]' % self.get_dep_title(versionInfo['dependnet']))
if pluginInfo['type'] != 5:
result = self.install_sync(pluginInfo, get)
else:
result = self.install_async(pluginInfo, get)
try:
if 'status' in result:
if result['status']:
public.run_thread(public.httpPost,
(public.GetConfigValue('home') +
'/api/panel/plugin_total', {
"pid": pluginInfo['id'],
'p_name': pluginInfo['name']
}, 3)) # 走线程
# public.httpPost(public.GetConfigValue('home') + '/api/panel/plugin_total',{"pid":pluginInfo['id'],'p_name':pluginInfo['name']},3)
except:
pass
return result
# 同步安装
def install_sync(self, pluginInfo, get):
if 'download' in pluginInfo['versions'][0]:
tmp_path = '/www/server/panel/temp'
if not os.path.exists(tmp_path): os.makedirs(tmp_path, mode=384)
public.ExecShell("rm -rf " + tmp_path + '/*')
toFile = tmp_path + '/' + pluginInfo['name'] + '.zip'
public.downloadFile('https://www.bt.cn/api/Pluginother/get_file?fname=' +
pluginInfo['versions'][0]['download'], toFile)
if public.FileMd5(toFile) != pluginInfo['versions'][0]['md5']:
try:
return json.loads(public.readFile(toFile))
except:
return public.returnMsg(False, '文件Hash校验失败,停止安装!')
update = False
if os.path.exists(pluginInfo['install_checks']):
update = pluginInfo['versions'][0]['version_msg']
return self.update_zip(None, toFile, update)
else:
# download_url = public.get_url() + '/install/plugin/' + pluginInfo['name'] + '/install.sh'
# toFile = '/tmp/%s.sh' % pluginInfo['name']
# public.downloadFile(download_url,toFile)
# self.set_pyenv(toFile)
# public.ExecShell('/bin/bash ' + toFile + ' install &> /tmp/panelShell.pl')
# if os.path.exists(pluginInfo['install_checks']):
# public.WriteLog('TYPE_SETUP','PLUGIN_INSTALL_LIB',(pluginInfo['title'],))
# if os.path.exists(toFile): os.remove(toFile)
# return public.returnMsg(True,'PLUGIN_INSTALL_SUCCESS')
# return public.returnMsg(False,'安装失败!')
if hasattr(get, 'min_version'):
get.version += '.' + get.min_version
return self.__install_plugin(pluginInfo['name'], get.version)
# 修复插件
def repair_plugin(self, get):
'''
@name 修复指定插件
@param plugin_name<string> 插件名称
@param version<string> 版本号
@param min_version<string> 子版本号
@return mixed
'''
if hasattr(get, 'min_version'):
get.version += '.' + get.min_version
return self.__repair_plugin(get.plugin_name, get.version)
# 更新插件
def upgrade_plugin(self, get):
'''
@name 更新指定插件/切换到指定版本
@param plugin_name<string> 插件名称
@param version<string> 版本号
@param min_version<string> 子版本号
@return mixed
'''
if hasattr(get, 'min_version'):
get.version += '.' + get.min_version
return self.__upgrade_plugin(get.plugin_name, get.version)
# 设置Python环境变量
def set_pyenv(self, filename):
if not os.path.exists(filename): return False
env_py = '/www/server/panel/pyenv/bin'
if not os.path.exists(env_py): return False
temp_file = public.readFile(filename)
env_path = [
'PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin'
]
rep_path = [
'PATH={}/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin'
.format(env_py + ":")
]
for i in range(len(env_path)):
temp_file = temp_file.replace(env_path[i], rep_path[i])
public.writeFile(filename, temp_file)
return True
def get_download_speed(self, get):
'''
@name 获取插件下载进度
@author hwliang<2021-06-25>
@param plugin_name<string> 插件名称
@return dict
'''
result = self.__get_download_speed(get.plugin_name)
return result
# 异步安装
def install_async(self, pluginInfo, get):
mtype = 'install'
mmsg = '安装'
if hasattr(get, 'upgrade'):
mtype = 'update'
mmsg = 'upgrade'
else:
if get.sName == 'mysql':
if os.path.exists('/www/server/mysql/bin'):
return public.returnMsg(False, '已安装MySQL数据库,请先卸载!')
if not 'type' in get: get.type = '0'
if int(get.type) > 4: get.type = '0'
if get.sName == 'nginx':
if get.version == '1.8':
return public.returnMsg(False,
'Nginx 1.8.1版本过旧,不再提供支持,请选择其它版本!')
if get.sName.find('php-') != -1: get.sName = get.sName.split('-')[0]
ols_execstr = ""
if "php" == get.sName and os.path.exists(
'/usr/local/lsws/bin/lswsctrl'):
ols_sName = 'php-ols'
ols_version = get.version.replace('.', '')
ols_execstr = " &> /tmp/panelExec.log && /bin/bash install_soft.sh {} {} " + ols_sName + " " + ols_version
php_path = '/www/server/php'
if not os.path.exists(php_path): os.makedirs(php_path)
apacheVersion = 'false'
if public.get_webserver() == 'apache':
apacheVersion = public.xss_version(
public.readFile('/www/server/apache/version.pl'))
public.writeFile('/var/bt_apacheVersion.pl', apacheVersion)
public.writeFile('/var/bt_setupPath.conf', '/www')
if os.path.exists('/usr/bin/apt-get'):
if get.type == '0':
get.type = '3'
else:
get.type = '4'
if ols_execstr:
ols_execstr = ols_execstr.format(get.type, mtype)
# alisql 版本号转小写
if get.version in ('AliSQL',):
get.version = get.version.lower()
execstr = "cd /www/server/panel/install && /bin/bash install_soft.sh {} {} {} {} {}".format(
get.type, mtype, get.sName, get.version, ols_execstr)
if get.sName == "phpmyadmin":
execstr += "&> /tmp/panelExec.log"
if public.get_webserver() == 'openlitespeed':
execstr += " && sleep 1 && /usr/local/lsws/bin/lswsctrl restart"
# execstr += " && echo '>>命令执行完成!'"
if not os.path.exists("/tmp/panelTask.pl"): # 如果当前任务队列并未执行,就把日志清空
public.writeFile('/tmp/panelExec.log', '')
lib_msg = self.check_install_lib(get.type)
task_id = public.M('tasks').add(
'id,name,type,status,addtime,execstr',
(None, mmsg + '[' + get.sName + '-' + get.version + ']',
'execshell', '0', time.strftime('%Y-%m-%d %H:%M:%S'), execstr))
msg = self.create_install_wait_msg(task_id, get.sName + '-' + get.version, is_update=(mmsg == 'upgrade'))
if lib_msg is not None: # 环境安装先于软件安装
msg = lib_msg
cache.delete('install_task')
public.writeFile('/tmp/panelTask.pl', 'True')
public.WriteLog('TYPE_SETUP', 'PLUGIN_ADD', (get.sName, get.version))
if not public.M('tasks').where("status=?", ('-1',)).count():
tip_file = '/dev/shm/.panelTask.pl'
tip_time = public.readFile(tip_file)
if not tip_time or time.time() - int(tip_time) > 600:
public.ExecShell("/www/server/panel/BT-Task")
return {
"status": True,
"msg_id": None if msg is None else msg.id,
"msg": '已将安装任务添加到队列!'
}
# return public.returnMsg(True, )
# 卸载插件
def uninstall_plugin(self, get):
pluginInfo = self.get_soft_find(get.sName)
if not pluginInfo: return public.returnMsg(False, '指定插件不存在!')
if not public.check_sys_write():
return public.returnCode(
False,
'<a style="color:red;">错误:检测到系统关键目录不可写!</a><br>1、如果安装了[宝塔系统加固],请先关闭<br>2、如果安装了云锁,请关闭[系统加固]功能<br>3、如果安装了安全狗,请关闭[系统防护]功能<br>4、如果使用了其它安全软件,请先卸载<br>'
, -1)
if pluginInfo['type'] != 5:
# 是否有卸载前置检查 -- START
'''
在插件对象中,增加uninstall_check方法,用于卸载前置检查
返回值:dict status:True/False msg:提示信息
示例:
def uninstall_check(self,get=None):
# 检查逻辑
status = False # 检查结果
if status:
return public.returnMsg(True,'卸载前置检查失败')
else:
return public.returnMsg(False,'卸载前置检查成功')
'''
plugin_object = Plugin(pluginInfo['name'])
try:
res = plugin_object.exec_fun(public.dict_obj(), 'uninstall_check')
if 'status' in res and res['status']:
res['status'] = False
public.start_syssafe() # 恢复系统加固服务
return res
except:
pass
# 是否有卸载前置检查 -- END
pluginPath = self.__install_path + '/' + pluginInfo['name']
installSh = pluginPath + '/install.sh'
uninstallSh = pluginPath + '/uninstall.sh'
if pluginInfo['type'] != 6 and not os.path.exists(
installSh) and not os.path.exists(uninstallSh):
download_url = session[
'download_url'] + '/install/plugin/' + pluginInfo[
'name'] + '/install.sh'
toFile = '/tmp/%s.sh' % pluginInfo['name']
public.downloadFile(download_url, toFile)
self.set_pyenv(toFile)
if os.path.exists(toFile):
if os.path.getsize(toFile) > 100:
public.ExecShell('/bin/bash ' + toFile + ' uninstall')
if os.path.exists(uninstallSh):
self.set_pyenv(uninstallSh)
public.ExecShell('/bin/bash {} uninstall'.format(uninstallSh))
elif os.path.exists(installSh):
self.set_pyenv(installSh)
public.ExecShell('/bin/bash {} uninstall'.format(installSh))
if os.path.exists(pluginPath):
public.ExecShell('rm -rf ' + pluginPath)
public.WriteLog('TYPE_SETUP', 'PLUGIN_UNINSTALL_SOFT',
(pluginInfo['title'],))
public.start_syssafe() # 恢复系统加固服务
return public.returnMsg(True, 'PLUGIN_UNINSTALL')
else:
if pluginInfo['name'] == 'mysql':
if public.M('databases').where('db_type=? AND type=?', (0, "MySQL")).count() > 0:
public.start_syssafe() # 恢复系统加固服务
return public.returnMsg(
False,
"本地数据库列表非空,为了您的数据安全,请先<span style='color:red;'>备份所有本地数据库数据</span>后删除现有本地数据库<br>强制卸载命令:rm -rf /www/server/mysql"
)
get.type = '0'
if session['server_os']['x'] != 'RHEL': get.type = '3'
get.sName = get.sName.lower()
if get.sName.find('php-') != -1:
get.sName = get.sName.split('-')[0]
if get.sName in ['redis']: get.version = ''
execstr = "cd /www/server/panel/install && /bin/bash install_soft.sh " + get.type + " uninstall " + get.sName.lower(
) + " " + get.version.replace('.', '')
public.ExecShell(execstr)
public.WriteLog('TYPE_SETUP', 'PLUGIN_UNINSTALL',
(get.sName, get.version))
# 设置PHP环境变量
if get.sName in ['php']:
public.ExecShell("btpython /www/server/panel/tools.py phpenv")
public.start_syssafe() # 恢复系统加固服务
return public.returnMsg(True, "PLUGIN_UNINSTALL")
def __is_bind_user(self):
'''
@name 检测是否绑定用户
@author hwliang<2021-06-23>
@return bool
'''
# user_info_file = self.__panel_path + '/data/userInfo.json'
# if not os.path.exists(user_info_file):
# raise public.PanelError('请先绑定宝塔帐号!')
return True
def __clear_panel_cache(self):
"""
@name 清理面板缓存
@return void
"""
slist = ['IS_PRO_OR_LTD_FOR_PANEL_DAILY']
for s in slist:
try:
cache.delete(s)
except:
pass
return True
"""
@name 获取软件列表更新时间
"""
def _get_flush_time(self):
try:
pl_time = 0
json_time = 0
# 已绑定用户时写入的文件
if os.path.exists(self.__plugin_save_file):
pl_time = os.path.getmtime(self.__plugin_save_file)
# 未绑定用户时写入的文件
if os.path.exists(self.__plugin_save_json_file):
json_time = os.path.getmtime(self.__plugin_save_json_file)
# 对比本地两个文件的时间戳,返回最新的
if pl_time > json_time:
return pl_time
return json_time
except:
pass
return 0
# 从云端取列表
def get_cloud_list(self, get=None):
force = False
force_time = 0
if hasattr(get, 'force'):
if int(get.force) == 1:
force = True
force_time = self._get_flush_time()
self.__clear_panel_cache()
tkey = 'is_flush_plugin_list'
last_flush_plugin_list_time = session.get(tkey)
if not isinstance(last_flush_plugin_list_time, int) or time.time() - last_flush_plugin_list_time > 86400:
# 更新最新的软件列表
session[tkey] = int(time.time())
_cmd = "nohup {} {}/script/flush_plugin.py > /dev/null 2>&1 &".format(
public.get_python_bin(), public.get_panel_path())
public.ExecShell(_cmd)
self.__is_bind_user()
skey = 'TNaMJdG3mDHKRS6Y'
softList = cache.get(skey)
if not softList or force:
softList = Plugin(False).get_plugin_list(force)
cache.set(skey, softList, 3600)
self.clean_panel_log()
if 'ip' in softList:
if public.is_ipv6(softList['ip']):
public.writeFile('data/v4.pl', ' -6 ')
else:
public.writeFile('data/v4.pl', ' -4 ')
sType = -3
qType = 0
try:
if hasattr(get, 'type'): sType = int(get['type'])
if sType != 10:
if hasattr(get, 'query'):
if get.query:
qType = sType
sType = 0
except:
pass
if type(softList) != dict:
softList = Plugin(False).get_plugin_list(False)
if type(softList) != dict:
softList = {"list": []}
return softList
if not 'list' in softList:
try:
nkey = 'get_flush_plugin_list'
if not session.get(nkey):
session[nkey] = 1
get = public.dict_obj()
get['force'] = 1
self.get_cloud_list(get)
except:
pass
err_msg = '获取插件列表失败'
if 'msg' in softList:
err_msg = softList['msg']
err_file = '{}.err'.format(self.__plugin_save_file)
if os.path.exists(err_file):
err = public.readFile(err_file)
if err:
err_msg = '{}\n{}'.format(err, err_msg)
err_msg = '{}\n{}'.format(public.get_user_info(), err_msg)
raise public.PanelError(err_msg)
softList['is_force'] = 0
if force_time > 0:
if self._get_flush_time() - force_time > 0:
softList['is_force'] = 1
# 只有第三方插件才会读取本地插件
if sType in (10, -3, -1):
softList['list'] = self.get_local_plugin(softList['list'])
softList['list'] = self.get_types(softList['list'], sType)
if hasattr(get, 'query'):
if get.query:
get.query = get.query.lower()
public.total_keyword(get.query)
tmpList = []
query_list = self.structure_search_list(get.query)
ps_compile = re.compile(r'<[0-9a-zA-Z\.\"\/\:\=\s\-_\?;@&\'\%\+\#]+>')
for softInfo in softList['list']:
if softInfo['title'].lower().find(get.query) != -1:
softInfo['soft_index'] = 0
softInfo['sort_tow'] = softInfo['title'].lower().find(get.query)
if softInfo['title'].find('PHP-') == 0:
softInfo['sort_tow'] = 0
tmpList.append(softInfo)
continue
elif softInfo['name'].lower().find(get.query) != -1:
softInfo['soft_index'] = 1
softInfo['sort_tow'] = softInfo['name'].lower().find(get.query)
tmpList.append(softInfo)
continue
elif softInfo['ps'].lower().find(get.query) != -1:
softInfo['soft_index'] = 2
softInfo['sort_tow'] = softInfo['ps'].lower().find(get.query)
tmpList.append(softInfo)
continue
else:
s_list_name = self.structure_search_list(softInfo['name'])
s_list_title = self.structure_search_list(softInfo['title'])
# 过滤分类
# if qType > 0 and softInfo['type'] != qType: continue
softInfo['sort_tow'] = 999
# 查询默认命中关键词
softInfo['keys'] = ''
n_info = self.check_soft_keyword(softInfo, get.query)
if n_info:
softInfo['soft_index'] = 3
tmpList.append(softInfo)
else:
if (query_list[0].lower() in s_list_name[0].lower() or
query_list[0].lower() in softInfo['ps'].lower() or
query_list[0] in s_list_title[0].lower()):
ps = ps_compile.sub('', softInfo['ps'].lower())
if softInfo['name'].lower().find(
query_list[0]) != -1 or softInfo['title'].lower(
).find(query_list[0]) != -1 or ps.find(
query_list[0]) != -1:
skey = ''
if softInfo['name'].lower().find(query_list[0]) != -1:
skey = "<span style='color:#fc6d26'>(匹配到插件名称:{}</span>".format(
softInfo['name'])
softInfo['keys'] = skey
softInfo['soft_index'] = 4
tmpList.append(softInfo)
softList['list'] = sorted(tmpList, key=lambda x: x['sort_tow'])
# 查询模块命中关键词
try:
m_list = []
for m_info in softList['m_list']:
m_info['keys'] = ''
n_info = self.check_soft_keyword(m_info, get.query)
if n_info:
m_list.append(m_info)
else:
ps = ps_compile.sub('', m_info['ps'].lower())
if m_info['title'].lower().find(get.query) != -1 or ps.find(get.query) != -1:
m_list.append(m_info)
m_info['keys'] = m_info['keys'].split('|')
if len(m_info['keys']) > 0:
key_info = m_info['keys'][0]
key_info = ' '.join(m_info['keys'])
m_info['ps'] += key_info
softList['m_list'] = m_list
except:
pass
else:
softList['m_list'] = []
return softList
def get_soft_list_thread(self, get):
'''
@name 获取软件列表(线程)
@param get
@return dict
'''
get.exists(["cid"])
session['is_flush_soft_list'] = 1
if not public.get_improvement():
get.cid = ''
public.ExecShell(
"nohup {} {}/script/flush_soft.py {} > /dev/null 2>&1 &".format(
public.get_python_bin(), public.get_panel_path(), get.cid))
return public.returnMsg(True, "ok")
def check_soft_keyword(self, softInfo, query):
"""
@name 检查命中关键词
"""
keyword = {}
try:
if 'keyword' in softInfo:
keyword = json.loads(softInfo['keyword'])
except:
pass
for key in keyword.keys():
if key.lower().find(query) != -1:
softInfo['keys'] = keyword[key]
return softInfo
return False
# 取提醒标记
def get_level_msg(self, level, s_time, endtime):
'''
level 提醒标记
s_time 当前时间戳
endtime 到期时间戳
'''
expire_day = (endtime - s_time) / 86400
if expire_day < 15 and expire_day > 7:
level = level + '15'
elif expire_day < 7 and expire_day > 3:
level = level + '7'
elif expire_day < 3 and expire_day > 0:
level = level + '3'
return level, expire_day
# 添加到期提醒
def add_expire_msg(self, title, level, name, expire_day, pid, endtime):
'''
title 软件标题
level 提醒标记
name 软件名称
expire_day 剩余天数
'''
import panelMessage # 引用消息提醒模块
pm = panelMessage.panelMessage()
pm.remove_message_level(level) # 删除旧的提醒
if expire_day > 15: return False
if pm.is_level(level): # 是否忽略
if level != name: # 到期还是即将到期
msg_last = '您的【{}】授权还有{}天到期'.format(title, int(expire_day) + 1)
else:
msg_last = '您的【{}】授权已到期'.format(title)
pl_msg = 'true'
if name in ['pro', 'ltd']:
pl_msg = 'false'
renew_msg = '<a class="btlink" onclick="bt.soft.product_pay_view({name:\'%s\',pid:%s,limit:\'%s\',plugin:%s,renew:%s});">立即续费</a>' % (
title, pid, name, pl_msg, endtime)
pm.create_message(level=level,
expire=7,
msg="{},为了不影响您正常使用【{}】功能,请及时续费,{}".format(
msg_last, title, renew_msg))
return True
return False
# 到期提醒
def expire_msg(self, data):
'''
data 插件列表
'''
s_time = time.time()
is_plugin = True
import panelMessage # 引用消息提醒模块
pm = panelMessage.panelMessage()
# 企业版到期提醒
if not data['ltd'] in [-1]:
if data['pro'] < 0 or (data['pro'] - s_time) / 86400 < 15:
level, expire_day = self.get_level_msg('ltd', s_time,
data['ltd'])
print(level, expire_day)
self.add_expire_msg('企业版', level, 'ltd', expire_day, 100000046,
data['ltd'])
pm.remove_message_level('pro')
return True
# 专业版到期提醒
if not data['pro'] in [-1, 0]:
level, expire_day = self.get_level_msg('pro', s_time, data['pro'])
self.add_expire_msg('专业版', level, 'pro', expire_day, 100000030,
data['pro'])
pm.remove_message_level('ltd')
is_plugin = False
return True
# 提交用户评分
def set_score(self, args):
try:
import panelAuth
pdata = panelAuth.panelAuth().create_serverid(None)
pdata['ps'] = args.ps
pdata['num'] = int(args.num)
pdata['pid'] = int(args.pid)
if 1 < pdata['num'] > 5:
return public.returnMsg(False, '评分范围[1-5]')
if not pdata['pid']: return public.returnMsg(False, '指定插件不存在!')
result = public.httpPost(
public.GetConfigValue('home') + '/api/panel/plugin_score',
pdata, 10)
result = json.loads(result)
return result
except:
return public.returnMsg(False, '连接服务器失败!')
# 获取指定插件评分
def get_score(self, args):
try:
import panelAuth
pdata = panelAuth.panelAuth().create_serverid(None)
pdata['pid'] = int(args.pid)
if not pdata['pid']:
return {
"total": 0,
"split": [
0,
0,
0,
0,
0
],
"page": "<div><span class='Pcurrent'>1</span><span class='Pcount'>共计0条数据</span></div>",
"data": []
}
u_args = ""
sp_tip = '?'
if 'p' in args:
u_args += sp_tip + 'p=' + args.p
sp_tip = '&'
if 'tojs' in args:
u_args += sp_tip + 'tojs=' + args.tojs
sp_tip = '&'
if 'limit_num' in args:
pdata['limit_num'] = int(args.limit_num)
result = public.httpPost(
public.GetConfigValue('home') + '/api/panel/get_plugin_socre' +
u_args, pdata, 10)
result = json.loads(result)
return result
except:
return public.returnMsg(False, '连接服务器失败!')
# 清除多余面板日志
def clean_panel_log(self):
try:
log_path = 'logs/request'
if not os.path.exists(log_path): return False
limit_num = 180
p_logs = sorted(os.listdir(log_path))
num = len(p_logs) - limit_num
if num > 0:
for i in range(num):
filename = log_path + '/' + p_logs[i]
if not os.path.exists(filename): continue
os.remove(filename)
today = public.getDate(format='%Y-%m-%d')
for fname in os.listdir(log_path):
fsplit = fname.split('.')
if fsplit[-1] != 'json':
continue
if fsplit[0] == today:
continue
public.ExecShell("cd {} && gzip {}".format(log_path, fname))
# 清理错误日志
public.clean_max_log('/www/server/panel/logs/error.log', 10, 20)
public.clean_max_log('/www/server/panel/logs/socks5.log', 10, 20)
public.clean_max_log('/www/server/panel/logs/oos.log', 10, 20)
return True
except:
return False
# 取本地插件1
def get_local_plugin(self, sList):
if not os.path.exists('plugin/'):
return sList
for name in os.listdir('plugin/'):
isExists = False
for softInfo in sList:
if name == softInfo['name']:
isExists = True
break
if isExists: continue
filename = 'plugin/' + name + '/info.json'
if not os.path.exists(filename): continue
tmpInfo = public.ReadFile(filename).strip()
if not tmpInfo: continue
try:
info = json.loads(tmpInfo)
if "versions" not in info:
continue
except:
continue
pluginInfo = self.get_local_plugin_info(info)
if not pluginInfo: continue
sList.append(pluginInfo)
return sList
# 检查是否正在安装
def check_setup_task(self, sName):
if sName.find('php-') != -1:
tmp = sName.split('-')
sName = tmp[0]
version = tmp[1]
isTask = '1'
for task in self.__tasks:
try:
tmpt = public.getStrBetween('[', ']', task['name'])
except:
continue
if not tmpt: continue
tmp1 = tmpt.split('-')
name1 = tmp1[0].lower()
if sName == 'php':
if name1 != sName or tmp1[1] != version: continue
isTask = task['status']
else:
if name1 == 'pure': name1 = 'pure-ftpd'
if name1 != sName: continue
isTask = task['status']
if isTask == '-1' or isTask == '0':
if task['name'].find('upgrade') != -1: isTask = '-2'
break
return isTask
# 构造本地插件信息
def get_local_plugin_info(self, info):
m_version = info['versions'].split(".")
if len(m_version) < 2: return None
if len(m_version) > 2:
tmp = m_version[:]
del (tmp[0])
m_version[1] = '.'.join(tmp)
try:
if not 'author' in info: info['author'] = '未知'
if not 'home' in info: info['home'] = '#'
pluginInfo = {
"id":
10000,
"pid":
0,
"sort":
9999,
"type":
10,
"price":
0,
"author":
info['author'],
"home":
info['home'],
"name":
info['name'],
"title":
info['title'],
"panel_pro":
1,
"panel_free":
1,
"panel_test":
1,
"ps":
info['ps'],
"version":
info['versions'],
"s_version":
"0",
"manager_version":
"1",
"c_manager_version":
"1",
"dependnet":
"",
"mutex":
"",
"install_checks":
"/www/server/panel/plugin/" + info['name'],
"uninsatll_checks":
"/www/server/panel/plugin/" + info['name'],
"compile_args":
0,
"version_coexist":
0,
"versions": [{
"m_version": m_version[0],
"version": m_version[1],
"dependnet": "",
"mem_limit": 32,
"cpu_limit": 1,
"os_limit": 0,
"setup": True
}],
"setup":
True,
"status":
True
}
except:
pluginInfo = None
return pluginInfo
# 处理分类
def get_types(self, sList, sType):
if sType == -3: return sList
newList = []
for sInfo in sList:
# 分类不是第三方插件
if sType not in (10, -1) and sInfo['type'] == 10: continue
# 指定分类,0表示所有分类,-1表示已安装
if sType in [sInfo['type'], 0, -1]:
newList.append(sInfo)
else:
try:
n_type = int(sInfo['ex2'])
if n_type == sInfo['type']: continue
if n_type == sType: newList.append(sInfo)
except:
pass
return newList
# 检查权限
def check_accept(self, get):
args = public.dict_obj()
args.type = '8'
p_list = self.get_cloud_list(args)
for p in p_list['list']:
if p['name'] == get.name:
if p_list['pro'] < 0 and p['endtime'] < 0: return False
break
args.type = '10'
p_list = self.get_cloud_list(args)
for p in p_list['list']:
if p['name'] == get.name:
if not 'endtime' in p: continue
if p['endtime'] < 0: return False
break
args.type = '12'
p_list = self.get_cloud_list(args)
for p in p_list['list']:
if not p['type'] in [12, '12']: continue
if p['name'] == get.name:
if not 'endtime' in p: continue
if p_list['ltd'] < 1 and p['endtime'] < 1: return False
break
return True
def sord_by_sordid(self, sList, query_str=None):
"""
@name 根据sordid排序;sordid越大,排序越靠前
企业版插件第一
专业版第二
以此类推
title匹配到就排第四其他(含第三方插件)排最后
@author hezhihong
@param sList 要排序的列表
@param query_str 排序字符串
"""
if not query_str: query_str = '-'
query_search_compile = re.compile(r'^' + query_str.upper())
for i in range(len(sList)):
if query_str and query_str.lower() == 'php' and sList[i]['name'].lower().find('php-') == 0:
sList[i]['sortid'] = 100
elif sList[i]['type'] == 12:
sList[i]['sortid'] = 12
elif sList[i]['type'] == 8:
sList[i]['sortid'] = 11
elif sList[i]['name'].find(query_str) != -1 and query_search_compile.search(sList[i]['name'].upper()) and sList[i]['type'] == 5:
sList[i]['sortid'] = 10
elif sList[i]['title'].find(query_str.upper()) != -1 and query_search_compile.search(sList[i]['name'].upper()) and sList[i]['type'] == 5:
sList[i]['sortid'] = 10
elif sList[i]['type'] == 5:
sList[i]['sortid'] = 9
elif sList[i]['type'] == 5:
sList[i]['sortid'] = 8
elif sList[i]['type'] == 13:
sList[i]['sortid'] = 7
elif sList[i]['type'] == 6:
sList[i]['sortid'] = 6
elif sList[i]['type'] == 7:
sList[i]['sortid'] = 5
elif sList[i]['type'] == 10:
sList[i]['sortid'] = 0
else:
sList[i]['sortid'] = 0
return sorted(sList, key=lambda x: x['sortid'], reverse=True)
# 2023/12/26 下午 3:32 根据get.query构造搜索列表
def structure_search_list(self, split_str):
'''
@name 根据get.query构造搜索列表
@author wzz <2023/12/26 下午 3:33>
@param split_str<string> 搜索字符串
@return list['', '']
'''
try:
is_split = False
if type(split_str) == str:
# 2023/12/26 下午 2:51 兼容php:7和nginx:1.20这种搜索
if split_str.find(":") != -1:
is_split = True
split_str = split_str.split(":")
# 2023/12/26 下午 2:51 兼容php 7和nginx 1.20这种搜索
if is_split is False:
if split_str.find(" ") != -1:
is_split = True
split_str = split_str.split(" ")
# 2023/12/26 下午 2:50 兼容 php-7和nginx-1.20这种搜索
if is_split is False:
if split_str.find("-") != -1:
is_split = True
split_str = split_str.split("-")
# 2023/12/26 下午 2:50 兼容php_7和nginx_1.20这种搜索
if is_split is False:
if split_str.find("_") != -1:
is_split = True
split_str = split_str.split("_")
# 2023/12/26 下午 2:50 兼容php7|php74|php7.4|php7.4.0和mysql8这种搜索
if is_split is False:
is_split = True
if re.findall(r'^[a-zA-Z]+', split_str) and re.findall(r'[0-9]\.[0-9]|[0-9]', split_str):
split_str = [re.findall(r'^[a-zA-Z]+', split_str)[0], re.findall(r'[0-9]\.[0-9]|[0-9]', split_str)[0]]
elif re.findall(r'^[a-zA-Z]+', split_str) and re.findall(r'[0-9]+', split_str):
split_str = [re.findall(r'^[a-zA-Z]+', split_str)[0], re.findall(r'[0-9]+', split_str)[0]]
elif re.findall(r'^[a-zA-Z]+', split_str):
split_str = [re.findall(r'^[a-zA-Z]+', split_str)[0], ""]
# 如果全部都不是上面的情况,那么就是单纯的php这种搜索
if is_split is False:
split_str = [split_str, ""]
if type(split_str) != list:
split_str = [split_str, ""]
return split_str
except:
print(traceback.format_exc())
return [split_str, '']
# 取软件列表
def get_soft_list(self, get=None):
if 'query' in get:
get.query = get.query.strip()
public.set_search_history("get_soft_list", "get_soft_list", get.query)
softList = self.get_cloud_list(get)
if not softList:
get.force = 1
softList = self.get_cloud_list(get)
if not softList: return public.returnMsg(False, '软件列表获取失败(401)!')
public.run_thread(self.get_cloud_list_status, args=(get,))
softList['list'] = self.set_coexist(softList['list'])
# softlist['list']中name字段匹配get.query的数据排序最前面 hezhihong
if 'query' in get and get.query and softList['list']:
query_list = self.structure_search_list(get.query)
softList['list'] = self.sord_by_sordid(
softList['list'], query_list[0])
if not 'type' in get: get.type = '0'
if get.type == '-1':
soft_list_tmp = []
softList['list'] = self.is_setup(softList['list'])
for val in softList['list']:
if 'setup' in val:
if val['setup']: soft_list_tmp.append(val)
softList['list'] = soft_list_tmp
if not hasattr(get, 'p'): get.p = '1'
if get.type == '10' and get.p == '1':
# 为第三方插件,且第一页时,优先显示已安装的插件
softList['list'] = self.check_isinstall(softList['list'])
softList['list'] = sorted(softList['list'], key=lambda x: x['status'], reverse=True)
softList['list'] = self.get_page(softList['list'], get)
else:
softList['list'] = self.get_page(softList['list'], get)
softList['list']['data'] = self.check_isinstall(softList['list']['data'])
# softList['list']['data'] = sorted(softList['list']['data'], key=lambda x: x['sort_tow'])
softList['apache22'] = False
softList['apache24'] = False
check_version_path = '/www/server/apache/version_check.pl'
if os.path.exists(check_version_path):
softList['apache24'] = True
if public.readFile(check_version_path).find('2.2') == 0:
softList['apache22'] = True
softList['apache24'] = False
if os.path.exists('data/not_recommend.pl'):
if 'recommend' in softList:
del (softList['recommend'])
if 'recommend' in softList:
for n in range(len(softList['recommend'])):
if softList['recommend'][n]['type'] != 'soft': continue
for i in range(len(softList['recommend'][n]['data'])):
check_path = '/www/server/panel/plugin/' + softList[
'recommend'][n]['data'][i]['name']
softList['recommend'][n]['data'][i][
'setup'] = os.path.exists(check_path)
softList['search_history'] = public.get_search_history("get_soft_list", "get_soft_list")
return softList
# 取首页软件列表
def get_index_list(self, get=None):
get.type = -3
softList = self.get_cloud_list(get)['list']
public.run_thread(self.get_cloud_list_status, args=(get,))
public.run_thread(self.is_verify_unbinding, args=(get,))
if not softList:
get.force = 1
softList = self.get_cloud_list(get)['list']
if not softList: return public.returnMsg(False, '软件列表获取失败(401)!')
softList = self.set_coexist(softList)
if not os.path.exists(self.__index):
public.writeFile(self.__index, '[]')
try:
indexList = json.loads(public.ReadFile(self.__index))
except Exception:
os.remove(self.__index)
public.writeFile(self.__index, '[]')
indexList = []
dataList = []
for index in indexList:
for softInfo in softList:
if softInfo['name'] == index: dataList.append(softInfo)
dataList = self.check_isinstall(dataList)
return dataList
# 添加到首页
def add_index(self, get):
sName = get.sName
if not os.path.exists(self.__index):
public.writeFile(self.__index, '[]')
indexList = json.loads(public.ReadFile(self.__index))
if sName in indexList: return public.returnMsg(False, '请不要重复添加!')
if len(indexList) >= 12:
softList = self.get_cloud_list(get)['list']
softList = self.set_coexist(softList)
for softInfo in softList:
if softInfo['name'] in indexList:
new_softInfo = self.check_status(softInfo)
if not new_softInfo['setup']:
indexList.remove(softInfo['name'])
public.writeFile(self.__index, json.dumps(indexList))
if len(indexList) >= 12:
return public.returnMsg(False, '首页最多只能显示12个软件!')
indexList.append(sName)
public.writeFile(self.__index, json.dumps(indexList))
return public.returnMsg(True, '添加成功!')
# 删除首页
def remove_index(self, get):
sName = get.sName
indexList = []
if not os.path.exists(self.__index):
public.writeFile(self.__index, '[]')
indexList = json.loads(public.ReadFile(self.__index))
if not sName in indexList: return public.returnMsg(True, '删除成功!')
indexList.remove(sName)
public.writeFile(self.__index, json.dumps(indexList))
return public.returnMsg(True, '删除成功!')
# 设置排序
def sort_index(self, get):
indexList = get.ssort.split('|')
public.writeFile(self.__index, json.dumps(indexList))
return public.returnMsg(True, '设置成功!')
# 取快捷软件列表
def get_link_list(self, get=None):
softList = self.get_cloud_list(get)['list']
softList = self.set_coexist(softList)
indexList = json.loads(public.ReadFile(self.__link))
dataList = []
for index in indexList:
for softInfo in softList:
if softInfo['name'] == index: dataList.append(softInfo)
dataList = self.check_isinstall(dataList)
return dataList
# 添加到快捷栏
def add_link(self, get):
sName = get.sName
indexList = json.loads(public.ReadFile(self.__link))
if sName in indexList: return public.returnMsg(False, '请不要重复添加!')
if len(indexList) >= 5:
return public.returnMsg(False, '快捷栏最多只能显示5个软件!')
indexList.append(sName)
public.writeFile(self.__link, json.dumps(indexList))
return public.returnMsg(True, '添加成功!')
# 删除快捷栏
def remove_link(self, get):
sName = get.sName
indexList = []
indexList = json.loads(public.ReadFile(self.__link))
if sName in indexList: return public.returnMsg(True, '删除成功!')
indexList.remove(sName)
public.writeFile(self.__link, json.dumps(indexList))
return public.returnMsg(True, '删除成功!')
# 设置快捷栏排序
def sort_link(self, get):
indexList = get.ssort.split('|')
public.writeFile(self.__link, json.dumps(indexList))
return public.returnMsg(True, '设置成功!')
# 处理共存软件
php_compile = re.compile(r"^php-\d{0,2}\.\d{0,2}(\.\d{0,2})?$")
def set_coexist(self, sList):
softList = []
collect_php = []
# 读取显示在前面的php版本
spath = '{}/data/php_order.json'.format(public.get_panel_path())
try:
priority_php = json.loads(public.readFile(spath))
except:
priority_php = []
no_priority_php = []
for sInfo in sList:
if sInfo['version_coexist'] != 1 or 'versions' not in sInfo:
softList.append(sInfo)
continue
for versionA in sInfo['versions']:
try:
sTmp = sInfo.copy()
v = versionA['m_version'].replace('.', '')
sTmp['title'] = sTmp['title'] + '-' + versionA['m_version']
sTmp['name'] = sTmp['name'] + '-' + versionA['m_version']
is_php = bool(self.php_compile.match(sTmp['name']))
sTmp['version'] = sTmp['version'].replace('{VERSION}', v)
sTmp['manager_version'] = sTmp['manager_version'].replace('{VERSION}', v)
sTmp['install_checks'] = sTmp['install_checks'].replace('{VERSION}', v)
sTmp['uninsatll_checks'] = sTmp['uninsatll_checks'].replace('{VERSION}', v)
sTmp['s_version'] = sTmp['s_version'].replace('{VERSION}', v)
sTmp['versions'] = []
sTmp['versions'].append(versionA)
if 'keys' in sTmp:
sTmp['keys'] = sTmp['keys'].replace('{VERSION}', v).replace('{NAME}', sTmp['name'])
if is_php is False:
softList.append(sTmp)
continue
# collect_php.append({"name": sTmp["name"], "version": versionA['m_version']})
filename = versionA['m_version'].replace(".", "")
temp_status = True if os.path.exists("/www/server/php/{}/bin/php".format(filename)) else False
collect_php.append({
"name": sTmp["name"],
"version": versionA["m_version"],
"status": temp_status
})
sTmp["other_php"] = collect_php
if type(priority_php) != list:
softList.append(sTmp)
continue
if versionA['m_version'] in priority_php:
softList.append(sTmp)
continue
no_priority_php.append(sTmp)
except:
continue
softList.extend(no_priority_php)
return softList
def is_setup(self, sList):
'''
@name 检查是否安装
@param sList<list> 软件列表
@return list
'''
for i in range(len(sList)):
sList[i]['setup'] = os.path.exists(sList[i]['install_checks'])
return sList
# 检测是否安装
def check_isinstall(self, sList):
if not os.path.exists(self.__index):
public.writeFile(self.__index, '[]')
try:
indexList = json.loads(public.ReadFile(self.__index))
except Exception:
os.remove(self.__index)
public.writeFile(self.__index, '[]')
indexList = []
for i in range(len(sList)):
sList[i]['index_display'] = sList[i]['name'] in indexList
sList[i] = self.check_status(sList[i])
return sList
def get_soft_ps(self, ps):
"""
@处理软件说明
"""
try:
index = ps.find('<a')
if index > 0:
ps = ps.replace(ps[:index], '<span class="description-line">{}</span>'.format(ps[:index]))
else:
ps = '<span class="description-line">{}</span>'.format(ps)
except:
pass
return ps
# 检查软件状态
def check_status(self, softInfo):
softInfo['setup'] = os.path.exists(softInfo['install_checks']) if "install_checks" in softInfo else False
softInfo['status'] = False
softInfo['task'] = self.check_setup_task(softInfo['name']) if "name" in softInfo else "1"
softInfo['is_beta'] = self.is_beta_plugin(softInfo['name']) if "name" in softInfo else False
softInfo['ps'] = self.get_soft_ps(softInfo['ps'])
if softInfo['name'] == 'nginx':
if os.path.exists('/www/server/nginx/sys_install.pl'):
softInfo['setup'] = True
softInfo['status'] = True
key_info = ''
if 'keys' in softInfo:
softInfo['keys'] = softInfo['keys'].split('|')
if len(softInfo['keys']) > 0:
key_info = softInfo['keys'][0]
# 点击跳转事件不在能在前端触发了,暂时屏蔽
for p in softInfo['keys'][::-1]:
if "onclick=" in p:
softInfo['keys'].remove(p)
if softInfo['setup']: key_info = ' '.join(softInfo['keys'])
softInfo['ps'] += key_info
if softInfo['name'].find('php-') != -1: softInfo['fpm'] = False
if softInfo['setup']:
softInfo['shell'] = softInfo['version']
softInfo['version'] = self.get_version_info(softInfo)
softInfo['status'] = True
softInfo['versions'] = self.tips_version(softInfo['versions'],
softInfo['version'])
softInfo['admin'] = os.path.exists('/www/server/panel/plugin/' +
softInfo['name'])
if 's_version' in softInfo and len(softInfo['s_version']) > 3:
pNames = softInfo['s_version'].split(',')
for pName in pNames:
if len(softInfo['manager_version']) > 5:
softInfo['status'] = self.process_exists(
pName, softInfo['manager_version'])
else:
softInfo['status'] = self.process_exists(pName)
if softInfo['status']: break
else:
softInfo['version'] = ""
if softInfo['version_coexist'] == 1:
if softInfo['id'] != 10000:
softInfo['icon'] = self.get_icon(softInfo['name'].split('-')[0])
else:
if 'min_image' in softInfo:
if softInfo['id'] != 10000:
softInfo['icon'] = self.get_icon(softInfo['name'], softInfo['min_image'])
else:
# if softInfo['id'] != 10000:
softInfo['icon'] = self.get_icon(softInfo['name'])
if softInfo['setup']:
if softInfo['name'].find('php-') != -1:
v2 = softInfo['versions'][0]['m_version'].replace('.', '')
softInfo['fpm'] = os.path.exists('/www/server/php/' + v2 +
'/sbin/php-fpm')
softInfo['status'] = self.get_php_status(v2)
pid_file = '/www/server/php/' + v2 + '/var/run/php-fpm.pid'
if not softInfo['fpm']:
softInfo['status'] = True
elif softInfo['status'] and os.path.exists(pid_file):
try:
softInfo['status'] = public.pid_exists(
int(public.readFile(pid_file)))
except:
if os.path.exists(pid_file):
os.remove(pid_file)
if softInfo['name'] == 'mysql':
softInfo['status'] = self.process_exists('mysqld')
if not softInfo['status']:
# 判断是否为mariadb
version_pl_file = '{}/mysql/version.pl'.format(public.get_panel_path())
version_pl = softInfo["version"]
if os.path.exists(version_pl_file): version_pl = public.readFile(version_pl_file)
if version_pl.find('mariadb') != -1:
softInfo['status'] = self.process_exists('mariadbd')
elif softInfo['name'] == 'redis':
softInfo['status'] = self.process_exists('redis')
# softInfo['status'] = True if os.path.exists("/www/server/redis/redis.pid") else False
elif softInfo['name'] == 'phpmyadmin':
softInfo['status'] = self.get_phpmyadmin_stat()
elif softInfo['name'] == 'openlitespeed':
pid_file = '/run/openlitespeed.pid'
if os.path.exists(pid_file):
pid = int(public.readFile(pid_file))
softInfo['status'] = public.pid_exists(pid)
elif softInfo['name'] == 'nginx':
# res = public.ExecShell('/etc/init.d/nginx status')
# softInfo['status'] = 'already running' in res[0]
softInfo['status'] = self.process_exists('nginx')
elif softInfo['name'] == 'mail_sys':
softInfo['status'] = False if not os.path.exists("/www/server/panel/plugin/mail_sys/mail_sys_main.py") else True
softInfo['setup'] = softInfo['status']
return softInfo
def test_dependnet_status(self, dependnet):
need = []
selected = []
if not dependnet:
return {"need": need, "selected": selected}
for dep in dependnet.split(','):
if not dep:
continue
if dep.find('|') != -1:
for name in dep.split('|'):
pluginInfo = self.get_soft_find(name)
if not pluginInfo: continue
if pluginInfo['setup'] is True:
break
else:
selected.append([i for i in dep.split('|') if bool(i)])
else:
pluginInfo = self.get_soft_find(dep)
if not pluginInfo: continue
if pluginInfo['setup'] is False:
need.append(dep)
return {"need": need, "selected": selected}
def get_php_status(self, phpversion):
'''
@name 获取指定PHP版本的服务状态
@author hwliang<2020-10-23>
@param phpversion string PHP版本
@return bool
'''
try:
php_status = os.path.exists('/tmp/php-cgi-' + phpversion + '.sock')
if php_status: return php_status
pid_file = '/www/server/php/{}/var/run/php-fpm.pid'.format(
phpversion)
if not os.path.exists(pid_file): return False
pid = int(public.readFile(pid_file))
return os.path.exists('/proc/{}/comm'.format(pid))
except:
return False
# 取phpmyadmin状态
def get_phpmyadmin_stat(self):
webserver = public.get_webserver()
if webserver == 'nginx':
filename = public.GetConfigValue(
'setup_path') + '/nginx/conf/nginx.conf'
elif webserver == 'apache':
filename = public.GetConfigValue(
'setup_path') + '/apache/conf/extra/httpd-vhosts.conf'
else:
filename = "/www/server/panel/vhost/openlitespeed/detail/phpmyadmin.conf"
if not os.path.exists(filename): return False
conf = public.readFile(filename)
if not conf: return False
is_start = conf.find('/www/server/stop') == -1
if is_start:
if webserver == 'nginx':
is_start = conf.find('allow 127.0.0.1;') == -1
elif webserver == 'apache':
is_start = conf.find(
'Allow from 127.0.0.1 ::1 localhost') == -1
return is_start
# 获取指定软件信息
def get_soft_find(self, get=None):
# return public.returnMsg(False, '您无操作此插件的权限,请联系管理员添加此插件的权限!')
if hasattr(get, 'sName'):
uid = session.get('uid')
if uid != 1 and uid:
if os.path.exists('/www/server/panel/plugin/users/authority/{}'.format(uid)):
user = public.readFile('/www/server/panel/plugin/users/authority/{}'.format(uid))
user = json.loads(self._decrypt(user))
if user['role'] != 'administrator':
if os.path.exists('/www/server/panel/plugin/users/authority/{}.plugin'.format(uid)):
data = public.readFile('/www/server/panel/plugin/users/authority/{}.plugin'.format(uid))
data = json.loads(self._decrypt(data))
if get['sName'] not in data['plugin']:
return public.returnMsg(False, '您无操作此插件的权限,请联系管理员添加此插件的权限!')
else:
return public.returnMsg(False, '您无操作此插件的权限,请联系管理员添加此插件的权限!')
args = public.dict_obj()
args.type = -3
if not self.__plugin_s_list:
softList = self.get_cloud_list(args)['list']
self.__plugin_s_list = self.set_coexist(softList)
try:
sName = get['sName']
except:
sName = get
for softInfo in self.__plugin_s_list:
if softInfo['name'] == sName:
if sName == 'phpmyadmin':
import ajax
ssl_info = ajax.ajax().get_phpmyadmin_ssl(None)
ssl_status = 'https://' if ssl_info['status'] else 'http://'
from BTPanel import get_phpmyadmin_dir
pmd = get_phpmyadmin_dir()
softInfo['ext'] = self.getPHPMyAdminStatus()
if softInfo['ext'] and pmd:
port = ssl_info['port'] if ssl_info['status'] else pmd[1]
softInfo['ext']['url'] = ssl_status + public.GetHost() + ':' + port + '/' + pmd[0]
if "php-" in sName:
v = softInfo["versions"][0]["m_version"]
v1 = v.replace(".", "")
if public.get_webserver() == "openlitespeed":
softInfo[
"php_ini"] = "/usr/local/lsws/lsphp{}/etc/php/{}/litespeed/php.ini".format(
v1, v)
if os.path.exists("/etc/redhat-release"):
softInfo[
"php_ini"] = "/usr/local/lsws/lsphp{}/etc/php.ini".format(
v1)
else:
softInfo[
"php_ini"] = "/www/server/php/{}/etc/php.ini".format(
v1)
# return self.check_status(softInfo)
res = self.check_status(softInfo)
if isinstance(get, public.dict_obj):
res['dependnet'] = self.test_dependnet_status(res['dependnet'])
return res
else:
return res
return False
def is_beta_plugin(self, plugin_name):
'''
@name 判断当前安装的插件是否为测试版
@author hwliang<2021-06-24>
@param plugin_name<string> 插件名称
@return bool
'''
info_file = self.__install_path + '/' + plugin_name + '/info.json'
if not os.path.exists(info_file): return False
try:
plugin_info = json.loads(public.readFile(info_file))
return plugin_info.get('beta', False)
except:
return False
# 获取版本信息
def get_version_info(self, sInfo):
version = ''
vFile1 = sInfo['uninsatll_checks'] + '/version_check.pl'
vFile2 = sInfo['uninsatll_checks'] + '/info.json'
if os.path.exists(vFile1):
version = public.xss_version(public.ReadFile(vFile1).strip())
if not version: os.remove(vFile1)
elif os.path.exists(vFile2):
v_tmp = public.ReadFile(vFile2).strip()
if v_tmp:
try:
version = json.loads(v_tmp)['versions']
except:
public.ExecShell('rm -f ' + vFile2)
else:
version = "1.0"
else:
exec_args = {
'nginx':
"/www/server/nginx/sbin/nginx -v 2>&1|grep version|awk '{print $3}'|cut -f2 -d'/'",
'apache':
"/www/server/apache/bin/httpd -v|grep version|awk '{print $3}'|cut -f2 -d'/'",
'mysql':
"/www/server/mysql/bin/mysql -V|grep Ver|awk '{print $5}'|cut -f1 -d','",
'php':
"/www/server/php/{VERSION}/bin/php -v|grep cli|awk '{print $2}'",
'pureftpd': "cat /www/server/pure-ftpd/version.pl",
'phpmyadmin': "cat /www/server/phpmyadmin/version.pl",
'tomcat':
"/www/server/tomcat/bin/version.sh|grep version|awk '{print $4}'|cut -f2 -d'/'",
'memcached':
"/usr/local/memcached/bin/memcached -V|awk '{print $2}'",
'redis':
"/www/server/redis/src/redis-server -v|awk '{print $3}'|cut -f2 -d'='",
'openlitespeed': "cat /usr/local/lsws/VERSION",
'gitlab': 'echo "8.8.5"'
}
exec_str = ''
if sInfo['name'] in exec_args: exec_str = exec_args[sInfo['name']]
if sInfo['version_coexist'] == 1:
v_tmp = sInfo['name'].split('-')
exec_str = exec_args[v_tmp[0]].replace(
'{VERSION}', v_tmp[1].replace('.', ''))
version = public.ExecShell(exec_str, 3)[0].strip()
if version:
public.writeFile(vFile1, version)
else:
vFile4 = sInfo['uninsatll_checks'] + '/version.pl'
if os.path.exists(vFile4):
version = public.xss_version(
public.readFile(vFile4).strip())
if sInfo['name'] == 'mysql':
vFile3 = sInfo['uninsatll_checks'] + '/version.pl'
version_str = None
if os.path.exists(vFile3):
version_str = public.xss_version(public.readFile(vFile3))
if version_str.find('AliSQL') != -1: version = 'AliSQL'
if version == 'Linux' and version_str:
version = version_str
public.writeFile(vFile1, version)
if sInfo['name'] == 'nginx':
if version.find('2.2.') != -1: version = '-Tengine' + version
return version.replace('p1', '')
# 标记当前安装的版本
def tips_version(self, versions, version):
if len(versions) == 1:
versions[0]['setup'] = True
return versions
try:
for i in range(len(versions)):
if version == (versions[i]['m_version'] + '.' +
versions[i]['version']):
versions[i]['setup'] = True
continue
if versions[i]['m_version'].find('greatsql') == -1:
vTmp = versions[i]['m_version'].split('_')
if len(vTmp) > 1:
vTmp = vTmp[1]
else:
vTmp = vTmp[0]
else:
vTmp = [versions[i]['m_version']]
vLen = len(vTmp)
versions[i]['setup'] = (version[:vLen] == vTmp)
except Exception as ex:
# print(ex)
pass
return versions
# 取pids
number_compile = re.compile(r"^\d+$")
def get_pids(self):
pids = []
for pid in os.listdir('/proc'):
if self.number_compile.match(pid): pids.append(pid)
return pids
# 进程是否存在
def process_exists(self, pname, exe=None):
pname = pname.strip().lower()
if pname in ["elasticsearch", "logstash", "kibana"]:
if os.path.exists("/var/run/elasticsearch/elasticsearch.pid"):
pid = int(public.readFile("/var/run/elasticsearch/elasticsearch.pid"))
return public.pid_exists(pid)
elif pname in ['mysqld', 'mariadbd']:
datadir = public.get_datadir()
if datadir:
pid_file = "{}/{}.pid".format(datadir, public.get_hostname())
if os.path.exists(pid_file):
try:
pid = int(public.readFile(pid_file))
status = public.pid_exists(pid)
if status: return status
except:
return False
try:
for proc in psutil.process_iter():
if proc.name() == "mysqld" or proc.name() == "mariadbd":
cmdline = proc.cmdline()
if os.path.exists('/www/server/mysql/sys_install.pl'):
return True
if cmdline and any("/www/server" in arg for arg in cmdline):
return True
else:
return False
except:
return False
elif pname in ['php-fpm'] and exe:
pid_file = exe.replace('sbin/php-fpm', '/var/run/php-fpm.pid')
if os.path.exists(pid_file):
try:
pid = int(public.readFile(pid_file))
return public.pid_exists(pid)
except:
return False
elif pname in ['nginx']:
pidfile = '{}/nginx/logs/nginx.pid'.format(public.get_setup_path())
if os.path.exists(pidfile):
try:
pid = int(public.readFile(pidfile))
return public.pid_exists(pid)
except:
return False
elif pname in ['redis', 'redis-server']:
pidfile = '{}/redis/redis.pid'.format(public.get_setup_path())
if os.path.exists(pidfile):
try:
pid = int(public.readFile(pidfile))
status = public.pid_exists(pid)
if not status:
status = True if os.path.exists('/proc/{}/status'.format(pid)) else False
return status
except:
return False
elif pname in ['pure-ftpd']:
pidfile = '/var/run/pure-ftpd.pid'
if os.path.exists(pidfile):
try:
pid = int(public.readFile(pidfile))
return public.pid_exists(pid)
except:
return False
elif pname in ['mongod']:
pidfile = '{}/mongodb/log/configsvr.pid'.format(public.get_setup_path())
if os.path.exists(pidfile):
try:
pid = int(public.readFile(pidfile))
return public.pid_exists(pid)
except:
return False
elif pname in ['memcached']:
pidfile = '/var/run/memcached.pid'
if os.path.exists(pidfile):
try:
pid = int(public.readFile(pidfile))
return public.pid_exists(pid)
except:
return False
elif pname in ['apache', 'httpd']:
pidfile = '{}/apache/logs/httpd.pid'.format(public.get_setup_path())
if os.path.exists(pidfile):
try:
pid = int(public.readFile(pidfile))
return public.pid_exists(pid)
except:
return False
elif pname in ['rabbitmq-server', 'rabbitmq', 'epmd', 'beam.smp']:
pidfile = '/var/lib/rabbitmq/mnesia/rabbit@{}.pid'.format(public.get_hostname().split('.')[0])
if os.path.exists(pidfile):
try:
pid = int(public.readFile(pidfile))
return public.pid_exists(pid)
except:
return False
else:
return False
elif pname in ['sphinx', 'searchd']:
pidfile = '{}/sphinx/log/searchd.pid'.format(public.get_setup_path())
if os.path.exists(pidfile):
try:
pid = int(public.readFile(pidfile))
return public.pid_exists(pid)
except:
return False
else:
return False
if not self.pids: self.pids = psutil.pids()
for pid in self.pids:
try:
l = '/proc/%s/exe' % pid
f = '/proc/%s/comm' % pid
p_exe = ''
p_name = ''
if os.path.exists(l):
p_exe = os.readlink(l)
if not p_name: p_name = p_exe.split('/')[-1]
if not p_name and os.path.exists(f):
fp = open(f, 'r')
p_name = fp.read().strip()
fp.close()
if not p_name: continue
if p_name == pname:
if not exe:
return True
else:
if p_exe == exe:
return True
except:
continue
return False
# 取分页
def get_page(self, data, get):
# 包含分页类
import page
# 实例化分页类
page = page.Page()
info = {}
info['count'] = len(data)
try:
info['row'] = int(getattr(get, "row", self.ROWS))
info['p'] = int(getattr(get, "p", 1))
except ValueError:
info['p'] = 1
info['row'] = self.ROWS
info['uri'] = {}
info['return_js'] = ''
if hasattr(get, 'tojs'):
info['return_js'] = get.tojs
# 获取分页数据
result = {}
result['page'] = page.GetPage(info)
n = 0
result['data'] = []
for i in range(info['count']):
if n >= page.ROW: break
if i < page.SHIFT: continue
n += 1
result['data'].append(data[i])
return result
# 取列表
def GetList(self, get=None):
try:
if not os.path.exists(self.__list): return []
data = json.loads(public.readFile(self.__list))
# 排序
data = sorted(data, key=lambda b: b['sort'], reverse=False)
# 获取非划分列表
n = 0
for dirinfo in os.listdir(self.__install_path):
isTrue = True
for tm in data:
if tm['name'] == dirinfo: isTrue = False
if not isTrue: continue
path = self.__install_path + '/' + dirinfo
if os.path.isdir(path):
jsonFile = path + '/info.json'
if os.path.exists(jsonFile):
try:
tmp = json.loads(public.readFile(jsonFile))
if not hasattr(get, 'type'):
get.type = 0
else:
get.type = int(get.type)
if get.type > 0:
try:
if get.type != tmp['id']: continue
except:
continue
tmp['pid'] = len(data) + 1000 + n
tmp['status'] = tmp['display']
tmp['display'] = 0
data.append(tmp)
except:
pass
# 索引列表
if get:
display = None
if hasattr(get, 'display'): display = True
if not hasattr(get, 'type'):
get.type = 0
else:
get.type = int(get.type)
if not hasattr(get, 'search'):
search = None
m = 0
else:
search = get.search.encode('utf-8').lower()
m = 1
tmp = []
for d in data:
if d['id'] != 10000:
self.get_icon(d['name'])
if display:
if d['display'] == 0: continue
i = 0
if get.type > 0:
if get.type == d['id']: i += 1
else:
i += 1
if search:
if d['name'].lower().find(search) != -1: i += 1
if d['name'].find(search) != -1: i += 1
if d['title'].lower().find(search) != -1: i += 1
if d['title'].find(search) != -1: i += 1
if get.type > 0 and get.type != d['type']: i -= 1
if i > m: tmp.append(d)
data = tmp
return data
except Exception as ex:
return str(ex)
# 获取图标
def get_icon(self, name, downFile=None):
ico_type = 'png'
'''/static/img/soft_ico/'''
iconFile = 'BTPanel/static/img/soft_ico/ico-{}.{}'.format(name, ico_type)
vue_iconFile = 'BTPanel/static/vite/images/soft_ico/ico-{}.{}'.format(name, ico_type)
if not os.path.exists(iconFile):
public.run_thread(self.download_icon, (name, iconFile, vue_iconFile, downFile, ico_type))
else:
size = os.path.getsize(iconFile)
if size == 0:
try:
os.remove(iconFile)
except:
pass
public.run_thread(self.download_icon, (name, iconFile, vue_iconFile, downFile, ico_type))
# public.print_log('图标已存在', iconFile)
return "ico-{}.{}".format(name, ico_type)
return 'icon_plug.svg'
# 下载图标
def download_icon(self, name, iconFile, vue_iconFile, downFile, ico_type='png'):
srcIcon = 'plugin/' + name + '/icon.' + ico_type
srcIconSvg = 'plugin/' + name + '/icon.svg'
skey = name + '_icon'
if cache.get(skey): return None
if os.path.exists(srcIcon) and os.path.getsize(srcIcon):
shutil.copyfile(srcIcon, iconFile)
shutil.copyfile(srcIcon, vue_iconFile)
if os.path.exists(srcIconSvg) and os.path.getsize(srcIconSvg):
shutil.copyfile(srcIconSvg, iconFile)
shutil.copyfile(srcIconSvg, vue_iconFile)
cache.set(skey, 1, 86400)
# 取分页
def GetPage(self, data, get):
# 包含分页类
import page
# 实例化分页类
page = page.Page()
info = {}
info['count'] = len(data)
info['row'] = self.ROWS
info['p'] = 1
if hasattr(get, 'p'):
info['p'] = int(get['p'])
info['uri'] = {}
info['return_js'] = ''
if hasattr(get, 'tojs'):
info['return_js'] = get.tojs
# 获取分页数据
result = {}
result['page'] = page.GetPage(info)
n = 0
result['data'] = []
for i in range(info['count']):
if n > page.ROW: break
if i < page.SHIFT: continue
n += 1
result['data'].append(data[i])
return result
# 取分类
def GetType(self, get=None):
try:
if not os.path.exists(self.__type): return False
data = json.loads(public.readFile(self.__type))
return data
except:
return False
# 取单个
def GetFind(self, name):
try:
data = self.GetList(None)
for d in data:
if d['name'] == name: return d
return None
except:
return None
# 设置
def SetField(self, name, key, value):
data = self.GetList(None)
for i in range(len(data)):
if data[i]['name'] != name: continue
data[i][key] = value
public.writeFile(self.__list, json.dumps(data))
return True
# 安装插件
def install(self, get):
pluginInfo = self.GetFind(get.name)
if not pluginInfo:
import json
pluginInfo = json.loads(
public.readFile(self.__install_path + '/' + get.name +
'/info.json'))
if pluginInfo['tip'] == 'lib':
if not os.path.exists(self.__install_path + '/' +
pluginInfo['name']):
public.ExecShell('mkdir -p ' + self.__install_path + '/' +
pluginInfo['name'])
if not 'download_url' in session:
session['download_url'] = public.get_url()
download_url = session[
'download_url'] + '/install/plugin/' + pluginInfo[
'name'] + '/install.sh'
toFile = self.__install_path + '/' + pluginInfo[
'name'] + '/install.sh'
public.downloadFile(download_url, toFile)
self.set_pyenv(toFile)
public.ExecShell('/bin/bash ' + toFile + ' install')
if self.checksSetup(
pluginInfo['name'], pluginInfo['checks'],
pluginInfo['versions'])[0]['status'] or os.path.exists(
self.__install_path + '/' + get.name):
public.WriteLog('TYPE_SETUP', 'PLUGIN_INSTALL_LIB',
(pluginInfo['title'],))
# public.ExecShell('rm -f ' + toFile);
return public.returnMsg(True, 'PLUGIN_INSTALL_SUCCESS')
return public.returnMsg(False, 'PLUGIN_INSTALL_ERR')
else:
import db, time
path = '/www/server/php'
if not os.path.exists(path): public.ExecShell("mkdir -p " + path)
issue = public.readFile('/etc/issue')
if session['server_os']['x'] != 'RHEL': get.type = '3'
apacheVersion = 'false'
if public.get_webserver() == 'apache':
apacheVersion = public.xss_version(
public.readFile('/www/server/apache/version.pl'))
public.writeFile('/var/bt_apacheVersion.pl', apacheVersion)
public.writeFile('/var/bt_setupPath.conf',
public.GetConfigValue('root_path'))
isTask = '/tmp/panelTask.pl'
mtype = 'install'
mmsg = '安装'
if hasattr(get, 'upgrade'):
if get.upgrade:
mtype = 'update'
mmsg = 'upgrade'
execstr = "cd /www/server/panel/install && /bin/bash install_soft.sh " + get.type + " " + mtype + " " + get.name + " " + get.version
sql = db.Sql()
if hasattr(get, 'id'):
id = get.id
else:
id = None
public.M('tasks').add(
'id,name,type,status,addtime,execstr',
(None, mmsg + '[' + get.name + '-' + get.version + ']',
'execshell', '0', time.strftime('%Y-%m-%d %H:%M:%S'),
execstr))
public.writeFile(isTask, 'True')
public.WriteLog('TYPE_SETUP', 'PLUGIN_ADD',
(get.name, get.version))
return public.returnMsg(True, 'PLUGIN_INSTALL')
# 卸载插件
def unInstall(self, get):
pluginInfo = self.GetFind(get.name)
if not pluginInfo:
import json
pluginInfo = json.loads(
public.readFile(self.__install_path + '/' + get.name +
'/info.json'))
if pluginInfo['tip'] == 'lib':
if not os.path.exists(self.__install_path + '/' +
pluginInfo['name']):
public.ExecShell('mkdir -p ' + self.__install_path + '/' +
pluginInfo['name'])
download_url = session[
'download_url'] + '/install/plugin/' + pluginInfo[
'name'] + '/install.sh'
toFile = self.__install_path + '/' + pluginInfo[
'name'] + '/uninstall.sh'
install_sh = self.__install_path + '/' + pluginInfo[
'name'] + '/install.sh'
if not os.path.exists(toFile) and not os.path.exists(install_sh):
public.downloadFile(download_url, toFile)
self.set_pyenv(toFile)
pluginPath = self.__install_path + '/' + pluginInfo['name']
if os.path.exists(toFile):
public.ExecShell('/bin/bash {} uninstall'.format(toFile))
elif os.path.exists(pluginPath + '/install.sh'):
public.ExecShell('/bin/bash ' + pluginPath +
'/install.sh uninstall')
if os.path.exists(pluginPath):
public.ExecShell('rm -rf ' + pluginPath)
public.WriteLog('TYPE_SETUP', 'PLUGIN_UNINSTALL_SOFT',
(pluginInfo['title'],))
return public.returnMsg(True, 'PLUGIN_UNINSTALL')
else:
get.type = '0'
issue = public.readFile('/etc/issue')
if session['server_os']['x'] != 'RHEL': get.type = '3'
public.writeFile('/var/bt_setupPath.conf',
public.GetConfigValue('root_path'))
execstr = "cd /www/server/panel/install && /bin/bash install_soft.sh " + get.type + " uninstall " + get.name.lower(
) + " " + get.version.replace('.', '')
public.ExecShell(execstr)
public.WriteLog('TYPE_SETUP', 'PLUGIN_UNINSTALL',
(get.name, get.version))
return public.returnMsg(True, "PLUGIN_UNINSTALL")
# 取产品信息
def getProductInfo(self, productName):
if not self.__product_list:
import panelAuth
Auth = panelAuth.panelAuth()
self.__product_list = Auth.get_business_plugin(None)
for product in self.__product_list:
if product['name'] == productName: return product
return None
# 取到期时间
def getEndDate(self, pluginName):
if not self.__plugin_list:
import panelAuth
Auth = panelAuth.panelAuth()
tmp = Auth.get_plugin_list(None)
if not tmp: return '未开通'
if not 'data' in tmp: return '未开通'
self.__plugin_list = tmp['data']
for pluinfo in self.__plugin_list:
if pluinfo['product'] == pluginName:
if not pluinfo['endtime'] or not pluinfo['state']: return '待支付'
if pluinfo['endtime'] < time.time(): return '已到期'
return time.strftime("%Y-%m-%d",
time.localtime(pluinfo['endtime']))
return '未开通'
# 取插件列表
def getPluginList(self, get):
import json
arr = self.GetList(get)
result = {}
if not arr:
result['data'] = arr
result['type'] = self.GetType(None)
return result
apacheVersion = ""
try:
apavFile = '/www/server/apache/version.pl'
if os.path.exists(apavFile):
apacheVersion = public.xss_version(
public.readFile(apavFile).strip())
except:
pass
result = self.GetPage(arr, get)
arr = result['data']
for i in range(len(arr)):
arr[i]['end'] = '--'
# if 'price' in arr[i]:
# if arr[i]['price'] > 0:
# arr[i]['end'] = self.getEndDate(arr[i]['title']);
# if os.path.exists('plugin/beta/config.conf'):
# if os.path.exists('plugin/' + arr[i]['name'] + '/' + arr[i]['name'] + '_main.py') and arr[i]['end'] == '未开通': arr[i]['end'] = '--';
if arr[i]['name'] == 'php':
if apacheVersion == '2.2':
arr[i]['versions'] = '5.2,5.3,5.4'
arr[i]['update'] = self.GetPv(arr[i]['versions'],
arr[i]['update'])
elif apacheVersion == '2.4':
arr[i]['versions'] = '5.3,5.4,5.5,5.6,7.0,7.1,7.2,7.3,7.4'
arr[i]['update'] = self.GetPv(arr[i]['versions'],
arr[i]['update'])
arr[i]['apache'] = apacheVersion
arr[i]['versions'] = self.checksSetup(
arr[i]['name'].replace('_soft', ''), arr[i]['checks'],
arr[i]['versions'])
try:
arr[i]['update'] = arr[i]['update'].split(',')
except:
arr[i]['update'] = []
# 是否强制使用插件模板 LIB_TEMPLATE
if os.path.exists(self.__install_path + '/' + arr[i]['name']):
arr[i]['tip'] = 'lib'
if arr[i]['tip'] == 'lib':
arr[i]['path'] = self.__install_path + '/' + arr[i][
'name'].replace('_soft', '')
arr[i]['config'] = os.path.exists(arr[i]['path'] +
'/index.html')
else:
arr[i]['path'] = '/www/server/' + arr[i]['name'].replace(
'_soft', '')
arr.append(public.M('tasks').where("status!=?", ('1',)).count())
result['data'] = arr
result['type'] = self.GetType(None)
return result
# GetPHPV
def GetPv(self, versions, update):
versions = versions.split(',')
update = update.split(',')
updates = []
for up in update:
if up[:3] in versions: updates.append(up)
return ','.join(updates)
# 保存插件排序
def savePluginSort(self, get):
ssort = get.ssort.split('|')
data = self.GetList(None)
l = len(data)
for i in range(len(ssort)):
if int(ssort[i]) > 1000: continue
for n in range(l):
if data[n]['pid'] == int(ssort[i]): data[n]['sort'] = i
public.writeFile(self.__list, json.dumps(data))
return public.returnMsg(True, 'PLUGIN_SORT')
# 检查是否安装
def checksSetup(self, name, checks, vers=''):
tmp = checks.split(',')
versions = []
path = '/www/server/' + name + '/version.pl'
v1 = ''
if os.path.exists(path):
v1 = public.xss_version(public.readFile(path).strip())
if name == 'nginx': v1 = v1.replace('1.10', '1.12')
if not self.__tasks:
self.__tasks = public.M('tasks').where(
"status!=?", ('1',)).field('status,name').select()
isStatus = 0
versArr = vers.split(',')
for v in versArr:
version = {}
v2 = v
if name == 'php': v2 = v2.replace('.', '')
status = False
for tm in tmp:
if name == 'php':
path = '/www/server/php/' + v2
if os.path.exists(path +
'/bin/php') and not os.path.exists(
path + '/version.pl'):
public.ExecShell(
"echo `" + path +
"/bin/php 2>/dev/null -v|grep cli|awk '{print $2}'` > "
+ path + '/version.pl')
try:
v1 = public.xss_version(
public.readFile(path + '/version.pl').strip())
if not v1:
public.ExecShell('rm -f ' + path + '/version.pl')
except:
v1 = ""
if os.path.exists(tm.replace('VERSION', v2)): status = True
else:
if os.path.exists(tm) and isStatus == 0:
if len(versArr) > 1:
im = v1.find(v)
if im != -1 and im < 3:
status = True
isStatus += 1
else:
status = True
isStatus += 1
# 处理任务标记
if not self.__tasks:
self.__tasks = public.M('tasks').where(
"status!=?", ('1',)).field('status,name').select()
isTask = '1'
for task in self.__tasks:
tmpt = public.getStrBetween('[', ']', task['name'])
if not tmpt: continue
tmp1 = tmpt.split('-')
name1 = tmp1[0].lower()
if name == 'php':
if name1 == name and tmp1[1] == v: isTask = task['status']
else:
if name1 == 'pure': name1 = 'pure-ftpd'
if name1 == name: isTask = task['status']
infoFile = 'plugin/' + name + '/info.json'
if os.path.exists(infoFile):
try:
tmps = json.loads(public.readFile(infoFile))
if tmps: v1 = tmps['versions']
except:
pass
if name == 'memcached':
if os.path.exists('/etc/init.d/memcached'):
v1 = session.get('memcachedv')
if not v1:
v1 = public.ExecShell(
"memcached -V|awk '{print $2}'")[0].strip()
session['memcachedv'] = v1
if name == 'apache':
if os.path.exists('/www/server/apache/bin/httpd'):
v1 = session.get('httpdv')
if not v1:
v1 = public.ExecShell(
"/www/server/apache/bin/httpd -v|grep Apache|awk '{print $3}'|sed 's/Apache\///'"
)[0].strip()
session['httpdv'] = v1
# if name == 'mysql':
# if os.path.exists('/www/server/mysql/bin/mysql'): v1 = public.ExecShell("mysql -V|awk '{print $5}'|sed 's/,//'")[0].strip();
version['status'] = status
version['version'] = v
version['task'] = isTask
version['no'] = v1
versions.append(version)
return self.checkRun(name, versions)
# 检查是否启动
def checkRun(self, name, versions):
if name == 'php':
path = '/www/server/php'
pids = psutil.pids()
for i in range(len(versions)):
if versions[i]['status']:
v4 = versions[i]['version'].replace('.', '')
versions[i]['run'] = os.path.exists('/tmp/php-cgi-' + v4 +
'.sock')
pid_file = path + '/' + v4 + '/var/run/php-fpm.pid'
versions[i]['process_id'] = public.readFile(pid_file)
if versions[i]['run'] and os.path.exists(pid_file):
if not int(public.readFile(pid_file)) in pids:
versions[i]['run'] = False
versions[i]['fpm'] = os.path.exists(
'/etc/init.d/php-fpm-' + v4)
phpConfig = self.GetPHPConfig(v4)
versions[i]['max'] = phpConfig['max']
versions[i]['maxTime'] = phpConfig['maxTime']
versions[i]['pathinfo'] = phpConfig['pathinfo']
versions[i]['display'] = os.path.exists(path + '/' + v4 +
'/display.pl')
if len(versions) < 5: versions[i]['run'] = True
elif name == 'nginx':
status = False
if os.path.exists('/etc/init.d/nginx'):
pidf = '/www/server/nginx/logs/nginx.pid'
if os.path.exists(pidf):
try:
pid = public.readFile(pidf)
pname = self.checkProcess(pid)
if pname: status = True
except:
status = False
for i in range(len(versions)):
versions[i]['run'] = False
if versions[i]['status']: versions[i]['run'] = status
elif name == 'apache':
status = False
if os.path.exists('/etc/init.d/httpd'):
pidf = '/www/server/apache/logs/httpd.pid'
if os.path.exists(pidf):
pid = public.readFile(pidf)
status = self.checkProcess(pid)
for i in range(len(versions)):
versions[i]['run'] = False
if versions[i]['status']: versions[i]['run'] = status
elif name == 'mysql':
status = os.path.exists('/tmp/mysql.sock')
for i in range(len(versions)):
versions[i]['run'] = False
if versions[i]['status']: versions[i]['run'] = status
elif name == 'tomcat':
status = False
if os.path.exists('/www/server/tomcat/logs/catalina-daemon.pid'):
if self.getPid('jsvc'): status = True
if not status:
if self.getPid('java'): status = True
for i in range(len(versions)):
versions[i]['run'] = False
if versions[i]['status']: versions[i]['run'] = status
elif name == 'pure-ftpd':
for i in range(len(versions)):
pidf = '/var/run/pure-ftpd.pid'
if os.path.exists(pidf):
pid = public.readFile(pidf)
versions[i]['run'] = self.checkProcess(pid)
if not versions[i]['run']:
public.ExecShell('rm -f ' + pidf)
elif name == 'phpmyadmin':
for i in range(len(versions)):
if versions[i]['status']:
versions[i] = self.getPHPMyAdminStatus()
elif name == 'redis':
for i in range(len(versions)):
pidf = '/var/run/redis_6379.pid'
if os.path.exists(pidf):
pid = public.readFile(pidf)
versions[i]['run'] = self.checkProcess(pid)
if not versions[i]['run']:
public.ExecShell('rm -f ' + pidf)
elif name == 'memcached':
for i in range(len(versions)):
pidf = '/var/run/memcached.pid'
if os.path.exists(pidf):
pid = public.readFile(pidf)
versions[i]['run'] = self.checkProcess(pid)
if not versions[i]['run']:
public.ExecShell('rm -f ' + pidf)
else:
for i in range(len(versions)):
if versions[i]['status']: versions[i]['run'] = True
return versions
# 取PHPMyAdmin状态
def getPHPMyAdminStatus(self):
import re
tmp = {}
setupPath = '/www/server'
configFile = setupPath + '/nginx/conf/nginx.conf'
pauth = False
pstatus = False
phpversion = "00"
phpport = '888'
if os.path.exists(configFile):
conf = public.readFile(configFile)
rep = r"listen\s+([0-9]+)\s*;"
rtmp = re.search(rep, conf)
if rtmp:
phpport = rtmp.groups()[0]
if conf.find('AUTH_START') != -1: pauth = True
if conf.find(setupPath + '/stop') == -1: pstatus = True
configFile = setupPath + '/nginx/conf/enable-php.conf'
if not os.path.exists(configFile):
public.writeFile(
configFile,
public.readFile(setupPath +
'/nginx/conf/enable-php-54.conf'))
conf = public.readFile(configFile)
rep = r"php-cgi-([0-9]+)\.sock"
rtmp = re.search(rep, conf)
if rtmp:
phpversion = rtmp.groups()[0]
else:
rep = r'127.0.0.1:10(\d{2,2})1'
rtmp = re.findall(rep, conf)
if rtmp:
phpversion = rtmp[0]
else:
rep = r"php-cgi.*\.sock"
public.writeFile(configFile, conf)
phpversion = '00'
configFile = setupPath + '/apache/conf/extra/httpd-vhosts.conf'
if os.path.exists(configFile):
conf = public.readFile(configFile)
rep = r"php-cgi-([0-9]+)\.sock"
rtmp = re.search(rep, conf)
if rtmp:
phpversion = rtmp.groups()[0]
rep = r"Listen\s+([0-9]+)\s*\n"
rtmp = re.search(rep, conf)
if rtmp:
phpport = rtmp.groups()[0]
if conf.find('AUTH_START') != -1: pauth = True
if conf.find('/www/server/stop') == -1: pstatus = True
if os.path.exists('/usr/local/lsws/bin/lswsctrl'):
result = self._get_ols_myphpadmin_info()
phpversion = result['php_version']
phpport = result['php_port']
pauth = result['pauth']
pstatus = result['pstatus']
try:
vfile = setupPath + '/phpmyadmin/version.pl'
if os.path.exists(vfile):
tmp['version'] = public.xss_version(
public.readFile(vfile).strip())
tmp['status'] = True
tmp['no'] = tmp['version']
else:
tmp['version'] = ""
tmp['status'] = False
tmp['no'] = ""
tmp['run'] = pstatus
tmp['phpversion'] = phpversion
tmp['port'] = phpport
tmp['auth'] = pauth
except Exception as ex:
tmp['status'] = False
tmp['error'] = str(ex)
return tmp
def _get_ols_myphpadmin_info(self):
filename = "/www/server/panel/vhost/openlitespeed/detail/phpmyadmin.conf"
conf = public.readFile(filename)
reg = '/usr/local/lsws/lsphp(\d+)/bin/lsphp'
php_v = re.search(reg, conf)
phpversion = '73'
phpport = '888'
if php_v:
phpversion = php_v.groups(1)
filename = '/www/server/panel/vhost/openlitespeed/listen/888.conf'
conf = public.readFile(filename)
reg = 'address\s+\*\:(\d+)'
php_port = re.search(reg, conf)
if php_port:
phpport = php_port.groups(1)
pauth = False
pstatus = False
if conf.find('/www/server/stop') == -1: pstatus = True
return {
'php_version': phpversion,
'php_port': phpport,
'pauth': pauth,
'pstatus': pstatus
}
# 取PHP配置
def GetPHPConfig(self, version):
import re
setupPath = '/www/server'
file = setupPath + "/php/" + version + "/etc/php.ini"
phpini = public.readFile(file)
file = setupPath + "/php/" + version + "/etc/php-fpm.conf"
phpfpm = public.readFile(file)
data = {}
try:
rep = r"upload_max_filesize\s*=\s*([0-9]+)M"
tmp = re.search(rep, phpini).groups()
data['max'] = tmp[0]
except:
data['max'] = '50'
try:
rep = r"request_terminate_timeout\s*=\s*([0-9]+)\n"
tmp = re.search(rep, phpfpm).groups()
data['maxTime'] = tmp[0]
except:
data['maxTime'] = 0
try:
rep = r"\n;*\s*cgi\.fix_pathinfo\s*=\s*([0-9]+)\s*\n"
tmp = re.search(rep, phpini).groups()
if tmp[0] == '1':
data['pathinfo'] = True
else:
data['pathinfo'] = False
except:
data['pathinfo'] = False
return data
# 名取PID
def getPid(self, pname):
try:
if not self.pids: self.pids = psutil.pids()
for pid in self.pids:
if psutil.Process(pid).name() == pname: return True
return False
except:
return True
# 检测指定进程是否存活
def checkProcess(self, pid):
try:
if not self.pids: self.pids = psutil.pids()
if int(pid) in self.pids: return True
return False
except:
return False
# 获取配置模板
def getConfigHtml(self, get):
filename = self.__install_path + '/' + get.name + '/index.html'
if not os.path.exists(filename):
return public.returnMsg(False, 'PLUGIN_GET_HTML')
mimetype = 'text/html'
cache_time = 0 if public.is_debug() else 86400
try:
self.plugin_open_total(get.name)
except:
pass
import flask
if flask.__version__ < "2.1.0":
return send_file(filename,
mimetype=mimetype,
as_attachment=True,
add_etags=True,
conditional=True,
cache_timeout=cache_time)
else:
return send_file(filename,
mimetype=mimetype,
as_attachment=True,
etag=True,
conditional=True,
max_age=cache_time)
def creatab_open_total_table(self, sql):
'''
@name 创建插件打开统计表
@author hwliang<2021-06-26>
@param sql<db.Sql> 数据库对像
@return void
'''
if not sql.table('sqlite_master').where(
'type=? AND name=?', ('table', 'open_total')).count():
csql = '''CREATE TABLE IF NOT EXISTS `open_total` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`plugin_name` REAL,
`num` INTEGER
)'''
sql.execute(csql, ())
def plugin_open_total(self, plugin_name):
'''
@name 插件打开统计
@author hwliang<2021-06-26>
@param plugin_name<string> 插件名称
@return void
'''
import db
sql = db.Sql().dbfile('plugin_total')
self.creatab_open_total_table(sql)
pdata = {"plugin_name": plugin_name, "num": 1}
num = sql.table('open_total').where('plugin_name=?',
plugin_name).getField('num')
if not num:
sql.table('open_total').insert(pdata)
else:
sql.table('open_total').where('plugin_name=?',
plugin_name).setField(
'num', num + 1)
def get_usually_plugin(self, get):
'''
@name 获取常用插件
@author hwliang<2021-06-26>
@param get<obj_dict>
@return list
'''
import db
sql = db.Sql().dbfile('plugin_total')
self.creatab_open_total_table(sql)
plugin_list = sql.table('open_total').order('num desc').limit(
10).select()
usually_list = []
for p in plugin_list:
plugin_info = self.get_soft_find(p['plugin_name'])
if plugin_info:
if plugin_info['setup']:
usually_list.append(plugin_info)
if len(usually_list) >= 5: break
return usually_list
def get_plugin_upgrades(self, get):
'''
@name 获取指定插件的近期更新历史
@author hwliang<2021-06-30>
@param get<obj_dict>{
plugin_name: string 插件名称
}
@return list
'''
plugin_name = get.get("plugin_name", None)
if not plugin_name:
return []
if getattr(get, 'show', 0):
plugin_info = self.__get_plugin_find(plugin_name)
if plugin_info and 'versions' in plugin_info:
return plugin_info['versions']
return []
else:
return self.__get_plugin_upgrades(plugin_name)
# 取插件信息
def getPluginInfo(self, get):
try:
pluginInfo = self.GetFind(get.name)
apacheVersion = ""
try:
apavFile = '/www/server/apache/version.pl'
if os.path.exists(apavFile):
apacheVersion = public.xss_version(
public.readFile(apavFile).strip())
except:
pass
if pluginInfo['name'] == 'php':
if apacheVersion == '2.2':
pluginInfo['versions'] = '5.2,5.3,5.4'
elif apacheVersion == '2.4':
pluginInfo[
'versions'] = '5.3,5.4,5.5,5.6,7.0,7.1,7.2,7.3,7.4'
pluginInfo['versions'] = self.checksSetup(pluginInfo['name'],
pluginInfo['checks'],
pluginInfo['versions'])
if get.name == 'php':
pluginInfo['phpSort'] = public.readFile(
'/www/server/php/sort.pl')
return pluginInfo
except:
return False
# 取插件状态
def getPluginStatus(self, get):
find = self.GetFind(get.name)
versions = []
path = '/www/server/php'
for version in find['versions'].split(','):
tmp = {}
tmp['version'] = version
if get.name == 'php':
tmp['status'] = os.path.exists(path + '/' +
version.replace(',', '') +
'/display.pl')
else:
tmp['status'] = find['status']
versions.append(tmp)
return versions
# 设置插件状态
def setPluginStatus(self, get):
if get.name == 'php':
isRemove = True
path = '/www/server/php'
if get.status == '0':
versions = self.GetFind(get.name)['versions']
if os.path.exists(path + '/' + get.version.replace('.', '') + '/display.pl'):
os.remove(path + '/' + get.version.replace('.', '') + '/display.pl')
for version in versions.split(','):
if os.path.exists(path + '/' + version.replace('.', '') +
'/display.pl'):
isRemove = False
break
else:
public.writeFile(
path + '/' + get.version.replace('.', '') + '/display.pl',
'True')
if isRemove:
self.SetField(get.name, 'display', int(get.status))
else:
self.SetField(get.name, 'display', int(get.status))
return public.returnMsg(True, 'SET_SUCCESS')
# 从云端获取插件列表
def getCloudPlugin(self, get):
if session.get('getCloudPlugin') and get != None:
return public.returnMsg(True, '您的插件列表已经是最新版本-1!')
import json
if not session.get('download_url'):
session['download_url'] = public.get_url()
# 获取列表
try:
newUrl = public.get_url()
if os.path.exists('plugin/beta/config.conf'):
download_url = newUrl + '/install/list.json'
else:
download_url = newUrl + '/install/list_pro.json'
data = json.loads(public.httpGet(download_url))
session['download_url'] = newUrl
except:
download_url = session['download_url'] + '/install/list_pro.json'
data = json.loads(public.httpGet(download_url))
n = i = j = 0
lists = self.GetList(None)
for i in range(len(data)):
for pinfo in lists:
if data[i]['name'] != pinfo['name']: continue
data[i]['display'] = pinfo['display']
if data[i]['default']:
get.name = data[i]['name']
self.install(get)
public.writeFile(self.__list, json.dumps(data))
# 获取分类
try:
download_url = session['download_url'] + '/install/type.json'
types = json.loads(public.httpGet(download_url))
public.writeFile(self.__type, json.dumps(types))
except:
pass
self.getCloudPHPExt(get)
self.GetCloudWarning(get)
session['getCloudPlugin'] = True
return public.returnMsg(True, 'PLUGIN_UPDATE')
# 刷新缓存
def flush_cache(self, get):
self.getCloudPlugin(None)
return public.returnMsg(True, '软件列表已更新!')
# 获取PHP扩展
def getCloudPHPExt(self, get=None):
import json
try:
key = 'php_ext_cache'
if cache.get(key): return 1
surl = public.get_url()
download_url = surl + '/install/lib/phplib.json'
tstr = public.httpGet(download_url)
data = json.loads(tstr)
if not data: return 2
public.writeFile('data/phplib.conf', json.dumps(data))
# download_url = surl + '/license/md5.txt'
# li_md5 = public.httpGet(download_url)
# if not li_md5: return 3
# li_md5 = li_md5.strip()
# if len(li_md5) != 32: return 4
# l_file = 'BTPanel/templates/default/license.html'
# lfile2 = 'data/license.md5'
# old_md5 = ''
# if os.path.exists(lfile2):
# old_md5 = public.readFile(lfile2)
# if li_md5 != old_md5:
# download_url = surl + '/license/license.html'
# public.downloadFile(download_url,l_file)
# old_md5 = public.FileMd5(l_file)
# if li_md5 == old_md5:
# public.writeFile(lfile2,old_md5)
# s_file = 'data/licenes.pl'
# if os.path.exists(s_file):
# os.remove(s_file)
cache.set(key, 86400)
return True
except:
return public.get_error_info()
# 刷新PHP扩展列表
def RegetCloudPHPExt(self, get=None):
import json
surl = public.get_url()
download_url = surl + '/install/lib/phplib.json'
tstr = public.httpGet(download_url)
data = json.loads(tstr)
if not data: return False
public.writeFile('data/phplib.conf', json.dumps(data))
return public.returnMsg(True, "更新成功!")
# 获取警告列表
def GetCloudWarning(self, get):
import json
if not session.get('download_url'):
session['download_url'] = public.get_url()
download_url = session['download_url'] + '/install/warning.json'
tstr = public.httpGet(download_url)
data = json.loads(tstr)
if not data: return False
wfile = 'data/warning.json'
wlist = json.loads(public.readFile(wfile))
for i in range(len(data['data'])):
for w in wlist['data']:
if data['data'][i]['name'] != w['name']: continue
data['data'][i]['ignore_count'] = w['ignore_count']
data['data'][i]['ignore_time'] = w['ignore_time']
public.writeFile(wfile, json.dumps(data))
return data
# 名取标题
def get_title_byname(self, get):
get.sName = get.name
find = self.get_soft_find(get)
return find['title']
# 数据解密
def _decrypt(self, data):
import PluginLoader
if not isinstance(data, str): return data
if not data: return data
if data.startswith('BT-0x:'):
res = PluginLoader.db_decrypt(data[6:])['msg']
return res
return data
# 请求插件事件
def a(self, get):
if not hasattr(get, 'name'):
return public.returnMsg(False, 'PLUGIN_INPUT_A')
try:
uid = session.get('uid')
if uid != 1 and uid:
if os.path.exists('/www/server/panel/plugin/users/authority/{}'.format(uid)):
user = public.readFile('/www/server/panel/plugin/users/authority/{}'.format(uid))
user = json.loads(self._decrypt(user))
if user['role'] != 'administrator':
if os.path.exists('/www/server/panel/plugin/users/authority/{}.plugin'.format(uid)):
data = public.readFile('/www/server/panel/plugin/users/authority/{}.plugin'.format(uid))
data = json.loads(self._decrypt(data))
if get.name not in data['plugin']:
return public.returnMsg(False, '您无操作此插件的权限,请联系管理员添加此插件的权限!')
else:
return public.returnMsg(False, '您无操作此插件的权限,请联系管理员添加此插件的权限!')
if not hasattr(get, 's'):
return public.returnMsg(True, '权限正常!')
except:
pass
try:
p = Plugin(get.name)
return p.exec_fun(get)
except:
return public.get_error_object(None, plugin_name=get.name)
# 上传插件包
def update_zip(self, get=None, tmp_file=None, update=False):
tmp_path = '/www/server/panel/temp'
if not os.path.exists(tmp_path):
os.makedirs(tmp_path, mode=384)
if tmp_file:
if not os.path.exists(tmp_file):
return public.returnMsg(False, '文件下载失败!')
if get:
public.ExecShell("rm -rf " + tmp_path + '/*')
tmp_file = tmp_path + '/plugin_tmp.zip'
plugin_zip_path = get.get("plugin_zip_path", "")
if plugin_zip_path:
if not plugin_zip_path.endswith('.zip'):
tmp_file = tmp_path + '/plugin_tmp.tar.gz'
if not os.path.exists(plugin_zip_path):
return public.returnMsg(False, '文件不存在!')
shutil.move(plugin_zip_path, tmp_file)
else:
from werkzeug.utils import secure_filename
from flask import request
f = request.files['plugin_zip']
if f.filename[-4:] != '.zip':
tmp_file = tmp_path + '/plugin_tmp.tar.gz'
f.save(tmp_file)
import panelTask
panelTask.bt_task()._unzip(tmp_file, tmp_path, '', '/dev/null')
os.remove(tmp_file)
p_info = tmp_path + '/info.json'
if not os.path.exists(p_info):
d_path = None
for df in os.walk(tmp_path):
if len(df[2]) < 3: continue
if not 'info.json' in df[2]: continue
if not 'install.sh' in df[2]: continue
if not os.path.exists(df[0] + '/info.json'): continue
d_path = df[0]
if d_path:
tmp_path = d_path
p_info = tmp_path + '/info.json'
try:
try:
data = json.loads(public.ReadFile(p_info))
except:
data = json.loads(public.ReadFile(p_info).decode('utf-8-sig'))
data['size'] = public.get_path_size(tmp_path)
if not 'author' in data: data['author'] = '未知'
if not 'home' in data:
data['home'] = 'https://www.bt.cn/bbs/forum-40-1.html'
plugin_path = '/www/server/panel/plugin/' + data[
'name'] + '/info.json'
data['old_version'] = '0'
data['tmp_path'] = tmp_path
if os.path.exists(plugin_path):
try:
old_info = json.loads(public.ReadFile(plugin_path))
data['old_version'] = old_info['versions']
except:
pass
except:
public.ExecShell("rm -rf " + tmp_path + '/*')
return public.returnMsg(False, '在压缩包中没有找到插件信息,请检查插件包!')
data['update'] = update
return data
# 导入插件包
def input_zip(self, get):
if not os.path.exists(get.tmp_path):
return public.returnMsg(False, '临时文件不存在,请重新上传!')
plugin_path = '/www/server/panel/plugin/' + get.plugin_name
if not os.path.exists(plugin_path): os.makedirs(plugin_path)
public.ExecShell("\cp -a -r " + get.tmp_path + '/* ' + plugin_path +
'/')
public.ExecShell('chmod -R 600 ' + plugin_path)
self.set_pyenv(plugin_path + '/install.sh')
public.ExecShell('cd ' + plugin_path +
' && bash install.sh install &> /tmp/panelShell.pl')
p_info = public.ReadFile(plugin_path + '/info.json')
public.ExecShell("rm -rf /www/server/panel/temp/*")
public.start_syssafe() # 恢复系统加固服务
if p_info:
# ----- 增加图标复制 hwliang<2021-03-23>,增加svg图标 2023-07-31 -----#
for ico_file in ['svg', 'png']:
icon_sfile = plugin_path + '/icon.' + ico_file
icon_dfile = '/www/server/panel/BTPanel/static/img/soft_ico/ico-{}.{}'.format(get.plugin_name, ico_file)
if os.path.exists(plugin_path + '/icon.' + ico_file):
import shutil
shutil.copyfile(icon_sfile, icon_dfile)
# ----- 增加图标复制 END -----#
public.WriteLog('软件管理',
'安装第三方插件[%s]' % json.loads(p_info)['title'])
return public.returnMsg(True, '安装成功!')
public.ExecShell("rm -rf " + plugin_path)
return public.returnMsg(False, '安装失败!')
# 导出插件包
def export_zip(self, get):
plugin_path = '/www/server/panel/plugin/' + get.plugin_name
if not os.path.exists(plugin_path):
return public.returnMsg(False, '指定插件不存在!')
get.sfile = plugin_path + '/'
get.dfile = '/www/server/panel/temp/bt_plugin_' + get.plugin_name + '.zip'
get.type = 'zip'
import files
files.files().Zip(get)
if not os.path.exists(get.dfile):
return public.returnMsg(False, '导出失败,请检查权限!')
return public.returnMsg(True, get.dfile)
# 获取编译参数
def get_make_args(self, get):
config_path = 'install/' + get.name
if not os.path.exists(config_path):
os.makedirs(config_path)
# 读支持的编译参数列表
make_args = []
for p_name in os.listdir(config_path):
path = os.path.join(config_path, p_name)
if not os.path.isdir(path): continue
make_info = {"name": p_name, "init": "", "args": "", "ps": ""}
init_file = os.path.join(path, 'init.sh')
args_file = os.path.join(path, 'args.pl')
ps_file = os.path.join(path, 'ps.pl')
if not os.path.exists(args_file):
continue
if os.path.exists(init_file):
make_info['init'] = public.readFile(init_file)
if os.path.exists(ps_file):
make_info['ps'] = public.readFile(ps_file)
make_info['args'] = public.readFile(args_file)
make_args.append(make_info)
# 读当前配置
data = {'args': make_args, 'config': ''}
config_file = config_path + '/config.pl'
if os.path.exists(config_file):
data['config'] = public.readFile(config_file)
return data
# 添加编译参数
def add_make_args(self, get):
get.args_name = get.args_name.strip()
get.name = get.name.strip()
get.ps = get.ps.strip()
if not re.match(r'^\w+$', get.args_name):
return public.returnMsg(False, '名称不合规只能是数字、字母、下划线')
config_path = os.path.join('install', get.name, get.args_name)
if not os.path.exists(config_path):
os.makedirs(config_path, 384)
init_file = os.path.join(config_path, 'init.sh')
args_file = os.path.join(config_path, 'args.pl')
ps_file = os.path.join(config_path, 'ps.pl')
public.writeFile(init_file, get.init.replace("\r\n", "\n"))
public.writeFile(args_file, get.args)
public.writeFile(ps_file, get.ps)
public.WriteLog('软件管理',
'添加自定义编译参数: {}:{}'.format(get.name, get.args_name))
return public.returnMsg(True, '添加成功!')
# 删除编译参数
def del_make_args(self, get):
get.args_name = get.args_name.strip()
get.name = get.name.strip()
if not re.match(r'^\w+$', get.args_name):
return public.returnMsg(False, '名称不合规只能是数字、字母、下划线')
config_path = os.path.join('install', get.name, get.args_name)
if not os.path.exists(config_path):
return public.returnMsg(False, '指定自定义编译参数不存在!')
public.ExecShell("rm -rf {}".format(config_path))
config_file = 'install/' + get.name + '/config.pl'
if os.path.exists(config_file):
config_data = public.readFile(config_file).split("\n")
if get.args_name in config_data:
config_data.remove(get.args_name)
public.writeFile(config_file, "\n".join(config_data))
public.WriteLog('软件管理',
'删除自定义编译参数: {}:{}'.format(get.name, get.args_name))
return public.returnMsg(True, '删除成功!')
# 设置当前编译参数
def set_make_args(self, get):
get.args_names = get.args_names.strip().split("\n")
get.name = get.name.strip()
config_file = 'install/' + get.name + '/config.pl'
config_data = []
for args_name in get.args_names:
path = 'install/' + get.name + '/' + args_name
if not os.path.exists(path): continue
if args_name in config_data: continue
config_data.append(args_name)
public.writeFile(config_file, "\n".join(config_data))
public.WriteLog(
'软件管理', '设置软件: {} 的自定义编译参数配置为: {}'.format(get.name, config_data))
return public.returnMsg(True, '设置成功!')
def get_mac_address(self):
import uuid
mac = uuid.UUID(int=uuid.getnode()).hex[-12:]
return ":".join([mac[e:e + 2] for e in range(0, 11, 2)])
# 获取云端帐户状态
def get_cloud_list_status(self, get):
try:
ikey = 'cloud_list_status'
if cache.get(ikey): return False
pdata = public.get_user_info()
if pdata['uid'] == -1: return '2'
pdata['mac'] = self.get_mac_address()
list_body = public.HttpPost(self._check_url, pdata)
if not list_body: return False
cache.set(ikey, 1, 600)
list_body = json.loads(list_body)
if not list_body['status'] and pdata['uid'] != -1:
public.writeFile(self.__path_error, "error")
msg = '''{% extends "layout.html" %}
{% block content %}
<div class="main-content pb55" style="min-height: 525px;">
<div class="container-fluid">
<div class="site_table_view bgw mtb15 pd15 text-center">
<div style="padding:50px">
<h1 class="h3"></h1>
'''
msg += list_body['title'] + list_body['body']
msg += '''
</div>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
{% endblock %}'''
public.writeFile(self.__error_html, msg)
return '3'
else:
if os.path.exists(self.__path_error):
os.remove(self.__path_error)
if os.path.exists(self.__error_html):
os.remove(self.__error_html)
return '2'
except:
if os.path.exists(self.__path_error): os.remove(self.__path_error)
if os.path.exists(self.__error_html): os.remove(self.__error_html)
return '1'
# 获取用户信息
def get_user_info(self):
user_file = '{}/data/userInfo.json'.format(public.get_panel_path())
if not os.path.exists(user_file): return {}
userInfo = {}
try:
userTmp = json.loads(public.readFile(user_file))
if not 'serverid' in userTmp or len(userTmp['serverid']) != 64:
import panelAuth
userTmp = panelAuth.panelAuth().create_serverid(None)
userInfo['uid'] = userTmp['uid']
userInfo['username'] = userTmp['username']
userInfo['secret_key'] = userTmp['secret_key']
userInfo['access_key'] = userTmp['access_key']
return userInfo
except:
pass
return False
# 判断是否解除绑定
def is_verify_unbinding(self, get):
try:
ikey = 'verify_unbinding'
if cache.get(ikey): return True
path = '{}/data/userInfo.json'.format(public.get_panel_path())
pdata = self.get_user_info()
if not pdata: return 'None'
list_body = public.HttpPost(self._unbinding_url, pdata)
if not list_body: return False
cache.set(ikey, 1, 600)
list_body = json.loads(list_body)
if not list_body['status']:
if os.path.exists(path): os.remove(path)
return False
return True
except:
pass
@classmethod
def __pull_popular_searches(cls):
data_popular_searches = os.path.join(public.get_panel_path(), "data/popular_searches.json")
if os.path.exists(data_popular_searches):
if time.time() - os.path.getmtime(data_popular_searches) < 86400:
try:
json.loads(public.readFile(data_popular_searches))
return
except:
pass
try:
resp = public.httpGet("{}/src/popular_searches.json".format(public.get_url()))
popular_searches = json.loads(resp)
if isinstance(popular_searches["push_plugin"], list):
public.writeFile(data_popular_searches, resp)
except:
pass
# 获取插件推荐列表
def push_plugin(self, get):
path = public.get_panel_path()
config_popular_searches = os.path.join(path, "config/popular_searches.json")
data_popular_searches = os.path.join(path, "data/popular_searches.json")
public.run_thread(self.__pull_popular_searches, ())
data = {}
try:
data = json.loads(public.readFile(data_popular_searches))
except:
pass
if 'push_plugin' in data and isinstance(data["push_plugin"], list):
return data["push_plugin"]
try:
data = json.loads(public.readFile(config_popular_searches))
if isinstance(data["push_plugin"], list):
return data["push_plugin"]
except:
pass
return []
@staticmethod
def check_plugin_settings(get=None):
try:
name = get.soft_name.strip()
except:
return public.returnMsg(False, "参数错误")
return SettingMager().check_plugin_settings(name)
@staticmethod
def save_plugin_settings(get=None):
try:
name = get.soft_name.strip()
except:
return public.returnMsg(False, "参数错误")
return SettingMager().save_plugin_settings(name)
@staticmethod
def load_plugin_settings(get=None):
try:
name = get.soft_name.strip()
except:
return public.returnMsg(False, "参数错误")
return SettingMager().load_plugin_settings(name)
@staticmethod
def create_install_wait_msg(task_id, soft, is_update):
from panel_msg.msg_file import message_mgr
install_status = "安装" if not is_update else "更新"
file_path = "/tmp/panelExec.log"
if not os.path.exists(file_path):
public.writeFile(file_path, "")
data = {
"soft_name": soft,
"install_status": "等待" + install_status + soft,
"file_name": file_path,
"self_type": "soft_install",
"status": 0,
"task_id": task_id
}
title = "等待" + install_status + soft
res = message_mgr.collect_message(title, ["软件商店", soft], data)
if isinstance(res, str):
public.WriteLog("消息盒子", "安装信息收集失败")
return None
return res
def set_plugin_ignore(self, get):
try:
if not hasattr(get, 'plugin_name') or not hasattr(get, 'plugin_version'):
return public.returnMsg(False, "参数错误")
if os.path.exists('/www/server/panel/data/plugin_ignore.json'):
plugin_ignore = json.loads(public.readFile('/www/server/panel/data/plugin_ignore.json'))
else:
plugin_ignore = []
data = {'name': get.plugin_name, 'version': get.plugin_version}
if data in plugin_ignore:
plugin_ignore.remove(data)
else:
plugin_ignore.append(data)
public.writeFile('/www/server/panel/data/plugin_ignore.json', json.dumps(plugin_ignore))
return public.returnMsg(True, "设置成功")
except:
return public.returnMsg(False, "设置失败")
def get_plugin_ignore(self, get):
if os.path.exists('/www/server/panel/data/plugin_ignore.json'):
plugin_ignore = json.loads(public.readFile('/www/server/panel/data/plugin_ignore.json'))
else:
plugin_ignore = []
return plugin_ignore
def check_install_lib(self, mtype):
task_data = public.M('tasks').where("name = ? AND status != ?", ("安装必要环境库", "1")).find()
if task_data:
# 已有安装必要环境库的进行中的任务了,不在添加
return None
need = False
# Install_Lib
if not os.path.exists("/etc/bt_lib.lock") and not os.path.exists("/etc/btlib.lock"):
need = True
# # Install_Openssl
# if not os.path.exists("/usr/local/openssl/lib/libssl.so"):
# need = True
# # Install_Pcre
# shell_str = "pcre-config --version|grep '^8.' 2>&1"
# out, _ = public.ExecShell(shell_str)
# if out.strip() == "":
# need = True
# # Install_Curl
# if not os.path.exists("/usr/local/curl/bin/curl"):
# need = True
# # Install_Mhash
# if not os.path.exists("/usr/local/lib/libmhash.so"):
# need = True
# # Install_Libmcrypt
# if not os.path.exists("/usr/local/lib/libmcrypt.so"):
# need = True
# # Install_Mcrypt
# if not (os.path.exists("/usr/bin/mcrypt") or os.path.exists("/usr/local/bin/mcrypt")):
# need = True
# # IInstall_Libiconv
# if not os.path.exists("/usr/local/libiconv"):
# need = True
# # Install_Freetype
# if not (os.path.exists("/usr/bin/freetype-config") or os.path.exists(
# "/usr/local/freetype/bin/freetype-config")):
# need = True
if need:
install_lib = """
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
mtype=$1
. /www/server/panel/install/public.sh
serverUrl=$NODE_URL/install
if [ ! -f 'lib.sh' ];then
wget --no-check-certificate -O lib.sh $serverUrl/$mtype/lib.sh
fi
libNull=`cat lib.sh`
if [ "$libNull" == '' ];then
wget --no-check-certificate -O lib.sh $serverUrl/$mtype/lib.sh
fi
bash lib.sh
echo '|-Successify --- 命令已执行! ---'
"""
public.writeFile("/www/server/panel/install/pre_install_lib.sh", install_lib)
execstr = "cd /www/server/panel/install && /bin/bash pre_install_lib.sh {}".format(mtype)
task_id = public.M('tasks').add(
'id,name,type,status,addtime,execstr',
(None, "安装必要环境库", 'execshell', '0', time.strftime('%Y-%m-%d %H:%M:%S'), execstr))
msg = self.create_install_wait_msg(task_id, "必要环境库", is_update=False)
return msg