国内流行的内容管理系统(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.

623 lines
21KB

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