[ Index ]

PHP Cross Reference of Joomla 1.5.26 DE

title

Body

[close]

/libraries/pear/archive_tar/ -> Archive_Tar.php (source)

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


Generated: Wed Mar 28 15:54:07 2012 Cross-referenced by PHPXref 0.7.1