PageRenderTime 44ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/admin/libs/archive.php

https://github.com/foxadmin/ReloadCMS
PHP | 590 lines | 449 code | 107 blank | 34 comment | 147 complexity | c8e4ef7d734cceed3afcbd5f44c691d5 MD5 | raw file
Possible License(s): Apache-2.0, BSD-3-Clause, LGPL-2.1
  1. <?
  2. ////////////////////////////////////////////////////////////////////////////////
  3. // Copyright (C) ReloadCMS Development Team //
  4. // http://reloadcms.com //
  5. // This product released under GNU General Public License v2 //
  6. ////////////////////////////////////////////////////////////////////////////////
  7. class archiveTar
  8. {
  9. var $archive_name = '';
  10. var $tmp_file = 0;
  11. var $file_pos = 0;
  12. var $isGzipped = true;
  13. var $errors = array();
  14. var $files = array();
  15. function archiveTar(){
  16. if (!isset($this->errors)) $this->errors = array();
  17. }
  18. /*
  19. * Create archive from array filenames or directories, example:
  20. * $backup = new archiveTar();
  21. * $backup->archive_name = './content.tgz';
  22. * $backup->createArchive(array('./config','./content'));
  23. */
  24. function createArchive($file_list){
  25. $result = false;
  26. if (file_exists($this->archive_name) && is_file($this->archive_name)) $newArchive = false;
  27. else $newArchive = true;
  28. if ($newArchive){
  29. if (!$this->openWrite())
  30. return false;
  31. } else {
  32. if (filesize($this->archive_name) == 0)
  33. return $this->openWrite();
  34. if ($this->isGzipped){
  35. $this->closeTmpFile();
  36. if (!@rename($this->archive_name, $this->archive_name.'.tmp')){
  37. $this->errors[] = __('Cannot rename').' '.$this->archive_name.__(' to ').$this->archive_name.'.tmp';
  38. return false;
  39. }
  40. $tmpArchive = @gzopen($this->archive_name.'.tmp', 'rb');
  41. if (!$tmpArchive){
  42. $this->errors[] = $this->archive_name.'.tmp '.__('is not readable');
  43. @rename($this->archive_name.'.tmp', $this->archive_name);
  44. return false;
  45. }
  46. if (!$this->openWrite()){
  47. @rename($this->archive_name.'.tmp', $this->archive_name);
  48. return false;
  49. }
  50. $buffer = @gzread($tmpArchive, 512);
  51. if (!@gzeof($tmpArchive)){
  52. do {
  53. $binaryData = pack('a512', $buffer);
  54. $this->writeBlock($binaryData);
  55. $buffer = @gzread($tmpArchive, 512);
  56. }
  57. while (!@gzeof($tmpArchive));
  58. }
  59. @gzclose($tmpArchive);
  60. @unlink($this->archive_name.'.tmp');
  61. } else {
  62. $this->tmp_file = @fopen($this->archive_name, 'r+b');
  63. if (!$this->tmp_file) return false;
  64. }
  65. }
  66. if (isset($file_list) && is_array($file_list)) {
  67. if (count($file_list)>0)
  68. $result = $this->packFileArray($file_list);
  69. } else $this->errors[] = __('No file').__(' to ').__('Archive');
  70. if (($result)&&(is_resource($this->tmp_file))){
  71. $binaryData = pack('a512', '');
  72. $this->writeBlock($binaryData);
  73. }
  74. $this->closeTmpFile();
  75. if ($newArchive && !$result){
  76. $this->closeTmpFile();
  77. @unlink($this->archive_name);
  78. }
  79. return $result;
  80. }
  81. /*
  82. * Restore archive including the path, example:
  83. * $Archive = new archiveTar();
  84. * $Archive->archive_name = './content.tgz';
  85. * $Archive->restoreArchive('./');
  86. */
  87. function restoreArchive($path){
  88. $fileName = $this->archive_name;
  89. if (!$this->isGzipped){
  90. if (@file_exists($fileName)){
  91. if ($fp = @fopen($fileName, 'rb')){
  92. $data = fread($fp, 2);
  93. fclose($fp);
  94. if ($data == '\37\213'){
  95. $this->isGzipped = true;
  96. }
  97. }
  98. }
  99. elseif ((substr($fileName, -2) == 'gz') OR (substr($fileName, -3) == 'tgz')) $this->isGzipped = true;
  100. }
  101. $result = true;
  102. if ($this->isGzipped)
  103. $this->tmp_file = @gzopen($fileName, 'rb');
  104. else
  105. $this->tmp_file = @fopen($fileName, 'rb');
  106. if (!$this->tmp_file){
  107. $this->errors[] = $fileName.' '.__('is not readable');
  108. return false;
  109. }
  110. $result = $this->unpackFileArray($path);
  111. $this->closeTmpFile();
  112. return $result;
  113. }
  114. /* Show errors when pack/unpack
  115. * Example:
  116. * $Archive = new archiveTar();
  117. * //pack or unpack;
  118. * echo $Archive->showErrors($Archive->archive_name);
  119. */
  120. function showErrors ($message = '') {
  121. $Errors = $this->errors;
  122. if(count($Errors)>0) {
  123. if (!empty($message)) $message = ' ('.$message.')';
  124. $message = __('Error occurred').$message.': <br/>';
  125. foreach ($Errors as $value)
  126. $message .= $value.'<br/>';
  127. return $message;
  128. } else return '';
  129. }
  130. function packFileArray($file_array){
  131. $result = true;
  132. if (!$this->tmp_file){
  133. $this->errors[] = __('Invalid file descriptor');
  134. return false;
  135. }
  136. if (!is_array($file_array) || count($file_array)<=0)
  137. return true;
  138. for ($i = 0; $i<count($file_array); $i++){
  139. $filename = $file_array[$i];
  140. if ($filename == $this->archive_name)
  141. continue;
  142. if (strlen($filename)<=0)
  143. continue;
  144. if (!file_exists($filename)){
  145. $this->errors[] = __('No file').' '.$filename;
  146. continue;
  147. }
  148. if (!$this->tmp_file){
  149. $this->errors[] = __('Invalid file descriptor');
  150. return false;
  151. }
  152. if (strlen($filename)<=0){
  153. $this->errors[] = __('Filename').' '.__('is incorrect');;
  154. return false;
  155. }
  156. $filename = str_replace('\\', '/', $filename);
  157. $keep_filename = $this->makeGoodPath($filename);
  158. if (is_file($filename)){
  159. if (($file = @fopen($filename, 'rb')) == 0){
  160. $this->errors[] = __('Mode ').__('is incorrect');
  161. }
  162. if(($this->file_pos == 0)){
  163. if(!$this->writeHeader($filename, $keep_filename))
  164. return false;
  165. }
  166. while (($buffer = fread($file, 512)) != ''){
  167. $binaryData = pack('a512', $buffer);
  168. $this->writeBlock($binaryData);
  169. }
  170. fclose($file);
  171. } else $this->writeHeader($filename, $keep_filename);
  172. if (@is_dir($filename)){
  173. if (!($handle = opendir($filename))){
  174. $this->errors[] = __('Error').': '.__('Directory ').$filename.__('is not readable');
  175. continue;
  176. }
  177. while (false !== ($dir = readdir($handle))){
  178. if ($dir!='.' && $dir!='..'){
  179. $file_array_tmp = array();
  180. if ($filename != '.')
  181. $file_array_tmp[] = $filename.'/'.$dir;
  182. else
  183. $file_array_tmp[] = $dir;
  184. $result = $this->packFileArray($file_array_tmp);
  185. }
  186. }
  187. unset($file_array_tmp);
  188. unset($dir);
  189. unset($handle);
  190. }
  191. }
  192. return $result;
  193. }
  194. function unpackFileArray($path){
  195. $path = str_replace('\\', '/', $path);
  196. if ($path == '' || (substr($path, 0, 1) != '/' && substr($path, 0, 3) != '../' && !strpos($path, ':')))
  197. $path = './'.$path;
  198. clearstatcache();
  199. while (strlen($binaryData = $this->readBlock()) != 0){
  200. if (!$this->readHeader($binaryData, $header))
  201. return false;
  202. if ($header['filename'] == '')
  203. continue;
  204. if ($header['typeflag'] == 'L'){ //reading long header
  205. $filename = '';
  206. $decr = floor($header['size']/512);
  207. for ($i = 0; $i < $decr; $i++){
  208. $content = $this->readBlock();
  209. $filename .= $content;
  210. }
  211. if (($laspiece = $header['size'] % 512) != 0){
  212. $content = $this->readBlock();
  213. $filename .= substr($content, 0, $laspiece);
  214. }
  215. $binaryData = $this->readBlock();
  216. if (!$this->readHeader($binaryData, $header))
  217. return false;
  218. else $header['filename'] = $filename;
  219. return true;
  220. }
  221. if (($path != './') && ($path != '/')){
  222. while (substr($path, -1) == '/')
  223. $path = substr($path, 0, strlen($path)-1);
  224. if (substr($header['filename'], 0, 1) == '/')
  225. $header['filename'] = $path.$header['filename'];
  226. else
  227. $header['filename'] = $path.'/'.$header['filename'];
  228. }
  229. if (file_exists($header['filename'])){
  230. if ((@is_dir($header['filename'])) && ($header['typeflag'] == '')){
  231. $this->errors[] =__('File ').$header['filename'].__(' already exists').__(' as folder');
  232. return false;
  233. }
  234. if ((is_file($header['filename'])) && ($header['typeflag'] == '5')){
  235. $this->errors[] =__('Cannot create directory').'. '.__('File ').$header['filename'].__(' already exists');
  236. return false;
  237. }
  238. if (!is_writeable($header['filename'])){
  239. $this->errors[] = __('Cannot write to file').'. '.__('File ').$header['filename'].__(' already exists');
  240. return false;
  241. }
  242. }
  243. elseif (($this->dirCheck(($header['typeflag'] == '5' ? $header['filename'] : dirname($header['filename'])))) != 1){
  244. $this->errors[] = __('Cannot create directory').' '.__(' for ').$header['filename'];
  245. return false;
  246. }
  247. if ($header['typeflag'] == '5'){
  248. if (!@file_exists($header['filename'])) {
  249. if (!@mkdir($header['filename'], 0777)) {
  250. $this->errors[] = __('Cannot create directory').' '.$header['filename'];
  251. return false;
  252. }
  253. }
  254. } else {
  255. if (($destination = @fopen($header['filename'], 'wb')) == 0)
  256. {
  257. $this->errors[] = __('Cannot write to file').' '.$header['filename'];
  258. return false;
  259. } else {
  260. $decr = floor($header['size']/512);
  261. for ($i = 0; $i < $decr; $i++)
  262. {
  263. $content = $this->readBlock();
  264. fwrite($destination, $content, 512);
  265. }
  266. if (($header['size'] % 512) != 0)
  267. {
  268. $content = $this->readBlock();
  269. fwrite($destination, $content, ($header['size'] % 512));
  270. }
  271. @fclose($destination);
  272. @touch($header['filename'], $header['time']);
  273. }
  274. clearstatcache();
  275. if (filesize($header['filename']) != $header['size'])
  276. {
  277. $this->errors[] = __('Size of file').' '.$header['filename'].' '.__('is incorrect');
  278. return false;
  279. }
  280. }
  281. if (($file_dir = dirname($header['filename'])) == $header['filename'])
  282. $file_dir = '';
  283. if ((substr($header['filename'], 0, 1) == '/') && ($file_dir == ''))
  284. $file_dir = '/';
  285. $this->dirs[] = $file_dir;
  286. $this->files[] = $header['filename'];
  287. }
  288. return true;
  289. }
  290. /*
  291. * Check if a directory exists and create it (including parent dirs) if not.
  292. */
  293. function dirCheck($dir){
  294. $parent_dir = dirname($dir);
  295. if ((@is_dir($dir)) or ($dir == ''))
  296. return true;
  297. if (($parent_dir != $dir) and ($parent_dir != '') and (!$this->dirCheck($parent_dir)))
  298. return false;
  299. if (!@mkdir($dir, 0777)){
  300. $this->errors[] = __('Cannot create directory').' '.$dir;
  301. return false;
  302. }
  303. return true;
  304. }
  305. function readHeader($binaryData, &$header){
  306. if (strlen($binaryData)==0){
  307. $header['filename'] = '';
  308. return true;
  309. }
  310. if (strlen($binaryData) != 512){
  311. $header['filename'] = '';
  312. $this->__('Invalid block size').': '.strlen($binaryData);
  313. return false;
  314. }
  315. $checksum = 0;
  316. for ($i = 0; $i < 148; $i++)
  317. $checksum+=ord(substr($binaryData, $i, 1));
  318. for ($i = 148; $i < 156; $i++)
  319. $checksum += ord(' ');
  320. for ($i = 156; $i < 512; $i++)
  321. $checksum+=ord(substr($binaryData, $i, 1));
  322. $unpack_data = unpack('a100filename/a8mode/a8user_id/a8group_id/a12size/a12time/a8checksum/a1typeflag/a100link/a6magic/a2version/a32uname/a32gname/a8devmajor/a8devminor', $binaryData);
  323. $header['checksum'] = OctDec(trim($unpack_data['checksum']));
  324. if ($header['checksum'] != $checksum){
  325. $header['filename'] = '';
  326. if (($checksum == 256) && ($header['checksum'] == 0)) return true;
  327. $this->errors[] = __('Error checksum for file ').$unpack_data['filename'];
  328. return false;
  329. }
  330. if (($header['typeflag'] = $unpack_data['typeflag']) == '5') $header['size'] = 0;
  331. $header['filename'] = trim($unpack_data['filename']);
  332. $header['mode'] = OctDec(trim($unpack_data['mode']));
  333. $header['user_id'] = OctDec(trim($unpack_data['user_id']));
  334. $header['group_id'] = OctDec(trim($unpack_data['group_id']));
  335. $header['size'] = OctDec(trim($unpack_data['size']));
  336. $header['time'] = OctDec(trim($unpack_data['time']));
  337. return true;
  338. }
  339. function writeHeader($filename, $keep_filename){
  340. $packF = 'a100a8a8a8a12A12';
  341. $packL = 'a1a100a6a2a32a32a8a8a155a12';
  342. if (strlen($keep_filename)<=0)
  343. $keep_filename = $filename;
  344. $filename_ready = $this->makeGoodPath($keep_filename);
  345. if (strlen($filename_ready) > 99){ //write long header
  346. $dataFirst = pack($packF, '././@LongLink', 0, 0, 0, sprintf('%11s ', DecOct(strlen($filename_ready))), 0);
  347. $dataLast = pack($packL, 'L', '', '', '', '', '', '', '', '', '');
  348. // Calculate the checksum
  349. $checksum = 0;
  350. // First part of the header
  351. for ($i = 0; $i < 148; $i++)
  352. $checksum += ord(substr($dataFirst, $i, 1));
  353. // Ignore the checksum value and replace it by ' ' (space)
  354. for ($i = 148; $i < 156; $i++)
  355. $checksum += ord(' ');
  356. // Last part of the header
  357. for ($i = 156, $j=0; $i < 512; $i++, $j++)
  358. $checksum += ord(substr($dataLast, $j, 1));
  359. // Write the first 148 bytes of the header in the archive
  360. $this->writeBlock($dataFirst, 148);
  361. // Write the calculated checksum
  362. $checksum = sprintf('%6s ', DecOct($checksum));
  363. $binaryData = pack('a8', $checksum);
  364. $this->writeBlock($binaryData, 8);
  365. // Write the last 356 bytes of the header in the archive
  366. $this->writeBlock($dataLast, 356);
  367. $tmp_filename = $this->makeGoodPath($filename_ready);
  368. $i = 0;
  369. while (($buffer = substr($tmp_filename, (($i++)*512), 512)) != ''){
  370. $binaryData = pack('a512', $buffer);
  371. $this->writeBlock($binaryData);
  372. }
  373. return true;
  374. }
  375. $file_info = stat($filename);
  376. if (@is_dir($filename)){
  377. $typeflag = '5';
  378. $size = sprintf('%11s ', DecOct(0));
  379. } else {
  380. $typeflag = '';
  381. clearstatcache();
  382. $size = sprintf('%11s ', DecOct(filesize($filename)));
  383. }
  384. $dataFirst = pack($packF, $filename_ready, sprintf('%6s ', DecOct(fileperms($filename))), sprintf('%6s ', DecOct($file_info[4])), sprintf('%6s ', DecOct($file_info[5])), $size, sprintf('%11s', DecOct(filemtime($filename))));
  385. $dataLast = pack($packL, $typeflag, '', '', '', '', '', '', '', '', '');
  386. $checksum = 0;
  387. for ($i = 0; $i < 148; $i++)
  388. $checksum += ord(substr($dataFirst, $i, 1));
  389. for ($i = 148; $i < 156; $i++)
  390. $checksum += ord(' ');
  391. for ($i = 156, $j = 0; $i < 512; $i++, $j++)
  392. $checksum += ord(substr($dataLast, $j, 1));
  393. $this->writeBlock($dataFirst, 148);
  394. $checksum = sprintf('%6s ', DecOct($checksum));
  395. $binaryData = pack('a8', $checksum);
  396. $this->writeBlock($binaryData, 8);
  397. $this->writeBlock($dataLast, 356);
  398. return true;
  399. }
  400. function openWrite(){
  401. if ($this->isGzipped)
  402. $this->tmp_file = @gzopen($this->archive_name, 'wb9f');
  403. else
  404. $this->tmp_file = @fopen($this->archive_name, 'wb');
  405. if (!($this->tmp_file)){
  406. $this->errors[] = __('Cannot write to file').' '.$this->archive_name;
  407. return false;
  408. }
  409. return true;
  410. }
  411. function readBlock(){
  412. if (is_resource($this->tmp_file)){
  413. if ($this->isGzipped)
  414. $block = @gzread($this->tmp_file, 512);
  415. else
  416. $block = @fread($this->tmp_file, 512);
  417. } else $block = '';
  418. return $block;
  419. }
  420. function writeBlock($data, $length = 0){
  421. if (is_resource($this->tmp_file)){
  422. if ($length === 0){
  423. if ($this->isGzipped)
  424. @gzputs($this->tmp_file, $data);
  425. else
  426. @fputs($this->tmp_file, $data);
  427. } else {
  428. if ($this->isGzipped)
  429. @gzputs($this->tmp_file, $data, $length);
  430. else
  431. @fputs($this->tmp_file, $data, $length);
  432. }
  433. }
  434. }
  435. function closeTmpFile(){
  436. if (is_resource($this->tmp_file)){
  437. if ($this->isGzipped)
  438. @gzclose($this->tmp_file);
  439. else
  440. @fclose($this->tmp_file);
  441. $this->tmp_file = 0;
  442. }
  443. }
  444. function makeGoodPath($path){
  445. if (strlen($path)>0){
  446. $path = str_replace('\\', '/', $path);
  447. $partPath = explode('/', $path);
  448. $els = count($partPath)-1;
  449. // Study directories from last to first
  450. for ($i = $els; $i>=0; $i--){
  451. // Look for current path
  452. if ($partPath[$i] == '.'){
  453. // Ignore this directory
  454. // Should be the first $i=0, but no check is done
  455. }
  456. elseif ($partPath[$i] == '..'){
  457. // Ignore it and ignore the $i-1
  458. $i--;
  459. }
  460. elseif (($partPath[$i] == '') and ($i!=$els) and ($i!=0)){
  461. // Ignore only the double '//' in path,
  462. // but not the first and last /
  463. } else
  464. $result = $partPath[$i].($i!=$els ? '/'.$result : '');
  465. }
  466. } else $result = '';
  467. return $result;
  468. }
  469. }
  470. ?>