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.

290 lines
15 KiB

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