diff --git a/app/command/DecryptFile.php b/app/command/DecryptFile.php new file mode 100644 index 0000000..f6c0b06 --- /dev/null +++ b/app/command/DecryptFile.php @@ -0,0 +1,115 @@ +setName('decrypt') + ->addArgument('type', Argument::REQUIRED, '文件类型,plugin:插件文件,module:模块文件,classdir:宝塔class目录,all:所有py文件') + ->addArgument('file', Argument::REQUIRED, '文件路径') + ->addArgument('os', Argument::OPTIONAL, '操作系统:Windows/Linux') + ->setDescription('解密宝塔面板python文件'); + } + + protected function execute(Input $input, Output $output) + { + $type = trim($input->getArgument('type')); + $file = trim($input->getArgument('file')); + + if(!file_exists($file)){ + $output->writeln('文件不存在'); + return; + } + + if($type == 'plugin'){ + $os = trim($input->getArgument('os')); + try{ + if(Plugins::decode_plugin_main_local($file, $os)){ + $output->writeln('文件解密成功!'); + }else{ + $output->writeln('文件解密失败!'); + } + }catch(\Exception $e){ + $output->writeln($e->getMessage()); + } + }elseif($type == 'module'){ + try{ + $res = Plugins::decode_module_file($file); + if($res == 2){ + $output->writeln('文件解密失败!'); + }elseif($res == 1){ + $output->writeln('文件解密成功!'); + } + }catch(\Exception $e){ + $output->writeln($e->getMessage()); + } + }elseif($type == 'classdir'){ + $file = rtrim($file, '/'); + if(!file_exists($file.'/common.py')){ + $output->writeln('当前路径非宝塔面板class目录'); + return; + } + $dirs = glob($file.'/*Model'); + foreach($dirs as $dir){ + if(!is_dir($dir))continue; + $files = glob($dir.'/*Model.py'); + foreach($files as $file){ + try{ + $res = Plugins::decode_module_file($file); + if($res == 2){ + $output->writeln('文件解密失败:'.$file); + }elseif($res == 1){ + $output->writeln('文件解密成功:'.$file); + } + }catch(\Exception $e){ + $output->writeln($e->getMessage().':'.$file); + } + } + } + }elseif($type == 'all'){ + $file = rtrim($file, '/'); + $this->scan_all_file($input, $output, $file); + }else{ + $output->writeln('未知文件类型'); + } + } + + private function scan_all_file(Input $input, Output $output, $path) { + $dir = opendir($path); + while(false !== ( $file = readdir($dir)) ) { + if (( $file != '.' ) && ( $file != '..' )) { + $filepath = $path . '/' . $file; + if ( is_dir($filepath) ) { + $this->scan_all_file($input, $output, $filepath); + } + elseif(substr($filepath, -3) == '.py') { + try{ + $res = Plugins::decode_module_file($filepath); + if($res == 2){ + $output->writeln('文件解密失败:'.$filepath); + }elseif($res == 1){ + $output->writeln('文件解密成功:'.$filepath); + } + }catch(\Exception $e){ + $output->writeln($e->getMessage().':'.$filepath); + } + } + } + } + closedir($dir); + } + + +} diff --git a/app/controller/Api.php b/app/controller/Api.php index 13d7558..30128d5 100644 --- a/app/controller/Api.php +++ b/app/controller/Api.php @@ -272,6 +272,12 @@ class Api extends BaseController return json($json_arr); } + //获取宝塔SSL列表 + public function get_ssl_list(){ + $data = bin2hex('[]'); + return json(['status'=>true, 'msg'=>'', 'data'=>$data]); + } + public function return_success(){ return json(['status'=>true, 'msg'=>1, 'data'=>(object)[]]); } diff --git a/app/lib/Plugins.php b/app/lib/Plugins.php index cc3f60a..b698e85 100644 --- a/app/lib/Plugins.php +++ b/app/lib/Plugins.php @@ -187,6 +187,8 @@ class Plugins $userinfo = $btapi->get_user_info(); if(isset($userinfo['uid'])){ $src = file_get_contents($main_filepath); + if($src===false)throw new Exception('文件打开失败'); + if(!$src || strpos($src, 'import ')!==false)return true; $uid = $userinfo['uid']; $serverid = $userinfo['serverid']; $key = md5(substr($serverid, 10, 16).$uid.$serverid); @@ -202,7 +204,7 @@ class Plugins if($tmp) $de_text .= $tmp; } } - if(!empty($de_text)){ + if(!empty($de_text) && strpos($de_text, 'import ')!==false){ file_put_contents($main_filepath, $de_text); return true; } @@ -212,6 +214,28 @@ class Plugins } } + public static function decode_module_file($filepath){ + $src = file_get_contents($filepath); + if($src===false)throw new Exception('文件打开失败'); + if(!$src || strpos($src, 'import ')!==false)return 0; + $key = 'Z2B87NEAS2BkxTrh'; + $iv = 'WwadH66EGWpeeTT6'; + $data_arr = explode("\n", $src); + $de_text = ''; + foreach($data_arr as $data){ + $data = trim($data); + if(!empty($data)){ + $tmp = openssl_decrypt($data, 'aes-128-cbc', $key, 0, $iv); + if($tmp) $de_text .= $tmp; + } + } + if(!empty($de_text) && strpos($de_text, 'import ')!==false){ + file_put_contents($filepath, $de_text); + return 1; + } + return 2; + } + //去除插件主程序文件授权校验 public static function noauth_plugin_main($main_filepath){ $data = file_get_contents($main_filepath); diff --git a/app/script/convert.sh b/app/script/convert.sh index 9468dc4..fba6792 100644 --- a/app/script/convert.sh +++ b/app/script/convert.sh @@ -1,6 +1,6 @@ #!/bin/bash -Linux_Version="7.9.3" +Linux_Version="7.9.4" Windows_Version="7.6.0" FILES=( diff --git a/config/console.php b/config/console.php index ed1aa26..e20f57a 100644 --- a/config/console.php +++ b/config/console.php @@ -6,5 +6,6 @@ return [ // 指令定义 'commands' => [ 'updateall' => 'app\command\UpdateAll', + 'decrypt' => 'app\command\DecryptFile', ], ]; diff --git a/data/plugins/folder/.gitkeep b/data/plugins/folder/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/data/plugins/main/.gitkeep b/data/plugins/main/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/data/plugins/other/other/.gitkeep b/data/plugins/other/other/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/data/plugins/package/.gitkeep b/data/plugins/package/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/data/win/plugins/folder/.gitkeep b/data/win/plugins/folder/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/data/win/plugins/main/.gitkeep b/data/win/plugins/main/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/data/win/plugins/package/.gitkeep b/data/win/plugins/package/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/install.sql b/install.sql index 7e6457d..f4acf0f 100644 --- a/install.sql +++ b/install.sql @@ -12,7 +12,7 @@ INSERT INTO `cloud_config` (`key`, `value`) VALUES ('bt_key', ''), ('whitelist', '0'), ('download_page', '1'), -('new_version', '7.9.3'), +('new_version', '7.9.4'), ('update_msg', '暂无更新日志'), ('update_date', '2022-07-13'), ('new_version_win', '7.6.0'), diff --git a/public/install/src/panel6.zip b/public/install/src/panel6.zip index 08e6df8..503c7b5 100644 Binary files a/public/install/src/panel6.zip and b/public/install/src/panel6.zip differ diff --git a/public/install/update/LinuxPanel-7.9.3.zip b/public/install/update/LinuxPanel-7.9.4.zip similarity index 72% rename from public/install/update/LinuxPanel-7.9.3.zip rename to public/install/update/LinuxPanel-7.9.4.zip index e08e508..19d4e21 100644 Binary files a/public/install/update/LinuxPanel-7.9.3.zip and b/public/install/update/LinuxPanel-7.9.4.zip differ diff --git a/public/install/update6.sh b/public/install/update6.sh index f09f929..e303128 100644 --- a/public/install/update6.sh +++ b/public/install/update6.sh @@ -42,7 +42,7 @@ download_Url=$NODE_URL setup_path=/www version=$(curl -Ss --connect-timeout 5 -m 2 $Btapi_Url/api/panel/get_version) if [ "$version" = '' ];then - version='7.9.3' + version='7.9.4' fi armCheck=$(uname -m|grep arm) if [ "${armCheck}" ];then diff --git a/public/install/update_panel.sh b/public/install/update_panel.sh index a0ab586..1a58490 100644 --- a/public/install/update_panel.sh +++ b/public/install/update_panel.sh @@ -70,7 +70,7 @@ select_node(){ get_version(){ version=$(curl -Ss --connect-timeout 5 -m 2 $Btapi_Url/api/panel/get_version) if [ "$version" = '' ];then - version='7.9.3' + version='7.9.4' fi } diff --git a/public/static/file/kaixin.zip b/public/static/file/kaixin.zip index d76f8af..5e0f213 100644 Binary files a/public/static/file/kaixin.zip and b/public/static/file/kaixin.zip differ diff --git a/route/app.php b/route/app.php index 8f1b3f1..d4c9b78 100644 --- a/route/app.php +++ b/route/app.php @@ -36,6 +36,9 @@ Route::group('api', function () { Route::get('/getIpAddress', 'api/get_ip_address'); Route::post('/Auth/GetAuthToken', 'api/get_auth_token'); Route::post('/Auth/GetBindCode', 'api/return_error'); + Route::post('/Auth/GetSSLList', 'api/get_ssl_list'); + Route::post('/Cert/get_order_list', 'api/return_empty_array'); + Route::post('/Cert/get_product_list', 'api/return_success'); Route::get('/Pluginother/get_file', 'api/download_plugin_other'); Route::post('/Pluginother/create_order', 'api/return_error'); diff --git a/wiki/files/PluginLoader.py b/wiki/files/PluginLoader.py index cf0f951..dddf911 100644 --- a/wiki/files/PluginLoader.py +++ b/wiki/files/PluginLoader.py @@ -36,6 +36,7 @@ def get_auth_state(): #执行插件方法(插件名,方法名,参数) def plugin_run(plugin_name, def_name, args): if not plugin_name or not def_name: return public.returnMsg(False,'插件名称和插件方法名称不能为空!') + if not path_check(plugin_name) or not path_check(def_name): return public.returnMsg(False,'插件名或方法名不能包含特殊符号!') p_path = public.get_plugin_path(plugin_name) if not os.path.exists(p_path + '/index.php') and not os.path.exists(p_path + '/%s_main.py' % plugin_name): return public.returnMsg(False,'插件不存在!') @@ -73,12 +74,21 @@ def plugin_run(plugin_name, def_name, args): #执行模块方法(模块名,方法名,参数) def module_run(mod_name, def_name, args): if not mod_name or not def_name: return public.returnMsg(False,'模块名称和模块方法名称不能为空!') + if not path_check(mod_name) or not path_check(def_name): return public.returnMsg(False,'模块名或方法名不能包含特殊符号!') + + if 'model_index' in args: + if args.model_index: + mod_file = "{}/{}Model/{}Model.py".format(public.get_class_path(),args.model_index,mod_name) + else: + mod_file = "{}/projectModel/{}Model.py".format(public.get_class_path(),mod_name) + else: + module_list = get_module_list() + for module_dir in module_list: + mod_file = "{}/{}/{}Model.py".format(public.get_class_path(),module_dir,mod_name) + if os.path.exists(mod_file): break - mod_file = "{}/projectModel/{}Model.py".format(public.get_class_path(),mod_name) if not os.path.exists(mod_file): - mod_file = "{}/databaseModel/{}Model.py".format(public.get_class_path(),mod_name) - if not os.path.exists(mod_file): - return public.returnMsg(False,'模块[%s]不存在' % mod_name) + return public.returnMsg(False,'模块[%s]不存在' % mod_name) def_object = public.get_script_object(mod_file) if not def_object: return public.returnMsg(False,'模块[%s]不存在!' % mod_name) @@ -92,3 +102,21 @@ def module_run(mod_name, def_name, args): result = run_object(args) return result +#获取模块文件夹列表 +def get_module_list(): + list = [] + class_path = public.get_class_path() + f_list = os.listdir(class_path) + for fname in f_list: + f_path = class_path+'/'+fname + if os.path.isdir(f_path) and len(fname) > 6 and fname.find('.') == -1 and fname.find('Model') != -1: + list.append(fname) + return list + +#检查路径是否合法 +def path_check(path): + list = ["./","..",",",";",":","?","'","\"","<",">","|","\\","\n","\r","\t","\b","\a","\f","\v","*","%","&","$","#","@","!","~","`","^","(",")","+","=","{","}","[","]"] + for i in path: + if i in list: + return False + return True diff --git a/wiki/files/bt.js b/wiki/files/bt.js index 25ac23a..81df382 100644 --- a/wiki/files/bt.js +++ b/wiki/files/bt.js @@ -40,8 +40,9 @@ if("undefined" != typeof bt && bt.hasOwnProperty("prompt_confirm")){ } if("undefined" != typeof database && database.hasOwnProperty("del_database")){ database.del_database = function (wid, dbname,obj, callback) { + var type = $('.database-pos .tabs-item.active').data('type') title = '', - tips = '是否确认【删除数据库】,删除后可能会影响业务使用!'; + tips = '是否确认【删除数据库】,'+ (type !== 'mysql'?'当前数据库暂不支持数据库回收站,删除后将无法恢复,请谨慎操作':',删除后可能会影响业务使用!'); if(obj && obj.db_type > 0) tips = '远程数据库不支持数据库回收站,删除后将无法恢复,请谨慎操作'; var title = typeof dbname === "function" ?'批量删除数据库':'删除数据库 [ '+ dbname +' ]'; layer.open({ @@ -157,7 +158,10 @@ if("undefined" != typeof bt && bt.hasOwnProperty("firewall") && bt.firewall.hasO if (port.indexOf('-') != -1) ports = port.split('-'); for (var i = 0; i < ports.length; i++) { if (!bt.check_port(ports[i])) { - layer.msg(lan.firewall.port_err, { icon: 5 }); + layer.msg('可用端口范围:1-65535', { icon: 2 }); + // layer.msg(lan.firewall.port_err, { + // icon: 5 + // }); return; } } diff --git a/wiki/update.md b/wiki/update.md index 818075e..7bb169e 100644 --- a/wiki/update.md +++ b/wiki/update.md @@ -10,9 +10,11 @@ - 将PluginLoader.py复制到class文件夹 +- 批量解密模块文件:执行 php think decrypt classdir <面板class文件夹路径> + - 全局搜索替换 https://api.bt.cn => http://www.example.com -- 全局搜索替换 https://www.bt.cn/api/ => http://www.example.com/api/(需排除clearModel.py) +- 全局搜索替换 https://www.bt.cn/api/ => http://www.example.com/api/(需排除clearModel.py、ipsModel.py) - 全局搜索替换 http://download.bt.cn/install/update6.sh => http://www.example.com/install/update6.sh @@ -40,6 +42,8 @@ - class/panelPlugin.py 文件,download_icon方法内替换 public.GetConfigValue('home') => 'https://www.bt.cn' +- class/panelPlugin.py 文件,删除public.total_keyword(get.query)这一行 + - class/panelPlugin.py 文件,set_pyenv方法内,temp_file = public.readFile(filename)这行代码下面加上 ```python @@ -61,16 +65,12 @@ "update_software_list": update_software_list, - "check_files_panel": check_files_panel, - "check_panel_msg": check_panel_msg, -- 去除面板日志上报:script/site_task.py 文件 - - 删除最下面 logs_analysis() 这1行 - - 去除首页广告:BTPanel/static/js/index.js 文件删除最下面index.recommend_paid_version()这一行 +- 去除内页广告:BTPanel/templates/default/layout.html 删除getPaymentStatus();这一行 + - 去除首页自动检测更新,避免频繁请求云端:BTPanel/static/js/index.js 文件注释掉bt.system.check_update这一段代码外的setTimeout - [可选]去除各种计算题:复制bt.js到 BTPanel/static/ ,在 BTPanel/templates/default/layout.html 的\前面加入