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

/libraries/pear/archive_tar/Archive_Tar.php

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