PageRenderTime 48ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/modules/system/tar.php

https://github.com/foxadmin/ReloadCMS
PHP | 617 lines | 348 code | 86 blank | 183 comment | 64 complexity | 1ac0dc7b3cfead3e447d424ece875615 MD5 | raw file
Possible License(s): Apache-2.0, BSD-3-Clause, LGPL-2.1
  1. <?php
  2. class tar {
  3. var $filename;
  4. var $isGzipped;
  5. var $tar_file;
  6. var $files;
  7. var $directories;
  8. var $numFiles;
  9. var $numDirectories;
  10. function tar() {
  11. return true;
  12. }
  13. /**
  14. * Computes the unsigned Checksum of a file's header
  15. * to try to ensure valid file
  16. *
  17. * @param string $bytestring
  18. *
  19. * @access private
  20. */
  21. function __computeUnsignedChecksum($bytestring)
  22. {
  23. $unsigned_chksum = '';
  24. for($i=0; $i<512; $i++)
  25. $unsigned_chksum += ord($bytestring[$i]);
  26. for($i=0; $i<8; $i++)
  27. $unsigned_chksum -= ord($bytestring[148 + $i]);
  28. $unsigned_chksum += ord(" ") * 8;
  29. return $unsigned_chksum;
  30. }
  31. /**
  32. * Converts a NULL padded string to a non-NULL padded string
  33. *
  34. * @param string $string
  35. *
  36. * @return string
  37. *
  38. * @access private
  39. **/
  40. function __parseNullPaddedString($string)
  41. {
  42. $position = strpos($string,chr(0));
  43. return substr($string,0,$position);
  44. }
  45. /**
  46. * This function parses the current TAR file
  47. *
  48. * @return bool always TRUE
  49. *
  50. * @access private
  51. **/
  52. function __parseTar()
  53. {
  54. // Read Files from archive
  55. $tar_length = strlen($this->tar_file);
  56. $main_offset = 0;
  57. $this->numFiles = 0;
  58. while ( $main_offset < $tar_length ) {
  59. // If we read a block of 512 nulls, we are at the end of the archive
  60. if(substr($this->tar_file,$main_offset,512) == str_repeat(chr(0),512))
  61. break;
  62. // Parse file name
  63. $file_name = $this->__parseNullPaddedString(substr($this->tar_file,$main_offset,100));
  64. // Parse the file mode
  65. $file_mode = substr($this->tar_file,$main_offset + 100,8);
  66. // Parse the file user ID
  67. $file_uid = octdec(substr($this->tar_file,$main_offset + 108,8));
  68. // Parse the file group ID
  69. $file_gid = octdec(substr($this->tar_file,$main_offset + 116,8));
  70. // Parse the file size
  71. $file_size = octdec(substr($this->tar_file,$main_offset + 124,12));
  72. // Parse the file update time - unix timestamp format
  73. $file_time = octdec(substr($this->tar_file,$main_offset + 136,12));
  74. // Parse Checksum
  75. $file_chksum = octdec(substr($this->tar_file,$main_offset + 148,6));
  76. // Parse user name
  77. $file_uname = $this->__parseNullPaddedString(substr($this->tar_file,$main_offset + 265,32));
  78. // Parse Group name
  79. $file_gname = $this->__parseNullPaddedString(substr($this->tar_file,$main_offset + 297,32));
  80. // Make sure our file is valid
  81. if($this->__computeUnsignedChecksum(substr($this->tar_file,$main_offset,512)) != $file_chksum)
  82. return false;
  83. // Parse File Contents
  84. $file_contents = substr($this->tar_file,$main_offset + 512,$file_size);
  85. /* ### Unused Header Information ###
  86. $activeFile["typeflag"] = substr($this->tar_file,$main_offset + 156,1);
  87. $activeFile["linkname"] = substr($this->tar_file,$main_offset + 157,100);
  88. $activeFile["magic"] = substr($this->tar_file,$main_offset + 257,6);
  89. $activeFile["version"] = substr($this->tar_file,$main_offset + 263,2);
  90. $activeFile["devmajor"] = substr($this->tar_file,$main_offset + 329,8);
  91. $activeFile["devminor"] = substr($this->tar_file,$main_offset + 337,8);
  92. $activeFile["prefix"] = substr($this->tar_file,$main_offset + 345,155);
  93. $activeFile["endheader"] = substr($this->tar_file,$main_offset + 500,12);
  94. */
  95. if ( $file_size > 0 ) {
  96. // Increment number of files
  97. $this->numFiles++;
  98. // Create us a new file in our array
  99. $activeFile = &$this->files[];
  100. // Asign Values
  101. $activeFile["name"] = $file_name;
  102. $activeFile["mode"] = $file_mode;
  103. $activeFile["size"] = $file_size;
  104. $activeFile["time"] = $file_time;
  105. $activeFile["user_id"] = $file_uid;
  106. $activeFile["group_id"] = $file_gid;
  107. $activeFile["user_name"] = $file_uname;
  108. $activeFile["group_name"] = $file_gname;
  109. $activeFile["checksum"] = $file_chksum;
  110. $activeFile["file"] = $file_contents;
  111. } else {
  112. // Increment number of directories
  113. $this->numDirectories++;
  114. // Create a new directory in our array
  115. $activeDir = &$this->directories[];
  116. // Assign values
  117. $activeDir["name"] = $file_name;
  118. $activeDir["mode"] = $file_mode;
  119. $activeDir["time"] = $file_time;
  120. $activeDir["user_id"] = $file_uid;
  121. $activeDir["group_id"] = $file_gid;
  122. $activeDir["user_name"] = $file_uname;
  123. $activeDir["group_name"] = $file_gname;
  124. $activeDir["checksum"] = $file_chksum;
  125. }
  126. // Move our offset the number of blocks we have processed
  127. $main_offset += 512 + (ceil($file_size / 512) * 512);
  128. }
  129. return true;
  130. }
  131. /**
  132. * Read a non gzipped tar file in for processing.
  133. *
  134. * @param string $filename full filename
  135. * @return bool always TRUE
  136. *
  137. * @access private
  138. **/
  139. function __readTar($filename='')
  140. {
  141. // Set the filename to load
  142. if(!$filename)
  143. $filename = $this->filename;
  144. // Read in the TAR file
  145. $fp = fopen($filename,"rb");
  146. $this->tar_file = fread($fp,filesize($filename));
  147. fclose($fp);
  148. if($this->tar_file[0] == chr(31) && $this->tar_file[1] == chr(139) && $this->tar_file[2] == chr(8)) {
  149. if(!function_exists("gzinflate"))
  150. return false;
  151. $this->isGzipped = true;
  152. $this->tar_file = gzinflate(substr($this->tar_file,10,-4));
  153. }
  154. // Parse the TAR file
  155. $this->__parseTar();
  156. return true;
  157. }
  158. /**
  159. * Generates a TAR file from the processed data
  160. *
  161. * @return bool always TRUE
  162. *
  163. * @access private
  164. **/
  165. function __generateTAR() {
  166. $this->tar_file = '';
  167. // Generate Records for each directory, if we have directories
  168. if($this->numDirectories > 0) {
  169. foreach($this->directories as $key => $information) {
  170. unset($header);
  171. // Generate tar header for this directory
  172. // Filename, Permissions, UID, GID, size, Time, checksum, typeflag, linkname, magic, version, user name, group name, devmajor, devminor, prefix, end
  173. $header = str_pad($information["name"],100,chr(0));
  174. $header .= str_pad(decoct($information["mode"]),7,"0",STR_PAD_LEFT) . chr(0);
  175. $header .= str_pad(decoct($information["user_id"]),7,"0",STR_PAD_LEFT) . chr(0);
  176. $header .= str_pad(decoct($information["group_id"]),7,"0",STR_PAD_LEFT) . chr(0);
  177. $header .= str_pad(decoct(0),11,"0",STR_PAD_LEFT) . chr(0);
  178. $header .= str_pad(decoct($information["time"]),11,"0",STR_PAD_LEFT) . chr(0);
  179. $header .= str_repeat(" ",8);
  180. $header .= "5";
  181. $header .= str_repeat(chr(0),100);
  182. $header .= str_pad("ustar",6,chr(32));
  183. $header .= chr(32) . chr(0);
  184. $header .= str_pad("",32,chr(0));
  185. $header .= str_pad("",32,chr(0));
  186. $header .= str_repeat(chr(0),8);
  187. $header .= str_repeat(chr(0),8);
  188. $header .= str_repeat(chr(0),155);
  189. $header .= str_repeat(chr(0),12);
  190. // Compute header checksum
  191. $checksum = str_pad(decoct($this->__computeUnsignedChecksum($header)),6,"0",STR_PAD_LEFT);
  192. for($i=0; $i<6; $i++) {
  193. $header[(148 + $i)] = substr($checksum,$i,1);
  194. }
  195. $header[154] = chr(0);
  196. $header[155] = chr(32);
  197. // Add new tar formatted data to tar file contents
  198. $this->tar_file .= $header;
  199. }
  200. }
  201. // Generate Records for each file, if we have files (We should...)
  202. if($this->numFiles > 0) {
  203. foreach($this->files as $key => $information) {
  204. // Generate the TAR header for this file
  205. // Filename, Permissions, UID, GID, size, Time, checksum, typeflag, linkname, magic, version, user name, group name, devmajor, devminor, prefix, end
  206. $header = str_pad($information['name'], 100, chr(0));
  207. $header .= str_pad(decoct($information['mode']),7,"0",STR_PAD_LEFT) . chr(0);
  208. $header .= str_pad(decoct($information['user_id']),7,"0",STR_PAD_LEFT) . chr(0);
  209. $header .= str_pad(decoct($information['group_id']),7,"0",STR_PAD_LEFT) . chr(0);
  210. $header .= str_pad(decoct($information['size']),11,"0",STR_PAD_LEFT) . chr(0);
  211. $header .= str_pad(decoct($information['time']),11,"0",STR_PAD_LEFT) . chr(0);
  212. $header .= str_repeat(" ",8);
  213. $header .= "0";
  214. $header .= str_repeat(chr(0),100);
  215. $header .= str_pad("ustar",6,chr(32));
  216. $header .= chr(32) . chr(0);
  217. $header .= str_pad($information["user_name"],32,chr(0)); // How do I get a file's user name from PHP?
  218. $header .= str_pad($information["group_name"],32,chr(0)); // How do I get a file's group name from PHP?
  219. $header .= str_repeat(chr(0),8);
  220. $header .= str_repeat(chr(0),8);
  221. $header .= str_repeat(chr(0),155);
  222. $header .= str_repeat(chr(0),12);
  223. // Compute header checksum
  224. $checksum = str_pad(decoct($this->__computeUnsignedChecksum($header)),6,"0",STR_PAD_LEFT);
  225. for($i=0; $i<6; $i++) {
  226. $header[(148 + $i)] = substr($checksum,$i,1);
  227. }
  228. $header[154] = chr(0);
  229. $header[155] = chr(32);
  230. // Pad file contents to byte count divisible by 512
  231. $file_contents = str_pad($information["file"],(ceil($information["size"] / 512) * 512),chr(0));
  232. // Add new tar formatted data to tar file contents
  233. $this->tar_file .= $header . $file_contents;
  234. }
  235. }
  236. // Add 512 bytes of NULLs to designate EOF
  237. $this->tar_file .= str_repeat(chr(0),512);
  238. return true;
  239. }
  240. /**
  241. * Open a TAR file
  242. *
  243. * @param string $filename
  244. * @return bool
  245. **/
  246. function openTAR($filename)
  247. {
  248. // Clear any values from previous tar archives
  249. unset($this->filename);
  250. unset($this->isGzipped);
  251. unset($this->tar_file);
  252. unset($this->files);
  253. unset($this->directories);
  254. unset($this->numFiles);
  255. unset($this->numDirectories);
  256. // If the tar file doesn't exist...
  257. if(!file_exists($filename))
  258. return false;
  259. $this->filename = $filename;
  260. // Parse this file
  261. $this->__readTar();
  262. return true;
  263. }
  264. /**
  265. * Appends a tar file to the end of the currently opened tar file.
  266. *
  267. * @param string $filename
  268. * @return bool
  269. **/
  270. function appendTar($filename)
  271. {
  272. // If the tar file doesn't exist...
  273. if(!file_exists($filename))
  274. return false;
  275. $this->__readTar($filename);
  276. return true;
  277. }
  278. /**
  279. * Retrieves information about a file in the current tar archive
  280. *
  281. * @param string $filename
  282. * @return string FALSE on fail
  283. **/
  284. function getFile($filename)
  285. {
  286. if ( $this->numFiles > 0 ) {
  287. foreach($this->files as $key => $information) {
  288. if($information["name"] == $filename)
  289. return $information;
  290. }
  291. }
  292. return false;
  293. }
  294. /**
  295. * Retrieves information about a directory in the current tar archive
  296. *
  297. * @param string $dirname
  298. * @return string FALSE on fail
  299. **/
  300. function getDirectory($dirname)
  301. {
  302. if($this->numDirectories > 0) {
  303. foreach($this->directories as $key => $information) {
  304. if($information["name"] == $dirname)
  305. return $information;
  306. }
  307. }
  308. return false;
  309. }
  310. /**
  311. * Check if this tar archive contains a specific file
  312. *
  313. * @param string $filename
  314. * @return bool
  315. **/
  316. function containsFile($filename)
  317. {
  318. if ( $this->numFiles > 0 ) {
  319. foreach($this->files as $key => $information) {
  320. if($information["name"] == $filename)
  321. return true;
  322. }
  323. }
  324. return false;
  325. }
  326. /**
  327. * Check if this tar archive contains a specific directory
  328. *
  329. * @param string $dirname
  330. * @return bool
  331. **/
  332. function containsDirectory($dirname)
  333. {
  334. if ( $this->numDirectories > 0 ) {
  335. foreach ( $this->directories as $key => $information ) {
  336. if ( $information["name"] == $dirname ) {
  337. return true;
  338. }
  339. }
  340. }
  341. return false;
  342. }
  343. /**
  344. * Add a directory to this tar archive
  345. *
  346. * @param string $dirname
  347. * @return bool
  348. **/
  349. function addDirectory($dirname, $recurse = false)
  350. {
  351. if ( !file_exists($dirname) ) {
  352. return false;
  353. }
  354. // Get directory information
  355. $file_information = stat($dirname);
  356. // Add directory to processed data
  357. $this->numDirectories++;
  358. $activeDir = &$this->directories[];
  359. $activeDir["name"] = $dirname;
  360. $activeDir["mode"] = @$file_information["mode"];
  361. $activeDir["time"] = @$file_information["time"];
  362. $activeDir["user_id"] = @$file_information["uid"];
  363. $activeDir["group_id"] = @$file_information["gid"];
  364. $activeDir["checksum"] = 0;
  365. if($recurse){
  366. $cdir = opendir($dirname);
  367. while($element = readdir($cdir)){
  368. if($element != '.' && $element != '..'){
  369. if(is_dir($dirname . '/' . $element)){
  370. $this->addDirectory($dirname . '/' . $element, true);
  371. } else {
  372. $this->addFile($dirname . '/' . $element);
  373. }
  374. }
  375. }
  376. closedir($cdir);
  377. }
  378. return true;
  379. }
  380. /**
  381. * Add a file to the tar archive
  382. *
  383. * @param string $filename
  384. * @param boolean $binary Binary file?
  385. * @return bool
  386. **/
  387. function addFile($filename, $binary = false)
  388. {
  389. // Make sure the file we are adding exists!
  390. if ( !file_exists($filename) ) {
  391. return false;
  392. }
  393. // Make sure there are no other files in the archive that have this same filename
  394. if ( $this->containsFile($filename) ) {
  395. return false;
  396. }
  397. // Get file information
  398. $file_information = stat($filename);
  399. // Read in the file's contents
  400. if (!$binary) {
  401. $fp = fopen($filename, "r");
  402. } else {
  403. $fp = fopen($filename, "rb");
  404. }
  405. if(filesize($filename) !== 0) $file_contents = fread($fp, filesize($filename));
  406. else $file_contents = '';
  407. fclose($fp);
  408. // Add file to processed data
  409. $this->numFiles++;
  410. $activeFile = &$this->files[];
  411. $activeFile["name"] = $filename;
  412. $activeFile["mode"] = $file_information["mode"];
  413. $activeFile["user_id"] = $file_information["uid"];
  414. $activeFile["group_id"] = $file_information["gid"];
  415. $activeFile["size"] = $file_information["size"];
  416. $activeFile["time"] = $file_information["mtime"];
  417. $activeFile["checksum"] = isset($checksum) ? $checksum : '';
  418. $activeFile["user_name"] = "";
  419. $activeFile["group_name"] = "";
  420. $activeFile["file"] = trim($file_contents);
  421. return true;
  422. }
  423. /**
  424. * Remove a file from the tar archive
  425. *
  426. * @param string $filename
  427. * @return bool
  428. **/
  429. function removeFile($filename)
  430. {
  431. if ( $this->numFiles > 0 ) {
  432. foreach ( $this->files as $key => $information ) {
  433. if ( $information["name"] == $filename ) {
  434. $this->numFiles--;
  435. unset($this->files[$key]);
  436. return true;
  437. }
  438. }
  439. }
  440. return false;
  441. }
  442. /**
  443. * Remove a directory from the tar archive
  444. *
  445. * @param string $dirname
  446. * @return bool
  447. **/
  448. function removeDirectory($dirname)
  449. {
  450. if ( $this->numDirectories > 0 ) {
  451. foreach ( $this->directories as $key => $information ) {
  452. if ( $information["name"] == $dirname ) {
  453. $this->numDirectories--;
  454. unset($this->directories[$key]);
  455. return true;
  456. }
  457. }
  458. }
  459. return false;
  460. }
  461. /**
  462. * Write the currently loaded tar archive to disk
  463. *
  464. * @return bool
  465. **/
  466. function saveTar()
  467. {
  468. if ( !$this->filename ) {
  469. return false;
  470. }
  471. // Write tar to current file using specified gzip compression
  472. $this->toTar($this->filename,$this->isGzipped);
  473. return true;
  474. }
  475. /**
  476. * Saves tar archive to a different file than the current file
  477. *
  478. * @param string $filename
  479. * @param bool $useGzip Use GZ compression?
  480. * @return bool
  481. **/
  482. function toTar($filename, $useGzip) {
  483. if(!$filename) {
  484. return false;
  485. }
  486. // Encode processed files into TAR file format
  487. $this->__generateTar();
  488. // GZ Compress the data if we need to
  489. if ( $useGzip ) {
  490. // Make sure we have gzip support
  491. if ( !function_exists("gzencode") ) {
  492. return false;
  493. }
  494. $file = gzencode($this->tar_file);
  495. } else {
  496. $file = $this->tar_file;
  497. }
  498. // Write the TAR file
  499. $fp = fopen($filename,"wb");
  500. fwrite($fp,$file);
  501. fclose($fp);
  502. return true;
  503. }
  504. /**
  505. * Sends tar archive to stdout
  506. *
  507. * @param string $filename
  508. * @param bool $useGzip Use GZ compression?
  509. * @return string
  510. **/
  511. function toTarOutput($filename,$useGzip)
  512. {
  513. if ( !$filename ) {
  514. return false;
  515. }
  516. // Encode processed files into TAR file format
  517. $this->__generateTar();
  518. // GZ Compress the data if we need to
  519. if ( $useGzip ) {
  520. // Make sure we have gzip support
  521. if ( !function_exists("gzencode") ) {
  522. return false;
  523. }
  524. $file = gzencode($this->tar_file);
  525. } else {
  526. $file = $this->tar_file;
  527. }
  528. return $file;
  529. }
  530. }
  531. ?>