PageRenderTime 56ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/includes/pear/Archive/tar.inc.php

https://bitbucket.org/hky/bytehoard2
PHP | 1759 lines | 1345 code | 192 blank | 222 comment | 337 complexity | 9c1c17481ddfadc446b493693368f4f7 MD5 | raw file
Possible License(s): GPL-2.0

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /* vim: set ts=4 sw=4: */
  3. // +----------------------------------------------------------------------+
  4. // | PHP Version 4 |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1997-2003 The PHP Group |
  7. // +----------------------------------------------------------------------+
  8. // | This source file is subject to version 3.0 of the PHP license, |
  9. // | that is bundled with this package in the file LICENSE, and is |
  10. // | available through the world-wide-web at the following url: |
  11. // | http://www.php.net/license/3_0.txt. |
  12. // | If you did not receive a copy of the PHP license and are unable to |
  13. // | obtain it through the world-wide-web, please send a note to |
  14. // | license@php.net so we can mail you a copy immediately. |
  15. // +----------------------------------------------------------------------+
  16. // | Author: Vincent Blavet <vincent@phpconcept.net> |
  17. // +----------------------------------------------------------------------+
  18. //
  19. // $Id: tar.inc.php,v 1.1 2005/06/17 18:52:01 andrewgodwin Exp $
  20. define ('ARCHIVE_TAR_ATT_SEPARATOR', 90001);
  21. /**
  22. * Creates a (compressed) Tar archive
  23. *
  24. * @author Vincent Blavet <vincent@phpconcept.net>
  25. * @version $Revision: 1.1 $
  26. * @package Archive
  27. */
  28. class Archive_Tar extends PEAR
  29. {
  30. /**
  31. * @var string Name of the Tar
  32. */
  33. var $_tarname='';
  34. /**
  35. * @var boolean if true, the Tar file will be gzipped
  36. */
  37. var $_compress=false;
  38. /**
  39. * @var string Type of compression : 'none', 'gz' or 'bz2'
  40. */
  41. var $_compress_type='none';
  42. /**
  43. * @var string Explode separator
  44. */
  45. var $_separator=' ';
  46. /**
  47. * @var file descriptor
  48. */
  49. var $_file=0;
  50. /**
  51. * @var string Local Tar name of a remote Tar (http:// or ftp://)
  52. */
  53. var $_temp_tarname='';
  54. // {{{ constructor
  55. /**
  56. * Archive_Tar Class constructor. This flavour of the constructor only
  57. * declare a new Archive_Tar object, identifying it by the name of the
  58. * tar file.
  59. * If the compress argument is set the tar will be read or created as a
  60. * gzip or bz2 compressed TAR file.
  61. *
  62. * @param string $p_tarname The name of the tar archive to create
  63. * @param string $p_compress can be null, 'gz' or 'bz2'. This
  64. * parameter indicates if gzip or bz2 compression
  65. * is required. For compatibility reason the
  66. * boolean value 'true' means 'gz'.
  67. * @access public
  68. */
  69. function Archive_Tar($p_tarname, $p_compress = null)
  70. {
  71. $this->PEAR();
  72. $this->_compress = false;
  73. $this->_compress_type = 'none';
  74. if (($p_compress === null) || ($p_compress == '')) {
  75. if (@file_exists($p_tarname)) {
  76. if ($fp = @fopen($p_tarname, "rb")) {
  77. // look for gzip magic cookie
  78. $data = fread($fp, 2);
  79. fclose($fp);
  80. if ($data == "\37\213") {
  81. $this->_compress = true;
  82. $this->_compress_type = 'gz';
  83. // No sure it's enought for a magic code ....
  84. } elseif ($data == "BZ") {
  85. $this->_compress = true;
  86. $this->_compress_type = 'bz2';
  87. }
  88. }
  89. } else {
  90. // probably a remote file or some file accessible
  91. // through a stream interface
  92. if (substr($p_tarname, -2) == 'gz') {
  93. $this->_compress = true;
  94. $this->_compress_type = 'gz';
  95. } elseif ((substr($p_tarname, -3) == 'bz2') ||
  96. (substr($p_tarname, -2) == 'bz')) {
  97. $this->_compress = true;
  98. $this->_compress_type = 'bz2';
  99. }
  100. }
  101. } else {
  102. if (($p_compress === true) || ($p_compress == 'gz')) {
  103. $this->_compress = true;
  104. $this->_compress_type = 'gz';
  105. } else if ($p_compress == 'bz2') {
  106. $this->_compress = true;
  107. $this->_compress_type = 'bz2';
  108. } else {
  109. die("Unsupported compression type '$p_compress'\n".
  110. "Supported types are 'gz' and 'bz2'.\n");
  111. return false;
  112. }
  113. }
  114. $this->_tarname = $p_tarname;
  115. if ($this->_compress) { // assert zlib or bz2 extension support
  116. if ($this->_compress_type == 'gz')
  117. $extname = 'zlib';
  118. else if ($this->_compress_type == 'bz2')
  119. $extname = 'bz2';
  120. if (!extension_loaded($extname)) {
  121. PEAR::loadExtension($extname);
  122. }
  123. if (!extension_loaded($extname)) {
  124. die("The extension '$extname' couldn't be found.\n".
  125. "Please make sure your version of PHP was built ".
  126. "with '$extname' support.\n");
  127. return false;
  128. }
  129. }
  130. }
  131. // }}}
  132. // {{{ destructor
  133. function _Archive_Tar()
  134. {
  135. $this->_close();
  136. // ----- Look for a local copy to delete
  137. if ($this->_temp_tarname != '')
  138. @unlink($this->_temp_tarname);
  139. $this->_PEAR();
  140. }
  141. // }}}
  142. // {{{ create()
  143. /**
  144. * This method creates the archive file and add the files / directories
  145. * that are listed in $p_filelist.
  146. * If a file with the same name exist and is writable, it is replaced
  147. * by the new tar.
  148. * The method return false and a PEAR error text.
  149. * The $p_filelist parameter can be an array of string, each string
  150. * representing a filename or a directory name with their path if
  151. * needed. It can also be a single string with names separated by a
  152. * single blank.
  153. * For each directory added in the archive, the files and
  154. * sub-directories are also added.
  155. * See also createModify() method for more details.
  156. *
  157. * @param array $p_filelist An array of filenames and directory names, or a
  158. * single string with names separated by a single
  159. * blank space.
  160. * @return true on success, false on error.
  161. * @see createModify()
  162. * @access public
  163. */
  164. function create($p_filelist)
  165. {
  166. return $this->createModify($p_filelist, '', '');
  167. }
  168. // }}}
  169. // {{{ add()
  170. /**
  171. * This method add the files / directories that are listed in $p_filelist in
  172. * the archive. If the archive does not exist it is created.
  173. * The method return false and a PEAR error text.
  174. * The files and directories listed are only added at the end of the archive,
  175. * even if a file with the same name is already archived.
  176. * See also createModify() method for more details.
  177. *
  178. * @param array $p_filelist An array of filenames and directory names, or a
  179. * single string with names separated by a single
  180. * blank space.
  181. * @return true on success, false on error.
  182. * @see createModify()
  183. * @access public
  184. */
  185. function add($p_filelist)
  186. {
  187. return $this->addModify($p_filelist, '', '');
  188. }
  189. // }}}
  190. // {{{ extract()
  191. function extract($p_path='')
  192. {
  193. return $this->extractModify($p_path, '');
  194. }
  195. // }}}
  196. // {{{ listContent()
  197. function listContent()
  198. {
  199. $v_list_detail = array();
  200. if ($this->_openRead()) {
  201. if (!$this->_extractList('', $v_list_detail, "list", '', '')) {
  202. unset($v_list_detail);
  203. $v_list_detail = 0;
  204. }
  205. $this->_close();
  206. }
  207. return $v_list_detail;
  208. }
  209. // }}}
  210. // {{{ createModify()
  211. /**
  212. * This method creates the archive file and add the files / directories
  213. * that are listed in $p_filelist.
  214. * If the file already exists and is writable, it is replaced by the
  215. * new tar. It is a create and not an add. If the file exists and is
  216. * read-only or is a directory it is not replaced. The method return
  217. * false and a PEAR error text.
  218. * The $p_filelist parameter can be an array of string, each string
  219. * representing a filename or a directory name with their path if
  220. * needed. It can also be a single string with names separated by a
  221. * single blank.
  222. * The path indicated in $p_remove_dir will be removed from the
  223. * memorized path of each file / directory listed when this path
  224. * exists. By default nothing is removed (empty path '')
  225. * The path indicated in $p_add_dir will be added at the beginning of
  226. * the memorized path of each file / directory listed. However it can
  227. * be set to empty ''. The adding of a path is done after the removing
  228. * of path.
  229. * The path add/remove ability enables the user to prepare an archive
  230. * for extraction in a different path than the origin files are.
  231. * See also addModify() method for file adding properties.
  232. *
  233. * @param array $p_filelist An array of filenames and directory names,
  234. * or a single string with names separated by
  235. * a single blank space.
  236. * @param string $p_add_dir A string which contains a path to be added
  237. * to the memorized path of each element in
  238. * the list.
  239. * @param string $p_remove_dir A string which contains a path to be
  240. * removed from the memorized path of each
  241. * element in the list, when relevant.
  242. * @return boolean true on success, false on error.
  243. * @access public
  244. * @see addModify()
  245. */
  246. function createModify($p_filelist, $p_add_dir, $p_remove_dir='')
  247. {
  248. $v_result = true;
  249. if (!$this->_openWrite())
  250. return false;
  251. if ($p_filelist != '') {
  252. if (is_array($p_filelist))
  253. $v_list = $p_filelist;
  254. elseif (is_string($p_filelist))
  255. $v_list = explode($this->_separator, $p_filelist);
  256. else {
  257. $this->_cleanFile();
  258. $this->_error('Invalid file list');
  259. return false;
  260. }
  261. $v_result = $this->_addList($v_list, $p_add_dir, $p_remove_dir);
  262. }
  263. if ($v_result) {
  264. $this->_writeFooter();
  265. $this->_close();
  266. } else
  267. $this->_cleanFile();
  268. return $v_result;
  269. }
  270. // }}}
  271. // {{{ addModify()
  272. /**
  273. * This method add the files / directories listed in $p_filelist at the
  274. * end of the existing archive. If the archive does not yet exists it
  275. * is created.
  276. * The $p_filelist parameter can be an array of string, each string
  277. * representing a filename or a directory name with their path if
  278. * needed. It can also be a single string with names separated by a
  279. * single blank.
  280. * The path indicated in $p_remove_dir will be removed from the
  281. * memorized path of each file / directory listed when this path
  282. * exists. By default nothing is removed (empty path '')
  283. * The path indicated in $p_add_dir will be added at the beginning of
  284. * the memorized path of each file / directory listed. However it can
  285. * be set to empty ''. The adding of a path is done after the removing
  286. * of path.
  287. * The path add/remove ability enables the user to prepare an archive
  288. * for extraction in a different path than the origin files are.
  289. * If a file/dir is already in the archive it will only be added at the
  290. * end of the archive. There is no update of the existing archived
  291. * file/dir. However while extracting the archive, the last file will
  292. * replace the first one. This results in a none optimization of the
  293. * archive size.
  294. * If a file/dir does not exist the file/dir is ignored. However an
  295. * error text is send to PEAR error.
  296. * If a file/dir is not readable the file/dir is ignored. However an
  297. * error text is send to PEAR error.
  298. *
  299. * @param array $p_filelist An array of filenames and directory
  300. * names, or a single string with names
  301. * separated by a single blank space.
  302. * @param string $p_add_dir A string which contains a path to be
  303. * added to the memorized path of each
  304. * element in the list.
  305. * @param string $p_remove_dir A string which contains a path to be
  306. * removed from the memorized path of
  307. * each element in the list, when
  308. * relevant.
  309. * @return true on success, false on error.
  310. * @access public
  311. */
  312. function addModify($p_filelist, $p_add_dir, $p_remove_dir='')
  313. {
  314. $v_result = true;
  315. if (!$this->_isArchive())
  316. $v_result = $this->createModify($p_filelist, $p_add_dir,
  317. $p_remove_dir);
  318. else {
  319. if (is_array($p_filelist))
  320. $v_list = $p_filelist;
  321. elseif (is_string($p_filelist))
  322. $v_list = explode($this->_separator, $p_filelist);
  323. else {
  324. $this->_error('Invalid file list');
  325. return false;
  326. }
  327. $v_result = $this->_append($v_list, $p_add_dir, $p_remove_dir);
  328. }
  329. return $v_result;
  330. }
  331. // }}}
  332. // {{{ addString()
  333. /**
  334. * This method add a single string as a file at the
  335. * end of the existing archive. If the archive does not yet exists it
  336. * is created.
  337. *
  338. * @param string $p_filename A string which contains the full
  339. * filename path that will be associated
  340. * with the string.
  341. * @param string $p_string The content of the file added in
  342. * the archive.
  343. * @return true on success, false on error.
  344. * @access public
  345. */
  346. function addString($p_filename, $p_string)
  347. {
  348. $v_result = true;
  349. if (!$this->_isArchive()) {
  350. if (!$this->_openWrite()) {
  351. return false;
  352. }
  353. $this->_close();
  354. }
  355. if (!$this->_openAppend())
  356. return false;
  357. // Need to check the get back to the temporary file ? ....
  358. $v_result = $this->_addString($p_filename, $p_string);
  359. $this->_writeFooter();
  360. $this->_close();
  361. return $v_result;
  362. }
  363. // }}}
  364. // {{{ extractModify()
  365. /**
  366. * This method extract all the content of the archive in the directory
  367. * indicated by $p_path. When relevant the memorized path of the
  368. * files/dir can be modified by removing the $p_remove_path path at the
  369. * beginning of the file/dir path.
  370. * While extracting a file, if the directory path does not exists it is
  371. * created.
  372. * While extracting a file, if the file already exists it is replaced
  373. * without looking for last modification date.
  374. * While extracting a file, if the file already exists and is write
  375. * protected, the extraction is aborted.
  376. * While extracting a file, if a directory with the same name already
  377. * exists, the extraction is aborted.
  378. * While extracting a directory, if a file with the same name already
  379. * exists, the extraction is aborted.
  380. * While extracting a file/directory if the destination directory exist
  381. * and is write protected, or does not exist but can not be created,
  382. * the extraction is aborted.
  383. * If after extraction an extracted file does not show the correct
  384. * stored file size, the extraction is aborted.
  385. * When the extraction is aborted, a PEAR error text is set and false
  386. * is returned. However the result can be a partial extraction that may
  387. * need to be manually cleaned.
  388. *
  389. * @param string $p_path The path of the directory where the
  390. * files/dir need to by extracted.
  391. * @param string $p_remove_path Part of the memorized path that can be
  392. * removed if present at the beginning of
  393. * the file/dir path.
  394. * @return boolean true on success, false on error.
  395. * @access public
  396. * @see extractList()
  397. */
  398. function extractModify($p_path, $p_remove_path)
  399. {
  400. $v_result = true;
  401. $v_list_detail = array();
  402. if ($v_result = $this->_openRead()) {
  403. $v_result = $this->_extractList($p_path, $v_list_detail,
  404. "complete", 0, $p_remove_path);
  405. $this->_close();
  406. }
  407. return $v_result;
  408. }
  409. // }}}
  410. // {{{ extractInString()
  411. /**
  412. * This method extract from the archive one file identified by $p_filename.
  413. * The return value is a string with the file content, or NULL on error.
  414. * @param string $p_filename The path of the file to extract in a string.
  415. * @return a string with the file content or NULL.
  416. * @access public
  417. */
  418. function extractInString($p_filename)
  419. {
  420. if ($this->_openRead()) {
  421. $v_result = $this->_extractInString($p_filename);
  422. $this->_close();
  423. } else {
  424. $v_result = NULL;
  425. }
  426. return $v_result;
  427. }
  428. // }}}
  429. // {{{ extractList()
  430. /**
  431. * This method extract from the archive only the files indicated in the
  432. * $p_filelist. These files are extracted in the current directory or
  433. * in the directory indicated by the optional $p_path parameter.
  434. * If indicated the $p_remove_path can be used in the same way as it is
  435. * used in extractModify() method.
  436. * @param array $p_filelist An array of filenames and directory names,
  437. * or a single string with names separated
  438. * by a single blank space.
  439. * @param string $p_path The path of the directory where the
  440. * files/dir need to by extracted.
  441. * @param string $p_remove_path Part of the memorized path that can be
  442. * removed if present at the beginning of
  443. * the file/dir path.
  444. * @return true on success, false on error.
  445. * @access public
  446. * @see extractModify()
  447. */
  448. function extractList($p_filelist, $p_path='', $p_remove_path='')
  449. {
  450. $v_result = true;
  451. $v_list_detail = array();
  452. if (is_array($p_filelist))
  453. $v_list = $p_filelist;
  454. elseif (is_string($p_filelist))
  455. $v_list = explode($this->_separator, $p_filelist);
  456. else {
  457. $this->_error('Invalid string list');
  458. return false;
  459. }
  460. if ($v_result = $this->_openRead()) {
  461. $v_result = $this->_extractList($p_path, $v_list_detail, "partial",
  462. $v_list, $p_remove_path);
  463. $this->_close();
  464. }
  465. return $v_result;
  466. }
  467. // }}}
  468. // {{{ setAttribute()
  469. /**
  470. * This method set specific attributes of the archive. It uses a variable
  471. * list of parameters, in the format attribute code + attribute values :
  472. * $arch->setAttribute(ARCHIVE_TAR_ATT_SEPARATOR, ',');
  473. * @param mixed $argv variable list of attributes and values
  474. * @return true on success, false on error.
  475. * @access public
  476. */
  477. function setAttribute()
  478. {
  479. $v_result = true;
  480. // ----- Get the number of variable list of arguments
  481. if (($v_size = func_num_args()) == 0) {
  482. return true;
  483. }
  484. // ----- Get the arguments
  485. $v_att_list = &func_get_args();
  486. // ----- Read the attributes
  487. $i=0;
  488. while ($i<$v_size) {
  489. // ----- Look for next option
  490. switch ($v_att_list[$i]) {
  491. // ----- Look for options that request a string value
  492. case ARCHIVE_TAR_ATT_SEPARATOR :
  493. // ----- Check the number of parameters
  494. if (($i+1) >= $v_size) {
  495. $this->_error('Invalid number of parameters for '
  496. .'attribute ARCHIVE_TAR_ATT_SEPARATOR');
  497. return false;
  498. }
  499. // ----- Get the value
  500. $this->_separator = $v_att_list[$i+1];
  501. $i++;
  502. break;
  503. default :
  504. $this->_error('Unknow attribute code '.$v_att_list[$i].'');
  505. return false;
  506. }
  507. // ----- Next attribute
  508. $i++;
  509. }
  510. return $v_result;
  511. }
  512. // }}}
  513. // {{{ _error()
  514. function _error($p_message)
  515. {
  516. // ----- To be completed
  517. $this->raiseError($p_message);
  518. }
  519. // }}}
  520. // {{{ _warning()
  521. function _warning($p_message)
  522. {
  523. // ----- To be completed
  524. $this->raiseError($p_message);
  525. }
  526. // }}}
  527. // {{{ _isArchive()
  528. function _isArchive($p_filename=NULL)
  529. {
  530. if ($p_filename == NULL) {
  531. $p_filename = $this->_tarname;
  532. }
  533. clearstatcache();
  534. return @is_file($p_filename);
  535. }
  536. // }}}
  537. // {{{ _openWrite()
  538. function _openWrite()
  539. {
  540. if ($this->_compress_type == 'gz')
  541. $this->_file = @gzopen($this->_tarname, "wb9");
  542. else if ($this->_compress_type == 'bz2')
  543. $this->_file = @bzopen($this->_tarname, "wb");
  544. else if ($this->_compress_type == 'none')
  545. $this->_file = @fopen($this->_tarname, "wb");
  546. else
  547. $this->_error('Unknown or missing compression type ('
  548. .$this->_compress_type.')');
  549. if ($this->_file == 0) {
  550. $this->_error('Unable to open in write mode \''
  551. .$this->_tarname.'\'');
  552. return false;
  553. }
  554. return true;
  555. }
  556. // }}}
  557. // {{{ _openRead()
  558. function _openRead()
  559. {
  560. if (strtolower(substr($this->_tarname, 0, 7)) == 'http://') {
  561. // ----- Look if a local copy need to be done
  562. if ($this->_temp_tarname == '') {
  563. $this->_temp_tarname = uniqid('tar').'.tmp';
  564. if (!$v_file_from = @fopen($this->_tarname, 'rb')) {
  565. $this->_error('Unable to open in read mode \''
  566. .$this->_tarname.'\'');
  567. $this->_temp_tarname = '';
  568. return false;
  569. }
  570. if (!$v_file_to = @fopen($this->_temp_tarname, 'wb')) {
  571. $this->_error('Unable to open in write mode \''
  572. .$this->_temp_tarname.'\'');
  573. $this->_temp_tarname = '';
  574. return false;
  575. }
  576. while ($v_data = @fread($v_file_from, 1024))
  577. @fwrite($v_file_to, $v_data);
  578. @fclose($v_file_from);
  579. @fclose($v_file_to);
  580. }
  581. // ----- File to open if the local copy
  582. $v_filename = $this->_temp_tarname;
  583. } else
  584. // ----- File to open if the normal Tar file
  585. $v_filename = $this->_tarname;
  586. if ($this->_compress_type == 'gz')
  587. $this->_file = @gzopen($v_filename, "rb");
  588. else if ($this->_compress_type == 'bz2')
  589. $this->_file = @bzopen($v_filename, "rb");
  590. else if ($this->_compress_type == 'none')
  591. $this->_file = @fopen($v_filename, "rb");
  592. else
  593. $this->_error('Unknown or missing compression type ('
  594. .$this->_compress_type.')');
  595. if ($this->_file == 0) {
  596. $this->_error('Unable to open in read mode \''.$v_filename.'\'');
  597. return false;
  598. }
  599. return true;
  600. }
  601. // }}}
  602. // {{{ _openReadWrite()
  603. function _openReadWrite()
  604. {
  605. if ($this->_compress_type == 'gz')
  606. $this->_file = @gzopen($this->_tarname, "r+b");
  607. else if ($this->_compress_type == 'bz2')
  608. $this->_file = @bzopen($this->_tarname, "r+b");
  609. else if ($this->_compress_type == 'none')
  610. $this->_file = @fopen($this->_tarname, "r+b");
  611. else
  612. $this->_error('Unknown or missing compression type ('
  613. .$this->_compress_type.')');
  614. if ($this->_file == 0) {
  615. $this->_error('Unable to open in read/write mode \''
  616. .$this->_tarname.'\'');
  617. return false;
  618. }
  619. return true;
  620. }
  621. // }}}
  622. // {{{ _close()
  623. function _close()
  624. {
  625. //if (isset($this->_file)) {
  626. if (is_resource($this->_file)) {
  627. if ($this->_compress_type == 'gz')
  628. @gzclose($this->_file);
  629. else if ($this->_compress_type == 'bz2')
  630. @bzclose($this->_file);
  631. else if ($this->_compress_type == 'none')
  632. @fclose($this->_file);
  633. else
  634. $this->_error('Unknown or missing compression type ('
  635. .$this->_compress_type.')');
  636. $this->_file = 0;
  637. }
  638. // ----- Look if a local copy need to be erase
  639. // Note that it might be interesting to keep the url for a time : ToDo
  640. if ($this->_temp_tarname != '') {
  641. @unlink($this->_temp_tarname);
  642. $this->_temp_tarname = '';
  643. }
  644. return true;
  645. }
  646. // }}}
  647. // {{{ _cleanFile()
  648. function _cleanFile()
  649. {
  650. $this->_close();
  651. // ----- Look for a local copy
  652. if ($this->_temp_tarname != '') {
  653. // ----- Remove the local copy but not the remote tarname
  654. @unlink($this->_temp_tarname);
  655. $this->_temp_tarname = '';
  656. } else {
  657. // ----- Remove the local tarname file
  658. @unlink($this->_tarname);
  659. }
  660. $this->_tarname = '';
  661. return true;
  662. }
  663. // }}}
  664. // {{{ _writeBlock()
  665. function _writeBlock($p_binary_data, $p_len=null)
  666. {
  667. if (is_resource($this->_file)) {
  668. if ($p_len === null) {
  669. if ($this->_compress_type == 'gz')
  670. @gzputs($this->_file, $p_binary_data);
  671. else if ($this->_compress_type == 'bz2')
  672. @bzwrite($this->_file, $p_binary_data);
  673. else if ($this->_compress_type == 'none')
  674. @fputs($this->_file, $p_binary_data);
  675. else
  676. $this->_error('Unknown or missing compression type ('
  677. .$this->_compress_type.')');
  678. } else {
  679. if ($this->_compress_type == 'gz')
  680. @gzputs($this->_file, $p_binary_data, $p_len);
  681. else if ($this->_compress_type == 'bz2')
  682. @bzwrite($this->_file, $p_binary_data, $p_len);
  683. else if ($this->_compress_type == 'none')
  684. @fputs($this->_file, $p_binary_data, $p_len);
  685. else
  686. $this->_error('Unknown or missing compression type ('
  687. .$this->_compress_type.')');
  688. }
  689. }
  690. return true;
  691. }
  692. // }}}
  693. // {{{ _readBlock()
  694. function _readBlock()
  695. {
  696. $v_block = null;
  697. if (is_resource($this->_file)) {
  698. if ($this->_compress_type == 'gz')
  699. $v_block = @gzread($this->_file, 512);
  700. else if ($this->_compress_type == 'bz2')
  701. $v_block = @bzread($this->_file, 512);
  702. else if ($this->_compress_type == 'none')
  703. $v_block = @fread($this->_file, 512);
  704. else
  705. $this->_error('Unknown or missing compression type ('
  706. .$this->_compress_type.')');
  707. }
  708. return $v_block;
  709. }
  710. // }}}
  711. // {{{ _jumpBlock()
  712. function _jumpBlock($p_len=null)
  713. {
  714. if (is_resource($this->_file)) {
  715. if ($p_len === null)
  716. $p_len = 1;
  717. if ($this->_compress_type == 'gz') {
  718. @gzseek($this->_file, @gztell($this->_file)+($p_len*512));
  719. }
  720. else if ($this->_compress_type == 'bz2') {
  721. // ----- Replace missing bztell() and bzseek()
  722. for ($i=0; $i<$p_len; $i++)
  723. $this->_readBlock();
  724. } else if ($this->_compress_type == 'none')
  725. @fseek($this->_file, @ftell($this->_file)+($p_len*512));
  726. else
  727. $this->_error('Unknown or missing compression type ('
  728. .$this->_compress_type.')');
  729. }
  730. return true;
  731. }
  732. // }}}
  733. // {{{ _writeFooter()
  734. function _writeFooter()
  735. {
  736. if (is_resource($this->_file)) {
  737. // ----- Write the last 0 filled block for end of archive
  738. $v_binary_data = pack("a512", '');
  739. $this->_writeBlock($v_binary_data);
  740. }
  741. return true;
  742. }
  743. // }}}
  744. // {{{ _addList()
  745. function _addList($p_list, $p_add_dir, $p_remove_dir)
  746. {
  747. $v_result=true;
  748. $v_header = array();
  749. // ----- Remove potential windows directory separator
  750. $p_add_dir = $this->_translateWinPath($p_add_dir);
  751. $p_remove_dir = $this->_translateWinPath($p_remove_dir, false);
  752. if (!$this->_file) {
  753. $this->_error('Invalid file descriptor');
  754. return false;
  755. }
  756. if (sizeof($p_list) == 0)
  757. return true;
  758. foreach ($p_list as $v_filename) {
  759. if (!$v_result) {
  760. break;
  761. }
  762. // ----- Skip the current tar name
  763. if ($v_filename == $this->_tarname)
  764. continue;
  765. if ($v_filename == '')
  766. continue;
  767. if (!file_exists($v_filename)) {
  768. $this->_warning("File '$v_filename' does not exist");
  769. continue;
  770. }
  771. // ----- Add the file or directory header
  772. if (!$this->_addFile($v_filename, $v_header, $p_add_dir, $p_remove_dir))
  773. return false;
  774. if (@is_dir($v_filename)) {
  775. if (!($p_hdir = opendir($v_filename))) {
  776. $this->_warning("Directory '$v_filename' can not be read");
  777. continue;
  778. }
  779. while (false !== ($p_hitem = readdir($p_hdir))) {
  780. if (($p_hitem != '.') && ($p_hitem != '..')) {
  781. if ($v_filename != ".")
  782. $p_temp_list[0] = $v_filename.'/'.$p_hitem;
  783. else
  784. $p_temp_list[0] = $p_hitem;
  785. $v_result = $this->_addList($p_temp_list,
  786. $p_add_dir,
  787. $p_remove_dir);
  788. }
  789. }
  790. unset($p_temp_list);
  791. unset($p_hdir);
  792. unset($p_hitem);
  793. }
  794. }
  795. return $v_result;
  796. }
  797. // }}}
  798. // {{{ _addFile()
  799. function _addFile($p_filename, &$p_header, $p_add_dir, $p_remove_dir)
  800. {
  801. if (!$this->_file) {
  802. $this->_error('Invalid file descriptor');
  803. return false;
  804. }
  805. if ($p_filename == '') {
  806. $this->_error('Invalid file name');
  807. return false;
  808. }
  809. // ----- Calculate the stored filename
  810. $p_filename = $this->_translateWinPath($p_filename, false);;
  811. $v_stored_filename = $p_filename;
  812. if (strcmp($p_filename, $p_remove_dir) == 0) {
  813. return true;
  814. }
  815. if ($p_remove_dir != '') {
  816. if (substr($p_remove_dir, -1) != '/')
  817. $p_remove_dir .= '/';
  818. if (substr($p_filename, 0, strlen($p_remove_dir)) == $p_remove_dir)
  819. $v_stored_filename = substr($p_filename, strlen($p_remove_dir));
  820. }
  821. $v_stored_filename = $this->_translateWinPath($v_stored_filename);
  822. if ($p_add_dir != '') {
  823. if (substr($p_add_dir, -1) == '/')
  824. $v_stored_filename = $p_add_dir.$v_stored_filename;
  825. else
  826. $v_stored_filename = $p_add_dir.'/'.$v_stored_filename;
  827. }
  828. $v_stored_filename = $this->_pathReduction($v_stored_filename);
  829. if ($this->_isArchive($p_filename)) {
  830. if (($v_file = @fopen($p_filename, "rb")) == 0) {
  831. $this->_warning("Unable to open file '".$p_filename
  832. ."' in binary read mode");
  833. return true;
  834. }
  835. if (!$this->_writeHeader($p_filename, $v_stored_filename))
  836. return false;
  837. while (($v_buffer = fread($v_file, 512)) != '') {
  838. $v_binary_data = pack("a512", "$v_buffer");
  839. $this->_writeBlock($v_binary_data);
  840. }
  841. fclose($v_file);
  842. } else {
  843. // ----- Only header for dir
  844. if (!$this->_writeHeader($p_filename, $v_stored_filename))
  845. return false;
  846. }
  847. return true;
  848. }
  849. // }}}
  850. // {{{ _addString()
  851. function _addString($p_filename, $p_string)
  852. {
  853. if (!$this->_file) {
  854. $this->_error('Invalid file descriptor');
  855. return false;
  856. }
  857. if ($p_filename == '') {
  858. $this->_error('Invalid file name');
  859. return false;
  860. }
  861. // ----- Calculate the stored filename
  862. $p_filename = $this->_translateWinPath($p_filename, false);;
  863. if (!$this->_writeHeaderBlock($p_filename, strlen($p_string),
  864. 0, 0, "", 0, 0))
  865. return false;
  866. $i=0;
  867. while (($v_buffer = substr($p_string, (($i++)*512), 512)) != '') {
  868. $v_binary_data = pack("a512", $v_buffer);
  869. $this->_writeBlock($v_binary_data);
  870. }
  871. return true;
  872. }
  873. // }}}
  874. // {{{ _writeHeader()
  875. function _writeHeader($p_filename, $p_stored_filename)
  876. {
  877. if ($p_stored_filename == '')
  878. $p_stored_filename = $p_filename;
  879. $v_reduce_filename = $this->_pathReduction($p_stored_filename);
  880. if (strlen($v_reduce_filename) > 99) {
  881. if (!$this->_writeLongHeader($v_reduce_filename))
  882. return false;
  883. }
  884. $v_info = stat($p_filename);
  885. $v_uid = sprintf("%6s ", DecOct($v_info[4]));
  886. $v_gid = sprintf("%6s ", DecOct($v_info[5]));
  887. $v_perms = sprintf("%6s ", DecOct(fileperms($p_filename)));
  888. $v_mtime = sprintf("%11s", DecOct(filemtime($p_filename)));
  889. if (@is_dir($p_filename)) {
  890. $v_typeflag = "5";
  891. $v_size = sprintf("%11s ", DecOct(0));
  892. } else {
  893. $v_typeflag = '';
  894. clearstatcache();
  895. $v_size = sprintf("%11s ", DecOct(filesize($p_filename)));
  896. }
  897. $v_linkname = '';
  898. $v_magic = '';
  899. $v_version = '';
  900. $v_uname = '';
  901. $v_gname = '';
  902. $v_devmajor = '';
  903. $v_devminor = '';
  904. $v_prefix = '';
  905. $v_binary_data_first = pack("a100a8a8a8a12A12",
  906. $v_reduce_filename, $v_perms, $v_uid,
  907. $v_gid, $v_size, $v_mtime);
  908. $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12",
  909. $v_typeflag, $v_linkname, $v_magic,
  910. $v_version, $v_uname, $v_gname,
  911. $v_devmajor, $v_devminor, $v_prefix, '');
  912. // ----- Calculate the checksum
  913. $v_checksum = 0;
  914. // ..... First part of the header
  915. for ($i=0; $i<148; $i++)
  916. $v_checksum += ord(substr($v_binary_data_first,$i,1));
  917. // ..... Ignore the checksum value and replace it by ' ' (space)
  918. for ($i=148; $i<156; $i++)
  919. $v_checksum += ord(' ');
  920. // ..... Last part of the header
  921. for ($i=156, $j=0; $i<512; $i++, $j++)
  922. $v_checksum += ord(substr($v_binary_data_last,$j,1));
  923. // ----- Write the first 148 bytes of the header in the archive
  924. $this->_writeBlock($v_binary_data_first, 148);
  925. // ----- Write the calculated checksum
  926. $v_checksum = sprintf("%6s ", DecOct($v_checksum));
  927. $v_binary_data = pack("a8", $v_checksum);
  928. $this->_writeBlock($v_binary_data, 8);
  929. // ----- Write the last 356 bytes of the header in the archive
  930. $this->_writeBlock($v_binary_data_last, 356);
  931. return true;
  932. }
  933. // }}}
  934. // {{{ _writeHeaderBlock()
  935. function _writeHeaderBlock($p_filename, $p_size, $p_mtime=0, $p_perms=0,
  936. $p_type='', $p_uid=0, $p_gid=0)
  937. {
  938. $p_filename = $this->_pathReduction($p_filename);
  939. if (strlen($p_filename) > 99) {
  940. if (!$this->_writeLongHeader($p_filename))
  941. return false;
  942. }
  943. if ($p_type == "5") {
  944. $v_size = sprintf("%11s ", DecOct(0));
  945. } else {
  946. $v_size = sprintf("%11s ", DecOct($p_size));
  947. }
  948. $v_uid = sprintf("%6s ", DecOct($p_uid));
  949. $v_gid = sprintf("%6s ", DecOct($p_gid));
  950. $v_perms = sprintf("%6s ", DecOct($p_perms));
  951. $v_mtime = sprintf("%11s", DecOct($p_mtime));
  952. $v_linkname = '';
  953. $v_magic = '';
  954. $v_version = '';
  955. $v_uname = '';
  956. $v_gname = '';
  957. $v_devmajor = '';
  958. $v_devminor = '';
  959. $v_prefix = '';
  960. $v_binary_data_first = pack("a100a8a8a8a12A12",
  961. $p_filename, $v_perms, $v_uid, $v_gid,
  962. $v_size, $v_mtime);
  963. $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12",
  964. $p_type, $v_linkname, $v_magic,
  965. $v_version, $v_uname, $v_gname,
  966. $v_devmajor, $v_devminor, $v_prefix, '');
  967. // ----- Calculate the checksum
  968. $v_checksum = 0;
  969. // ..... First part of the header
  970. for ($i=0; $i<148; $i++)
  971. $v_checksum += ord(substr($v_binary_data_first,$i,1));
  972. // ..... Ignore the checksum value and replace it by ' ' (space)
  973. for ($i=148; $i<156; $i++)
  974. $v_checksum += ord(' ');
  975. // ..... Last part of the header
  976. for ($i=156, $j=0; $i<512; $i++, $j++)
  977. $v_checksum += ord(substr($v_binary_data_last,$j,1));
  978. // ----- Write the first 148 bytes of the header in the archive
  979. $this->_writeBlock($v_binary_data_first, 148);
  980. // ----- Write the calculated checksum
  981. $v_checksum = sprintf("%6s ", DecOct($v_checksum));
  982. $v_binary_data = pack("a8", $v_checksum);
  983. $this->_writeBlock($v_binary_data, 8);
  984. // ----- Write the last 356 bytes of the header in the archive
  985. $this->_writeBlock($v_binary_data_last, 356);
  986. return true;
  987. }
  988. // }}}
  989. // {{{ _writeLongHeader()
  990. function _writeLongHeader($p_filename)
  991. {
  992. $v_size = sprintf("%11s ", DecOct(strlen($p_filename)));
  993. $v_typeflag = 'L';
  994. $v_linkname = '';
  995. $v_magic = '';
  996. $v_version = '';
  997. $v_uname = '';
  998. $v_gname = '';
  999. $v_devmajor = '';
  1000. $v_devminor = '';
  1001. $v_prefix = '';
  1002. $v_binary_data_first = pack("a100a8a8a8a12A12",
  1003. '././@LongLink', 0, 0, 0, $v_size, 0);
  1004. $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12",
  1005. $v_typeflag, $v_linkname, $v_magic,
  1006. $v_version, $v_uname, $v_gname,
  1007. $v_devmajor, $v_devminor, $v_prefix, '');
  1008. // ----- Calculate the checksum
  1009. $v_checksum = 0;
  1010. // ..... First part of the header
  1011. for ($i=0; $i<148; $i++)
  1012. $v_checksum += ord(substr($v_binary_data_first,$i,1));
  1013. // ..... Ignore the checksum value and replace it by ' ' (space)
  1014. for ($i=148; $i<156; $i++)
  1015. $v_checksum += ord(' ');
  1016. // ..... Last part of the header
  1017. for ($i=156, $j=0; $i<512; $i++, $j++)
  1018. $v_checksum += ord(substr($v_binary_data_last,$j,1));
  1019. // ----- Write the first 148 bytes of the header in the archive
  1020. $this->_writeBlock($v_binary_data_first, 148);
  1021. // ----- Write the calculated checksum
  1022. $v_checksum = sprintf("%6s ", DecOct($v_checksum));
  1023. $v_binary_data = pack("a8", $v_checksum);
  1024. $this->_writeBlock($v_binary_data, 8);
  1025. // ----- Write the last 356 bytes of the header in the archive
  1026. $this->_writeBlock($v_binary_data_last, 356);
  1027. // ----- Write the filename as content of the block
  1028. $i=0;
  1029. while (($v_buffer = substr($p_filename, (($i++)*512), 512)) != '') {
  1030. $v_binary_data = pack("a512", "$v_buffer");
  1031. $this->_writeBlock($v_binary_data);
  1032. }
  1033. return true;
  1034. }
  1035. // }}}
  1036. // {{{ _readHeader()
  1037. function _readHeader($v_binary_data, &$v_header)
  1038. {
  1039. if (strlen($v_binary_data)==0) {
  1040. $v_header['filename'] = '';
  1041. return true;
  1042. }
  1043. if (strlen($v_binary_data) != 512) {
  1044. $v_header['filename'] = '';
  1045. $this->_error('Invalid block size : '.strlen($v_binary_data));
  1046. return false;
  1047. }
  1048. // ----- Calculate the checksum
  1049. $v_checksum = 0;
  1050. // ..... First part of the header
  1051. for ($i=0; $i<148; $i++)
  1052. $v_checksum+=ord(substr($v_binary_data,$i,1));
  1053. // ..... Ignore the checksum value and replace it by ' ' (space)
  1054. for ($i=148; $i<156; $i++)
  1055. $v_checksum += ord(' ');
  1056. // ..... Last part of the header
  1057. for ($i=156; $i<512; $i++)
  1058. $v_checksum+=ord(substr($v_binary_data,$i,1));
  1059. $v_data = unpack("a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/"
  1060. ."a8checksum/a1typeflag/a100link/a6magic/a2version/"
  1061. ."a32uname/a32gname/a8devmajor/a8devminor",
  1062. $v_binary_data);
  1063. // ----- Extract the checksum
  1064. $v_header['checksum'] = OctDec(trim($v_data['checksum']));
  1065. if ($v_header['checksum'] != $v_checksum) {
  1066. $v_header['filename'] = '';
  1067. // ----- Look for last block (empty block)
  1068. if (($v_checksum == 256) && ($v_header['checksum'] == 0))
  1069. return true;
  1070. $this->_error('Invalid checksum for file "'.$v_data['filename']
  1071. .'" : '.$v_checksum.' calculated, '
  1072. .$v_header['checksum'].' expected');
  1073. return false;
  1074. }
  1075. // ----- Extract the properties
  1076. $v_header['filename'] = trim($v_data['filename']);
  1077. $v_header['mode'] = OctDec(trim($v_data['mode']));
  1078. $v_header['uid'] = OctDec(trim($v_data['uid']));
  1079. $v_header['gid'] = OctDec(trim($v_data['gid']));
  1080. $v_header['size'] = OctDec(trim($v_data['size']));
  1081. $v_header['mtime'] = OctDec(trim($v_data['mtime']));
  1082. if (($v_header['typeflag'] = $v_data['typeflag']) == "5") {
  1083. $v_header['size'] = 0;
  1084. }
  1085. /* ----- All these fields are removed form the header because
  1086. they do not carry interesting info
  1087. $v_header[link] = trim($v_data[link]);
  1088. $v_header[magic] = trim($v_data[magic]);
  1089. $v_header[version] = trim($v_data[version]);
  1090. $v_header[uname] = trim($v_data[uname]);
  1091. $v_header[gname] = trim($v_data[gname]);
  1092. $v_header[devmajor] = trim($v_data[devmajor]);
  1093. $v_header[devminor] = trim($v_data[devminor]);
  1094. */
  1095. return true;
  1096. }
  1097. // }}}
  1098. // {{{ _readLongHeader()
  1099. function _readLongHeader(&$v_header)
  1100. {
  1101. $v_filename = '';
  1102. $n = floor($v_header['size']/512);
  1103. for ($i=0; $i<$n; $i++) {
  1104. $v_content = $this->_readBlock();
  1105. $v_filename .= $v_content;
  1106. }
  1107. if (($v_header['size'] % 512) != 0) {
  1108. $v_content = $this->_readBlock();
  1109. $v_filename .= $v_content;
  1110. }
  1111. // ----- Read the next header
  1112. $v_binary_data = $this->_readBlock();
  1113. if (!$this->_readHeader($v_binary_data, $v_header))
  1114. return false;
  1115. $v_header['filename'] = $v_filename;
  1116. return true;
  1117. }
  1118. // }}}
  1119. // {{{ _extractInString()
  1120. /**
  1121. * This method extract from the archive one file identified by $p_filename.
  1122. * The return value is a string with the file content, or NULL on error.
  1123. * @param string $p_filename The path of the file to extract in a string.
  1124. * @return a string with the file content or NULL.
  1125. * @access private
  1126. */
  1127. function _extractInString($p_filename)
  1128. {
  1129. $v_result_str = "";
  1130. While (strlen($v_binary_data = $this->_readBlock()) != 0)
  1131. {
  1132. if (!$this->_readHeader($v_binary_data, $v_header))
  1133. return NULL;
  1134. if ($v_header['filename'] == '')
  1135. continue;
  1136. // ----- Look for long filename
  1137. if ($v_header['typeflag'] == 'L') {
  1138. if (!$this->_readLongHeader($v_header))
  1139. return NULL;
  1140. }
  1141. if ($v_header['filename'] == $p_filename) {
  1142. if ($v_header['typeflag'] == "5") {
  1143. $this->_error('Unable to extract in string a directory '
  1144. .'entry {'.$v_header['filename'].'}');
  1145. return NULL;
  1146. } else {
  1147. $n = floor($v_header['size']/512);
  1148. for ($i=0; $i<$n; $i++) {
  1149. $v_result_str .= $this->_readBlock();
  1150. }
  1151. if (($v_header['size'] % 512) != 0) {
  1152. $v_content = $this->_readBlock();
  1153. $v_result_str .= substr($v_content, 0,
  1154. ($v_header['size'] % 512));
  1155. }
  1156. return $v_result_str;
  1157. }
  1158. } else {
  1159. $this->_jumpBlock(ceil(($v_header['size']/512)));
  1160. }
  1161. }
  1162. return NULL;
  1163. }
  1164. // }}}
  1165. // {{{ _extractList()
  1166. function _extractList($p_path, &$p_list_detail, $p_mode,
  1167. $p_file_list, $p_remove_path)
  1168. {
  1169. $v_result=true;
  1170. $v_nb = 0;
  1171. $v_extract_all = true;
  1172. $v_listing = false;
  1173. $p_path = $this->_translateWinPath($p_path, false);
  1174. if ($p_path == '' || (substr($p_path, 0, 1) != '/'
  1175. && substr($p_path, 0, 3) != "../" && !strpos($p_path, ':'))) {
  1176. $p_path = "./".$p_path;
  1177. }
  1178. $p_remove_path = $this->_translateWinPath($p_remove_path);
  1179. // ----- Look for path to remove format (should end by /)
  1180. if (($p_remove_path != '') && (substr($p_remove_path, -1) != '/'))
  1181. $p_remove_path .= '/';
  1182. $p_remove_path_size = strlen($p_remove_path);
  1183. switch ($p_mode) {
  1184. case "complete" :
  1185. $v_extract_all = TRUE;
  1186. $v_listing = FALSE;
  1187. break;
  1188. case "partial" :
  1189. $v_extract_all = FALSE;
  1190. $v_listing = FALSE;
  1191. break;
  1192. case "list" :
  1193. $v_extract_all = FALSE;
  1194. $v_listing = TRUE;
  1195. break;
  1196. default :
  1197. $this->_error('Invalid extract mode ('.$p_mode.')');
  1198. return false;
  1199. }
  1200. clearstatcache();
  1201. while (strlen($v_binary_data = $this->_readBlock()) != 0)
  1202. {
  1203. $v_extract_file = FALSE;
  1204. $v_extraction_stopped = 0;
  1205. if (!$this->_readHeader($v_binary_data, $v_header))
  1206. return false;
  1207. if ($v_header['filename'] == '') {
  1208. continue;
  1209. }
  1210. // ----- Look for long filename
  1211. if ($v_header['typeflag'] == 'L') {
  1212. if (!$this->_readLongHeader($v_header))
  1213. return false;
  1214. }
  1215. if ((!$v_extract_all) && (is_array($p_file_list))) {
  1216. // ----- By default no unzip if the file is not found
  1217. $v_extract_file = false;
  1218. for ($i=0; $i<sizeof($p_file_list); $i++) {
  1219. // ----- Look if it is a directory
  1220. if (substr($p_file_list[$i], -1) == '/') {
  1221. // ----- Look if the directory is in the filename path
  1222. if ((strlen($v_header['filename']) > strlen($p_file_list[$i]))
  1223. && (substr($v_header['filename'], 0, strlen($p_file_list[$i]))
  1224. == $p_file_list[$i])) {
  1225. $v_extract_file = TRUE;
  1226. break;
  1227. }
  1228. }
  1229. // ----- It is a file, so compare the file names
  1230. elseif ($p_file_list[$i] == $v_header['filename']) {
  1231. $v_extract_file = TRUE;
  1232. break;
  1233. }
  1234. }
  1235. } else {
  1236. $v_extract_file = TRUE;
  1237. }
  1238. // ----- Look if this file need to be extracted

Large files files are truncated, but you can click here to view the full file