Source for file tar.php

Documentation is available at tar.php

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

Documentation generated on Fri, 08 Jun 2007 12:21:32 +0300 by phpDocumentor 1.3.0RC3