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

637 line
22KB

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