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.

285 lines
14 KiB

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