Server IP : 172.67.216.182 / Your IP : 172.69.176.86 Web Server : Apache System : Linux krdc-ubuntu-s-2vcpu-4gb-amd-blr1-01.localdomain 5.15.0-142-generic #152-Ubuntu SMP Mon May 19 10:54:31 UTC 2025 x86_64 User : www ( 1000) PHP Version : 7.4.33 Disable Function : passthru,exec,system,putenv,chroot,chgrp,chown,shell_exec,popen,proc_open,pcntl_exec,ini_alter,ini_restore,dl,openlog,syslog,readlink,symlink,popepassthru,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,imap_open,apache_setenv MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : ON Directory : /www/server/panel/class_v2/ |
Upload File : |
#coding: utf-8 #------------------------------------------------------------------- # aaPanel #------------------------------------------------------------------- # Copyright (c) 2015-2099 aaPanel(www.aapanel.com) All rights reserved. #------------------------------------------------------------------- # Author: hwliang <[email protected]> #------------------------------------------------------------------- import threading import public import os import sys import json import time import psutil import re import shutil import requests from BTPanel import session, cache, send_file if sys.version_info[0] == 3: from importlib import reload class mget: pass class panelPlugin: __list = 'data/list.json' __type = 'data/type.json' __index = 'config/index.json' __link = 'config/link.json' __official_url = public.OfficialApiBase() def __init__(self): self.__isTable = None self.__tasks = None self.__product_list = None self.__plugin_list = None self.__exists_names = {} self.__plugin_s_list = [] self.__plugin_info = None self.__plugin_name = None self.__plugin_object = None self.__plugin_list = None self.__panel_path = '/www/server/panel' self.__plugin_path = self.__panel_path + '/plugin/' self.__plugin_save_file = self.__panel_path + '/data/plugin_bin.pl' self.__api_root_url = self.__official_url + '/api' self.__api_url = self.__api_root_url + '/panel/get_plugin_list' self.__download_url = self.__api_root_url + '/panel/download_plugin' self.__download_d_main_url = self.__api_root_url + '/panel/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' self.__tmp_path = self.__panel_path + '/temp/' self.__plugin_timeout = 3600 self.__is_php = False self.__install_opt = 'i' self.__pid = 0 self.__path_error = self.__panel_path + '/data/error_pl.pl' self.__error_html = '/www/server/panel/BTPanel/templates/default/block_error.html' self.__sub_rules = [] self.__replace_rule = [] self.pids = None self.ROWS = 15 self.__install_path = '/www/server/panel/plugin' if not self.__tasks: try: self.__tasks = public.M('tasks').where("status!=?", ('1',)).field('status,name').select() except: self.__tasks = [] if not os.path.exists(self.__tmp_path): os.makedirs(self.__tmp_path, 0o755) 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, 'Installer temporary file does not exist: {}'.format(get.tmp_path)) if not get.install_opt in ['r', 'i', 'u']: return public.returnMsg(False, 'Installation Options[install_opt]Error, only for i.install r.fix u.upgrade') if not get.plugin_name: return public.returnMsg(False, 'Plugin name cannot be null!') return self.__input_plugin(get.tmp_path, get.plugin_name, get.install_opt) 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('Specifies that the source directory does not exist:{}'.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, 'The plugin is currently being used, please restart the panel and try again!') opts = {'i': 'install', 'u': 'upgrade', 'r': 'repair'} i_opts = { 'i': 'install.sh install', 'u': 'upgrade.sh', 'r': 'repair.sh' } if not os.path.exists(filename): return public.returnMsg(False, 'File validation failed, please reinstall this plugin!') 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() print('cd ' + plugin_path_panel + ' && bash {} &> /tmp/panelShell.pl'.format(i_opts[input_install_opt])) 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('software management', '{}plug-in (software component)[{}]'.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)) # 线程 if os.path.exists(log_file): os.remove(log_file) return public.returnMsg(True, '{}successes!'.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, '{}fail (e.g. experiments): <pre>{}</pre>'.format(opts[input_install_opt], err_msg)) # 检查依赖 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_sys_write(self): test_file = '/etc/init.d/bt_10000100.pl' public.writeFile(test_file,'True') if os.path.exists(test_file): if public.readFile(test_file) == 'True': os.remove(test_file) return True os.remove(test_file) return False #检查互斥 def check_mutex(self,mutex): if mutex == -1: return True mutexs = mutex.split(',') for name in mutexs: pluginInfo = self.get_soft_find(name) if pluginInfo['status'] == -1: continue if pluginInfo.get('message', ''): pluginInfo = pluginInfo['message'] # public.print_log("0000sdfdsjfjj {}".format(pluginInfo)) if pluginInfo['setup'] == True: self.mutex_title = pluginInfo['title'] return False return True #检查依赖 def check_dependent(self,dependent): if not dependent: return True dependents = dependent.split(',') status = True for dep in dependents: if not dep: continue if dep.find('|') != -1: names = dep.split('|') for name in names: pluginInfo = self.get_soft_find(name)['message'] if not pluginInfo: return True if pluginInfo['setup'] == True: status = True break else: status = False else: pluginInfo = self.get_soft_find(dep)['message'] 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 # 检查安装限制 def check_install_limit(self,get): pluginInfo_status = 0 if not hasattr(get,'pluginInfo'): pluginInfos = self.get_soft_find(get.sName) pluginInfo = pluginInfos['message'] pluginInfo_status = pluginInfos['status'] else: pluginInfo = get.pluginInfo 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 pluginInfo_status == -1 or not pluginInfo: return public.return_message(-1, 0, public.lang("The specified plugin does not exist!")) if pluginInfo.get('message', ''): pluginInfo = pluginInfo['message'] self.mutex_title = pluginInfo['mutex'] if not self.check_mutex(pluginInfo['mutex']): return public.return_message(-1, 0, public.lang('Please uninstall [{}] first',self.mutex_title)) if not hasattr(get, 'id'): if not self.check_dependent(pluginInfo['dependent']): return public.return_message(-1, 0, public.lang('Depends on the following software, please install [{}] first',pluginInfo['dependent'])) if 'version' in get: for versionInfo in pluginInfo['versions']: if versionInfo['m_version'] != get.version: continue if not 'type' in get: get.type = '0' if int(get.type) > 4: get.type = '0' if get.type == '0': if not self.check_cpu_limit(versionInfo['cpu_limit']): return public.return_message(-1, 0, public.lang("At least [{0}] CPU cores are required to install", versionInfo['cpu_limit'])) if not self.check_mem_limit(versionInfo['mem_limit']): return public.return_message(-1, 0, public.lang("At least [{0} MB] memory is required to install", versionInfo['mem_limit'])) if not self.check_os_limit(versionInfo['os_limit']): m_ps = {0: "All", 1: "Centos", 2: "Ubuntu/Debian"} return public.return_message(-1, 0, public.lang('Only supports [{}] system',m_ps[int(versionInfo['os_limit'])])) if not hasattr(get, 'id'): if not self.check_dependent(versionInfo['dependent']): return public.return_message(-1, 0, public.lang('Depend on the following software, please install first [{}]',versionInfo['dependent'])) # 获取插件安装包下载进度 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 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, public.lang("Installation process canceled!")) #安装插件 def install_plugin(self,get): str1 = public.lang("System critical directory is not writable!") str2 = public.lang("1. If [System Hardening] is installed, please turn it off") str3 = public.lang("2. If Yunsuo is installed, please turn off [System Hardening] feature") str4 = public.lang("3. If Safedog is installed, please turn off [System Protection] feature") str5 = public.lang("4. If other security software is used, please uninstall it") if not self.check_sys_write(): return public.return_message(-1, 0, '<a style=color:red;>ERROR:{}</a><br>{}<br><br>{}<br>{}<br><br>'.format(str1, str2, str3, str4)) if not 'sName' in get: return public.return_message(-1, 0, public.lang("Please specify the software name!")) #处理ols还不支持php81的情况 # if get.sName == "php-8.1" and public.get_webserver() == 'openlitespeed': # return public.return_msg_gettext(False, public.lang("Sorry, currently OLS official does not support php8.1")) pluginInfo = self.get_soft_find(get.sName)['message'] get.pluginInfo = pluginInfo check_result = self.check_install_limit(get) if check_result: return check_result if pluginInfo['name'] in ['dns_manager','mail_sys']: pluginInfo['type'] = 5 if pluginInfo['type'] != 5: result = self.install_sync(pluginInfo,get) else: result = self.install_async(pluginInfo,get) # public.print_log("hdfh222 {}".format(result)) try: # if 'status' in result: # if result['status']: # public.arequests('post','{}/api/setupCount/setupPlugin'.format(self.__official_url),data={"pid":pluginInfo['id'],'p_name':pluginInfo['name']},timeout=3) if result: public.arequests('post', '{}/api/setupCount/setupPlugin'.format(self.__official_url), data={"pid": pluginInfo['id'], 'p_name': pluginInfo['name']}, timeout=3) # get.force = 1 # self.get_cloud_list(get) except: pass sts = 0 if result.get('status', None): sts = 0 if result['status'] else -1 # public.print_log("0000 5555555555 {}".format(result)) result = result['msg'] # public.print_log("hdfh44444 {}".format(result)) return public.return_message(sts, 0, result) #同步安装 def install_sync(self,pluginInfo,get): import panelAuth try: token = panelAuth.panelAuth().create_serverid(None)['token'] except: # return public.returnMsg(False,'Please log in as aaPanel account first') token = None 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('{}/api/plugin/download?filename={}&token={}'.format( self.__official_url, pluginInfo['versions'][0]['download'], token ),toFile) if public.FileMd5(toFile) != pluginInfo['versions'][0]['md5']: return public.return_msg_gettext(False, public.lang("File hash verification failed, stop installation!")) 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'] + '_en/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.write_log_gettext('Installer','Successfully installed plugin [{}]',(pluginInfo['title'],)) # if os.path.exists(toFile): os.remove(toFile) # return public.return_msg_gettext(True,'Installation succeeded!') # return public.return_msg_gettext(False,'Installation failed') if hasattr(get, 'min_version'): get.version += '.' + get.min_version return self.__install_plugin(pluginInfo['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 install_async(self,pluginInfo,get): # # 只取主版本号与子版本号,忽略修订号 # if 'version' in get: # get.version = '.'.join(str(get.version).split('.')[:2]) mtype = 'install' mmsg = public.lang("Install") if hasattr(get, 'upgrade'): mtype = 'update' mmsg = 'upgrade' 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.return_msg_gettext(False, public.lang("Nginx 1.8.1 is too old, no longer available, please choose another version!")) 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) 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": # 当面板开启SSL时,标记下次打开phpmyadmin时需要设置SSL if os.path.exists('{}/data/ssl.pl'.format(public.get_panel_path())): with open('{}/data/phpmyadmin_ssl.mark'.format(public.get_panel_path()), 'w') as fp: fp.write('1') execstr += "&> /tmp/panelExec.log" if public.get_webserver() == 'openlitespeed': execstr += " && sleep 1 && /usr/local/lsws/bin/lswsctrl restart" # 清理日志文件 if os.path.exists("/tmp/panelExec.log"): public.writeFile("/tmp/panelExec.log","") 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)) cache.delete('install_task') public.writeFile('/tmp/panelTask.pl','True') public.write_log_gettext('Installer','Successfully added intallation task [{}-{}]',(get.sName,get.version)) return public.return_msg_gettext(True, public.lang("Installation task added to queue")) #卸载插件 def uninstall_plugin(self,get): pluginInfo = self.get_soft_find(get.sName) if pluginInfo['status'] == -1: return public.return_message(-1, 0, public.lang("The specified plugin does not exist!")) # public.print_log(" 卸载插件 --{}".format(pluginInfo)) pluginInfo= pluginInfo['message'] if pluginInfo['type'] != 5: pluginPath = self.__install_path + '/' + pluginInfo['name'] if pluginInfo['type'] != 6: download_url = session['download_url'] + '/install/plugin/' + pluginInfo['name'] + '_en/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(pluginPath + '/install.sh'): self.set_pyenv(pluginPath + '/install.sh') public.ExecShell('/bin/bash ' + pluginPath + '/install.sh uninstall') if os.path.exists(pluginPath): public.ExecShell('rm -rf ' + pluginPath) public.write_log_gettext('Installer','Successfully uninstalled software [{}]',(pluginInfo['title'],)) return public.return_message(0, 0, public.lang("Uninstallaton succeeded")) else: if pluginInfo['name'] == 'mysql': if public.M('databases').where('db_type=?',0).count() > 0: return public.return_message(-1, 0, public.lang("The database list is not empty. For your data security, please backup and delete the existing database.<br>Forced uninstall command: rm -rf /www/server/mysql")) if pluginInfo['name'] == 'nginx': import nginx nginx.nginx().del_all_log_format(get) if pluginInfo['name'] == 'apache': import apache apache.apache().del_all_log_format(get) 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] execstr = "cd /www/server/panel/install && /bin/bash install_soft.sh "+get.type+" uninstall " + get.sName.lower() + " "+ get.version.replace('.','') public.ExecShell(execstr) public.write_log_gettext('Installer','Successfully unintalled [{}-{}]',(get.sName,get.version)) return public.return_message(0, 0, public.lang("Uninstallation succeeded")) #从云端取列表 def get_cloud_list(self, get=None): force = 0 if get and hasattr(get, 'force'): force = int(get.force) if 'focre_cloud' in session: if session['focre_cloud']: force = 1 session['focre_cloud'] = False if 'init_cloud' not in session: force = 1 session['init_cloud'] = True softList = public.load_soft_list(True if force == 1 else False) if get and 'init' in get: if softList: if 'success' not in softList: return softList if force > 0: public.ExecShell('rm -f /tmp/bmac_*') public.run_thread(self.getCloudPHPExt) # 专业版和企业版到期提醒,aaPanel目前没有先注释 # self.expire_msg(softList) try: p_token = cache.get('p_token') if p_token is None: p_token = 'bmac_' + public.Md5(public.get_mac_address()) cache.set('p_token', p_token) public.writeFile("/tmp/" + p_token, str(softList['pro'])) public.writeFile('/tmp/{}.time'.format(p_token), str(int(time.time()))) except: pass sType = 0 try: if hasattr(get,'type'): sType = int(get['type']) if hasattr(get,'query'): if get.query: # 关键词统计 参数keyword import panelAuth import requests countUrl = '{}/api/panel/submit_keyword'.format(self.__official_url) pdata = panelAuth.panelAuth().create_serverid(None) url_headers = {} if 'token' in pdata: url_headers = {"authorization": "bt {}".format(pdata['token'])} pdata['environment_info'] = json.dumps(public.fetch_env_info()) keyword = { "keyword": get.query } threading.Thread(target=lambda: requests.post(countUrl, params=keyword, headers=url_headers, verify=False, timeout=3), daemon=True).start() sType = 0 except:pass # 扫描本地插件并追加到软件列表中 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() tmpList = [] for softInfo in softList['list']: if softInfo['name'].lower().find(get.query) != -1 or \ softInfo['title'].lower().find(get.query) != -1 or \ softInfo['ps'].lower().find(get.query) != -1: tmpList.append(softInfo) softList['list'] = tmpList for softInfo in softList['list']: if 'uninsatll_checks' not in softInfo: softInfo['uninsatll_checks'] = softInfo['uninstall_checks'] return softList #取提醒标记 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.return_message(-1, 0, public.lang("Scoring range [1-5]")) if not pdata['pid']: return public.return_message(-1, 0, public.lang("The specified plugin does not exist!")) result = public.httpPost(public.GetConfigValue('home') + '/api/panel/plugin_score',pdata,10) result = json.loads(result) return result except: return public.return_message(-1, 0, public.lang("Connection failure!")) #获取指定插件评分 def get_score(self,args): try: import panelAuth pdata = panelAuth.panelAuth().create_serverid(None) pdata['pid'] = int(args.pid) if not pdata['pid']: return public.return_message(0, 0, []) 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) # public.print_log(" 获取指定插件评分 {}".format(result)) result = json.loads(result) return result except: public.print_log(public.get_error_info()) return public.return_message(-1, 0, public.lang("Connection failure!")) #清除多余面板日志 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 #取本地插件 def get_local_plugin(self,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) 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 not self.__tasks: self.__tasks = public.M('tasks').where("status!=?",('1',)).field('status,name').select() if sName.find('php-') != -1: tmp = sName.split('-') sName = tmp[0] version = tmp[1] isTask = '1' for task in self.__tasks: tmpt = public.getStrBetween('[',']',task['name']) 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, "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", "dependent": "", "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], "dependent": "", "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 <= 0: return sList sType = [sType] # if sType != 12: # sType = [sType] # else: # sType = [sType,8] newList = [] for sInfo in sList: if int(sInfo['type']) in sType: newList.append(sInfo) 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 int(p_list['pro']) < 0 and int(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 int(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 int(p_list['ltd']) < 1 and int(p['endtime']) < 1: return False break return True #取软件列表 def get_soft_list(self,get = None): softList = self.get_cloud_list(get) if not softList: get.force = 1 softList = self.get_cloud_list(get) if not softList: return public.return_message(-1, 0, public.lang('Failed to get software list ({})',"401")) softList['list'] = self.set_coexist(softList['list']) if not 'type' in get: get.type = '0' if get.type == '-1': soft_list_tmp = [] softList['list'] = self.check_isinstall(softList['list']) for val in softList['list']: if 'setup' in val: if val['setup']: soft_list_tmp.append(val) softList['list'] = soft_list_tmp 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['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 return public.return_message(0, 0, softList) #取首页软件列表 def get_index_list(self, get=None): softList = self.get_cloud_list(get)['list'] if not softList: get.force = 1 softList = self.get_cloud_list(get)['list'] if not softList: return public.return_message(-1, 0,public.lang('Failed to get software list ({})',"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) title_has_version_reg = re.compile(r'-\d+(?:\.\d+)+$') # 过滤软件列表 ret = [] for item in dataList: if not item.get('setup', False): continue item['title'] = title_has_version_reg.sub('', item['title']) ret.append(item) return public.return_message(0, 0, ret) #添加到首页 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.return_message(-1, 0, public.lang("Please do NOT repeat adding")) if len(indexList) >= 12: softList = self.get_cloud_list(get)['list'] softList = self.set_coexist(softList) for softInfo in softList: # return softList if softInfo['name'] == 'php': for i in softInfo['versions']: php_v = 'php-'+ i['m_version'] if not os.path.exists('/www/server/php/{}'.format(i['m_version']))\ and php_v in indexList: indexList.remove(php_v) 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.return_message(-1, 0, public.lang("Dashboard only display up to 12 software!")) indexList.append(sName) public.writeFile(self.__index,json.dumps(indexList)) return public.return_message(0, 0, public.lang("Setup successfully!")) #删除首页 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.return_message(0, 0, public.lang("Successfully deleted!")) indexList.remove(sName) public.writeFile(self.__index,json.dumps(indexList)) return public.return_message(0, 0, public.lang("Successfully deleted!")) #设置排序 def sort_index(self,get): indexList = get.ssort.split('|') public.writeFile(self.__index,json.dumps(indexList)) return public.return_message(0, 0, public.lang("Setup successfully!")) #取快捷软件列表 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.return_message(-1, 0, public.lang("Please do NOT repeat adding")) if len(indexList) >= 5: return public.return_message(-1, 0, public.lang("Shortcut Bar only display up to 5 software!")) indexList.append(sName) public.writeFile(self.__link,json.dumps(indexList)) return public.return_message(0, 0, public.lang("Setup successfully!")) #删除快捷栏 def remove_link(self,get): sName = get.sName indexList = [] indexList = json.loads(public.ReadFile(self.__link)) if sName in indexList: return public.return_message(0, 0, public.lang("Successfully deleted!")) indexList.remove(sName) public.writeFile(self.__link,json.dumps(indexList)) return public.return_message(0, 0, public.lang("Successfully deleted!")) #设置快捷栏排序 def sort_link(self,get): indexList = get.ssort.split('|') public.writeFile(self.__link,json.dumps(indexList)) return public.return_message(0, 0, public.lang("Setup successfully!")) #处理共存软件 def set_coexist(self,sList): softList = [] for sInfo in sList: try: if sInfo['version_coexist'] == 1 and 'versions' in sInfo: 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'] 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) if 'uninsatll_checks' not in sTmp: sTmp['uninsatll_checks'] = sTmp['uninstall_checks'].replace('{VERSION}',v) else: sTmp['uninsatll_checks'] = sTmp['uninsatll_checks'].replace('{VERSION}',v) sTmp['s_version'] = sTmp['s_version'].replace('{VERSION}',v) sTmp['versions'] = [] sTmp['versions'].append(versionA) softList.append(sTmp) except: continue else: softList.append(sInfo) except: continue return softList #检测是否安装 def check_isinstall(self,sList): if not os.path.exists(self.__index): public.writeFile(self.__index,'[]') indexList = json.loads(public.ReadFile(self.__index)) 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']) key_info = '' if 'keys' in softInfo: softInfo['keys'] = softInfo['keys'].split('|') if len(softInfo['keys']) > 0: key_info = softInfo['keys'][0] 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: self.get_icon(softInfo['name'].split('-')[0]) else: if 'min_image' in softInfo: if softInfo['id'] != 10000: self.get_icon(softInfo['name'],softInfo['min_image']) else: # if softInfo['id'] != 10000: self.get_icon(softInfo['name']) 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']: softInfo['status'] = self.process_exists('mariadbd') if softInfo['name'] == 'phpmyadmin': softInfo['status'] = self.get_phpmyadmin_stat() if 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) return softInfo 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_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): if not self.__plugin_s_list: softList = self.get_cloud_list(get)['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': # 检查是否需要开启SSL if os.path.exists('{}/data/phpmyadmin_ssl.mark'.format(public.get_panel_path())) and os.path.exists('{}/phpmyadmin'.format(public.get_setup_path())): os.remove('{}/data/phpmyadmin_ssl.mark'.format(public.get_panel_path())) from ajax_v2 import ajax ajax().set_phpmyadmin_ssl(public.to_dict_obj({'v': '1'})) from BTPanel import get_phpmyadmin_dir pmd = get_phpmyadmin_dir() softInfo['ext'] = self.getPHPMyAdminStatus() if softInfo['ext'] and pmd: port = softInfo['ext']['ssl_port'] if softInfo['ext'].get('ssl_enabled', False) else pmd[1] softInfo['ext']['url'] = 'http' + ('s' if softInfo['ext'].get('ssl_enabled', False) else '') + '://' + 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) if sName == 'mail_sys': softInfo['mail_sys_status'] = self._check_mail_sys(None)["status"] return public.success_v2(self.check_status(softInfo)) return public.fail_v2('failed to get soft info') def _check_mail_sys(self, args): if os.path.exists('/etc/postfix/sqlite_virtual_domains_maps.cf'): # public.ExecShell('{} -e "message_size_limit = 102400000"'.format(self._get_postconf())) # 修改postfix mydestination配置项 result = public.readFile("/etc/postfix/main.cf") if not result: return public.returnMsg(False, "No postfix configuration file found") result = re.search(r"\n*mydestination\s*=(.+)", result) if not result: return public.returnMsg(False, "The postfix configuration file did not find the mydestination parameter") result = result.group(1) if 'localhost' in result or '$myhostname' in result or '$mydomain' in result: public.ExecShell('{} -e "mydestination =" && systemctl restart postfix'.format(self._get_postconf())) # 修改dovecot配置 dovecot_conf = public.readFile("/etc/dovecot/dovecot.conf") if not dovecot_conf or not re.search(r"\n*protocol\s*imap", dovecot_conf): return public.returnMsg(False, 'Failed to configure dovecot') # 修复之前版本未安装opendkim的问题 # if not (os.path.exists("/usr/sbin/opendkim") and os.path.exists("/etc/opendkim.conf") and os.path.exists("/etc/opendkim")): # if not self.setup_opendkim(): # return public.returnMsg(False, 'Failed to configure opendkim 1') return public.returnMsg(True, 'MAIL_SERVER_EXIST') else: return public.returnMsg(False, 'NOT_INSTALL_MAIL_SERVER') def _get_postconf(self): if os.path.exists("/usr/sbin/postconf"): return "/usr/sbin/postconf" elif os.path.exists("/sbin/postconf"): return "/sbin/postconf" else: return "postconf" #获取版本信息 def get_version_info(self,sInfo): version = '' # 2025-06-12 修复拼写错误, 但是不知道是否继续沿用的问题. key = "uninsatll_checks" if sInfo.get("uninsatll_checks") else "uninstall_checks" vFile1 = sInfo[key] + '/version_check.pl' vFile2 = sInfo[key] + '/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)[0].strip() if version: public.writeFile(vFile1,version) else: vFile4 = sInfo[key] + '/version.pl' if os.path.exists(vFile4): version = public.xss_version(public.readFile(vFile4).strip()) if sInfo['name'] == 'mysql': vFile3 = sInfo[key] + '/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 for i in range(len(versions)): if version == (versions[i]['m_version'] + '.' + versions[i]['version']): versions[i]['setup'] = True continue vTmp = versions[i]['m_version'].split('_') if len(vTmp) > 1: vTmp = vTmp[1] else: vTmp = vTmp[0] vLen = len(vTmp) versions[i]['setup'] = (version[:vLen] == vTmp) return versions #取pids def get_pids(self): pids = [] for pid in os.listdir('/proc'): if re.match(r"^\d+$",pid): pids.append(pid) return pids #进程是否存在 def process_exists(self,pname,exe = None): if 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 if 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 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) # info['row'] = self.ROWS # info['p'] = 1 # if hasattr(get,'p'): # try: # info['p'] = int(get['p']) # except: # info['p'] = 1 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): iconFile = 'BTPanel/static/img/soft_ico/ico-' + name + '.png' if not os.path.exists(iconFile): public.run_thread(self.download_icon,(name,iconFile,downFile)) else: size = os.path.getsize(iconFile) if size == 0: public.run_thread(self.download_icon,(name,iconFile,downFile)) # self.download_icon(name,iconFile,downFile) #下载图标 def download_icon(self,name,iconFile,downFile): srcIcon = 'plugin/' + name + '/icon.png' skey = name+'_icon' if cache.get(skey): return None if os.path.exists(srcIcon): public.ExecShell(r"\cp -a -r " + srcIcon + " " + iconFile) else: if downFile: public.ExecShell('wget -O ' + iconFile + ' ' + public.GetConfigValue('home') + downFile + " &") else: public.ExecShell('wget -O ' + iconFile + ' ' + public.get_url() + '/install/plugin/' + name + '/icon.png' + " &") 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.write_log_gettext('Installer','Successfully installed plugin [{}]',(pluginInfo['title'],)) #public.ExecShell('rm -f ' + toFile); return public.return_message(0, 0, public.lang("Installation succeeded!")) return public.return_message(-1, 0, public.lang("Installation failed!")) 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 = 'install' 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 sql.table('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.write_log_gettext('Installer','Successfully added intallation task [{}-{}]',(get.name,get.version)) return public.return_message(0, 0, public.lang("Installation task added to queue")) #卸载插件 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.write_log_gettext('Installer','Successfully uninstalled software [{}]',(pluginInfo['title'],)) return public.return_message(0, 0, public.lang("Uninstallation succeeded")) 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','Successfully uninstalled [{}-{}]',(get.name,get.version)) return public.returnMsg(True, public.lang("Uninstallation succeeded")) #取产品信息 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 public.lang("NOT opened") if not 'data' in tmp: return public.lang("NOT opened") self.__plugin_list = tmp['data'] for pluinfo in self.__plugin_list: if pluinfo['product'] == pluginName: if not pluinfo['endtime'] or not pluinfo['state']: return public.lang("To be paid") if pluinfo['endtime'] < time.time(): return public.lang("Expired") return time.strftime("%Y-%m-%d",time.localtime(pluinfo['endtime'])); return public.lang("NOT opened") #取插件列表 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.return_message(0, 0, public.lang("Sort saved")) #检查是否安装 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(r"/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 = "54" phpport = '888' ssl_port = '887' ssl_enabled = False 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] # SSL配置文件查看 ssl_config_file = '{}/vhost/nginx/phpmyadmin.conf'.format(public.get_panel_path()) if os.path.exists(ssl_config_file) and os.path.getsize(ssl_config_file) > 10: tmps = public.readFile(ssl_config_file) m = re.search(r"listen\s*(\d+)", tmps) if m is not None: ssl_enabled = True ssl_port = m.group(1) # 2025/6/4 修复php额外密码访问 if os.path.exists(setupPath + '/panel/vhost/nginx/phpmyadmin.conf'): php_conf = public.readFile(os.path.join(setupPath, 'panel/vhost/nginx/phpmyadmin.conf')) else: php_conf = configFile if php_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 = '54' 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] # SSL配置文件查看 ssl_config_file = '{}/vhost/apache/phpmyadmin.conf'.format(public.get_panel_path()) if os.path.exists(ssl_config_file) and os.path.getsize(ssl_config_file) > 10: tmps = public.readFile(ssl_config_file) m = re.search(r"Listen\s*(\d+)", tmps) if m is not None: ssl_enabled = True ssl_port = m.group(1) # 2025/6/4 修复php额外密码访问 if os.path.exists(setupPath + '/panel/vhost/apache/phpmyadmin.conf'): php_conf = public.readFile(os.path.join(setupPath, 'panel/vhost/apache/phpmyadmin.conf')) else: php_conf = configFile if php_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() if result: 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['ssl_enabled'] = ssl_enabled tmp['port'] = phpport tmp['ssl_port'] = ssl_port 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) if not conf:return False reg = r'/usr/local/lsws/lsphp(\d+)/bin/lsphp' php_v = re.search(reg,conf) phpversion = '73' phpport = '888' if php_v: phpversion = php_v.group(1) filename = '/www/server/panel/vhost/openlitespeed/listen/888.conf' conf = public.readFile(filename) reg = r'address\s+\*\:(\d+)' php_port = re.search(reg,conf) if php_port: phpport = php_port.group(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.return_message(-1, 0, public.lang("This plugin does NOT have template!")) mimetype = 'text/html' cache_time = 0 if public.is_debug() else 86400 self.plugin_open_total(get.name) 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['status'] == 0: plugin_info = plugin_info['message'] # public.print_log("获取常用插件 ---{}".format(plugin_info)) if plugin_info.get('setup', False): usually_list.append(plugin_info) if len(usually_list) >= 5: break return public.return_message(0, 0, 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.plugin_name 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'] public.ExecShell('rm -f ' + 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.return_message(0, 0, public.lang("Setup successfully!")) #从云端获取插件列表 def getCloudPlugin(self,get): if session.get('getCloudPlugin') and get != None: return public.return_message(0, 0,'Your plugin list is already the latest version {}!',("-1",)) import json if not session.get('download_url'): session['download_url'] = 'http://node.aapanel.com' #获取列表 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.return_message(0, 0, public.lang("Software list updated!")) #刷新缓存 def flush_cache(self,get): self.getCloudPlugin(None) return public.return_message(0, 0, public.lang("Software list updated!")) #获取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() #获取警告列表 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)['message'] return find['title'] def a(self, get): try: return public.run_plugin_v2(get.name, get.s, 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.return_msg_gettext(False, public.lang("File download failed!")) if get: public.ExecShell("rm -rf " + tmp_path + '/*') tmp_file = tmp_path + '/plugin_tmp.zip' 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'] = public.lang("Unknown") if not 'home' in data: data['home'] = 'https://www.aapanel.com/forum' 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.return_msg_gettext(False, public.lang("No plugin info found in the archive, please check the plugin package!")) data['update'] = update return data 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.return_message(-1, 0, public.lang("File download failed!")) if get: public.ExecShell("rm -rf " + tmp_path + '/*') tmp_file = tmp_path + '/plugin_tmp.zip' 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'] = public.lang("Unknown") if not 'home' in data: data['home'] = 'https://www.aapanel.com/forum' 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.return_message(-1, 0, public.lang("No plugin info found in the archive, please check the plugin package!")) data['update'] = update return public.return_message(0, 0, data) #导入插件包 def input_zip(self,get): if not os.path.exists(get.tmp_path): return public.return_message(-1, 0, public.lang("Temporary file does NOT exist, please re-upload!")) plugin_path = '/www/server/panel/plugin/' + get.plugin_name if not os.path.exists(plugin_path): os.makedirs(plugin_path) public.ExecShell(r"\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/*") if p_info: #----- 增加图标复制 hwliang<2021-03-23> -----# icon_sfile = plugin_path + '/icon.png' icon_dfile = '/www/server/panel/BTPanel/static/img/soft_ico/ico-{}.png'.format(get.plugin_name) if os.path.exists(plugin_path + '/icon.png'): import shutil shutil.copyfile(icon_sfile,icon_dfile) #----- 增加图标复制 END -----# public.write_log_gettext('Software manager','Installed third-party plugin [{}]' ,(json.loads(p_info)['title'],)) return public.return_message(0, 0, public.lang("Installation succeeded!")) public.ExecShell("rm -rf " + plugin_path) return public.return_message(-1, 0, public.lang("Installation failed")) #导出插件包 def export_zip(self,get): plugin_path = '/www/server/panel/plugin/' + get.plugin_name if not os.path.exists(plugin_path): return public.return_message(-1, 0, public.lang("The specified plugin does not exist!")) 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.return_message(-1, 0, public.lang("Export failed, please check permissions!")) return public.return_message(0, 0,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 public.return_message(0, 0, 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.return_message(-1, 0, public.lang("Non-compliant names can only be numbers, letters, underscores")) 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.write_log_gettext('Software manager','Add custom compilation parameters: {}:{}',(get.name,get.args_name)) return public.return_message(0, 0, public.lang("Setup successfully!")) #删除编译参数 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.return_message(-1, 0, public.lang("Non-compliant names can only be numbers, letters, underscores")) config_path = os.path.join('install' , get.name , get.args_name) if not os.path.exists(config_path): return public.return_message(-1, 0, public.lang("The specified custom compilation parameters do not exist!")) 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.write_log_gettext('Software manager','Remove custom compilation parameters: {}:{}',(get.name,get.args_name)) return public.return_message(0, 0, public.lang("Successfully deleted")) #设置当前编译参数 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.write_log_gettext('Software manager','Setup software: Custom compilation parameters for {} are configured as: {}'.format(get.name,config_data)) return public.return_message(0, 0, public.lang("Setup successfully!")) # 安装插件 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(public.get_msg_gettext('Plugin not found')) if not plugin_info['versions']: raise public.PanelError(public.get_msg_gettext('Not available versions for this plugin')) 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 __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 # 获取插件最近1条更新日志 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 # 获取插件最近10条更新日志 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 __get_plugin_find(self, upgrade_plugin_name=None): ''' @name 获取指定软件信息 @author hwliang<2021-06-15> @param upgrade_plugin_name<string> 插件名称 @return dict ''' self.__ensure_plugin_list_obtained(True) 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 __check_dependent(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['dependent']: return {} deployment_list = {} for dependent_plu_name in plugin_info['dependent'].split(','): p_info = self.__get_plugin_find(dependent_plu_name) if not p_info: continue deployment_list[dependent_plu_name] = os.path.exists( p_info['install_checks']) return deployment_list # 读取指定文件 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 __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, public.lang("File download failed!")) 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) 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'] = 'aapanel' if not 'home' in plugin_data_info: plugin_data_info['home'] = '{}'.format(public.OfficialApiBase()) 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['dependent'] = self.__check_dependent(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 raise public.HintException(not_check.get('msg', 'not support yet')) return plugin_data_info # 检测是否为不支持的平台和系统位数 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, 'Not supported for {}bit systems.'.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, 'Not supported for {},{}'.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' pdata['environment_info'] = json.dumps(public.fetch_env_info(), ensure_ascii=False) 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, public.lang('Error: Name or service not known')) elif 'Failed to establish a new connection' in str_ex: return public.returnMsg(False, public.lang('Error: Failed to establish a new connection')) elif 'Read timed out' in str_ex: return public.returnMsg(False, public.lang('Error: Read timed out')) elif 'Connection refused' in str_ex: return public.returnMsg(False, public.lang('Error: Connection refused')) elif 'Remote end closed connection without response' in str_ex: return public.returnMsg(False, public.lang('Error: Remote end closed connection without response')) else: return public.returnMsg(False, public.lang('Error: {}',str_ex)) finally: urllib3_conn.allowed_gai_family = old_family try: headers_total_size = int(download_res.headers['File-size']) except: try: return json.loads(download_res.text).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, public.lang('Download timeout: {}',ex_str)) if "No space left on device" in ex_str: return public.returnMsg(False, public.lang('No available disk space.')) 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, public.lang('Verify package checksum failed.')) else: while True: time.sleep(1) if not cache.get(pkey): break return '' return filename # 获取插件安装包下载进度 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, public.lang('Cannot get progress bar for this plugin.')) 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 __ensure_plugin_list_obtained(self, force: bool = False): if force or not self.__plugin_list: self.__plugin_list = public.load_soft_list(force)