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.

228 lines
9.5 KiB

3 years ago
  1. <?php
  2. namespace app\lib;
  3. use Exception;
  4. use ZipArchive;
  5. class Plugins
  6. {
  7. private static function get_btapi(){
  8. $bt_url = config_get('bt_url');
  9. $bt_key = config_get('bt_key');
  10. if(!$bt_url || !$bt_key) throw new Exception('请先配置好宝塔面板接口信息');
  11. $btapi = new Btapi($bt_url, $bt_key);
  12. return $btapi;
  13. }
  14. //刷新插件列表
  15. public static function refresh_plugin_list(){
  16. $btapi = self::get_btapi();
  17. $result = $btapi->get_plugin_list();
  18. if($result && isset($result['list']) && isset($result['type'])){
  19. if(empty($result['list']) || empty($result['type'])){
  20. throw new Exception('获取插件列表失败:插件列表为空');
  21. }
  22. self::save_plugin_list($result);
  23. }else{
  24. throw new Exception('获取插件列表失败:'.($result['msg']?$result['msg']:'面板连接失败'));
  25. }
  26. }
  27. //保存插件列表
  28. private static function save_plugin_list($data){
  29. $data['ip'] = '127.0.0.1';
  30. $data['serverid'] = '';
  31. $data['beta'] = 0;
  32. $data['uid'] = 1;
  33. $data['skey'] = '';
  34. $list = [];
  35. foreach($data['list'] as $plugin){
  36. if(isset($plugin['endtime'])) $plugin['endtime'] = 0;
  37. $list[] = $plugin;
  38. }
  39. $data['list'] = $list;
  40. if($data['pro']>-1) $data['pro'] = 0;
  41. if($data['ltd']>-1) $data['ltd'] = strtotime('+1 year');
  42. $json_file = get_data_dir().'config/plugin_list.json';
  43. if(!file_put_contents($json_file, json_encode($data))){
  44. throw new Exception('保存插件列表失败,文件无写入权限');
  45. }
  46. }
  47. //获取插件列表
  48. public static function get_plugin_list(){
  49. $json_file = get_data_dir().'config/plugin_list.json';
  50. if(file_exists($json_file)){
  51. $data = file_get_contents($json_file);
  52. $json_arr = json_decode($data, true);
  53. if($json_arr){
  54. return $json_arr;
  55. }
  56. }
  57. return false;
  58. }
  59. //获取一个插件信息
  60. public static function get_plugin_info($name){
  61. $json_arr = self::get_plugin_list();
  62. if(!$json_arr) return null;
  63. foreach($json_arr['list'] as $plugin){
  64. if($plugin['name'] == $name){
  65. return $plugin;
  66. }
  67. }
  68. return null;
  69. }
  70. //下载插件(自动判断是否第三方)
  71. public static function download_plugin($plugin_name, $version){
  72. $plugin_info = Plugins::get_plugin_info($plugin_name);
  73. if(!$plugin_info) throw new Exception('未找到该插件信息');
  74. if($plugin_info['type'] == 10 && isset($plugin_info['versions'][0]['download'])){
  75. $fname = $plugin_info['versions'][0]['download'];
  76. $filemd5 = $plugin_info['versions'][0]['md5'];
  77. Plugins::download_plugin_other($fname, $filemd5);
  78. if(isset($plugin_info['min_image']) && strpos($plugin_info['min_image'], 'fname=')){
  79. $fname = substr($plugin_info['min_image'], strpos($plugin_info['min_image'], '?fname=')+7);
  80. Plugins::download_plugin_other($fname);
  81. }
  82. }else{
  83. Plugins::download_plugin_package($plugin_name, $version);
  84. }
  85. }
  86. //下载插件包
  87. public static function download_plugin_package($plugin_name, $version){
  88. $filepath = get_data_dir().'plugins/package/'.$plugin_name.'-'.$version.'.zip';
  89. $btapi = self::get_btapi();
  90. $result = $btapi->get_plugin_filename($plugin_name, $version);
  91. if($result && isset($result['status'])){
  92. if($result['status'] == true){
  93. $filename = $result['filename'];
  94. self::download_file($btapi, $filename, $filepath);
  95. if(file_exists($filepath)){
  96. $zip = new ZipArchive;
  97. if ($zip->open($filepath) === true)
  98. {
  99. $zip->extractTo(get_data_dir().'plugins/folder/'.$plugin_name.'-'.$version);
  100. $zip->close();
  101. $main_filepath = get_data_dir().'plugins/folder/'.$plugin_name.'-'.$version.'/'.$plugin_name.'/'.$plugin_name.'_main.py';
  102. if(file_exists($main_filepath) && filesize($main_filepath)>10){
  103. if(!strpos(file_get_contents($main_filepath), 'import ')){ //加密py文件,需要解密
  104. self::decode_plugin_main($plugin_name, $version, $main_filepath);
  105. $zip->open($filepath, ZipArchive::CREATE);
  106. $zip->addFile($main_filepath, $plugin_name.'/'.$plugin_name.'_main.py');
  107. $zip->close();
  108. }
  109. }
  110. }else{
  111. throw new Exception('插件包解压缩失败');
  112. }
  113. return true;
  114. }else{
  115. throw new Exception('下载插件包失败,本地文件不存在');
  116. }
  117. }else{
  118. throw new Exception('下载插件包失败:'.($result['msg']?$result['msg']:'未知错误'));
  119. }
  120. }else{
  121. throw new Exception('下载插件包失败,接口返回错误');
  122. }
  123. }
  124. //下载插件主程序文件
  125. public static function download_plugin_main($plugin_name, $version){
  126. $filepath = get_data_dir().'plugins/main/'.$plugin_name.'-'.$version.'.dat';
  127. $btapi = self::get_btapi();
  128. $result = $btapi->get_plugin_main_filename($plugin_name, $version);
  129. if($result && isset($result['status'])){
  130. if($result['status'] == true){
  131. $filename = $result['filename'];
  132. self::download_file($btapi, $filename, $filepath);
  133. if(file_exists($filepath)){
  134. return true;
  135. }else{
  136. throw new Exception('下载插件主程序文件失败,本地文件不存在');
  137. }
  138. }else{
  139. throw new Exception('下载插件主程序文件失败:'.($result['msg']?$result['msg']:'未知错误'));
  140. }
  141. }else{
  142. throw new Exception('下载插件主程序文件失败,接口返回错误');
  143. }
  144. }
  145. //解密并下载插件主程序文件
  146. public static function decode_plugin_main($plugin_name, $version, $main_filepath){
  147. $btapi = self::get_btapi();
  148. $result = $btapi->get_decode_plugin_main($plugin_name, $version);
  149. if($result && isset($result['status'])){
  150. if($result['status'] == true){
  151. $filename = $result['filename'];
  152. self::download_file($btapi, $filename, $main_filepath);
  153. return true;
  154. }else{
  155. throw new Exception('解密插件主程序文件失败:'.($result['msg']?$result['msg']:'未知错误'));
  156. }
  157. }else{
  158. throw new Exception('解密插件主程序文件失败,接口返回错误');
  159. }
  160. }
  161. //下载插件其他文件
  162. public static function download_plugin_other($fname, $filemd5 = null){
  163. $filepath = get_data_dir().'plugins/other/'.$fname;
  164. @mkdir(dirname($filepath), 0777, true);
  165. $btapi = self::get_btapi();
  166. $result = $btapi->get_plugin_other_filename($fname);
  167. if($result && isset($result['status'])){
  168. if($result['status'] == true){
  169. $filename = $result['filename'];
  170. self::download_file($btapi, $filename, $filepath);
  171. if(file_exists($filepath)){
  172. if($filemd5 && md5_file($filepath) != $filemd5){
  173. unlink($filepath);
  174. throw new Exception('插件文件MD5校验失败');
  175. }
  176. return true;
  177. }else{
  178. throw new Exception('下载插件文件失败,本地文件不存在');
  179. }
  180. }else{
  181. throw new Exception('下载插件文件失败:'.($result['msg']?$result['msg']:'未知错误'));
  182. }
  183. }else{
  184. throw new Exception('下载插件文件失败,接口返回错误');
  185. }
  186. }
  187. //下载文件
  188. private static function download_file($btapi, $filename, $filepath){
  189. try{
  190. $btapi->download($filename, $filepath);
  191. }catch(Exception $e){
  192. unlink($filepath);
  193. //宝塔bug小文件下载失败,改用base64下载
  194. $result = $btapi->get_file($filename);
  195. if($result && isset($result['status']) && $result['status']==true){
  196. $filedata = base64_decode($result['data']);
  197. if(strlen($filedata) < 4096 && substr($filedata,0,1)=='{' && substr($filedata,-1,1)=='}'){
  198. $arr = json_decode($filedata, true);
  199. if($arr){
  200. throw new Exception('获取文件失败:'.($arr['msg']?$arr['msg']:'未知错误'));
  201. }
  202. }
  203. if(!$filedata){
  204. throw new Exception('获取文件失败:文件内容为空');
  205. }
  206. file_put_contents($filepath, $filedata);
  207. }elseif($result){
  208. throw new Exception('获取文件失败:'.($result['msg']?$result['msg']:'未知错误'));
  209. }else{
  210. throw new Exception('获取文件失败:未知错误');
  211. }
  212. }
  213. }
  214. }