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

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