国内流行的内容管理系统(CMS)多端全媒体解决方案 https://www.dedebiz.com
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.

588 lines
21KB

  1. <?php
  2. if (!defined('DEDEINC')) exit('dedebiz');
  3. /**
  4. * 织梦模块类
  5. *
  6. * @version $Id: dedemodule.class.php 1 10:31 2010年7月6日Z tianya $
  7. * @package DedeBIZ.Libraries
  8. * @copyright Copyright (c) 2022, DedeBIZ.COM
  9. * @license https://www.dedebiz.com/license
  10. * @link https://www.dedebiz.com
  11. */
  12. require_once(DEDEINC.'/charset.func.php');
  13. require_once(DEDEINC.'/dedeatt.class.php');
  14. require_once(DEDEINC.'/libraries/dedehttpdown.class.php');
  15. function base64url_encode($data)
  16. {
  17. return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
  18. }
  19. function base64url_decode($data)
  20. {
  21. return base64_decode(str_pad(strtr($data, '-_', '+/'), strlen($data) % 4, '=', STR_PAD_RIGHT));
  22. }
  23. class DedeModule
  24. {
  25. var $modulesPath;
  26. var $modulesUrl;
  27. var $modules;
  28. var $fileListNames;
  29. var $sysLang;
  30. var $moduleLang;
  31. function __construct($modulespath = '', $modulesUrl = '')
  32. {
  33. global $cfg_soft_lang;
  34. $this->sysLang = $this->moduleLang = $cfg_soft_lang;
  35. $this->fileListNames = array();
  36. $this->modulesPath = $modulespath;
  37. $this->modulesUrl = $modulesUrl;
  38. }
  39. function DedeModule($modulespath = '')
  40. {
  41. $this->__construct($modulespath);
  42. }
  43. /**
  44. * 枚举系统里已经存在的模块(缓存功能实际上只作hash与文件名的解析,在此不特别处理)
  45. *
  46. * @access public
  47. * @param string $moduletype 模块类型
  48. * @return string
  49. */
  50. function GetModuleList($moduletype = '')
  51. {
  52. if (is_array($this->modules)) return $this->modules;
  53. $dh = dir($this->modulesPath) or die("没找到模块目录:({$this->modulesPath})");
  54. $fp = @fopen($this->modulesPath.'/modulescache.php', 'w') or die('读取文件权限出错,目录文件'.$this->modulesPath.'/modulescache.php不可写');
  55. fwrite($fp, "<"."?php\r\n");
  56. fwrite($fp, "global \$allmodules;\r\n");
  57. while ($filename = $dh->read()) {
  58. if (preg_match("/\.xml$/i", $filename)) {
  59. $minfos = $this->GetModuleInfo(str_replace('.xml', '', $filename));
  60. if ($minfos == null) {
  61. continue;
  62. }
  63. if (isset($minfos['moduletype']) && $moduletype != '' && $moduletype != $minfos['moduletype']) {
  64. continue;
  65. }
  66. if ($minfos['hash'] != '') {
  67. $this->modules[$minfos['hash']] = $minfos;
  68. fwrite($fp, '$'."GLOBALS['allmodules']['{$minfos['hash']}']='{$filename}';\r\n");
  69. }
  70. }
  71. }
  72. fwrite($fp, '?'.'>');
  73. fclose($fp);
  74. $dh->Close();
  75. return $this->modules;
  76. }
  77. /**
  78. * 从远程获取模块信息
  79. *
  80. * @access public
  81. * @param string $moduletype 模块类型
  82. * @return string
  83. */
  84. function GetModuleUrlList($moduletype = '', $url = '')
  85. {
  86. return false;
  87. }
  88. /**
  89. * 转换编码
  90. *
  91. * @access public
  92. * @param string $str 字符串
  93. * @return string
  94. */
  95. function AppCode(&$str)
  96. {
  97. if ($this->moduleLang == $this->sysLang) {
  98. return $str;
  99. } else {
  100. if ($this->sysLang == 'utf-8') {
  101. if ($this->moduleLang == 'gbk') return gb2utf8($str);
  102. if ($this->moduleLang == 'big5') return gb2utf8(big52gb($str));
  103. } else if ($this->sysLang == 'gbk') {
  104. if ($this->moduleLang == 'utf-8') return utf82gb($str);
  105. if ($this->moduleLang == 'big5') return big52gb($str);
  106. } else if ($this->sysLang == 'big5') {
  107. if ($this->moduleLang == 'utf-8') return gb2big5(utf82gb($str));
  108. if ($this->moduleLang == 'gbk') return gb2big5($str);
  109. } else {
  110. return $str;
  111. }
  112. }
  113. }
  114. /**
  115. * 获得指定hash的模块文件
  116. *
  117. * @access public
  118. * @param string $hash hash文件
  119. * @return string
  120. */
  121. function GetHashFile($hash)
  122. {
  123. include_once($this->modulesPath.'/modulescache.php');
  124. if (isset($GLOBALS['allmodules'][$hash])) return $GLOBALS['allmodules'][$hash];
  125. else return $hash.'.xml';
  126. }
  127. /**
  128. * 获得某模块的基本信息
  129. *
  130. * @access public
  131. * @param string $hash hash
  132. * @param string $ftype 文件类型
  133. * @return string
  134. */
  135. function GetModuleInfo($hash, $ftype = 'hash')
  136. {
  137. if ($ftype == 'file') $filename = $hash;
  138. else if (!empty($this->modulesUrl)) {
  139. $filename = $this->modulesUrl.$hash.'.xml';
  140. } else $filename = $this->modulesPath.'/'.$this->GetHashFile($hash);
  141. $start = 0;
  142. $minfos = array();
  143. $minfos['name'] = $minfos['info'] = $minfos['time'] = '';
  144. $minfos['hash'] = $minfos['indexname'] = $minfos['indexurl'] = '';
  145. $minfos['ismember'] = $minfos['autosetup'] = $minfos['autodel'] = 0;
  146. //$minfos['filename'] = $filename;
  147. if (empty($this->modulesUrl)) {
  148. $minfos['filesize'] = filesize($filename) / 1024;
  149. $minfos['filesize'] = number_format($minfos['filesize'], 2, '.', '').' Kb';
  150. }
  151. $fp = fopen($filename, 'r') or die("文件 {$filename} 不存在或不可读!");
  152. $n = 0;
  153. while (!feof($fp)) {
  154. $n++;
  155. if ($n > 30) break;
  156. $line = fgets($fp, 1024);
  157. if ($start == 0) {
  158. if (preg_match("/<baseinfo/is", $line)) $start = 1;
  159. } else {
  160. if (preg_match("/<\/baseinfo/is", $line)) break;
  161. $line = trim($line);
  162. list($skey, $svalue) = explode('=', $line);
  163. $skey = trim($skey);
  164. $minfos[$skey] = $svalue;
  165. }
  166. }
  167. fclose($fp);
  168. if (empty($minfos['lang'])) {
  169. $minfos['lang'] = "utf-8";
  170. }
  171. if (isset($minfos['lang'])) $this->moduleLang = trim($minfos['lang']);
  172. else $this->moduleLang = 'gbk';
  173. if ($this->sysLang == 'gb2312') $this->sysLang = 'gbk';
  174. if ($this->moduleLang == 'gb2312') $this->moduleLang = 'gbk';
  175. if ($this->sysLang != $this->moduleLang) {
  176. foreach ($minfos as $k => $v) $minfos[$k] = $this->AppCode($v);
  177. }
  178. if (isset($minfos['pubkey'])) {
  179. //验证模块信息
  180. $pubKey = @base64url_decode($minfos['pubkey']);
  181. @openssl_public_decrypt(base64url_decode($minfos['info']), $decontent, $pubKey);
  182. $enInfo = (array)json_decode($decontent);
  183. if (count($enInfo) == 0) {
  184. return null;
  185. }
  186. if ($enInfo['module_name'] != $minfos['name'] || $enInfo['dev_id'] != $minfos['dev_id']) {
  187. return null;
  188. }
  189. }
  190. return $minfos;
  191. }
  192. /**
  193. * 获得某模块的基本信息
  194. *
  195. * @access public
  196. * @param string $hash hash
  197. * @param string $ftype 文件类型
  198. * @return string
  199. */
  200. function GetFileXml($hash, $ftype = 'hash')
  201. {
  202. if ($ftype == 'file') $filename = $hash;
  203. else $filename = $this->modulesPath.'/'.$this->GetHashFile($hash);
  204. $filexml = '';
  205. $fp = fopen($filename, 'r') or die("文件 {$filename} 不存在或不可读!");
  206. $start = 0;
  207. while (!feof($fp)) {
  208. $line = fgets($fp, 1024);
  209. if ($start == 0) {
  210. if (preg_match("/<modulefiles/is", $line)) {
  211. $filexml .= $line;
  212. $start = 1;
  213. }
  214. continue;
  215. } else {
  216. $filexml .= $line;
  217. }
  218. }
  219. fclose($fp);
  220. return $filexml;
  221. }
  222. /**
  223. * 获得系统文件的内容
  224. * 指安装、删除、协议文件
  225. *
  226. * @access public
  227. * @param string $hashcode hash码
  228. * @param string $ntype 文件类型
  229. * @param string $enCode 是否加密
  230. * @return string
  231. */
  232. function GetSystemFile($hashcode, $ntype, $enCode = TRUE)
  233. {
  234. $this->GetModuleInfo($hashcode, $ntype);
  235. $start = FALSE;
  236. $filename = $this->modulesPath.'/'.$this->GetHashFile($hashcode);
  237. $fp = fopen($filename, 'r') or die("文件 {$filename} 不存在或不可读!");
  238. $okdata = '';
  239. while (!feof($fp)) {
  240. $line = fgets($fp, 1024);
  241. if (!$start) {
  242. //2011-6-7 修复模块打包程序中上传程序安装生成为空白文件
  243. if (preg_match("#<{$ntype}>#i", $line)) $start = TRUE;
  244. } else {
  245. if (preg_match("#<\/{$ntype}#i", $line)) break;
  246. $okdata .= $line;
  247. unset($line);
  248. }
  249. }
  250. fclose($fp);
  251. $okdata = trim($okdata);
  252. if (!empty($okdata) && $enCode) $okdata = base64_decode($okdata);
  253. $okdata = $this->AppCode($okdata);
  254. return $okdata;
  255. }
  256. /**
  257. * 把某系统文件转换为文件
  258. *
  259. * @access public
  260. * @param string $hashcode hash码
  261. * @param string $ntype 文件类型
  262. * @return string 返回文件名
  263. */
  264. function WriteSystemFile($hashcode, $ntype)
  265. {
  266. $filename = $hashcode."-{$ntype}.php";
  267. $fname = $this->modulesPath.'/'.$filename;
  268. $filect = $this->GetSystemFile($hashcode, $ntype);
  269. $fp = fopen($fname, 'w') or die('生成 {$ntype} 文件失败');
  270. fwrite($fp, $filect);
  271. fclose($fp);
  272. return $filename;
  273. }
  274. /**
  275. * 删除系统文件
  276. *
  277. * @access public
  278. * @param string $hashcode hash码
  279. * @param string $ntype 文件类型
  280. * @return void
  281. */
  282. function DelSystemFile($hashcode, $ntype)
  283. {
  284. $filename = $this->modulesPath.'/'.$hashcode."-{$ntype}.php";
  285. unlink($filename);
  286. }
  287. /**
  288. * 检查是否已经存在指定的模块
  289. *
  290. * @access public
  291. * @param string $hashcode hash码
  292. * @return bool 如果存在则返回True,否则为False
  293. */
  294. function HasModule($hashcode)
  295. {
  296. $modulefile = $this->modulesPath.'/'.$this->GetHashFile($hashcode);
  297. if (file_exists($modulefile) && !is_dir($modulefile)) return TRUE;
  298. else return FALSE;
  299. }
  300. /**
  301. * 读取文件,返回编码后的文件内容
  302. *
  303. * @access public
  304. * @param string $filename 文件名
  305. * @param string $isremove 是否删除
  306. * @return string
  307. */
  308. function GetEncodeFile($filename, $isremove = FALSE)
  309. {
  310. $fp = fopen($filename, 'r') or die("文件 {$filename} 不存在或不可读!");
  311. $str = @fread($fp, filesize($filename));
  312. fclose($fp);
  313. if ($isremove) @unlink($filename);
  314. if (!empty($str)) return base64_encode($str);
  315. else return '';
  316. }
  317. /**
  318. * 获取模块包里的文件名列表
  319. *
  320. * @access public
  321. * @param string $hashcode hash码
  322. * @return string 返回文件列表
  323. */
  324. function GetFileLists($hashcode)
  325. {
  326. $dap = new DedeAttParse();
  327. $filelists = array();
  328. $modulefile = $this->modulesPath.'/'.$this->GetHashFile($hashcode);
  329. $fp = fopen($modulefile, 'r') or die("文件 {$modulefile} 不存在或不可读!");
  330. $i = 0;
  331. while (!feof($fp)) {
  332. $line = fgets($fp, 1024);
  333. if (preg_match("/^[\s]{0,}<file/i", $line)) {
  334. $i++;
  335. $line = trim(preg_replace("/[><]/", "", $line));
  336. $dap->SetSource($line);
  337. $filelists[$i]['type'] = $dap->CAtt->GetAtt('type');
  338. $filelists[$i]['name'] = $dap->CAtt->GetAtt('name');
  339. }
  340. }
  341. fclose($fp);
  342. return $filelists;
  343. }
  344. /**
  345. * 删除已安装模块附带的文件
  346. *
  347. * @access public
  348. * @param string $hashcode hash码
  349. * @param string $isreplace 是否替换
  350. * @return string
  351. */
  352. function DeleteFiles($hashcode, $isreplace = 0)
  353. {
  354. if ($isreplace == 0) return TRUE;
  355. else {
  356. $dap = new DedeAttParse();
  357. $modulefile = $this->modulesPath.'/'.$this->GetHashFile($hashcode);
  358. $fp = fopen($modulefile, 'r') or die("文件 {$modulefile} 不存在或不可读!");
  359. $i = 0;
  360. $dirs = '';
  361. while (!feof($fp)) {
  362. $line = fgets($fp, 1024);
  363. if (preg_match("/^[\s]{0,}<file/i", $line)) {
  364. $i++;
  365. $line = trim(preg_replace("/[><]/", "", $line));
  366. $dap->SetSource($line);
  367. $filetype = $dap->CAtt->GetAtt('type');
  368. $filename = $dap->CAtt->GetAtt('name');
  369. $filename = str_replace("\\", "/", $filename);
  370. if ($filetype == 'dir') {
  371. $dirs[] = $filename;
  372. } else {
  373. @unlink($filename);
  374. }
  375. }
  376. }
  377. $okdirs = array();
  378. if (is_array($dirs)) {
  379. $st = count($dirs) - 1;
  380. for ($i = $st; $i >= 0; $i--) {
  381. @rmdir($dirs[$i]);
  382. }
  383. }
  384. fclose($fp);
  385. }
  386. return TRUE;
  387. }
  388. /**
  389. * 把模块包里的文件写入服务器
  390. *
  391. * @access public
  392. * @param string $hashcode hash码
  393. * @param string $isreplace 是否替换
  394. * @return string
  395. */
  396. function WriteFiles($hashcode, $isreplace = 3)
  397. {
  398. global $AdminBaseDir;
  399. $dap = new DedeAttParse();
  400. $modulefile = $this->modulesPath.'/'.$this->GetHashFile($hashcode);
  401. $fp = fopen($modulefile, 'r') or die("文件 {$modulefile} 不存在或不可读!");
  402. $i = 0;
  403. while (!feof($fp)) {
  404. $line = fgets($fp, 1024);
  405. if (preg_match("/^[\s]{0,}<file/i", $line)) {
  406. $i++;
  407. $line = trim(preg_replace("/[><]/", "", $line));
  408. $dap->SetSource($line);
  409. $filetype = $dap->CAtt->GetAtt('type');
  410. $filename = $dap->CAtt->GetAtt('name');
  411. $filename = str_replace("\\", "/", $filename);
  412. if (!empty($AdminBaseDir)) $filename = $AdminBaseDir.$filename;
  413. if ($filetype == 'dir') {
  414. if (!is_dir($filename)) {
  415. @mkdir($filename, $GLOBALS['cfg_dir_purview']);
  416. }
  417. @chmod($filename, $GLOBALS['cfg_dir_purview']);
  418. } else {
  419. $this->TestDir($filename);
  420. if ($isreplace == 0) continue;
  421. if ($isreplace == 3) {
  422. if (is_file($filename)) {
  423. $copyname = @preg_replace("/([^\/]{1,}$)/", "bak-$1", $filename);
  424. @copy($filename, $copyname);
  425. }
  426. }
  427. if (!empty($filename)) {
  428. $fw = fopen($filename, 'w') or die("写入文件 {$filename} 失败,请检查相关目录的权限");
  429. $ct = '';
  430. while (!feof($fp)) {
  431. $l = fgets($fp, 1024);
  432. if (preg_match("/^[\s]{0,}<\/file/i", trim($l))) {
  433. break;
  434. }
  435. $ct .= $l;
  436. }
  437. $ct = base64_decode($ct);
  438. if ($this->sysLang != $this->moduleLang) {
  439. //转换内码
  440. if (preg_match('/\.(xml|php|inc|txt|htm|html|shtml|tpl|css)$/', $filename)) {
  441. $ct = $this->AppCode($ct);
  442. }
  443. //转换HTML编码标识
  444. if (preg_match('/\.(php|htm|html|shtml|inc|tpl)$/i', $filename)) {
  445. if ($this->sysLang == 'big5') $charset = 'charset=big5';
  446. else if ($this->sysLang == 'utf-8') $charset = 'charset=gb2312';
  447. else $charset = 'charset=gb2312';
  448. $ct = preg_match("/charset=([a-z0-9-]*)/i", $charset, $ct);
  449. }
  450. }
  451. fwrite($fw, $ct);
  452. fclose($fw);
  453. }
  454. }
  455. }
  456. }
  457. fclose($fp);
  458. return TRUE;
  459. }
  460. /**
  461. * 测试某文件的文件夹是否创建
  462. *
  463. * @access public
  464. * @param string $filename 文件名称
  465. * @return string
  466. */
  467. function TestDir($filename)
  468. {
  469. $fs = explode('/', $filename);
  470. $fn = count($fs) - 1;
  471. $ndir = '';
  472. for ($i = 0; $i < $fn; $i++) {
  473. if ($ndir != '') $ndir = $ndir.'/'.$fs[$i];
  474. else $ndir = $fs[$i];
  475. $rs = @is_dir($ndir);
  476. if (!$rs) {
  477. @mkdir($ndir, $GLOBALS['cfg_dir_purview']);
  478. @chmod($ndir, $GLOBALS['cfg_dir_purview']);
  479. }
  480. }
  481. return TRUE;
  482. }
  483. /**
  484. * 获取某个目录或文件的打包数据
  485. *
  486. * @access public
  487. * @param string $basedir 基本目录
  488. * @param string $f
  489. * @param string $fp 文件指针
  490. * @return bool
  491. */
  492. function MakeEncodeFile($basedir, $f, $fp)
  493. {
  494. $this->fileListNames = array();
  495. $this->MakeEncodeFileRun($basedir, $f, $fp);
  496. return TRUE;
  497. }
  498. /**
  499. * 测试目标文件
  500. *
  501. * @access public
  502. * @param string $basedir 基本目录
  503. * @param string $f
  504. * @return bool
  505. */
  506. function MakeEncodeFileTest($basedir, $f)
  507. {
  508. $this->fileListNames = array();
  509. $this->MakeEncodeFileRunTest($basedir, $f);
  510. return TRUE;
  511. }
  512. /**
  513. * 检测某个目录或文件的打包数据,递归
  514. *
  515. * @access public
  516. * @param string $basedir 基本目录
  517. * @param string $f
  518. * @return void
  519. */
  520. function MakeEncodeFileRunTest($basedir, $f)
  521. {
  522. $filename = $basedir.'/'.$f;
  523. if (isset($this->fileListNames[$f])) return;
  524. else if (preg_match("/Thumbs\.db/i", $f)) return;
  525. else $this->fileListNames[$f] = 1;
  526. $fileList = '';
  527. if (!file_exists($filename)) {
  528. ShowMsg("文件或文件夹: {$filename} 不存在,无法进行编译", "-1");
  529. exit();
  530. }
  531. if (is_dir($filename)) {
  532. $dh = dir($filename);
  533. while ($filename = $dh->read()) {
  534. if ($filename[0] == '.' || strtolower($filename) == 'cvs') continue;
  535. $nfilename = $f.'/'.$filename;
  536. $this->MakeEncodeFileRunTest($basedir, $nfilename);
  537. }
  538. }
  539. }
  540. /**
  541. * 获取个目录或文件的打包数据,递归
  542. *
  543. * @access public
  544. * @param string $basedir 基本目录
  545. * @param string $f
  546. * @param string $fp 文件指针
  547. * @return string
  548. */
  549. function MakeEncodeFileRun($basedir, $f, $fp)
  550. {
  551. $filename = $basedir.'/'.$f;
  552. if (isset($this->fileListNames[$f])) return;
  553. else if (preg_match("#Thumbs\.db#i", $f)) return;
  554. else $this->fileListNames[$f] = 1;
  555. $fileList = '';
  556. if (is_dir($filename)) {
  557. $fileList .= "<file type='dir' name='$f'>\r\n";
  558. $fileList .= "</file>\r\n";
  559. fwrite($fp, $fileList);
  560. $dh = dir($filename);
  561. while ($filename = $dh->read()) {
  562. if ($filename[0] == '.' || strtolower($filename) == 'cvs') continue;
  563. $nfilename = $f.'/'.$filename;
  564. $this->MakeEncodeFileRun($basedir, $nfilename, $fp);
  565. }
  566. } else {
  567. $fileList .= "<file type='file' name='$f'>\r\n";
  568. $fileList .= $this->GetEncodeFile($filename);
  569. $fileList .= "\r\n</file>\r\n";
  570. fwrite($fp, $fileList);
  571. }
  572. }
  573. /**
  574. * 清理
  575. *
  576. * @access public
  577. * @return void
  578. */
  579. function Clear()
  580. {
  581. unset($this->modules);
  582. unset($this->fileList);
  583. unset($this->fileListNames);
  584. }
  585. }//End Class
  586. ?>