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.

292 lines
15 KiB

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