btcloud/app/lib/Plugins.php
2023-08-03 14:43:09 +08:00

350 lines
16 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace app\lib;
use Exception;
use ZipArchive;
class Plugins
{
private static function get_btapi($os){
if($os == 'Windows'){
$bt_url = config_get('wbt_url');
$bt_key = config_get('wbt_key');
}else{
$bt_url = config_get('bt_url');
$bt_key = config_get('bt_key');
}
if(!$bt_url || !$bt_key) throw new Exception('请先配置好宝塔面板接口信息');
$btapi = new Btapi($bt_url, $bt_key);
return $btapi;
}
//刷新插件列表
public static function refresh_plugin_list($os = 'Linux'){
$btapi = self::get_btapi($os);
$result = $btapi->get_plugin_list();
if($result && isset($result['list']) && isset($result['type'])){
if(empty($result['list']) || empty($result['type'])){
throw new Exception('获取插件列表失败:插件列表为空');
}
self::save_plugin_list($result, $os);
}else{
throw new Exception('获取插件列表失败:'.(isset($result['msg'])?$result['msg']:'面板连接失败'));
}
}
//保存插件列表
private static function save_plugin_list($data, $os){
$data['ip'] = '127.0.0.1';
$data['serverid'] = '';
$data['beta'] = 0;
$data['uid'] = 1;
$data['skey'] = '';
$list = [];
foreach($data['list'] as $plugin){
if(isset($plugin['endtime'])) $plugin['endtime'] = 0;
$list[] = $plugin;
}
$data['list'] = $list;
$data['ltd'] = strtotime('+10 year');
$json_file = get_data_dir($os).'config/plugin_list.json';
if(!file_put_contents($json_file, json_encode($data))){
throw new Exception('保存插件列表失败,文件无写入权限');
}
}
//获取插件列表
public static function get_plugin_list($os = 'Linux'){
$json_file = get_data_dir($os).'config/plugin_list.json';
if(file_exists($json_file)){
$data = file_get_contents($json_file);
$json_arr = json_decode($data, true);
if($json_arr){
return $json_arr;
}
}
return false;
}
//获取一个插件信息
public static function get_plugin_info($name, $os = 'Linux'){
$json_arr = self::get_plugin_list($os);
if(!$json_arr) return null;
foreach($json_arr['list'] as $plugin){
if($plugin['name'] == $name){
return $plugin;
}
}
return null;
}
//下载插件(自动判断是否第三方)
public static function download_plugin($plugin_name, $version, $os = 'Linux'){
$plugin_info = Plugins::get_plugin_info($plugin_name, $os);
if(!$plugin_info) throw new Exception('未找到该插件信息');
if($plugin_info['type'] == 10 && isset($plugin_info['versions'][0]['download'])){
if($plugin_info['price'] == 0){
$btapi = self::get_btapi($os);
$btapi->create_plugin_other_order($plugin_info['id']);
}
$fname = $plugin_info['versions'][0]['download'];
$filemd5 = $plugin_info['versions'][0]['md5'];
Plugins::download_plugin_other($fname, $filemd5, $os);
if(isset($plugin_info['min_image']) && strpos($plugin_info['min_image'], 'fname=')){
$fname = substr($plugin_info['min_image'], strpos($plugin_info['min_image'], '?fname=')+7);
Plugins::download_plugin_other($fname, null, $os);
}
}else{
Plugins::download_plugin_package($plugin_name, $version, $os);
}
}
//下载插件包
public static function download_plugin_package($plugin_name, $version, $os = 'Linux'){
$filepath = get_data_dir($os).'plugins/package/'.$plugin_name.'-'.$version.'.zip';
$btapi = self::get_btapi($os);
$result = $btapi->get_plugin_filename($plugin_name, $version);
if($result && isset($result['status'])){
if($result['status'] == true){
$filename = $result['filename'];
self::download_file($btapi, $filename, $filepath);
if(file_exists($filepath)){
$zip = new ZipArchive;
if ($zip->open($filepath) === true)
{
$zip->extractTo(get_data_dir($os).'plugins/folder/'.$plugin_name.'-'.$version);
$zip->close();
$main_filepath = get_data_dir($os).'plugins/folder/'.$plugin_name.'-'.$version.'/'.$plugin_name.'/'.$plugin_name.'_main.py';
if(file_exists($main_filepath) && filesize($main_filepath)>10){
if(!strpos(file_get_contents($main_filepath), 'import ')){ //加密py文件需要解密
self::decode_plugin_main($plugin_name, $version, $main_filepath, $os);
self::noauth_plugin_main($main_filepath);
$zip->open($filepath, ZipArchive::CREATE);
$zip->addFile($main_filepath, $plugin_name.'/'.$plugin_name.'_main.py');
$zip->close();
}
}
}else{
throw new Exception('插件包解压缩失败');
}
return true;
}else{
throw new Exception('下载插件包失败,本地文件不存在');
}
}else{
throw new Exception('下载插件包失败:'.($result['msg']?$result['msg']:'未知错误'));
}
}else{
throw new Exception('下载插件包失败,接口返回错误');
}
}
//下载插件主程序文件
public static function download_plugin_main($plugin_name, $version, $os = 'Linux'){
$filepath = get_data_dir($os).'plugins/main/'.$plugin_name.'-'.$version.'.dat';
$btapi = self::get_btapi($os);
$result = $btapi->get_plugin_main_filename($plugin_name, $version);
if($result && isset($result['status'])){
if($result['status'] == true){
$filename = $result['filename'];
self::download_file($btapi, $filename, $filepath);
if(file_exists($filepath)){
return true;
}else{
throw new Exception('下载插件主程序文件失败,本地文件不存在');
}
}else{
throw new Exception('下载插件主程序文件失败:'.($result['msg']?$result['msg']:'未知错误'));
}
}else{
throw new Exception('下载插件主程序文件失败,接口返回错误');
}
}
//解密并下载插件主程序文件
public static function decode_plugin_main($plugin_name, $version, $main_filepath, $os = 'Linux'){
if(self::decode_plugin_main_local($main_filepath, $os)) return true;
$btapi = self::get_btapi($os);
$result = $btapi->get_decode_plugin_main($plugin_name, $version);
if($result && isset($result['status'])){
if($result['status'] == true){
$filename = $result['filename'];
self::download_file($btapi, $filename, $main_filepath);
return true;
}else{
throw new Exception('解密插件主程序文件失败:'.($result['msg']?$result['msg']:'未知错误'));
}
}else{
throw new Exception('解密插件主程序文件失败,接口返回错误');
}
}
//本地解密插件主程序文件
public static function decode_plugin_main_local($main_filepath, $os = 'Linux'){
$btapi = self::get_btapi($os);
$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);
$iv = md5($key.$serverid);
$key = substr($key, 8, 16);
$iv = substr($iv, 8, 16);
$data_arr = explode("\n", $src);
$de_text = '';
foreach($data_arr as $data){
$data = trim($data);
if(!empty($data) && strlen($data)!=24){
$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($main_filepath, $de_text);
return true;
}
return false;
}else{
throw new Exception('解密插件主程序文件失败,获取用户信息失败');
}
}
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);
if(!$data) return false;
$data = str_replace('\'http://www.bt.cn/api/panel/get_soft_list_test', 'public.GetConfigValue(\'home\')+\'/api/panel/get_soft_list_test', $data);
$data = str_replace('\'https://www.bt.cn/api/panel/get_soft_list_test', 'public.GetConfigValue(\'home\')+\'/api/panel/get_soft_list_test', $data);
$data = str_replace('\'http://www.bt.cn/api/panel/get_soft_list', 'public.GetConfigValue(\'home\')+\'/api/panel/get_soft_list', $data);
$data = str_replace('\'https://www.bt.cn/api/panel/get_soft_list', 'public.GetConfigValue(\'home\')+\'/api/panel/get_soft_list', $data);
$data = str_replace('\'http://www.bt.cn/api/panel/notpro', 'public.GetConfigValue(\'home\')+\'/api/panel/notpro', $data);
$data = str_replace('\'https://www.bt.cn/api/panel/notpro', 'public.GetConfigValue(\'home\')+\'/api/panel/notpro', $data);
$data = str_replace('\'http://www.bt.cn/api/wpanel/get_soft_list_test', 'public.GetConfigValue(\'home\')+\'/api/wpanel/get_soft_list_test', $data);
$data = str_replace('\'https://www.bt.cn/api/wpanel/get_soft_list_test', 'public.GetConfigValue(\'home\')+\'/api/wpanel/get_soft_list_test', $data);
$data = str_replace('\'http://www.bt.cn/api/wpanel/get_soft_list', 'public.GetConfigValue(\'home\')+\'/api/wpanel/get_soft_list', $data);
$data = str_replace('\'https://www.bt.cn/api/wpanel/get_soft_list', 'public.GetConfigValue(\'home\')+\'/api/wpanel/get_soft_list', $data);
$data = str_replace('\'http://www.bt.cn/api/wpanel/notpro', 'public.GetConfigValue(\'home\')+\'/api/wpanel/notpro', $data);
$data = str_replace('\'https://www.bt.cn/api/wpanel/notpro', 'public.GetConfigValue(\'home\')+\'/api/wpanel/notpro', $data);
$data = str_replace('\'http://www.bt.cn/api/bt_waf/getSpiders', 'public.GetConfigValue(\'home\')+\'/api/bt_waf/getSpiders', $data);
$data = str_replace('\'https://www.bt.cn/api/bt_waf/getSpiders', 'public.GetConfigValue(\'home\')+\'/api/bt_waf/getSpiders', $data);
file_put_contents($main_filepath, $data);
}
//下载插件其他文件
public static function download_plugin_other($fname, $filemd5 = null, $os = 'Linux'){
$filepath = get_data_dir().'plugins/other/'.$fname;
@mkdir(dirname($filepath), 0777, true);
$btapi = self::get_btapi($os);
$result = $btapi->get_plugin_other_filename($fname);
if($result && isset($result['status'])){
if($result['status'] == true){
$filename = $result['filename'];
self::download_file($btapi, $filename, $filepath);
if(file_exists($filepath)){
if($filemd5 && md5_file($filepath) != $filemd5){
$msg = filesize($filepath) < 300 ? file_get_contents($filepath) : '插件文件MD5校验失败';
@unlink($filepath);
throw new Exception($msg);
}
return true;
}else{
throw new Exception('下载插件文件失败,本地文件不存在');
}
}else{
throw new Exception('下载插件文件失败:'.($result['msg']?$result['msg']:'未知错误'));
}
}else{
throw new Exception('下载插件文件失败,接口返回错误');
}
}
//下载文件
private static function download_file($btapi, $filename, $filepath){
try{
$btapi->download($filename, $filepath);
}catch(Exception $e){
@unlink($filepath);
//宝塔bug小文件下载失败改用base64下载
$result = $btapi->get_file($filename);
if($result && isset($result['status']) && $result['status']==true){
$filedata = base64_decode($result['data']);
if(strlen($filedata) < 4096 && substr($filedata,0,1)=='{' && substr($filedata,-1,1)=='}'){
$arr = json_decode($filedata, true);
if($arr){
throw new Exception('获取文件失败:'.($arr['msg']?$arr['msg']:'未知错误'));
}
}
if(!$filedata){
throw new Exception('获取文件失败:文件内容为空');
}
file_put_contents($filepath, $filedata);
}elseif($result){
throw new Exception('获取文件失败:'.($result['msg']?$result['msg']:'未知错误'));
}else{
throw new Exception('获取文件失败:未知错误');
}
}
}
//刷新一键部署列表
public static function refresh_deplist($os = 'Linux'){
$btapi = self::get_btapi($os);
$result = $btapi->get_deplist();
if($result && isset($result['list']) && isset($result['type'])){
if(empty($result['list']) || empty($result['type'])){
throw new Exception('获取一键部署列表失败:一键部署列表为空');
}
$json_file = get_data_dir($os).'config/deployment_list.json';
if(!file_put_contents($json_file, json_encode($result))){
throw new Exception('保存一键部署列表失败,文件无写入权限');
}
}else{
throw new Exception('获取一键部署列表失败:'.(isset($result['msg'])?$result['msg']:'面板连接失败'));
}
}
//获取一键部署列表
public static function get_deplist($os = 'Linux'){
$json_file = get_data_dir($os).'config/deployment_list.json';
if(file_exists($json_file)){
$data = file_get_contents($json_file);
$json_arr = json_decode($data, true);
if($json_arr){
return $json_arr;
}
}
return false;
}
}