You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

307 lines
16 KiB

2 years ago
13 hours ago
2 years ago
13 hours ago
2 years ago
11 months ago
2 years ago
11 months ago
2 years ago
11 months ago
2 years ago
1 year ago
2 years ago
1 year ago
2 years ago
1 year ago
2 years ago
11 months ago
2 years ago
11 months ago
2 years ago
9 months ago
2 years ago
9 months ago
2 years ago
1 year ago
2 years ago
11 months ago
2 years ago
9 months ago
2 years ago
  1. <?php
  2. namespace app\lib;
  3. use Exception;
  4. use ZipArchive;
  5. class BtPlugins
  6. {
  7. private $btapi;
  8. private $os;
  9. //需屏蔽的插件名称列表
  10. public static $block_plugins = ['dns', 'bt_boce', 'ssl_verify', 'firewall', 'KylinOperatingSystem', 'KingdeeApusicDistributedCache', 'BorlandCacheServer', 'GBase8s', 'KingdeeApusicLoadBalancer', 'BorlandWebServer'];
  11. public static $skip_plugins = ['php_filter', 'enterprise_backup', 'tamper_drive'];
  12. public function __construct($os){
  13. $this->os = $os;
  14. if($os == 'en'){
  15. $bt_url = config_get('enbt_url');
  16. $bt_key = config_get('enbt_key');
  17. }elseif($os == 'Windows'){
  18. $bt_url = config_get('wbt_url');
  19. $bt_key = config_get('wbt_key');
  20. }else{
  21. $bt_url = config_get('bt_url');
  22. $bt_key = config_get('bt_key');
  23. }
  24. if(!$bt_url || !$bt_key) throw new Exception('请先配置好宝塔面板接口信息');
  25. $this->btapi = new Btapi($bt_url, $bt_key);
  26. }
  27. //获取插件列表
  28. public function get_plugin_list(){
  29. $result = $this->btapi->get_plugin_list();
  30. if($result && isset($result['list']) && isset($result['type'])){
  31. if(empty($result['list']) || empty($result['type'])){
  32. throw new Exception('获取插件列表失败:插件列表为空');
  33. }
  34. $newlist = [];
  35. foreach($result['list'] as $item){
  36. if(!in_array($item['name'], self::$block_plugins)) $newlist[] = $item;
  37. }
  38. $result['list'] = $newlist;
  39. return $result;
  40. }else{
  41. throw new Exception('获取插件列表失败:'.(isset($result['msg'])?$result['msg']:'面板连接失败'));
  42. }
  43. }
  44. //下载插件(自动判断是否第三方)
  45. public function download_plugin($plugin_name, $version, $plugin_info){
  46. if($plugin_info['type'] == 10 && isset($plugin_info['versions'][0]['download'])){
  47. if($plugin_info['price'] == 0){
  48. $this->btapi->create_plugin_other_order($plugin_info['id']);
  49. }
  50. $fname = $plugin_info['versions'][0]['download'];
  51. $filemd5 = $plugin_info['versions'][0]['md5'];
  52. $this->download_plugin_other($fname, $filemd5);
  53. if(isset($plugin_info['min_image']) && strpos($plugin_info['min_image'], 'fname=')){
  54. $fname = substr($plugin_info['min_image'], strpos($plugin_info['min_image'], '?fname=')+7);
  55. $this->download_plugin_other($fname);
  56. }
  57. }else{
  58. $this->download_plugin_package($plugin_name, $version);
  59. }
  60. }
  61. //下载插件包
  62. private function download_plugin_package($plugin_name, $version){
  63. $filepath = get_data_dir($this->os).'plugins/package/'.$plugin_name.'-'.$version.'.zip';
  64. $result = $this->btapi->get_plugin_filename($plugin_name, $version);
  65. if($result && isset($result['status'])){
  66. if($result['status'] == true){
  67. $filename = $result['filename'];
  68. $this->download_file($filename, $filepath);
  69. if(file_exists($filepath)){
  70. $zip = new ZipArchive;
  71. if ($zip->open($filepath) === true)
  72. {
  73. $plugins_dir = get_data_dir($this->os).'plugins/folder/'.$plugin_name.'-'.$version;
  74. $zip->extractTo($plugins_dir, $plugin_name.'/'.$plugin_name.'_main.py');
  75. $zip->close();
  76. $main_filepath = $plugins_dir.'/'.$plugin_name.'/'.$plugin_name.'_main.py';
  77. if(file_exists($main_filepath) && filesize($main_filepath)>10){
  78. if(!strpos(file_get_contents($main_filepath), 'import ')){ //加密py文件,需要解密
  79. $this->decode_plugin_main($plugin_name, $version, $main_filepath);
  80. $this->noauth_plugin_main($main_filepath);
  81. $zip->open($filepath, ZipArchive::CREATE);
  82. $zip->addFile($main_filepath, $plugin_name.'/'.$plugin_name.'_main.py');
  83. $zip->close();
  84. }
  85. }
  86. deleteDir($plugins_dir);
  87. }else{
  88. unlink($filepath);
  89. throw new Exception('插件包解压缩失败');
  90. }
  91. return true;
  92. }else{
  93. throw new Exception('下载插件包失败,本地文件不存在');
  94. }
  95. }else{
  96. throw new Exception('下载插件包失败:'.($result['msg']?$result['msg']:'未知错误'));
  97. }
  98. }else{
  99. throw new Exception('下载插件包失败,接口返回错误');
  100. }
  101. }
  102. //下载插件主程序文件
  103. public function download_plugin_main($plugin_name, $version){
  104. $filepath = get_data_dir($this->os).'plugins/main/'.$plugin_name.'-'.$version.'.dat';
  105. $result = $this->btapi->get_plugin_main_filename($plugin_name, $version);
  106. if($result && isset($result['status'])){
  107. if($result['status'] == true){
  108. $filename = $result['filename'];
  109. $this->download_file($filename, $filepath);
  110. if(file_exists($filepath)){
  111. return true;
  112. }else{
  113. throw new Exception('下载插件主程序文件失败,本地文件不存在');
  114. }
  115. }else{
  116. throw new Exception('下载插件主程序文件失败:'.($result['msg']?$result['msg']:'未知错误'));
  117. }
  118. }else{
  119. throw new Exception('下载插件主程序文件失败,接口返回错误');
  120. }
  121. }
  122. //解密并下载插件主程序文件
  123. private function decode_plugin_main($plugin_name, $version, $main_filepath){
  124. if($this->decode_plugin_main_local($main_filepath)) return true;
  125. $result = $this->btapi->get_decode_plugin_main($plugin_name, $version);
  126. if($result && isset($result['status'])){
  127. if($result['status'] == true){
  128. $filename = $result['filename'];
  129. $this->download_file($filename, $main_filepath);
  130. return true;
  131. }else{
  132. throw new Exception('解密插件主程序文件失败:'.($result['msg']?$result['msg']:'未知错误'));
  133. }
  134. }else{
  135. throw new Exception('解密插件主程序文件失败,接口返回错误');
  136. }
  137. }
  138. //本地解密插件主程序文件
  139. public function decode_plugin_main_local($main_filepath){
  140. $userinfo = $this->btapi->get_user_info();
  141. if(isset($userinfo['uid'])){
  142. $src = file_get_contents($main_filepath);
  143. if($src===false)throw new Exception('文件打开失败');
  144. if(!$src || strpos($src, 'import ')!==false)return true;
  145. $uid = $userinfo['uid'];
  146. $serverid = $userinfo['serverid'];
  147. $key = md5(substr($serverid, 10, 16).$uid.$serverid);
  148. $iv = md5($key.$serverid);
  149. $key = substr($key, 8, 16);
  150. $iv = substr($iv, 8, 16);
  151. $data_arr = explode("\n", $src);
  152. $de_text = '';
  153. foreach($data_arr as $data){
  154. $data = trim($data);
  155. if(!empty($data)){
  156. $tmp = openssl_decrypt($data, 'aes-128-cbc', $key, 0, $iv);
  157. if($tmp !== false) $de_text .= $tmp;
  158. }
  159. }
  160. if(!empty($de_text) && strpos($de_text, 'import ')!==false){
  161. file_put_contents($main_filepath, $de_text);
  162. return true;
  163. }
  164. return false;
  165. }else{
  166. throw new Exception('解密插件主程序文件失败,获取用户信息失败');
  167. }
  168. }
  169. //去除插件主程序文件授权校验
  170. private function noauth_plugin_main($main_filepath){
  171. $data = file_get_contents($main_filepath);
  172. if(!$data) return false;
  173. $data = str_replace('\'http://www.bt.cn/api/panel/get_soft_list_test', 'public.GetConfigValue(\'home\')+\'/api/panel/get_soft_list_test', $data);
  174. $data = str_replace('\'https://www.bt.cn/api/panel/get_soft_list_test', 'public.GetConfigValue(\'home\')+\'/api/panel/get_soft_list_test', $data);
  175. $data = str_replace('\'http://www.bt.cn/api/panel/get_soft_list', 'public.GetConfigValue(\'home\')+\'/api/panel/get_soft_list', $data);
  176. $data = str_replace('\'https://www.bt.cn/api/panel/get_soft_list', 'public.GetConfigValue(\'home\')+\'/api/panel/get_soft_list', $data);
  177. $data = str_replace('\'http://www.bt.cn/api/panel/notpro', 'public.GetConfigValue(\'home\')+\'/api/panel/notpro', $data);
  178. $data = str_replace('\'https://www.bt.cn/api/panel/notpro', 'public.GetConfigValue(\'home\')+\'/api/panel/notpro', $data);
  179. $data = str_replace('\'http://www.bt.cn/api/wpanel/get_soft_list_test', 'public.GetConfigValue(\'home\')+\'/api/wpanel/get_soft_list_test', $data);
  180. $data = str_replace('\'https://www.bt.cn/api/wpanel/get_soft_list_test', 'public.GetConfigValue(\'home\')+\'/api/wpanel/get_soft_list_test', $data);
  181. $data = str_replace('\'http://www.bt.cn/api/wpanel/get_soft_list', 'public.GetConfigValue(\'home\')+\'/api/wpanel/get_soft_list', $data);
  182. $data = str_replace('\'https://www.bt.cn/api/wpanel/get_soft_list', 'public.GetConfigValue(\'home\')+\'/api/wpanel/get_soft_list', $data);
  183. $data = str_replace('\'http://www.bt.cn/api/wpanel/notpro', 'public.GetConfigValue(\'home\')+\'/api/wpanel/notpro', $data);
  184. $data = str_replace('\'https://www.bt.cn/api/wpanel/notpro', 'public.GetConfigValue(\'home\')+\'/api/wpanel/notpro', $data);
  185. $data = str_replace('"https://www.bt.cn/api/bt_waf/get_malicious', 'public.GetConfigValue(\'home\')+"/api/bt_waf/get_malicious', $data);
  186. $data = str_replace('\'http://www.bt.cn/api/bt_waf/getSpiders', 'public.GetConfigValue(\'home\')+\'/api/bt_waf/getSpiders', $data);
  187. $data = str_replace('\'https://www.bt.cn/api/bt_waf/getSpiders', 'public.GetConfigValue(\'home\')+\'/api/bt_waf/getSpiders', $data);
  188. $data = str_replace('\'http://www.bt.cn/api/bt_waf/addSpider', 'public.GetConfigValue(\'home\')+\'/api/bt_waf/addSpider', $data);
  189. $data = str_replace('\'https://www.bt.cn/api/bt_waf/addSpider', 'public.GetConfigValue(\'home\')+\'/api/bt_waf/addSpider', $data);
  190. $data = str_replace('\'https://www.bt.cn/api/bt_waf/getVulScanInfoList', 'public.GetConfigValue(\'home\')+\'/api/bt_waf/getVulScanInfoList', $data);
  191. $data = str_replace('\'https://www.bt.cn/api/bt_waf/reportInterceptFail', 'public.GetConfigValue(\'home\')+\'/api/bt_waf/reportInterceptFail', $data);
  192. $data = str_replace('"https://www.bt.cn/api/bt_waf/reportInterceptFail', 'public.GetConfigValue(\'home\')+"/api/bt_waf/reportInterceptFail', $data);
  193. $data = str_replace('\'https://www.bt.cn/api/v2/contact/nps/questions', 'public.GetConfigValue(\'home\')+\'/panel/notpro', $data);
  194. $data = str_replace('\'https://www.bt.cn/api/v2/contact/nps/submit', 'public.GetConfigValue(\'home\')+\'/panel/notpro', $data);
  195. $data = str_replace('\'http://www.bt.cn/api/Auth', 'public.GetConfigValue(\'home\')+\'/api/Auth', $data);
  196. $data = str_replace('\'https://www.bt.cn/api/Auth', 'public.GetConfigValue(\'home\')+\'/api/Auth', $data);
  197. $data = str_replace('\'https://brandnew.aapanel.com/api/panel/getSoftList', 'public.OfficialApiBase()+\'/api/panel/getSoftList', $data);
  198. file_put_contents($main_filepath, $data);
  199. }
  200. //下载插件其他文件
  201. private function download_plugin_other($fname, $filemd5 = null){
  202. $filepath = get_data_dir().'plugins/other/'.$fname;
  203. @mkdir(dirname($filepath), 0777, true);
  204. $result = $this->btapi->get_plugin_other_filename($fname);
  205. if($result && isset($result['status'])){
  206. if($result['status'] == true){
  207. $filename = $result['filename'];
  208. $this->download_file($filename, $filepath);
  209. if(file_exists($filepath)){
  210. if($filemd5 && md5_file($filepath) != $filemd5){
  211. $msg = filesize($filepath) < 300 ? file_get_contents($filepath) : '插件文件MD5校验失败';
  212. @unlink($filepath);
  213. throw new Exception($msg);
  214. }
  215. return true;
  216. }else{
  217. throw new Exception('下载插件文件失败,本地文件不存在');
  218. }
  219. }else{
  220. throw new Exception('下载插件文件失败:'.($result['msg']?$result['msg']:'未知错误'));
  221. }
  222. }else{
  223. throw new Exception('下载插件文件失败,接口返回错误');
  224. }
  225. }
  226. //下载文件
  227. private function download_file($filename, $filepath){
  228. try{
  229. $this->btapi->download($filename, $filepath);
  230. }catch(Exception $e){
  231. @unlink($filepath);
  232. //宝塔bug小文件下载失败,改用base64下载
  233. $result = $this->btapi->get_file($filename);
  234. if($result && isset($result['status']) && $result['status']==true){
  235. $filedata = base64_decode($result['data']);
  236. if(strlen($filedata) < 4096 && substr($filedata,0,1)=='{' && substr($filedata,-1,1)=='}'){
  237. $arr = json_decode($filedata, true);
  238. if($arr){
  239. throw new Exception('获取文件失败:'.($arr['msg']?$arr['msg']:'未知错误'));
  240. }
  241. }
  242. if(!$filedata){
  243. throw new Exception('获取文件失败:文件内容为空');
  244. }
  245. file_put_contents($filepath, $filedata);
  246. }elseif($result){
  247. throw new Exception('获取文件失败:'.($result['msg']?$result['msg']:'未知错误'));
  248. }else{
  249. throw new Exception('获取文件失败:未知错误');
  250. }
  251. }
  252. }
  253. //获取一键部署列表
  254. public function get_deplist(){
  255. $result = $this->btapi->get_deplist();
  256. if($result && isset($result['list']) && isset($result['type'])){
  257. if(empty($result['list']) || empty($result['type'])){
  258. throw new Exception('获取一键部署列表失败:一键部署列表为空');
  259. }
  260. return $result;
  261. }else{
  262. throw new Exception('获取一键部署列表失败:'.(isset($result['msg'])?$result['msg']:'面板连接失败'));
  263. }
  264. }
  265. //获取蜘蛛IP列表
  266. public function btwaf_getspiders(){
  267. $result = $this->btapi->btwaf_getspiders();
  268. if(isset($result['status']) && $result['status']){
  269. return $result['data'];
  270. }else{
  271. throw new Exception(isset($result['msg'])?$result['msg']:'获取失败');
  272. }
  273. }
  274. //获取堡塔恶意情报IP库
  275. public function btwaf_getmalicious(){
  276. $result = $this->btapi->btwaf_getmalicious();
  277. if(isset($result['success'])){
  278. return $result;
  279. }elseif(isset($result['msg'])){
  280. throw new Exception($result['msg']);
  281. }else{
  282. throw new Exception(isset($result['res'])?$result['res']:'获取失败');
  283. }
  284. }
  285. }