Skip navigation
Help

system.tar.inc

  1. drupal
    1. 7 drupal/modules/system/system.tar.inc

Classes

NameDescription
Archive_TarCreates a (compressed) Tar archive *

Constants

NameDescription
ARCHIVE_TAR_ATT_SEPARATOR
ARCHIVE_TAR_END_BLOCK

File

drupal/modules/system/system.tar.inc
View source
  1. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  2. /**
  3. * File::CSV
  4. *
  5. * PHP versions 4 and 5
  6. *
  7. * Copyright (c) 1997-2008,
  8. * Vincent Blavet <vincent@phpconcept.net>
  9. * All rights reserved.
  10. *
  11. * Redistribution and use in source and binary forms, with or without
  12. * modification, are permitted provided that the following conditions are met:
  13. *
  14. * * Redistributions of source code must retain the above copyright notice,
  15. * this list of conditions and the following disclaimer.
  16. * * Redistributions in binary form must reproduce the above copyright
  17. * notice, this list of conditions and the following disclaimer in the
  18. * documentation and/or other materials provided with the distribution.
  19. *
  20. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  21. * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  22. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  23. * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
  24. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  25. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  26. * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  27. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  28. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  29. * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  30. *
  31. *
  32. * @category File_Formats
  33. * @package Archive_Tar
  34. * @author Vincent Blavet <vincent@phpconcept.net>
  35. * @copyright 1997-2008 The Authors
  36. * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
  37. * @version CVS: Id: Tar.php,v 1.43 2008/10/30 17:58:42 dufuz Exp
  38. * @link http://pear.php.net/package/Archive_Tar
  39. */
  40. //require_once 'PEAR.php';
  41. //
  42. //
  43. define ('ARCHIVE_TAR_ATT_SEPARATOR', 90001);
  44. define ('ARCHIVE_TAR_END_BLOCK', pack("a512", ''));
  45. /**
  46. * Creates a (compressed) Tar archive
  47. *
  48. * @author Vincent Blavet <vincent@phpconcept.net>
  49. * @version Revision: 1.43
  50. * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
  51. * @package Archive_Tar
  52. */
  53. class Archive_Tar // extends PEAR
  54. {
  55. /**
  56. * @var string Name of the Tar
  57. */
  58. var $_tarname='';
  59. /**
  60. * @var boolean if true, the Tar file will be gzipped
  61. */
  62. var $_compress=false;
  63. /**
  64. * @var string Type of compression : 'none', 'gz' or 'bz2'
  65. */
  66. var $_compress_type='none';
  67. /**
  68. * @var string Explode separator
  69. */
  70. var $_separator=' ';
  71. /**
  72. * @var file descriptor
  73. */
  74. var $_file=0;
  75. /**
  76. * @var string Local Tar name of a remote Tar (http:// or ftp://)
  77. */
  78. var $_temp_tarname='';
  79. // {{{ constructor
  80. /**
  81. * Archive_Tar Class constructor. This flavour of the constructor only
  82. * declare a new Archive_Tar object, identifying it by the name of the
  83. * tar file.
  84. * If the compress argument is set the tar will be read or created as a
  85. * gzip or bz2 compressed TAR file.
  86. *
  87. * @param string $p_tarname The name of the tar archive to create
  88. * @param string $p_compress can be null, 'gz' or 'bz2'. This
  89. * parameter indicates if gzip or bz2 compression
  90. * is required. For compatibility reason the
  91. * boolean value 'true' means 'gz'.
  92. * @access public
  93. */
  94. // function Archive_Tar($p_tarname, $p_compress = null)
  95. function __construct($p_tarname, $p_compress = null)
  96. {
  97. // $this->PEAR();
  98. $this->_compress = false;
  99. $this->_compress_type = 'none';
  100. if (($p_compress === null) || ($p_compress == '')) {
  101. if (@file_exists($p_tarname)) {
  102. if ($fp = @fopen($p_tarname, "rb")) {
  103. // look for gzip magic cookie
  104. $data = fread($fp, 2);
  105. fclose($fp);
  106. if ($data == "\37\213") {
  107. $this->_compress = true;
  108. $this->_compress_type = 'gz';
  109. // No sure it's enought for a magic code ....
  110. } elseif ($data == "BZ") {
  111. $this->_compress = true;
  112. $this->_compress_type = 'bz2';
  113. }
  114. }
  115. } else {
  116. // probably a remote file or some file accessible
  117. // through a stream interface
  118. if (substr($p_tarname, -2) == 'gz') {
  119. $this->_compress = true;
  120. $this->_compress_type = 'gz';
  121. } elseif ((substr($p_tarname, -3) == 'bz2') ||
  122. (substr($p_tarname, -2) == 'bz')) {
  123. $this->_compress = true;
  124. $this->_compress_type = 'bz2';
  125. }
  126. }
  127. } else {
  128. if (($p_compress === true) || ($p_compress == 'gz')) {
  129. $this->_compress = true;
  130. $this->_compress_type = 'gz';
  131. } else if ($p_compress == 'bz2') {
  132. $this->_compress = true;
  133. $this->_compress_type = 'bz2';
  134. } else {
  135. die("Unsupported compression type '$p_compress'\n".
  136. "Supported types are 'gz' and 'bz2'.\n");
  137. return false;
  138. }
  139. }
  140. $this->_tarname = $p_tarname;
  141. if ($this->_compress) { // assert zlib or bz2 extension support
  142. if ($this->_compress_type == 'gz')
  143. $extname = 'zlib';
  144. else if ($this->_compress_type == 'bz2')
  145. $extname = 'bz2';
  146. if (!extension_loaded($extname)) {
  147. // PEAR::loadExtension($extname);
  148. $this->loadExtension($extname);
  149. }
  150. if (!extension_loaded($extname)) {
  151. die("The extension '$extname' couldn't be found.\n".
  152. "Please make sure your version of PHP was built ".
  153. "with '$extname' support.\n");
  154. return false;
  155. }
  156. }
  157. }
  158. // }}}
  159. /**
  160. * OS independant PHP extension load. Remember to take care
  161. * on the correct extension name for case sensitive OSes.
  162. * The function is the copy of PEAR::loadExtension().
  163. *
  164. * @param string $ext The extension name
  165. * @return bool Success or not on the dl() call
  166. */
  167. function loadExtension($ext)
  168. {
  169. if (!extension_loaded($ext)) {
  170. // if either returns true dl() will produce a FATAL error, stop that
  171. if ((ini_get('enable_dl') != 1) || (ini_get('safe_mode') == 1)) {
  172. return false;
  173. }
  174. if (OS_WINDOWS) {
  175. $suffix = '.dll';
  176. } elseif (PHP_OS == 'HP-UX') {
  177. $suffix = '.sl';
  178. } elseif (PHP_OS == 'AIX') {
  179. $suffix = '.a';
  180. } elseif (PHP_OS == 'OSX') {
  181. $suffix = '.bundle';
  182. } else {
  183. $suffix = '.so';
  184. }
  185. return @dl('php_'.$ext.$suffix) || @dl($ext.$suffix);
  186. }
  187. return true;
  188. }
  189. // {{{ destructor
  190. // function _Archive_Tar()
  191. function __destruct()
  192. {
  193. $this->_close();
  194. // ----- Look for a local copy to delete
  195. if ($this->_temp_tarname != '')
  196. @drupal_unlink($this->_temp_tarname);
  197. // $this->_PEAR();
  198. }
  199. // }}}
  200. // {{{ create()
  201. /**
  202. * This method creates the archive file and add the files / directories
  203. * that are listed in $p_filelist.
  204. * If a file with the same name exist and is writable, it is replaced
  205. * by the new tar.
  206. * The method return false and a PEAR error text.
  207. * The $p_filelist parameter can be an array of string, each string
  208. * representing a filename or a directory name with their path if
  209. * needed. It can also be a single string with names separated by a
  210. * single blank.
  211. * For each directory added in the archive, the files and
  212. * sub-directories are also added.
  213. * See also createModify() method for more details.
  214. *
  215. * @param array $p_filelist An array of filenames and directory names, or a
  216. * single string with names separated by a single
  217. * blank space.
  218. * @return true on success, false on error.
  219. * @see createModify()
  220. * @access public
  221. */
  222. function create($p_filelist)
  223. {
  224. return $this->createModify($p_filelist, '', '');
  225. }
  226. // }}}
  227. // {{{ add()
  228. /**
  229. * This method add the files / directories that are listed in $p_filelist in
  230. * the archive. If the archive does not exist it is created.
  231. * The method return false and a PEAR error text.
  232. * The files and directories listed are only added at the end of the archive,
  233. * even if a file with the same name is already archived.
  234. * See also createModify() method for more details.
  235. *
  236. * @param array $p_filelist An array of filenames and directory names, or a
  237. * single string with names separated by a single
  238. * blank space.
  239. * @return true on success, false on error.
  240. * @see createModify()
  241. * @access public
  242. */
  243. function add($p_filelist)
  244. {
  245. return $this->addModify($p_filelist, '', '');
  246. }
  247. // }}}
  248. // {{{ extract()
  249. function extract($p_path='')
  250. {
  251. return $this->extractModify($p_path, '');
  252. }
  253. // }}}
  254. // {{{ listContent()
  255. function listContent()
  256. {
  257. $v_list_detail = array();
  258. if ($this->_openRead()) {
  259. if (!$this->_extractList('', $v_list_detail, "list", '', '')) {
  260. unset($v_list_detail);
  261. $v_list_detail = 0;
  262. }
  263. $this->_close();
  264. }
  265. return $v_list_detail;
  266. }
  267. // }}}
  268. // {{{ createModify()
  269. /**
  270. * This method creates the archive file and add the files / directories
  271. * that are listed in $p_filelist.
  272. * If the file already exists and is writable, it is replaced by the
  273. * new tar. It is a create and not an add. If the file exists and is
  274. * read-only or is a directory it is not replaced. The method return
  275. * false and a PEAR error text.
  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. * See also addModify() method for file adding properties.
  290. *
  291. * @param array $p_filelist An array of filenames and directory names,
  292. * or a single string with names separated by
  293. * a single blank space.
  294. * @param string $p_add_dir A string which contains a path to be added
  295. * to the memorized path of each element in
  296. * the list.
  297. * @param string $p_remove_dir A string which contains a path to be
  298. * removed from the memorized path of each
  299. * element in the list, when relevant.
  300. * @return boolean true on success, false on error.
  301. * @access public
  302. * @see addModify()
  303. */
  304. function createModify($p_filelist, $p_add_dir, $p_remove_dir='')
  305. {
  306. $v_result = true;
  307. if (!$this->_openWrite())
  308. return false;
  309. if ($p_filelist != '') {
  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->_cleanFile();
  316. $this->_error('Invalid file list');
  317. return false;
  318. }
  319. $v_result = $this->_addList($v_list, $p_add_dir, $p_remove_dir);
  320. }
  321. if ($v_result) {
  322. $this->_writeFooter();
  323. $this->_close();
  324. } else
  325. $this->_cleanFile();
  326. return $v_result;
  327. }
  328. // }}}
  329. // {{{ addModify()
  330. /**
  331. * This method add the files / directories listed in $p_filelist at the
  332. * end of the existing archive. If the archive does not yet exists it
  333. * is created.
  334. * The $p_filelist parameter can be an array of string, each string
  335. * representing a filename or a directory name with their path if
  336. * needed. It can also be a single string with names separated by a
  337. * single blank.
  338. * The path indicated in $p_remove_dir will be removed from the
  339. * memorized path of each file / directory listed when this path
  340. * exists. By default nothing is removed (empty path '')
  341. * The path indicated in $p_add_dir will be added at the beginning of
  342. * the memorized path of each file / directory listed. However it can
  343. * be set to empty ''. The adding of a path is done after the removing
  344. * of path.
  345. * The path add/remove ability enables the user to prepare an archive
  346. * for extraction in a different path than the origin files are.
  347. * If a file/dir is already in the archive it will only be added at the
  348. * end of the archive. There is no update of the existing archived
  349. * file/dir. However while extracting the archive, the last file will
  350. * replace the first one. This results in a none optimization of the
  351. * archive size.
  352. * If a file/dir does not exist the file/dir is ignored. However an
  353. * error text is send to PEAR error.
  354. * If a file/dir is not readable the file/dir is ignored. However an
  355. * error text is send to PEAR error.
  356. *
  357. * @param array $p_filelist An array of filenames and directory
  358. * names, or a single string with names
  359. * separated by a single blank space.
  360. * @param string $p_add_dir A string which contains a path to be
  361. * added to the memorized path of each
  362. * element in the list.
  363. * @param string $p_remove_dir A string which contains a path to be
  364. * removed from the memorized path of
  365. * each element in the list, when
  366. * relevant.
  367. * @return true on success, false on error.
  368. * @access public
  369. */
  370. function addModify($p_filelist, $p_add_dir, $p_remove_dir='')
  371. {
  372. $v_result = true;
  373. if (!$this->_isArchive())
  374. $v_result = $this->createModify($p_filelist, $p_add_dir,
  375. $p_remove_dir);
  376. else {
  377. if (is_array($p_filelist))
  378. $v_list = $p_filelist;
  379. elseif (is_string($p_filelist))
  380. $v_list = explode($this->_separator, $p_filelist);
  381. else {
  382. $this->_error('Invalid file list');
  383. return false;
  384. }
  385. $v_result = $this->_append($v_list, $p_add_dir, $p_remove_dir);
  386. }
  387. return $v_result;
  388. }
  389. // }}}
  390. // {{{ addString()
  391. /**
  392. * This method add a single string as a file at the
  393. * end of the existing archive. If the archive does not yet exists it
  394. * is created.
  395. *
  396. * @param string $p_filename A string which contains the full
  397. * filename path that will be associated
  398. * with the string.
  399. * @param string $p_string The content of the file added in
  400. * the archive.
  401. * @return true on success, false on error.
  402. * @access public
  403. */
  404. function addString($p_filename, $p_string)
  405. {
  406. $v_result = true;
  407. if (!$this->_isArchive()) {
  408. if (!$this->_openWrite()) {
  409. return false;
  410. }
  411. $this->_close();
  412. }
  413. if (!$this->_openAppend())
  414. return false;
  415. // Need to check the get back to the temporary file ? ....
  416. $v_result = $this->_addString($p_filename, $p_string);
  417. $this->_writeFooter();
  418. $this->_close();
  419. return $v_result;
  420. }
  421. // }}}
  422. // {{{ extractModify()
  423. /**
  424. * This method extract all the content of the archive in the directory
  425. * indicated by $p_path. When relevant the memorized path of the
  426. * files/dir can be modified by removing the $p_remove_path path at the
  427. * beginning of the file/dir path.
  428. * While extracting a file, if the directory path does not exists it is
  429. * created.
  430. * While extracting a file, if the file already exists it is replaced
  431. * without looking for last modification date.
  432. * While extracting a file, if the file already exists and is write
  433. * protected, the extraction is aborted.
  434. * While extracting a file, if a directory with the same name already
  435. * exists, the extraction is aborted.
  436. * While extracting a directory, if a file with the same name already
  437. * exists, the extraction is aborted.
  438. * While extracting a file/directory if the destination directory exist
  439. * and is write protected, or does not exist but can not be created,
  440. * the extraction is aborted.
  441. * If after extraction an extracted file does not show the correct
  442. * stored file size, the extraction is aborted.
  443. * When the extraction is aborted, a PEAR error text is set and false
  444. * is returned. However the result can be a partial extraction that may
  445. * need to be manually cleaned.
  446. *
  447. * @param string $p_path The path of the directory where the
  448. * files/dir need to by extracted.
  449. * @param string $p_remove_path Part of the memorized path that can be
  450. * removed if present at the beginning of
  451. * the file/dir path.
  452. * @return boolean true on success, false on error.
  453. * @access public
  454. * @see extractList()
  455. */
  456. function extractModify($p_path, $p_remove_path)
  457. {
  458. $v_result = true;
  459. $v_list_detail = array();
  460. if ($v_result = $this->_openRead()) {
  461. $v_result = $this->_extractList($p_path, $v_list_detail,
  462. "complete", 0, $p_remove_path);
  463. $this->_close();
  464. }
  465. return $v_result;
  466. }
  467. // }}}
  468. // {{{ extractInString()
  469. /**
  470. * This method extract from the archive one file identified by $p_filename.
  471. * The return value is a string with the file content, or NULL on error.
  472. * @param string $p_filename The path of the file to extract in a string.
  473. * @return a string with the file content or NULL.
  474. * @access public
  475. */
  476. function extractInString($p_filename)
  477. {
  478. if ($this->_openRead()) {
  479. $v_result = $this->_extractInString($p_filename);
  480. $this->_close();
  481. } else {
  482. $v_result = NULL;
  483. }
  484. return $v_result;
  485. }
  486. // }}}
  487. // {{{ extractList()
  488. /**
  489. * This method extract from the archive only the files indicated in the
  490. * $p_filelist. These files are extracted in the current directory or
  491. * in the directory indicated by the optional $p_path parameter.
  492. * If indicated the $p_remove_path can be used in the same way as it is
  493. * used in extractModify() method.
  494. * @param array $p_filelist An array of filenames and directory names,
  495. * or a single string with names separated
  496. * by a single blank space.
  497. * @param string $p_path The path of the directory where the
  498. * files/dir need to by extracted.
  499. * @param string $p_remove_path Part of the memorized path that can be
  500. * removed if present at the beginning of
  501. * the file/dir path.
  502. * @return true on success, false on error.
  503. * @access public
  504. * @see extractModify()
  505. */
  506. function extractList($p_filelist, $p_path='', $p_remove_path='')
  507. {
  508. $v_result = true;
  509. $v_list_detail = array();
  510. if (is_array($p_filelist))
  511. $v_list = $p_filelist;
  512. elseif (is_string($p_filelist))
  513. $v_list = explode($this->_separator, $p_filelist);
  514. else {
  515. $this->_error('Invalid string list');
  516. return false;
  517. }
  518. if ($v_result = $this->_openRead()) {
  519. $v_result = $this->_extractList($p_path, $v_list_detail, "partial",
  520. $v_list, $p_remove_path);
  521. $this->_close();
  522. }
  523. return $v_result;
  524. }
  525. // }}}
  526. // {{{ setAttribute()
  527. /**
  528. * This method set specific attributes of the archive. It uses a variable
  529. * list of parameters, in the format attribute code + attribute values :
  530. * $arch->setAttribute(ARCHIVE_TAR_ATT_SEPARATOR, ',');
  531. * @param mixed $argv variable list of attributes and values
  532. * @return true on success, false on error.
  533. * @access public
  534. */
  535. function setAttribute()
  536. {
  537. $v_result = true;
  538. // ----- Get the number of variable list of arguments
  539. if (($v_size = func_num_args()) == 0) {
  540. return true;
  541. }
  542. // ----- Get the arguments
  543. $v_att_list = &func_get_args();
  544. // ----- Read the attributes
  545. $i=0;
  546. while ($i<$v_size) {
  547. // ----- Look for next option
  548. switch ($v_att_list[$i]) {
  549. // ----- Look for options that request a string value
  550. case ARCHIVE_TAR_ATT_SEPARATOR :
  551. // ----- Check the number of parameters
  552. if (($i+1) >= $v_size) {
  553. $this->_error('Invalid number of parameters for '
  554. .'attribute ARCHIVE_TAR_ATT_SEPARATOR');
  555. return false;
  556. }
  557. // ----- Get the value
  558. $this->_separator = $v_att_list[$i+1];
  559. $i++;
  560. break;
  561. default :
  562. $this->_error('Unknow attribute code '.$v_att_list[$i].'');
  563. return false;
  564. }
  565. // ----- Next attribute
  566. $i++;
  567. }
  568. return $v_result;
  569. }
  570. // }}}
  571. // {{{ _error()
  572. function _error($p_message)
  573. {
  574. // ----- To be completed
  575. // $this->raiseError($p_message);
  576. throw new Exception($p_message);
  577. }
  578. // }}}
  579. // {{{ _warning()
  580. function _warning($p_message)
  581. {
  582. // ----- To be completed
  583. // $this->raiseError($p_message);
  584. throw new Exception($p_message);
  585. }
  586. // }}}
  587. // {{{ _isArchive()
  588. function _isArchive($p_filename=NULL)
  589. {
  590. if ($p_filename == NULL) {
  591. $p_filename = $this->_tarname;
  592. }
  593. clearstatcache();
  594. return @is_file($p_filename) && !@is_link($p_filename);
  595. }
  596. // }}}
  597. // {{{ _openWrite()
  598. function _openWrite()
  599. {
  600. if ($this->_compress_type == 'gz')
  601. $this->_file = @gzopen($this->_tarname, "wb9");
  602. else if ($this->_compress_type == 'bz2')
  603. $this->_file = @bzopen($this->_tarname, "w");
  604. else if ($this->_compress_type == 'none')
  605. $this->_file = @fopen($this->_tarname, "wb");
  606. else
  607. $this->_error('Unknown or missing compression type ('
  608. .$this->_compress_type.')');
  609. if ($this->_file == 0) {
  610. $this->_error('Unable to open in write mode \''
  611. .$this->_tarname.'\'');
  612. return false;
  613. }
  614. return true;
  615. }
  616. // }}}
  617. // {{{ _openRead()
  618. function _openRead()
  619. {
  620. if (strtolower(substr($this->_tarname, 0, 7)) == 'http://') {
  621. // ----- Look if a local copy need to be done
  622. if ($this->_temp_tarname == '') {
  623. $this->_temp_tarname = uniqid('tar').'.tmp';
  624. if (!$v_file_from = @fopen($this->_tarname, 'rb')) {
  625. $this->_error('Unable to open in read mode \''
  626. .$this->_tarname.'\'');
  627. $this->_temp_tarname = '';
  628. return false;
  629. }
  630. if (!$v_file_to = @fopen($this->_temp_tarname, 'wb')) {
  631. $this->_error('Unable to open in write mode \''
  632. .$this->_temp_tarname.'\'');
  633. $this->_temp_tarname = '';
  634. return false;
  635. }
  636. while ($v_data = @fread($v_file_from, 1024))
  637. @fwrite($v_file_to, $v_data);
  638. @fclose($v_file_from);
  639. @fclose($v_file_to);
  640. }
  641. // ----- File to open if the local copy
  642. $v_filename = $this->_temp_tarname;
  643. } else
  644. // ----- File to open if the normal Tar file
  645. $v_filename = $this->_tarname;
  646. if ($this->_compress_type == 'gz')
  647. $this->_file = @gzopen($v_filename, "rb");
  648. else if ($this->_compress_type == 'bz2')
  649. $this->_file = @bzopen($v_filename, "r");
  650. else if ($this->_compress_type == 'none')
  651. $this->_file = @fopen($v_filename, "rb");
  652. else
  653. $this->_error('Unknown or missing compression type ('
  654. .$this->_compress_type.')');
  655. if ($this->_file == 0) {
  656. $this->_error('Unable to open in read mode \''.$v_filename.'\'');
  657. return false;
  658. }
  659. return true;
  660. }
  661. // }}}
  662. // {{{ _openReadWrite()
  663. function _openReadWrite()
  664. {
  665. if ($this->_compress_type == 'gz')
  666. $this->_file = @gzopen($this->_tarname, "r+b");
  667. else if ($this->_compress_type == 'bz2') {
  668. $this->_error('Unable to open bz2 in read/write mode \''
  669. .$this->_tarname.'\' (limitation of bz2 extension)');
  670. return false;
  671. } else if ($this->_compress_type == 'none')
  672. $this->_file = @fopen($this->_tarname, "r+b");
  673. else
  674. $this->_error('Unknown or missing compression type ('
  675. .$this->_compress_type.')');
  676. if ($this->_file == 0) {
  677. $this->_error('Unable to open in read/write mode \''
  678. .$this->_tarname.'\'');
  679. return false;
  680. }
  681. return true;
  682. }
  683. // }}}
  684. // {{{ _close()
  685. function _close()
  686. {
  687. //if (isset($this->_file)) {
  688. if (is_resource($this->_file)) {
  689. if ($this->_compress_type == 'gz')
  690. @gzclose($this->_file);
  691. else if ($this->_compress_type == 'bz2')
  692. @bzclose($this->_file);
  693. else if ($this->_compress_type == 'none')
  694. @fclose($this->_file);
  695. else
  696. $this->_error('Unknown or missing compression type ('
  697. .$this->_compress_type.')');
  698. $this->_file = 0;
  699. }
  700. // ----- Look if a local copy need to be erase
  701. // Note that it might be interesting to keep the url for a time : ToDo
  702. if ($this->_temp_tarname != '') {
  703. @drupal_unlink($this->_temp_tarname);
  704. $this->_temp_tarname = '';
  705. }
  706. return true;
  707. }
  708. // }}}
  709. // {{{ _cleanFile()
  710. function _cleanFile()
  711. {
  712. $this->_close();
  713. // ----- Look for a local copy
  714. if ($this->_temp_tarname != '') {
  715. // ----- Remove the local copy but not the remote tarname
  716. @drupal_unlink($this->_temp_tarname);
  717. $this->_temp_tarname = '';
  718. } else {
  719. // ----- Remove the local tarname file
  720. @drupal_unlink($this->_tarname);
  721. }
  722. $this->_tarname = '';
  723. return true;
  724. }
  725. // }}}
  726. // {{{ _writeBlock()
  727. function _writeBlock($p_binary_data, $p_len=null)
  728. {
  729. if (is_resource($this->_file)) {
  730. if ($p_len === null) {
  731. if ($this->_compress_type == 'gz')
  732. @gzputs($this->_file, $p_binary_data);
  733. else if ($this->_compress_type == 'bz2')
  734. @bzwrite($this->_file, $p_binary_data);
  735. else if ($this->_compress_type == 'none')
  736. @fputs($this->_file, $p_binary_data);
  737. else
  738. $this->_error('Unknown or missing compression type ('
  739. .$this->_compress_type.')');
  740. } else {
  741. if ($this->_compress_type == 'gz')
  742. @gzputs($this->_file, $p_binary_data, $p_len);
  743. else if ($this->_compress_type == 'bz2')
  744. @bzwrite($this->_file, $p_binary_data, $p_len);
  745. else if ($this->_compress_type == 'none')
  746. @fputs($this->_file, $p_binary_data, $p_len);
  747. else
  748. $this->_error('Unknown or missing compression type ('
  749. .$this->_compress_type.')');
  750. }
  751. }
  752. return true;
  753. }
  754. // }}}
  755. // {{{ _readBlock()
  756. function _readBlock()
  757. {
  758. $v_block = null;
  759. if (is_resource($this->_file)) {
  760. if ($this->_compress_type == 'gz')
  761. $v_block = @gzread($this->_file, 512);
  762. else if ($this->_compress_type == 'bz2')
  763. $v_block = @bzread($this->_file, 512);
  764. else if ($this->_compress_type == 'none')
  765. $v_block = @fread($this->_file, 512);
  766. else
  767. $this->_error('Unknown or missing compression type ('
  768. .$this->_compress_type.')');
  769. }
  770. return $v_block;
  771. }
  772. // }}}
  773. // {{{ _jumpBlock()
  774. function _jumpBlock($p_len=null)
  775. {
  776. if (is_resource($this->_file)) {
  777. if ($p_len === null)
  778. $p_len = 1;
  779. if ($this->_compress_type == 'gz') {
  780. @gzseek($this->_file, gztell($this->_file)+($p_len*512));
  781. }
  782. else if ($this->_compress_type == 'bz2') {
  783. // ----- Replace missing bztell() and bzseek()
  784. for ($i=0; $i<$p_len; $i++)
  785. $this->_readBlock();
  786. } else if ($this->_compress_type == 'none')
  787. @fseek($this->_file, ftell($this->_file)+($p_len*512));
  788. else
  789. $this->_error('Unknown or missing compression type ('
  790. .$this->_compress_type.')');
  791. }
  792. return true;
  793. }
  794. // }}}
  795. // {{{ _writeFooter()
  796. function _writeFooter()
  797. {
  798. if (is_resource($this->_file)) {
  799. // ----- Write the last 0 filled block for end of archive
  800. $v_binary_data = pack('a1024', '');
  801. $this->_writeBlock($v_binary_data);
  802. }
  803. return true;
  804. }
  805. // }}}
  806. // {{{ _addList()
  807. function _addList($p_list, $p_add_dir, $p_remove_dir)
  808. {
  809. $v_result=true;
  810. $v_header = array();
  811. // ----- Remove potential windows directory separator
  812. $p_add_dir = $this->_translateWinPath($p_add_dir);
  813. $p_remove_dir = $this->_translateWinPath($p_remove_dir, false);
  814. if (!$this->_file) {
  815. $this->_error('Invalid file descriptor');
  816. return false;
  817. }
  818. if (sizeof($p_list) == 0)
  819. return true;
  820. foreach ($p_list as $v_filename) {
  821. if (!$v_result) {
  822. break;
  823. }
  824. // ----- Skip the current tar name
  825. if ($v_filename == $this->_tarname)
  826. continue;
  827. if ($v_filename == '')
  828. continue;
  829. if (!file_exists($v_filename)) {
  830. $this->_warning("File '$v_filename' does not exist");
  831. continue;
  832. }
  833. // ----- Add the file or directory header
  834. if (!$this->_addFile($v_filename, $v_header, $p_add_dir, $p_remove_dir))
  835. return false;
  836. if (@is_dir($v_filename) && !@is_link($v_filename)) {
  837. if (!($p_hdir = opendir($v_filename))) {
  838. $this->_warning("Directory '$v_filename' can not be read");
  839. continue;
  840. }
  841. while (false !== ($p_hitem = readdir($p_hdir))) {
  842. if (($p_hitem != '.') && ($p_hitem != '..')) {
  843. if ($v_filename != ".")
  844. $p_temp_list[0] = $v_filename.'/'.$p_hitem;
  845. else
  846. $p_temp_list[0] = $p_hitem;
  847. $v_result = $this->_addList($p_temp_list,
  848. $p_add_dir,
  849. $p_remove_dir);
  850. }
  851. }
  852. unset($p_temp_list);
  853. unset($p_hdir);
  854. unset($p_hitem);
  855. }
  856. }
  857. return $v_result;
  858. }
  859. // }}}
  860. // {{{ _addFile()
  861. function _addFile($p_filename, &$p_header, $p_add_dir, $p_remove_dir)
  862. {
  863. if (!$this->_file) {
  864. $this->_error('Invalid file descriptor');
  865. return false;
  866. }
  867. if ($p_filename == '') {
  868. $this->_error('Invalid file name');
  869. return false;
  870. }
  871. // ----- Calculate the stored filename
  872. $p_filename = $this->_translateWinPath($p_filename, false);;
  873. $v_stored_filename = $p_filename;
  874. if (strcmp($p_filename, $p_remove_dir) == 0) {
  875. return true;
  876. }
  877. if ($p_remove_dir != '') {
  878. if (substr($p_remove_dir, -1) != '/')
  879. $p_remove_dir .= '/';
  880. if (substr($p_filename, 0, strlen($p_remove_dir)) == $p_remove_dir)
  881. $v_stored_filename = substr($p_filename, strlen($p_remove_dir));
  882. }
  883. $v_stored_filename = $this->_translateWinPath($v_stored_filename);
  884. if ($p_add_dir != '') {
  885. if (substr($p_add_dir, -1) == '/')
  886. $v_stored_filename = $p_add_dir.$v_stored_filename;
  887. else
  888. $v_stored_filename = $p_add_dir.'/'.$v_stored_filename;
  889. }
  890. $v_stored_filename = $this->_pathReduction($v_stored_filename);
  891. if ($this->_isArchive($p_filename)) {
  892. if (($v_file = @fopen($p_filename, "rb")) == 0) {
  893. $this->_warning("Unable to open file '".$p_filename
  894. ."' in binary read mode");
  895. return true;
  896. }
  897. if (!$this->_writeHeader($p_filename, $v_stored_filename))
  898. return false;
  899. while (($v_buffer = fread($v_file, 512)) != '') {
  900. $v_binary_data = pack("a512", "$v_buffer");
  901. $this->_writeBlock($v_binary_data);
  902. }
  903. fclose($v_file);
  904. } else {
  905. // ----- Only header for dir
  906. if (!$this->_writeHeader($p_filename, $v_stored_filename))
  907. return false;
  908. }
  909. return true;
  910. }
  911. // }}}
  912. // {{{ _addString()
  913. function _addString($p_filename, $p_string)
  914. {
  915. if (!$this->_file) {
  916. $this->_error('Invalid file descriptor');
  917. return false;
  918. }
  919. if ($p_filename == '') {
  920. $this->_error('Invalid file name');
  921. return false;
  922. }
  923. // ----- Calculate the stored filename
  924. $p_filename = $this->_translateWinPath($p_filename, false);;
  925. if (!$this->_writeHeaderBlock($p_filename, strlen($p_string),
  926. time(), 384, "", 0, 0))
  927. return false;
  928. $i=0;
  929. while (($v_buffer = substr($p_string, (($i++)*512), 512)) != '') {
  930. $v_binary_data = pack("a512", $v_buffer);
  931. $this->_writeBlock($v_binary_data);
  932. }
  933. return true;
  934. }
  935. // }}}
  936. // {{{ _writeHeader()
  937. function _writeHeader($p_filename, $p_stored_filename)
  938. {
  939. if ($p_stored_filename == '')
  940. $p_stored_filename = $p_filename;
  941. $v_reduce_filename = $this->_pathReduction($p_stored_filename);
  942. if (strlen($v_reduce_filename) > 99) {
  943. if (!$this->_writeLongHeader($v_reduce_filename))
  944. return false;
  945. }
  946. $v_info = lstat($p_filename);
  947. $v_uid = sprintf("%6s ", DecOct($v_info[4]));
  948. $v_gid = sprintf("%6s ", DecOct($v_info[5]));
  949. $v_perms = sprintf("%6s ", DecOct($v_info['mode']));
  950. $v_mtime = sprintf("%11s", DecOct($v_info['mode']));
  951. $v_linkname = '';
  952. if (@is_link($p_filename)) {
  953. $v_typeflag = '2';
  954. $v_linkname = readlink($p_filename);
  955. $v_size = sprintf("%11s ", DecOct(0));
  956. } elseif (@is_dir($p_filename)) {
  957. $v_typeflag = "5";
  958. $v_size = sprintf("%11s ", DecOct(0));
  959. } else {
  960. $v_typeflag = '';
  961. clearstatcache();
  962. $v_size = sprintf("%11s ", DecOct($v_info['size']));
  963. }
  964. $v_magic = '';
  965. $v_version = '';
  966. $v_uname = '';
  967. $v_gname = '';
  968. $v_devmajor = '';
  969. $v_devminor = '';
  970. $v_prefix = '';
  971. $v_binary_data_first = pack("a100a8a8a8a12A12",
  972. $v_reduce_filename, $v_perms, $v_uid,
  973. $v_gid, $v_size, $v_mtime);
  974. $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12",
  975. $v_typeflag, $v_linkname, $v_magic,
  976. $v_version, $v_uname, $v_gname,
  977. $v_devmajor, $v_devminor, $v_prefix, '');
  978. // ----- Calculate the checksum
  979. $v_checksum = 0;
  980. // ..... First part of the header
  981. for ($i=0; $i<148; $i++)
  982. $v_checksum += ord(substr($v_binary_data_first,$i,1));
  983. // ..... Ignore the checksum value and replace it by ' ' (space)
  984. for ($i=148; $i<156; $i++)
  985. $v_checksum += ord(' ');
  986. // ..... Last part of the header
  987. for ($i=156, $j=0; $i<512; $i++, $j++)
  988. $v_checksum += ord(substr($v_binary_data_last,$j,1));
  989. // ----- Write the first 148 bytes of the header in the archive
  990. $this->_writeBlock($v_binary_data_first, 148);
  991. // ----- Write the calculated checksum
  992. $v_checksum = sprintf("%6s ", DecOct($v_checksum));
  993. $v_binary_data = pack("a8", $v_checksum);
  994. $this->_writeBlock($v_binary_data, 8);
  995. // ----- Write the last 356 bytes of the header in the archive
  996. $this->_writeBlock($v_binary_data_last, 356);
  997. return true;
  998. }
  999. // }}}
  1000. // {{{ _writeHeaderBlock()
  1001. function _writeHeaderBlock($p_filename, $p_size, $p_mtime=0, $p_perms=0,
  1002. $p_type='', $p_uid=0, $p_gid=0)
  1003. {
  1004. $p_filename = $this->_pathReduction($p_filename);
  1005. if (strlen($p_filename) > 99) {
  1006. if (!$this->_writeLongHeader($p_filename))
  1007. return false;
  1008. }
  1009. if ($p_type == "5") {
  1010. $v_size = sprintf("%11s ", DecOct(0));
  1011. } else {
  1012. $v_size = sprintf("%11s ", DecOct($p_size));
  1013. }
  1014. $v_uid = sprintf("%6s ", DecOct($p_uid));
  1015. $v_gid = sprintf("%6s ", DecOct($p_gid));
  1016. $v_perms = sprintf("%6s ", DecOct($p_perms));
  1017. $v_mtime = sprintf("%11s", DecOct($p_mtime));
  1018. $v_linkname = '';
  1019. $v_magic = '';
  1020. $v_version = '';
  1021. $v_uname = '';
  1022. $v_gname = '';
  1023. $v_devmajor = '';
  1024. $v_devminor = '';
  1025. $v_prefix = '';
  1026. $v_binary_data_first = pack("a100a8a8a8a12A12",
  1027. $p_filename, $v_perms, $v_uid, $v_gid,
  1028. $v_size, $v_mtime);
  1029. $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12",
  1030. $p_type, $v_linkname, $v_magic,
  1031. $v_version, $v_uname, $v_gname,
  1032. $v_devmajor, $v_devminor, $v_prefix, '');
  1033. // ----- Calculate the checksum
  1034. $v_checksum = 0;
  1035. // ..... First part of the header
  1036. for ($i=0; $i<148; $i++)
  1037. $v_checksum += ord(substr($v_binary_data_first,$i,1));
  1038. // ..... Ignore the checksum value and replace it by ' ' (space)
  1039. for ($i=148; $i<156; $i++)
  1040. $v_checksum += ord(' ');
  1041. // ..... Last part of the header
  1042. for ($i=156, $j=0; $i<512; $i++, $j++)
  1043. $v_checksum += ord(substr($v_binary_data_last,$j,1));
  1044. // ----- Write the first 148 bytes of the header in the archive
  1045. $this->_writeBlock($v_binary_data_first, 148);
  1046. // ----- Write the calculated checksum
  1047. $v_checksum = sprintf("%6s ", DecOct($v_checksum));
  1048. $v_binary_data = pack("a8", $v_checksum);
  1049. $this->_writeBlock($v_binary_data, 8);
  1050. // ----- Write the last 356 bytes of the header in the archive
  1051. $this->_writeBlock($v_binary_data_last, 356);
  1052. return true;
  1053. }
  1054. // }}}
  1055. // {{{ _writeLongHeader()
  1056. function _writeLongHeader($p_filename)
  1057. {
  1058. $v_size = sprintf("%11s ", DecOct(strlen($p_filename)));
  1059. $v_typeflag = 'L';
  1060. $v_linkname = '';
  1061. $v_magic = '';
  1062. $v_version = '';
  1063. $v_uname = '';
  1064. $v_gname = '';
  1065. $v_devmajor = '';
  1066. $v_devminor = '';
  1067. $v_prefix = '';
  1068. $v_binary_data_first = pack("a100a8a8a8a12A12",
  1069. '././@LongLink', 0, 0, 0, $v_size, 0);
  1070. $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12",
  1071. $v_typeflag, $v_linkname, $v_magic,
  1072. $v_version, $v_uname, $v_gname,
  1073. $v_devmajor, $v_devminor, $v_prefix, '');
  1074. // ----- Calculate the checksum
  1075. $v_checksum = 0;
  1076. // ..... First part of the header
  1077. for ($i=0; $i<148; $i++)
  1078. $v_checksum += ord(substr($v_binary_data_first,$i,1));
  1079. // ..... Ignore the checksum value and replace it by ' ' (space)
  1080. for ($i=148; $i<156; $i++)
  1081. $v_checksum += ord(' ');
  1082. // ..... Last part of the header
  1083. for ($i=156, $j=0; $i<512; $i++, $j++)
  1084. $v_checksum += ord(substr($v_binary_data_last,$j,1));
  1085. // ----- Write the first 148 bytes of the header in the archive
  1086. $this->_writeBlock($v_binary_data_first, 148);
  1087. // ----- Write the calculated checksum
  1088. $v_checksum = sprintf("%6s ", DecOct($v_checksum));
  1089. $v_binary_data = pack("a8", $v_checksum);
  1090. $this->_writeBlock($v_binary_data, 8);
  1091. // ----- Write the last 356 bytes of the header in the archive
  1092. $this->_writeBlock($v_binary_data_last, 356);
  1093. // ----- Write the filename as content of the block
  1094. $i=0;
  1095. while (($v_buffer = substr($p_filename, (($i++)*512), 512)) != '') {
  1096. $v_binary_data = pack("a512", "$v_buffer");
  1097. $this->_writeBlock($v_binary_data);
  1098. }
  1099. return true;
  1100. }
  1101. // }}}
  1102. // {{{ _readHeader()
  1103. function _readHeader($v_binary_data, &$v_header)
  1104. {
  1105. if (strlen($v_binary_data)==0) {
  1106. $v_header['filename'] = '';
  1107. return true;
  1108. }
  1109. if (strlen($v_binary_data) != 512) {
  1110. $v_header['filename'] = '';
  1111. $this->_error('Invalid block size : '.strlen($v_binary_data));
  1112. return false;
  1113. }
  1114. if (!is_array($v_header)) {
  1115. $v_header = array();
  1116. }
  1117. // ----- Calculate the checksum
  1118. $v_checksum = 0;
  1119. // ..... First part of the header
  1120. for ($i=0; $i<148; $i++)
  1121. $v_checksum+=ord(substr($v_binary_data,$i,1));
  1122. // ..... Ignore the checksum value and replace it by ' ' (space)
  1123. for ($i=148; $i<156; $i++)
  1124. $v_checksum += ord(' ');
  1125. // ..... Last part of the header
  1126. for ($i=156; $i<512; $i++)
  1127. $v_checksum+=ord(substr($v_binary_data,$i,1));
  1128. $v_data = unpack("a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/"
  1129. ."a8checksum/a1typeflag/a100link/a6magic/a2version/"
  1130. ."a32uname/a32gname/a8devmajor/a8devminor",
  1131. $v_binary_data);
  1132. // ----- Extract the checksum
  1133. $v_header['checksum'] = OctDec(trim($v_data['checksum']));
  1134. if ($v_header['checksum'] != $v_checksum) {
  1135. $v_header['filename'] = '';
  1136. // ----- Look for last block (empty block)
  1137. if (($v_checksum == 256) && ($v_header['checksum'] == 0))
  1138. return true;
  1139. $this->_error('Invalid checksum for file "'.$v_data['filename']
  1140. .'" : '.$v_checksum.' calculated, '
  1141. .$v_header['checksum'].' expected');
  1142. return false;
  1143. }
  1144. // ----- Extract the properties
  1145. $v_header['filename'] = trim($v_data['filename']);
  1146. if ($this->_maliciousFilename($v_header['filename'])) {
  1147. $this->_error('Malicious .tar detected, file "' . $v_header['filename'] .
  1148. '" will not install in desired directory tree');
  1149. return false;
  1150. }
  1151. $v_header['mode'] = OctDec(trim($v_data['mode']));
  1152. $v_header['uid'] = OctDec(trim($v_data['uid']));
  1153. $v_header['gid'] = OctDec(trim($v_data['gid']));
  1154. $v_header['size'] = OctDec(trim($v_data['size']));
  1155. $v_header['mtime'] = OctDec(trim($v_data['mtime']));
  1156. if (($v_header['typeflag'] = $v_data['typeflag']) == "5") {
  1157. $v_header['size'] = 0;
  1158. }
  1159. $v_header['link'] = trim($v_data['link']);
  1160. /* ----- All these fields are removed form the header because
  1161. they do not carry interesting info
  1162. $v_header[magic] = trim($v_data[magic]);
  1163. $v_header[version] = trim($v_data[version]);
  1164. $v_header[uname] = trim($v_data[uname]);
  1165. $v_header[gname] = trim($v_data[gname]);
  1166. $v_header[devmajor] = trim($v_data[devmajor]);
  1167. $v_header[devminor] = trim($v_data[devminor]);
  1168. */
  1169. return true;
  1170. }
  1171. // }}}
  1172. // {{{ _maliciousFilename()
  1173. /**
  1174. * Detect and report a malicious file name
  1175. *
  1176. * @param string $file
  1177. * @return bool
  1178. * @access private
  1179. */
  1180. function _maliciousFilename($file)
  1181. {
  1182. if (strpos($file, '/../') !== false) {
  1183. return true;
  1184. }
  1185. if (strpos($file, '../') === 0) {
  1186. return true;
  1187. }
  1188. return false;
  1189. }
  1190. // }}}
  1191. // {{{ _readLongHeader()
  1192. function _readLongHeader(&$v_header)
  1193. {
  1194. $v_filename = '';
  1195. $n = floor($v_header['size']/512);
  1196. for ($i=0; $i<$n; $i++) {
  1197. $v_content = $this->_readBlock();
  1198. $v_filename .= $v_content;
  1199. }
  1200. if (($v_header['size'] % 512) != 0) {
  1201. $v_content = $this->_readBlock();
  1202. $v_filename .= $v_content;
  1203. }
  1204. // ----- Read the next header
  1205. $v_binary_data = $this->_readBlock();
  1206. if (!$this->_readHeader($v_binary_data, $v_header))
  1207. return false;
  1208. $v_filename = trim($v_filename);
  1209. $v_header['filename'] = $v_filename;
  1210. if ($this->_maliciousFilename($v_filename)) {
  1211. $this->_error('Malicious .tar detected, file "' . $v_filename .
  1212. '" will not install in desired directory tree');
  1213. return false;
  1214. }
  1215. return true;
  1216. }
  1217. // }}}
  1218. // {{{ _extractInString()
  1219. /**
  1220. * This method extract from the archive one file identified by $p_filename.
  1221. * The return value is a string with the file content, or NULL on error.
  1222. * @param string $p_filename The path of the file to extract in a string.
  1223. * @return a string with the file content or NULL.
  1224. * @access private
  1225. */
  1226. function _extractInString($p_filename)
  1227. {
  1228. $v_result_str = "";
  1229. While (strlen($v_binary_data = $this->_readBlock()) != 0)
  1230. {
  1231. if (!$this->_readHeader($v_binary_data, $v_header))
  1232. return NULL;
  1233. if ($v_header['filename'] == '')
  1234. continue;
  1235. // ----- Look for long filename
  1236. if ($v_header['typeflag'] == 'L') {
  1237. if (!$this->_readLongHeader($v_header))
  1238. return NULL;
  1239. }
  1240. if ($v_header['filename'] == $p_filename) {
  1241. if ($v_header['typeflag'] == "5") {
  1242. $this->_error('Unable to extract in string a directory '
  1243. .'entry {'.$v_header['filename'].'}');
  1244. return NULL;
  1245. } else {
  1246. $n = floor($v_header['size']/512);
  1247. for ($i=0; $i<$n; $i++) {
  1248. $v_result_str .= $this->_readBlock();
  1249. }
  1250. if (($v_header['size'] % 512) != 0) {
  1251. $v_content = $this->_readBlock();
  1252. $v_result_str .= substr($v_content, 0,
  1253. ($v_header['size'] % 512));
  1254. }
  1255. return $v_result_str;
  1256. }
  1257. } else {
  1258. $this->_jumpBlock(ceil(($v_header['size']/512)));
  1259. }
  1260. }
  1261. return NULL;
  1262. }
  1263. // }}}
  1264. // {{{ _extractList()
  1265. function _extractList($p_path, &$p_list_detail, $p_mode,
  1266. $p_file_list, $p_remove_path)
  1267. {
  1268. $v_result=true;
  1269. $v_nb = 0;
  1270. $v_extract_all = true;
  1271. $v_listing = false;
  1272. $p_path = $this->_translateWinPath($p_path, false);
  1273. if ($p_path == '' || (substr($p_path, 0, 1) != '/'
  1274. && substr($p_path, 0, 3) != "../" && !strpos($p_path, ':'))) {
  1275. $p_path = "./".$p_path;
  1276. }
  1277. $p_remove_path = $this->_translateWinPath($p_remove_path);
  1278. // ----- Look for path to remove format (should end by /)
  1279. if (($p_remove_path != '') && (substr($p_remove_path, -1) != '/'))
  1280. $p_remove_path .= '/';
  1281. $p_remove_path_size = strlen($p_remove_path);
  1282. switch ($p_mode) {
  1283. case "complete" :
  1284. $v_extract_all = TRUE;
  1285. $v_listing = FALSE;
  1286. break;
  1287. case "partial" :
  1288. $v_extract_all = FALSE;
  1289. $v_listing = FALSE;
  1290. break;
  1291. case "list" :
  1292. $v_extract_all = FALSE;
  1293. $v_listing = TRUE;
  1294. break;
  1295. default :
  1296. $this->_error('Invalid extract mode ('.$p_mode.')');
  1297. return false;
  1298. }
  1299. clearstatcache();
  1300. while (strlen($v_binary_data = $this->_readBlock()) != 0)
  1301. {
  1302. $v_extract_file = FALSE;
  1303. $v_extraction_stopped = 0;
  1304. if (!$this->_readHeader($v_binary_data, $v_header))
  1305. return false;
  1306. if ($v_header['filename'] == '') {
  1307. continue;
  1308. }
  1309. // ----- Look for long filename
  1310. if ($v_header['typeflag'] == 'L') {
  1311. if (!$this->_readLongHeader($v_header))
  1312. return false;
  1313. }
  1314. if ((!$v_extract_all) && (is_array($p_file_list))) {
  1315. // ----- By default no unzip if the file is not found
  1316. $v_extract_file = false;
  1317. for ($i=0; $i<sizeof($p_file_list); $i++) {
  1318. // ----- Look if it is a directory
  1319. if (substr($p_file_list[$i], -1) == '/') {
  1320. // ----- Look if the directory is in the filename path
  1321. if ((strlen($v_header['filename']) > strlen($p_file_list[$i]))
  1322. && (substr($v_header['filename'], 0, strlen($p_file_list[$i]))
  1323. == $p_file_list[$i])) {
  1324. $v_extract_file = TRUE;
  1325. break;
  1326. }
  1327. }
  1328. // ----- It is a file, so compare the file names
  1329. elseif ($p_file_list[$i] == $v_header['filename']) {
  1330. $v_extract_file = TRUE;
  1331. break;
  1332. }
  1333. }
  1334. } else {
  1335. $v_extract_file = TRUE;
  1336. }
  1337. // ----- Look if this file need to be extracted
  1338. if (($v_extract_file) && (!$v_listing))
  1339. {
  1340. if (($p_remove_path != '')
  1341. && (substr($v_header['filename'], 0, $p_remove_path_size)
  1342. == $p_remove_path))
  1343. $v_header['filename'] = substr($v_header['filename'],
  1344. $p_remove_path_size);
  1345. if (($p_path != './') && ($p_path != '/')) {
  1346. while (substr($p_path, -1) == '/')
  1347. $p_path = substr($p_path, 0, strlen($p_path)-1);
  1348. if (substr($v_header['filename'], 0, 1) == '/')
  1349. $v_header['filename'] = $p_path.$v_header['filename'];
  1350. else
  1351. $v_header['filename'] = $p_path.'/'.$v_header['filename'];
  1352. }
  1353. if (file_exists($v_header['filename'])) {
  1354. if ( (@is_dir($v_header['filename']))
  1355. && ($v_header['typeflag'] == '')) {
  1356. $this->_error('File '.$v_header['filename']
  1357. .' already exists as a directory');
  1358. return false;
  1359. }
  1360. if ( ($this->_isArchive($v_header['filename']))
  1361. && ($v_header['typeflag'] == "5")) {
  1362. $this->_error('Directory '.$v_header['filename']
  1363. .' already exists as a file');
  1364. return false;
  1365. }
  1366. if (!is_writeable($v_header['filename'])) {
  1367. $this->_error('File '.$v_header['filename']
  1368. .' already exists and is write protected');
  1369. return false;
  1370. }
  1371. if (filemtime($v_header['filename']) > $v_header['mtime']) {
  1372. // To be completed : An error or silent no replace ?
  1373. }
  1374. }
  1375. // ----- Check the directory availability and create it if necessary
  1376. elseif (($v_result
  1377. = $this->_dirCheck(($v_header['typeflag'] == "5"
  1378. ?$v_header['filename']
  1379. :dirname($v_header['filename'])))) != 1) {
  1380. $this->_error('Unable to create path for '.$v_header['filename']);
  1381. return false;
  1382. }
  1383. if ($v_extract_file) {
  1384. if ($v_header['typeflag'] == "5") {
  1385. if (!@file_exists($v_header['filename'])) {
  1386. // Drupal integration.
  1387. // Changed the code to use drupal_mkdir() instead of mkdir().
  1388. if (!@drupal_mkdir($v_header['filename'], 0777)) {
  1389. $this->_error('Unable to create directory {'
  1390. .$v_header['filename'].'}');
  1391. return false;
  1392. }
  1393. }
  1394. } elseif ($v_header['typeflag'] == "2") {
  1395. if (@file_exists($v_header['filename'])) {
  1396. @drupal_unlink($v_header['filename']);
  1397. }
  1398. if (!@symlink($v_header['link'], $v_header['filename'])) {
  1399. $this->_error('Unable to extract symbolic link {'
  1400. .$v_header['filename'].'}');
  1401. return false;
  1402. }
  1403. } else {
  1404. if (($v_dest_file = @fopen($v_header['filename'], "wb")) == 0) {
  1405. $this->_error('Error while opening {'.$v_header['filename']
  1406. .'} in write binary mode');
  1407. return false;
  1408. } else {
  1409. $n = floor($v_header['size']/512);
  1410. for ($i=0; $i<$n; $i++) {
  1411. $v_content = $this->_readBlock();
  1412. fwrite($v_dest_file, $v_content, 512);
  1413. }
  1414. if (($v_header['size'] % 512) != 0) {
  1415. $v_content = $this->_readBlock();
  1416. fwrite($v_dest_file, $v_content, ($v_header['size'] % 512));
  1417. }
  1418. @fclose($v_dest_file);
  1419. // ----- Change the file mode, mtime
  1420. @touch($v_header['filename'], $v_header['mtime']);
  1421. if ($v_header['mode'] & 0111) {
  1422. // make file executable, obey umask
  1423. $mode = fileperms($v_header['filename']) | (~umask() & 0111);
  1424. @chmod($v_header['filename'], $mode);
  1425. }
  1426. }
  1427. // ----- Check the file size
  1428. clearstatcache();
  1429. if (filesize($v_header['filename']) != $v_header['size']) {
  1430. $this->_error('Extracted file '.$v_header['filename']
  1431. .' does not have the correct file size \''
  1432. .filesize($v_header['filename'])
  1433. .'\' ('.$v_header['size']
  1434. .' expected). Archive may be corrupted.');
  1435. return false;
  1436. }
  1437. }
  1438. } else {
  1439. $this->_jumpBlock(ceil(($v_header['size']/512)));
  1440. }
  1441. } else {
  1442. $this->_jumpBlock(ceil(($v_header['size']/512)));
  1443. }
  1444. /* TBC : Seems to be unused ...
  1445. if ($this->_compress)
  1446. $v_end_of_file = @gzeof($this->_file);
  1447. else
  1448. $v_end_of_file = @feof($this->_file);
  1449. */
  1450. if ($v_listing || $v_extract_file || $v_extraction_stopped) {
  1451. // ----- Log extracted files
  1452. if (($v_file_dir = dirname($v_header['filename']))
  1453. == $v_header['filename'])
  1454. $v_file_dir = '';
  1455. if ((substr($v_header['filename'], 0, 1) == '/') && ($v_file_dir == ''))
  1456. $v_file_dir = '/';
  1457. $p_list_detail[$v_nb++] = $v_header;
  1458. if (is_array($p_file_list) && (count($p_list_detail) == count($p_file_list))) {
  1459. return true;
  1460. }
  1461. }
  1462. }
  1463. return true;
  1464. }
  1465. // }}}
  1466. // {{{ _openAppend()
  1467. function _openAppend()
  1468. {
  1469. if (filesize($this->_tarname) == 0)
  1470. return $this->_openWrite();
  1471. if ($this->_compress) {
  1472. $this->_close();
  1473. if (!@rename($this->_tarname, $this->_tarname.".tmp")) {
  1474. $this->_error('Error while renaming \''.$this->_tarname
  1475. .'\' to temporary file \''.$this->_tarname
  1476. .'.tmp\'');
  1477. return false;
  1478. }
  1479. if ($this->_compress_type == 'gz')
  1480. $v_temp_tar = @gzopen($this->_tarname.".tmp", "rb");
  1481. elseif ($this->_compress_type == 'bz2')
  1482. $v_temp_tar = @bzopen($this->_tarname.".tmp", "r");
  1483. if ($v_temp_tar == 0) {
  1484. $this->_error('Unable to open file \''.$this->_tarname
  1485. .'.tmp\' in binary read mode');
  1486. @rename($this->_tarname.".tmp", $this->_tarname);
  1487. return false;
  1488. }
  1489. if (!$this->_openWrite()) {
  1490. @rename($this->_tarname.".tmp", $this->_tarname);
  1491. return false;
  1492. }
  1493. if ($this->_compress_type == 'gz') {
  1494. while (!@gzeof($v_temp_tar)) {
  1495. $v_buffer = @gzread($v_temp_tar, 512);
  1496. if ($v_buffer == ARCHIVE_TAR_END_BLOCK) {
  1497. // do not copy end blocks, we will re-make them
  1498. // after appending
  1499. continue;
  1500. }
  1501. $v_binary_data = pack("a512", $v_buffer);
  1502. $this->_writeBlock($v_binary_data);
  1503. }
  1504. @gzclose($v_temp_tar);
  1505. }
  1506. elseif ($this->_compress_type == 'bz2') {
  1507. while (strlen($v_buffer = @bzread($v_temp_tar, 512)) > 0) {
  1508. if ($v_buffer == ARCHIVE_TAR_END_BLOCK) {
  1509. continue;
  1510. }
  1511. $v_binary_data = pack("a512", $v_buffer);
  1512. $this->_writeBlock($v_binary_data);
  1513. }
  1514. @bzclose($v_temp_tar);
  1515. }
  1516. if (!@drupal_unlink($this->_tarname.".tmp")) {
  1517. $this->_error('Error while deleting temporary file \''
  1518. .$this->_tarname.'.tmp\'');
  1519. }
  1520. } else {
  1521. // ----- For not compressed tar, just add files before the last
  1522. // one or two 512 bytes block
  1523. if (!$this->_openReadWrite())
  1524. return false;
  1525. clearstatcache();
  1526. $v_size = filesize($this->_tarname);
  1527. // We might have zero, one or two end blocks.
  1528. // The standard is two, but we should try to handle
  1529. // other cases.
  1530. fseek($this->_file, $v_size - 1024);
  1531. if (fread($this->_file, 512) == ARCHIVE_TAR_END_BLOCK) {
  1532. fseek($this->_file, $v_size - 1024);
  1533. }
  1534. elseif (fread($this->_file, 512) == ARCHIVE_TAR_END_BLOCK) {
  1535. fseek($this->_file, $v_size - 512);
  1536. }
  1537. }
  1538. return true;
  1539. }
  1540. // }}}
  1541. // {{{ _append()
  1542. function _append($p_filelist, $p_add_dir='', $p_remove_dir='')
  1543. {
  1544. if (!$this->_openAppend())
  1545. return false;
  1546. if ($this->_addList($p_filelist, $p_add_dir, $p_remove_dir))
  1547. $this->_writeFooter();
  1548. $this->_close();
  1549. return true;
  1550. }
  1551. // }}}
  1552. // {{{ _dirCheck()
  1553. /**
  1554. * Check if a directory exists and create it (including parent
  1555. * dirs) if not.
  1556. *
  1557. * @param string $p_dir directory to check
  1558. *
  1559. * @return bool TRUE if the directory exists or was created
  1560. */
  1561. function _dirCheck($p_dir)
  1562. {
  1563. clearstatcache();
  1564. if ((@is_dir($p_dir)) || ($p_dir == ''))
  1565. return true;
  1566. $p_parent_dir = dirname($p_dir);
  1567. if (($p_parent_dir != $p_dir) &&
  1568. ($p_parent_dir != '') &&
  1569. (!$this->_dirCheck($p_parent_dir)))
  1570. return false;
  1571. // Drupal integration.
  1572. // Changed the code to use drupal_mkdir() instead of mkdir().
  1573. if (!@drupal_mkdir($p_dir, 0777)) {
  1574. $this->_error("Unable to create directory '$p_dir'");
  1575. return false;
  1576. }
  1577. return true;
  1578. }
  1579. // }}}
  1580. // {{{ _pathReduction()
  1581. /**
  1582. * Compress path by changing for example "/dir/foo/../bar" to "/dir/bar",
  1583. * rand emove double slashes.
  1584. *
  1585. * @param string $p_dir path to reduce
  1586. *
  1587. * @return string reduced path
  1588. *
  1589. * @access private
  1590. *
  1591. */
  1592. function _pathReduction($p_dir)
  1593. {
  1594. $v_result = '';
  1595. // ----- Look for not empty path
  1596. if ($p_dir != '') {
  1597. // ----- Explode path by directory names
  1598. $v_list = explode('/', $p_dir);
  1599. // ----- Study directories from last to first
  1600. for ($i=sizeof($v_list)-1; $i>=0; $i--) {
  1601. // ----- Look for current path
  1602. if ($v_list[$i] == ".") {
  1603. // ----- Ignore this directory
  1604. // Should be the first $i=0, but no check is done
  1605. }
  1606. else if ($v_list[$i] == "..") {
  1607. // ----- Ignore it and ignore the $i-1
  1608. $i--;
  1609. }
  1610. else if ( ($v_list[$i] == '')
  1611. && ($i!=(sizeof($v_list)-1))
  1612. && ($i!=0)) {
  1613. // ----- Ignore only the double '//' in path,
  1614. // but not the first and last /
  1615. } else {
  1616. $v_result = $v_list[$i].($i!=(sizeof($v_list)-1)?'/'
  1617. .$v_result:'');
  1618. }
  1619. }
  1620. }
  1621. $v_result = strtr($v_result, '\\', '/');
  1622. return $v_result;
  1623. }
  1624. // }}}
  1625. // {{{ _translateWinPath()
  1626. function _translateWinPath($p_path, $p_remove_disk_letter=true)
  1627. {
  1628. if (defined('OS_WINDOWS') && OS_WINDOWS) {
  1629. // ----- Look for potential disk letter
  1630. if ( ($p_remove_disk_letter)
  1631. && (($v_position = strpos($p_path, ':')) != false)) {
  1632. $p_path = substr($p_path, $v_position+1);
  1633. }
  1634. // ----- Change potential windows directory separator
  1635. if ((strpos($p_path, '\\') > 0) || (substr($p_path, 0,1) == '\\')) {
  1636. $p_path = strtr($p_path, '\\', '/');
  1637. }
  1638. }
  1639. return $p_path;
  1640. }
  1641. // }}}
  1642. }