cms/drupal/modules/system/system.tar.inc
changeset 541 e756a8c72c3d
equal deleted inserted replaced
540:07239de796bb 541:e756a8c72c3d
       
     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  * @category  File_Formats
       
    34  * @package   Archive_Tar
       
    35  * @author    Vincent Blavet <vincent@phpconcept.net>
       
    36  * @copyright 1997-2010 The Authors
       
    37  * @license   http://www.opensource.org/licenses/bsd-license.php New BSD License
       
    38  * @version   CVS: $Id$
       
    39  * @link      http://pear.php.net/package/Archive_Tar
       
    40  */
       
    41 
       
    42  /**
       
    43  * Note on Drupal 8 porting.
       
    44  * This file origin is Tar.php, release 1.4.0 (stable) with some code
       
    45  * from PEAR.php, release 1.9.5 (stable) both at http://pear.php.net.
       
    46  * To simplify future porting from pear of this file, you should not
       
    47  * do cosmetic or other non significant changes to this file.
       
    48  * The following changes have been done:
       
    49  *  Added namespace Drupal\Core\Archiver.
       
    50  *  Removed require_once 'PEAR.php'.
       
    51  *  Added defintion of OS_WINDOWS taken from PEAR.php.
       
    52  *  Renamed class to ArchiveTar.
       
    53  *  Removed extends PEAR from class.
       
    54  *  Removed call parent:: __construct().
       
    55  *  Changed PEAR::loadExtension($extname) to this->loadExtension($extname).
       
    56  *  Added function loadExtension() taken from PEAR.php.
       
    57  *  Changed all calls of unlink() to drupal_unlink().
       
    58  *  Changed $this->error_object = &$this->raiseError($p_message)
       
    59  *  to throw new \Exception($p_message).
       
    60  */
       
    61 
       
    62  /**
       
    63  * Note on Drupal 7 backporting from Drupal 8.
       
    64  * File origin is core/lib/Drupal/Core/Archiver/ArchiveTar.php from Drupal 8.
       
    65  * The following changes have been done:
       
    66  *  Removed namespace Drupal\Core\Archiver.
       
    67  *  Renamed class to Archive_Tar.
       
    68  *  Changed \Exception to Exception.
       
    69  */
       
    70 
       
    71 
       
    72 // Drupal removal require_once 'PEAR.php'.
       
    73 
       
    74 // Drupal addition OS_WINDOWS as defined in PEAR.php.
       
    75 if (substr(PHP_OS, 0, 3) == 'WIN') {
       
    76     define('OS_WINDOWS', true);
       
    77 } else {
       
    78     define('OS_WINDOWS', false);
       
    79 }
       
    80 
       
    81 define('ARCHIVE_TAR_ATT_SEPARATOR', 90001);
       
    82 define('ARCHIVE_TAR_END_BLOCK', pack("a512", ''));
       
    83 
       
    84 if (!function_exists('gzopen') && function_exists('gzopen64')) {
       
    85     function gzopen($filename, $mode, $use_include_path = 0)
       
    86     {
       
    87         return gzopen64($filename, $mode, $use_include_path);
       
    88     }
       
    89 }
       
    90 
       
    91 if (!function_exists('gztell') && function_exists('gztell64')) {
       
    92     function gztell($zp)
       
    93     {
       
    94         return gztell64($zp);
       
    95     }
       
    96 }
       
    97 
       
    98 if (!function_exists('gzseek') && function_exists('gzseek64')) {
       
    99     function gzseek($zp, $offset, $whence = SEEK_SET)
       
   100     {
       
   101         return gzseek64($zp, $offset, $whence);
       
   102     }
       
   103 }
       
   104 
       
   105 /**
       
   106  * Creates a (compressed) Tar archive
       
   107  *
       
   108  * @package Archive_Tar
       
   109  * @author  Vincent Blavet <vincent@phpconcept.net>
       
   110  * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
       
   111  * @version $Revision$
       
   112  */
       
   113 // Drupal change class Archive_Tar extends PEAR.
       
   114 class Archive_Tar
       
   115 {
       
   116     /**
       
   117      * @var string Name of the Tar
       
   118      */
       
   119     public $_tarname = '';
       
   120 
       
   121     /**
       
   122      * @var boolean if true, the Tar file will be gzipped
       
   123      */
       
   124     public $_compress = false;
       
   125 
       
   126     /**
       
   127      * @var string Type of compression : 'none', 'gz', 'bz2' or 'lzma2'
       
   128      */
       
   129     public $_compress_type = 'none';
       
   130 
       
   131     /**
       
   132      * @var string Explode separator
       
   133      */
       
   134     public $_separator = ' ';
       
   135 
       
   136     /**
       
   137      * @var file descriptor
       
   138      */
       
   139     public $_file = 0;
       
   140 
       
   141     /**
       
   142      * @var string Local Tar name of a remote Tar (http:// or ftp://)
       
   143      */
       
   144     public $_temp_tarname = '';
       
   145 
       
   146     /**
       
   147      * @var string regular expression for ignoring files or directories
       
   148      */
       
   149     public $_ignore_regexp = '';
       
   150 
       
   151     /**
       
   152      * @var object PEAR_Error object
       
   153      */
       
   154     public $error_object = null;
       
   155 
       
   156     /**
       
   157      * Archive_Tar Class constructor. This flavour of the constructor only
       
   158      * declare a new Archive_Tar object, identifying it by the name of the
       
   159      * tar file.
       
   160      * If the compress argument is set the tar will be read or created as a
       
   161      * gzip or bz2 compressed TAR file.
       
   162      *
       
   163      * @param string $p_tarname The name of the tar archive to create
       
   164      * @param string $p_compress can be null, 'gz', 'bz2' or 'lzma2'. This
       
   165      *               parameter indicates if gzip, bz2 or lzma2 compression
       
   166      *               is required.  For compatibility reason the
       
   167      *               boolean value 'true' means 'gz'.
       
   168      *
       
   169      * @return bool
       
   170      */
       
   171     public function __construct($p_tarname, $p_compress = null)
       
   172     {
       
   173         // Drupal removal parent::__construct().
       
   174 
       
   175         $this->_compress = false;
       
   176         $this->_compress_type = 'none';
       
   177         if (($p_compress === null) || ($p_compress == '')) {
       
   178             if (@file_exists($p_tarname)) {
       
   179                 if ($fp = @fopen($p_tarname, "rb")) {
       
   180                     // look for gzip magic cookie
       
   181                     $data = fread($fp, 2);
       
   182                     fclose($fp);
       
   183                     if ($data == "\37\213") {
       
   184                         $this->_compress = true;
       
   185                         $this->_compress_type = 'gz';
       
   186                         // No sure it's enought for a magic code ....
       
   187                     } elseif ($data == "BZ") {
       
   188                         $this->_compress = true;
       
   189                         $this->_compress_type = 'bz2';
       
   190                     } elseif (file_get_contents($p_tarname, false, null, 1, 4) == '7zXZ') {
       
   191                         $this->_compress = true;
       
   192                         $this->_compress_type = 'lzma2';
       
   193                     }
       
   194                 }
       
   195             } else {
       
   196                 // probably a remote file or some file accessible
       
   197                 // through a stream interface
       
   198                 if (substr($p_tarname, -2) == 'gz') {
       
   199                     $this->_compress = true;
       
   200                     $this->_compress_type = 'gz';
       
   201                 } elseif ((substr($p_tarname, -3) == 'bz2') ||
       
   202                     (substr($p_tarname, -2) == 'bz')
       
   203                 ) {
       
   204                     $this->_compress = true;
       
   205                     $this->_compress_type = 'bz2';
       
   206                 } else {
       
   207                     if (substr($p_tarname, -2) == 'xz') {
       
   208                         $this->_compress = true;
       
   209                         $this->_compress_type = 'lzma2';
       
   210                     }
       
   211                 }
       
   212             }
       
   213         } else {
       
   214             if (($p_compress === true) || ($p_compress == 'gz')) {
       
   215                 $this->_compress = true;
       
   216                 $this->_compress_type = 'gz';
       
   217             } else {
       
   218                 if ($p_compress == 'bz2') {
       
   219                     $this->_compress = true;
       
   220                     $this->_compress_type = 'bz2';
       
   221                 } else {
       
   222                     if ($p_compress == 'lzma2') {
       
   223                         $this->_compress = true;
       
   224                         $this->_compress_type = 'lzma2';
       
   225                     } else {
       
   226                         $this->_error(
       
   227                             "Unsupported compression type '$p_compress'\n" .
       
   228                             "Supported types are 'gz', 'bz2' and 'lzma2'.\n"
       
   229                         );
       
   230                         return false;
       
   231                     }
       
   232                 }
       
   233             }
       
   234         }
       
   235         $this->_tarname = $p_tarname;
       
   236         if ($this->_compress) { // assert zlib or bz2 or xz extension support
       
   237             if ($this->_compress_type == 'gz') {
       
   238                 $extname = 'zlib';
       
   239             } else {
       
   240                 if ($this->_compress_type == 'bz2') {
       
   241                     $extname = 'bz2';
       
   242                 } else {
       
   243                     if ($this->_compress_type == 'lzma2') {
       
   244                         $extname = 'xz';
       
   245                     }
       
   246                 }
       
   247             }
       
   248 
       
   249             if (!extension_loaded($extname)) {
       
   250                 // Drupal change PEAR::loadExtension($extname).
       
   251                 $this->loadExtension($extname);
       
   252             }
       
   253             if (!extension_loaded($extname)) {
       
   254                 $this->_error(
       
   255                     "The extension '$extname' couldn't be found.\n" .
       
   256                     "Please make sure your version of PHP was built " .
       
   257                     "with '$extname' support.\n"
       
   258                 );
       
   259                 return false;
       
   260             }
       
   261         }
       
   262     }
       
   263 
       
   264     public function __destruct()
       
   265     {
       
   266         $this->_close();
       
   267         // ----- Look for a local copy to delete
       
   268         if ($this->_temp_tarname != '') {
       
   269             @drupal_unlink($this->_temp_tarname);
       
   270         }
       
   271     }
       
   272 
       
   273     // Drupal addition from PEAR.php.
       
   274     /**
       
   275     * OS independent PHP extension load. Remember to take care
       
   276     * on the correct extension name for case sensitive OSes.
       
   277     *
       
   278     * @param string $ext The extension name
       
   279     * @return bool Success or not on the dl() call
       
   280     */
       
   281     function loadExtension($ext)
       
   282     {
       
   283         if (extension_loaded($ext)) {
       
   284             return true;
       
   285         }
       
   286 
       
   287         // if either returns true dl() will produce a FATAL error, stop that
       
   288         if (
       
   289             function_exists('dl') === false ||
       
   290             ini_get('enable_dl') != 1 ||
       
   291             ini_get('safe_mode') == 1
       
   292         ) {
       
   293             return false;
       
   294         }
       
   295 
       
   296         if (OS_WINDOWS) {
       
   297             $suffix = '.dll';
       
   298         } elseif (PHP_OS == 'HP-UX') {
       
   299             $suffix = '.sl';
       
   300         } elseif (PHP_OS == 'AIX') {
       
   301             $suffix = '.a';
       
   302         } elseif (PHP_OS == 'OSX') {
       
   303             $suffix = '.bundle';
       
   304         } else {
       
   305             $suffix = '.so';
       
   306         }
       
   307 
       
   308         return @dl('php_'.$ext.$suffix) || @dl($ext.$suffix);
       
   309     }
       
   310 
       
   311 
       
   312     /**
       
   313      * This method creates the archive file and add the files / directories
       
   314      * that are listed in $p_filelist.
       
   315      * If a file with the same name exist and is writable, it is replaced
       
   316      * by the new tar.
       
   317      * The method return false and a PEAR error text.
       
   318      * The $p_filelist parameter can be an array of string, each string
       
   319      * representing a filename or a directory name with their path if
       
   320      * needed. It can also be a single string with names separated by a
       
   321      * single blank.
       
   322      * For each directory added in the archive, the files and
       
   323      * sub-directories are also added.
       
   324      * See also createModify() method for more details.
       
   325      *
       
   326      * @param array $p_filelist An array of filenames and directory names, or a
       
   327      *              single string with names separated by a single
       
   328      *              blank space.
       
   329      *
       
   330      * @return true on success, false on error.
       
   331      * @see    createModify()
       
   332      */
       
   333     public function create($p_filelist)
       
   334     {
       
   335         return $this->createModify($p_filelist, '', '');
       
   336     }
       
   337 
       
   338     /**
       
   339      * This method add the files / directories that are listed in $p_filelist in
       
   340      * the archive. If the archive does not exist it is created.
       
   341      * The method return false and a PEAR error text.
       
   342      * The files and directories listed are only added at the end of the archive,
       
   343      * even if a file with the same name is already archived.
       
   344      * See also createModify() method for more details.
       
   345      *
       
   346      * @param array $p_filelist An array of filenames and directory names, or a
       
   347      *              single string with names separated by a single
       
   348      *              blank space.
       
   349      *
       
   350      * @return true on success, false on error.
       
   351      * @see    createModify()
       
   352      * @access public
       
   353      */
       
   354     public function add($p_filelist)
       
   355     {
       
   356         return $this->addModify($p_filelist, '', '');
       
   357     }
       
   358 
       
   359     /**
       
   360      * @param string $p_path
       
   361      * @param bool $p_preserve
       
   362      * @return bool
       
   363      */
       
   364     public function extract($p_path = '', $p_preserve = false)
       
   365     {
       
   366         return $this->extractModify($p_path, '', $p_preserve);
       
   367     }
       
   368 
       
   369     /**
       
   370      * @return array|int
       
   371      */
       
   372     public function listContent()
       
   373     {
       
   374         $v_list_detail = array();
       
   375 
       
   376         if ($this->_openRead()) {
       
   377             if (!$this->_extractList('', $v_list_detail, "list", '', '')) {
       
   378                 unset($v_list_detail);
       
   379                 $v_list_detail = 0;
       
   380             }
       
   381             $this->_close();
       
   382         }
       
   383 
       
   384         return $v_list_detail;
       
   385     }
       
   386 
       
   387     /**
       
   388      * This method creates the archive file and add the files / directories
       
   389      * that are listed in $p_filelist.
       
   390      * If the file already exists and is writable, it is replaced by the
       
   391      * new tar. It is a create and not an add. If the file exists and is
       
   392      * read-only or is a directory it is not replaced. The method return
       
   393      * false and a PEAR error text.
       
   394      * The $p_filelist parameter can be an array of string, each string
       
   395      * representing a filename or a directory name with their path if
       
   396      * needed. It can also be a single string with names separated by a
       
   397      * single blank.
       
   398      * The path indicated in $p_remove_dir will be removed from the
       
   399      * memorized path of each file / directory listed when this path
       
   400      * exists. By default nothing is removed (empty path '')
       
   401      * The path indicated in $p_add_dir will be added at the beginning of
       
   402      * the memorized path of each file / directory listed. However it can
       
   403      * be set to empty ''. The adding of a path is done after the removing
       
   404      * of path.
       
   405      * The path add/remove ability enables the user to prepare an archive
       
   406      * for extraction in a different path than the origin files are.
       
   407      * See also addModify() method for file adding properties.
       
   408      *
       
   409      * @param array $p_filelist An array of filenames and directory names,
       
   410      *                             or a single string with names separated by
       
   411      *                             a single blank space.
       
   412      * @param string $p_add_dir A string which contains a path to be added
       
   413      *                             to the memorized path of each element in
       
   414      *                             the list.
       
   415      * @param string $p_remove_dir A string which contains a path to be
       
   416      *                             removed from the memorized path of each
       
   417      *                             element in the list, when relevant.
       
   418      *
       
   419      * @return boolean true on success, false on error.
       
   420      * @see addModify()
       
   421      */
       
   422     public function createModify($p_filelist, $p_add_dir, $p_remove_dir = '')
       
   423     {
       
   424         $v_result = true;
       
   425 
       
   426         if (!$this->_openWrite()) {
       
   427             return false;
       
   428         }
       
   429 
       
   430         if ($p_filelist != '') {
       
   431             if (is_array($p_filelist)) {
       
   432                 $v_list = $p_filelist;
       
   433             } elseif (is_string($p_filelist)) {
       
   434                 $v_list = explode($this->_separator, $p_filelist);
       
   435             } else {
       
   436                 $this->_cleanFile();
       
   437                 $this->_error('Invalid file list');
       
   438                 return false;
       
   439             }
       
   440 
       
   441             $v_result = $this->_addList($v_list, $p_add_dir, $p_remove_dir);
       
   442         }
       
   443 
       
   444         if ($v_result) {
       
   445             $this->_writeFooter();
       
   446             $this->_close();
       
   447         } else {
       
   448             $this->_cleanFile();
       
   449         }
       
   450 
       
   451         return $v_result;
       
   452     }
       
   453 
       
   454     /**
       
   455      * This method add the files / directories listed in $p_filelist at the
       
   456      * end of the existing archive. If the archive does not yet exists it
       
   457      * is created.
       
   458      * The $p_filelist parameter can be an array of string, each string
       
   459      * representing a filename or a directory name with their path if
       
   460      * needed. It can also be a single string with names separated by a
       
   461      * single blank.
       
   462      * The path indicated in $p_remove_dir will be removed from the
       
   463      * memorized path of each file / directory listed when this path
       
   464      * exists. By default nothing is removed (empty path '')
       
   465      * The path indicated in $p_add_dir will be added at the beginning of
       
   466      * the memorized path of each file / directory listed. However it can
       
   467      * be set to empty ''. The adding of a path is done after the removing
       
   468      * of path.
       
   469      * The path add/remove ability enables the user to prepare an archive
       
   470      * for extraction in a different path than the origin files are.
       
   471      * If a file/dir is already in the archive it will only be added at the
       
   472      * end of the archive. There is no update of the existing archived
       
   473      * file/dir. However while extracting the archive, the last file will
       
   474      * replace the first one. This results in a none optimization of the
       
   475      * archive size.
       
   476      * If a file/dir does not exist the file/dir is ignored. However an
       
   477      * error text is send to PEAR error.
       
   478      * If a file/dir is not readable the file/dir is ignored. However an
       
   479      * error text is send to PEAR error.
       
   480      *
       
   481      * @param array $p_filelist An array of filenames and directory
       
   482      *                             names, or a single string with names
       
   483      *                             separated by a single blank space.
       
   484      * @param string $p_add_dir A string which contains a path to be
       
   485      *                             added to the memorized path of each
       
   486      *                             element in the list.
       
   487      * @param string $p_remove_dir A string which contains a path to be
       
   488      *                             removed from the memorized path of
       
   489      *                             each element in the list, when
       
   490      *                             relevant.
       
   491      *
       
   492      * @return true on success, false on error.
       
   493      */
       
   494     public function addModify($p_filelist, $p_add_dir, $p_remove_dir = '')
       
   495     {
       
   496         $v_result = true;
       
   497 
       
   498         if (!$this->_isArchive()) {
       
   499             $v_result = $this->createModify(
       
   500                 $p_filelist,
       
   501                 $p_add_dir,
       
   502                 $p_remove_dir
       
   503             );
       
   504         } else {
       
   505             if (is_array($p_filelist)) {
       
   506                 $v_list = $p_filelist;
       
   507             } elseif (is_string($p_filelist)) {
       
   508                 $v_list = explode($this->_separator, $p_filelist);
       
   509             } else {
       
   510                 $this->_error('Invalid file list');
       
   511                 return false;
       
   512             }
       
   513 
       
   514             $v_result = $this->_append($v_list, $p_add_dir, $p_remove_dir);
       
   515         }
       
   516 
       
   517         return $v_result;
       
   518     }
       
   519 
       
   520     /**
       
   521      * This method add a single string as a file at the
       
   522      * end of the existing archive. If the archive does not yet exists it
       
   523      * is created.
       
   524      *
       
   525      * @param string $p_filename A string which contains the full
       
   526      *                           filename path that will be associated
       
   527      *                           with the string.
       
   528      * @param string $p_string The content of the file added in
       
   529      *                           the archive.
       
   530      * @param bool|int $p_datetime A custom date/time (unix timestamp)
       
   531      *                           for the file (optional).
       
   532      * @param array $p_params An array of optional params:
       
   533      *                               stamp => the datetime (replaces
       
   534      *                                   datetime above if it exists)
       
   535      *                               mode => the permissions on the
       
   536      *                                   file (600 by default)
       
   537      *                               type => is this a link?  See the
       
   538      *                                   tar specification for details.
       
   539      *                                   (default = regular file)
       
   540      *                               uid => the user ID of the file
       
   541      *                                   (default = 0 = root)
       
   542      *                               gid => the group ID of the file
       
   543      *                                   (default = 0 = root)
       
   544      *
       
   545      * @return true on success, false on error.
       
   546      */
       
   547     public function addString($p_filename, $p_string, $p_datetime = false, $p_params = array())
       
   548     {
       
   549         $p_stamp = @$p_params["stamp"] ? $p_params["stamp"] : ($p_datetime ? $p_datetime : time());
       
   550         $p_mode = @$p_params["mode"] ? $p_params["mode"] : 0600;
       
   551         $p_type = @$p_params["type"] ? $p_params["type"] : "";
       
   552         $p_uid = @$p_params["uid"] ? $p_params["uid"] : "";
       
   553         $p_gid = @$p_params["gid"] ? $p_params["gid"] : "";
       
   554         $v_result = true;
       
   555 
       
   556         if (!$this->_isArchive()) {
       
   557             if (!$this->_openWrite()) {
       
   558                 return false;
       
   559             }
       
   560             $this->_close();
       
   561         }
       
   562 
       
   563         if (!$this->_openAppend()) {
       
   564             return false;
       
   565         }
       
   566 
       
   567         // Need to check the get back to the temporary file ? ....
       
   568         $v_result = $this->_addString($p_filename, $p_string, $p_datetime, $p_params);
       
   569 
       
   570         $this->_writeFooter();
       
   571 
       
   572         $this->_close();
       
   573 
       
   574         return $v_result;
       
   575     }
       
   576 
       
   577     /**
       
   578      * This method extract all the content of the archive in the directory
       
   579      * indicated by $p_path. When relevant the memorized path of the
       
   580      * files/dir can be modified by removing the $p_remove_path path at the
       
   581      * beginning of the file/dir path.
       
   582      * While extracting a file, if the directory path does not exists it is
       
   583      * created.
       
   584      * While extracting a file, if the file already exists it is replaced
       
   585      * without looking for last modification date.
       
   586      * While extracting a file, if the file already exists and is write
       
   587      * protected, the extraction is aborted.
       
   588      * While extracting a file, if a directory with the same name already
       
   589      * exists, the extraction is aborted.
       
   590      * While extracting a directory, if a file with the same name already
       
   591      * exists, the extraction is aborted.
       
   592      * While extracting a file/directory if the destination directory exist
       
   593      * and is write protected, or does not exist but can not be created,
       
   594      * the extraction is aborted.
       
   595      * If after extraction an extracted file does not show the correct
       
   596      * stored file size, the extraction is aborted.
       
   597      * When the extraction is aborted, a PEAR error text is set and false
       
   598      * is returned. However the result can be a partial extraction that may
       
   599      * need to be manually cleaned.
       
   600      *
       
   601      * @param string $p_path The path of the directory where the
       
   602      *                               files/dir need to by extracted.
       
   603      * @param string $p_remove_path Part of the memorized path that can be
       
   604      *                               removed if present at the beginning of
       
   605      *                               the file/dir path.
       
   606      * @param boolean $p_preserve Preserve user/group ownership of files
       
   607      *
       
   608      * @return boolean true on success, false on error.
       
   609      * @see    extractList()
       
   610      */
       
   611     public function extractModify($p_path, $p_remove_path, $p_preserve = false)
       
   612     {
       
   613         $v_result = true;
       
   614         $v_list_detail = array();
       
   615 
       
   616         if ($v_result = $this->_openRead()) {
       
   617             $v_result = $this->_extractList(
       
   618                 $p_path,
       
   619                 $v_list_detail,
       
   620                 "complete",
       
   621                 0,
       
   622                 $p_remove_path,
       
   623                 $p_preserve
       
   624             );
       
   625             $this->_close();
       
   626         }
       
   627 
       
   628         return $v_result;
       
   629     }
       
   630 
       
   631     /**
       
   632      * This method extract from the archive one file identified by $p_filename.
       
   633      * The return value is a string with the file content, or NULL on error.
       
   634      *
       
   635      * @param string $p_filename The path of the file to extract in a string.
       
   636      *
       
   637      * @return a string with the file content or NULL.
       
   638      */
       
   639     public function extractInString($p_filename)
       
   640     {
       
   641         if ($this->_openRead()) {
       
   642             $v_result = $this->_extractInString($p_filename);
       
   643             $this->_close();
       
   644         } else {
       
   645             $v_result = null;
       
   646         }
       
   647 
       
   648         return $v_result;
       
   649     }
       
   650 
       
   651     /**
       
   652      * This method extract from the archive only the files indicated in the
       
   653      * $p_filelist. These files are extracted in the current directory or
       
   654      * in the directory indicated by the optional $p_path parameter.
       
   655      * If indicated the $p_remove_path can be used in the same way as it is
       
   656      * used in extractModify() method.
       
   657      *
       
   658      * @param array $p_filelist An array of filenames and directory names,
       
   659      *                               or a single string with names separated
       
   660      *                               by a single blank space.
       
   661      * @param string $p_path The path of the directory where the
       
   662      *                               files/dir need to by extracted.
       
   663      * @param string $p_remove_path Part of the memorized path that can be
       
   664      *                               removed if present at the beginning of
       
   665      *                               the file/dir path.
       
   666      * @param boolean $p_preserve Preserve user/group ownership of files
       
   667      *
       
   668      * @return true on success, false on error.
       
   669      * @see    extractModify()
       
   670      */
       
   671     public function extractList($p_filelist, $p_path = '', $p_remove_path = '', $p_preserve = false)
       
   672     {
       
   673         $v_result = true;
       
   674         $v_list_detail = array();
       
   675 
       
   676         if (is_array($p_filelist)) {
       
   677             $v_list = $p_filelist;
       
   678         } elseif (is_string($p_filelist)) {
       
   679             $v_list = explode($this->_separator, $p_filelist);
       
   680         } else {
       
   681             $this->_error('Invalid string list');
       
   682             return false;
       
   683         }
       
   684 
       
   685         if ($v_result = $this->_openRead()) {
       
   686             $v_result = $this->_extractList(
       
   687                 $p_path,
       
   688                 $v_list_detail,
       
   689                 "partial",
       
   690                 $v_list,
       
   691                 $p_remove_path,
       
   692                 $p_preserve
       
   693             );
       
   694             $this->_close();
       
   695         }
       
   696 
       
   697         return $v_result;
       
   698     }
       
   699 
       
   700     /**
       
   701      * This method set specific attributes of the archive. It uses a variable
       
   702      * list of parameters, in the format attribute code + attribute values :
       
   703      * $arch->setAttribute(ARCHIVE_TAR_ATT_SEPARATOR, ',');
       
   704      *
       
   705      * @return true on success, false on error.
       
   706      */
       
   707     public function setAttribute()
       
   708     {
       
   709         $v_result = true;
       
   710 
       
   711         // ----- Get the number of variable list of arguments
       
   712         if (($v_size = func_num_args()) == 0) {
       
   713             return true;
       
   714         }
       
   715 
       
   716         // ----- Get the arguments
       
   717         $v_att_list = & func_get_args();
       
   718 
       
   719         // ----- Read the attributes
       
   720         $i = 0;
       
   721         while ($i < $v_size) {
       
   722 
       
   723             // ----- Look for next option
       
   724             switch ($v_att_list[$i]) {
       
   725                 // ----- Look for options that request a string value
       
   726                 case ARCHIVE_TAR_ATT_SEPARATOR :
       
   727                     // ----- Check the number of parameters
       
   728                     if (($i + 1) >= $v_size) {
       
   729                         $this->_error(
       
   730                             'Invalid number of parameters for '
       
   731                             . 'attribute ARCHIVE_TAR_ATT_SEPARATOR'
       
   732                         );
       
   733                         return false;
       
   734                     }
       
   735 
       
   736                     // ----- Get the value
       
   737                     $this->_separator = $v_att_list[$i + 1];
       
   738                     $i++;
       
   739                     break;
       
   740 
       
   741                 default :
       
   742                     $this->_error('Unknown attribute code ' . $v_att_list[$i] . '');
       
   743                     return false;
       
   744             }
       
   745 
       
   746             // ----- Next attribute
       
   747             $i++;
       
   748         }
       
   749 
       
   750         return $v_result;
       
   751     }
       
   752 
       
   753     /**
       
   754      * This method sets the regular expression for ignoring files and directories
       
   755      * at import, for example:
       
   756      * $arch->setIgnoreRegexp("#CVS|\.svn#");
       
   757      *
       
   758      * @param string $regexp regular expression defining which files or directories to ignore
       
   759      */
       
   760     public function setIgnoreRegexp($regexp)
       
   761     {
       
   762         $this->_ignore_regexp = $regexp;
       
   763     }
       
   764 
       
   765     /**
       
   766      * This method sets the regular expression for ignoring all files and directories
       
   767      * matching the filenames in the array list at import, for example:
       
   768      * $arch->setIgnoreList(array('CVS', '.svn', 'bin/tool'));
       
   769      *
       
   770      * @param array $list a list of file or directory names to ignore
       
   771      *
       
   772      * @access public
       
   773      */
       
   774     public function setIgnoreList($list)
       
   775     {
       
   776         $regexp = str_replace(array('#', '.', '^', '$'), array('\#', '\.', '\^', '\$'), $list);
       
   777         $regexp = '#/' . join('$|/', $list) . '#';
       
   778         $this->setIgnoreRegexp($regexp);
       
   779     }
       
   780 
       
   781     /**
       
   782      * @param string $p_message
       
   783      */
       
   784     public function _error($p_message)
       
   785     {
       
   786         // Drupal change $this->error_object = $this->raiseError($p_message).
       
   787         throw new Exception($p_message);
       
   788     }
       
   789 
       
   790     /**
       
   791      * @param string $p_message
       
   792      */
       
   793     public function _warning($p_message)
       
   794     {
       
   795         // Drupal change $this->error_object = $this->raiseError($p_message).
       
   796         throw new Exception($p_message);
       
   797     }
       
   798 
       
   799     /**
       
   800      * @param string $p_filename
       
   801      * @return bool
       
   802      */
       
   803     public function _isArchive($p_filename = null)
       
   804     {
       
   805         if ($p_filename == null) {
       
   806             $p_filename = $this->_tarname;
       
   807         }
       
   808         clearstatcache();
       
   809         return @is_file($p_filename) && !@is_link($p_filename);
       
   810     }
       
   811 
       
   812     /**
       
   813      * @return bool
       
   814      */
       
   815     public function _openWrite()
       
   816     {
       
   817         if ($this->_compress_type == 'gz' && function_exists('gzopen')) {
       
   818             $this->_file = @gzopen($this->_tarname, "wb9");
       
   819         } else {
       
   820             if ($this->_compress_type == 'bz2' && function_exists('bzopen')) {
       
   821                 $this->_file = @bzopen($this->_tarname, "w");
       
   822             } else {
       
   823                 if ($this->_compress_type == 'lzma2' && function_exists('xzopen')) {
       
   824                     $this->_file = @xzopen($this->_tarname, 'w');
       
   825                 } else {
       
   826                     if ($this->_compress_type == 'none') {
       
   827                         $this->_file = @fopen($this->_tarname, "wb");
       
   828                     } else {
       
   829                         $this->_error(
       
   830                             'Unknown or missing compression type ('
       
   831                             . $this->_compress_type . ')'
       
   832                         );
       
   833                         return false;
       
   834                     }
       
   835                 }
       
   836             }
       
   837         }
       
   838 
       
   839         if ($this->_file == 0) {
       
   840             $this->_error(
       
   841                 'Unable to open in write mode \''
       
   842                 . $this->_tarname . '\''
       
   843             );
       
   844             return false;
       
   845         }
       
   846 
       
   847         return true;
       
   848     }
       
   849 
       
   850     /**
       
   851      * @return bool
       
   852      */
       
   853     public function _openRead()
       
   854     {
       
   855         if (strtolower(substr($this->_tarname, 0, 7)) == 'http://') {
       
   856 
       
   857             // ----- Look if a local copy need to be done
       
   858             if ($this->_temp_tarname == '') {
       
   859                 $this->_temp_tarname = uniqid('tar') . '.tmp';
       
   860                 if (!$v_file_from = @fopen($this->_tarname, 'rb')) {
       
   861                     $this->_error(
       
   862                         'Unable to open in read mode \''
       
   863                         . $this->_tarname . '\''
       
   864                     );
       
   865                     $this->_temp_tarname = '';
       
   866                     return false;
       
   867                 }
       
   868                 if (!$v_file_to = @fopen($this->_temp_tarname, 'wb')) {
       
   869                     $this->_error(
       
   870                         'Unable to open in write mode \''
       
   871                         . $this->_temp_tarname . '\''
       
   872                     );
       
   873                     $this->_temp_tarname = '';
       
   874                     return false;
       
   875                 }
       
   876                 while ($v_data = @fread($v_file_from, 1024)) {
       
   877                     @fwrite($v_file_to, $v_data);
       
   878                 }
       
   879                 @fclose($v_file_from);
       
   880                 @fclose($v_file_to);
       
   881             }
       
   882 
       
   883             // ----- File to open if the local copy
       
   884             $v_filename = $this->_temp_tarname;
       
   885         } else {
       
   886             // ----- File to open if the normal Tar file
       
   887 
       
   888             $v_filename = $this->_tarname;
       
   889         }
       
   890 
       
   891         if ($this->_compress_type == 'gz' && function_exists('gzopen')) {
       
   892             $this->_file = @gzopen($v_filename, "rb");
       
   893         } else {
       
   894             if ($this->_compress_type == 'bz2' && function_exists('bzopen')) {
       
   895                 $this->_file = @bzopen($v_filename, "r");
       
   896             } else {
       
   897                 if ($this->_compress_type == 'lzma2' && function_exists('xzopen')) {
       
   898                     $this->_file = @xzopen($v_filename, "r");
       
   899                 } else {
       
   900                     if ($this->_compress_type == 'none') {
       
   901                         $this->_file = @fopen($v_filename, "rb");
       
   902                     } else {
       
   903                         $this->_error(
       
   904                             'Unknown or missing compression type ('
       
   905                             . $this->_compress_type . ')'
       
   906                         );
       
   907                         return false;
       
   908                     }
       
   909                 }
       
   910             }
       
   911         }
       
   912 
       
   913         if ($this->_file == 0) {
       
   914             $this->_error('Unable to open in read mode \'' . $v_filename . '\'');
       
   915             return false;
       
   916         }
       
   917 
       
   918         return true;
       
   919     }
       
   920 
       
   921     /**
       
   922      * @return bool
       
   923      */
       
   924     public function _openReadWrite()
       
   925     {
       
   926         if ($this->_compress_type == 'gz') {
       
   927             $this->_file = @gzopen($this->_tarname, "r+b");
       
   928         } else {
       
   929             if ($this->_compress_type == 'bz2') {
       
   930                 $this->_error(
       
   931                     'Unable to open bz2 in read/write mode \''
       
   932                     . $this->_tarname . '\' (limitation of bz2 extension)'
       
   933                 );
       
   934                 return false;
       
   935             } else {
       
   936                 if ($this->_compress_type == 'lzma2') {
       
   937                     $this->_error(
       
   938                         'Unable to open lzma2 in read/write mode \''
       
   939                         . $this->_tarname . '\' (limitation of lzma2 extension)'
       
   940                     );
       
   941                     return false;
       
   942                 } else {
       
   943                     if ($this->_compress_type == 'none') {
       
   944                         $this->_file = @fopen($this->_tarname, "r+b");
       
   945                     } else {
       
   946                         $this->_error(
       
   947                             'Unknown or missing compression type ('
       
   948                             . $this->_compress_type . ')'
       
   949                         );
       
   950                         return false;
       
   951                     }
       
   952                 }
       
   953             }
       
   954         }
       
   955 
       
   956         if ($this->_file == 0) {
       
   957             $this->_error(
       
   958                 'Unable to open in read/write mode \''
       
   959                 . $this->_tarname . '\''
       
   960             );
       
   961             return false;
       
   962         }
       
   963 
       
   964         return true;
       
   965     }
       
   966 
       
   967     /**
       
   968      * @return bool
       
   969      */
       
   970     public function _close()
       
   971     {
       
   972         //if (isset($this->_file)) {
       
   973         if (is_resource($this->_file)) {
       
   974             if ($this->_compress_type == 'gz') {
       
   975                 @gzclose($this->_file);
       
   976             } else {
       
   977                 if ($this->_compress_type == 'bz2') {
       
   978                     @bzclose($this->_file);
       
   979                 } else {
       
   980                     if ($this->_compress_type == 'lzma2') {
       
   981                         @xzclose($this->_file);
       
   982                     } else {
       
   983                         if ($this->_compress_type == 'none') {
       
   984                             @fclose($this->_file);
       
   985                         } else {
       
   986                             $this->_error(
       
   987                                 'Unknown or missing compression type ('
       
   988                                 . $this->_compress_type . ')'
       
   989                             );
       
   990                         }
       
   991                     }
       
   992                 }
       
   993             }
       
   994 
       
   995             $this->_file = 0;
       
   996         }
       
   997 
       
   998         // ----- Look if a local copy need to be erase
       
   999         // Note that it might be interesting to keep the url for a time : ToDo
       
  1000         if ($this->_temp_tarname != '') {
       
  1001             @drupal_unlink($this->_temp_tarname);
       
  1002             $this->_temp_tarname = '';
       
  1003         }
       
  1004 
       
  1005         return true;
       
  1006     }
       
  1007 
       
  1008     /**
       
  1009      * @return bool
       
  1010      */
       
  1011     public function _cleanFile()
       
  1012     {
       
  1013         $this->_close();
       
  1014 
       
  1015         // ----- Look for a local copy
       
  1016         if ($this->_temp_tarname != '') {
       
  1017             // ----- Remove the local copy but not the remote tarname
       
  1018             @drupal_unlink($this->_temp_tarname);
       
  1019             $this->_temp_tarname = '';
       
  1020         } else {
       
  1021             // ----- Remove the local tarname file
       
  1022             @drupal_unlink($this->_tarname);
       
  1023         }
       
  1024         $this->_tarname = '';
       
  1025 
       
  1026         return true;
       
  1027     }
       
  1028 
       
  1029     /**
       
  1030      * @param mixed $p_binary_data
       
  1031      * @param integer $p_len
       
  1032      * @return bool
       
  1033      */
       
  1034     public function _writeBlock($p_binary_data, $p_len = null)
       
  1035     {
       
  1036         if (is_resource($this->_file)) {
       
  1037             if ($p_len === null) {
       
  1038                 if ($this->_compress_type == 'gz') {
       
  1039                     @gzputs($this->_file, $p_binary_data);
       
  1040                 } else {
       
  1041                     if ($this->_compress_type == 'bz2') {
       
  1042                         @bzwrite($this->_file, $p_binary_data);
       
  1043                     } else {
       
  1044                         if ($this->_compress_type == 'lzma2') {
       
  1045                             @xzwrite($this->_file, $p_binary_data);
       
  1046                         } else {
       
  1047                             if ($this->_compress_type == 'none') {
       
  1048                                 @fputs($this->_file, $p_binary_data);
       
  1049                             } else {
       
  1050                                 $this->_error(
       
  1051                                     'Unknown or missing compression type ('
       
  1052                                     . $this->_compress_type . ')'
       
  1053                                 );
       
  1054                             }
       
  1055                         }
       
  1056                     }
       
  1057                 }
       
  1058             } else {
       
  1059                 if ($this->_compress_type == 'gz') {
       
  1060                     @gzputs($this->_file, $p_binary_data, $p_len);
       
  1061                 } else {
       
  1062                     if ($this->_compress_type == 'bz2') {
       
  1063                         @bzwrite($this->_file, $p_binary_data, $p_len);
       
  1064                     } else {
       
  1065                         if ($this->_compress_type == 'lzma2') {
       
  1066                             @xzwrite($this->_file, $p_binary_data, $p_len);
       
  1067                         } else {
       
  1068                             if ($this->_compress_type == 'none') {
       
  1069                                 @fputs($this->_file, $p_binary_data, $p_len);
       
  1070                             } else {
       
  1071                                 $this->_error(
       
  1072                                     'Unknown or missing compression type ('
       
  1073                                     . $this->_compress_type . ')'
       
  1074                                 );
       
  1075                             }
       
  1076                         }
       
  1077                     }
       
  1078                 }
       
  1079             }
       
  1080         }
       
  1081         return true;
       
  1082     }
       
  1083 
       
  1084     /**
       
  1085      * @return null|string
       
  1086      */
       
  1087     public function _readBlock()
       
  1088     {
       
  1089         $v_block = null;
       
  1090         if (is_resource($this->_file)) {
       
  1091             if ($this->_compress_type == 'gz') {
       
  1092                 $v_block = @gzread($this->_file, 512);
       
  1093             } else {
       
  1094                 if ($this->_compress_type == 'bz2') {
       
  1095                     $v_block = @bzread($this->_file, 512);
       
  1096                 } else {
       
  1097                     if ($this->_compress_type == 'lzma2') {
       
  1098                         $v_block = @xzread($this->_file, 512);
       
  1099                     } else {
       
  1100                         if ($this->_compress_type == 'none') {
       
  1101                             $v_block = @fread($this->_file, 512);
       
  1102                         } else {
       
  1103                             $this->_error(
       
  1104                                 'Unknown or missing compression type ('
       
  1105                                 . $this->_compress_type . ')'
       
  1106                             );
       
  1107                         }
       
  1108                     }
       
  1109                 }
       
  1110             }
       
  1111         }
       
  1112         return $v_block;
       
  1113     }
       
  1114 
       
  1115     /**
       
  1116      * @param null $p_len
       
  1117      * @return bool
       
  1118      */
       
  1119     public function _jumpBlock($p_len = null)
       
  1120     {
       
  1121         if (is_resource($this->_file)) {
       
  1122             if ($p_len === null) {
       
  1123                 $p_len = 1;
       
  1124             }
       
  1125 
       
  1126             if ($this->_compress_type == 'gz') {
       
  1127                 @gzseek($this->_file, gztell($this->_file) + ($p_len * 512));
       
  1128             } else {
       
  1129                 if ($this->_compress_type == 'bz2') {
       
  1130                     // ----- Replace missing bztell() and bzseek()
       
  1131                     for ($i = 0; $i < $p_len; $i++) {
       
  1132                         $this->_readBlock();
       
  1133                     }
       
  1134                 } else {
       
  1135                     if ($this->_compress_type == 'lzma2') {
       
  1136                         // ----- Replace missing xztell() and xzseek()
       
  1137                         for ($i = 0; $i < $p_len; $i++) {
       
  1138                             $this->_readBlock();
       
  1139                         }
       
  1140                     } else {
       
  1141                         if ($this->_compress_type == 'none') {
       
  1142                             @fseek($this->_file, $p_len * 512, SEEK_CUR);
       
  1143                         } else {
       
  1144                             $this->_error(
       
  1145                                 'Unknown or missing compression type ('
       
  1146                                 . $this->_compress_type . ')'
       
  1147                             );
       
  1148                         }
       
  1149                     }
       
  1150                 }
       
  1151             }
       
  1152         }
       
  1153         return true;
       
  1154     }
       
  1155 
       
  1156     /**
       
  1157      * @return bool
       
  1158      */
       
  1159     public function _writeFooter()
       
  1160     {
       
  1161         if (is_resource($this->_file)) {
       
  1162             // ----- Write the last 0 filled block for end of archive
       
  1163             $v_binary_data = pack('a1024', '');
       
  1164             $this->_writeBlock($v_binary_data);
       
  1165         }
       
  1166         return true;
       
  1167     }
       
  1168 
       
  1169     /**
       
  1170      * @param array $p_list
       
  1171      * @param string $p_add_dir
       
  1172      * @param string $p_remove_dir
       
  1173      * @return bool
       
  1174      */
       
  1175     public function _addList($p_list, $p_add_dir, $p_remove_dir)
       
  1176     {
       
  1177         $v_result = true;
       
  1178         $v_header = array();
       
  1179 
       
  1180         // ----- Remove potential windows directory separator
       
  1181         $p_add_dir = $this->_translateWinPath($p_add_dir);
       
  1182         $p_remove_dir = $this->_translateWinPath($p_remove_dir, false);
       
  1183 
       
  1184         if (!$this->_file) {
       
  1185             $this->_error('Invalid file descriptor');
       
  1186             return false;
       
  1187         }
       
  1188 
       
  1189         if (sizeof($p_list) == 0) {
       
  1190             return true;
       
  1191         }
       
  1192 
       
  1193         foreach ($p_list as $v_filename) {
       
  1194             if (!$v_result) {
       
  1195                 break;
       
  1196             }
       
  1197 
       
  1198             // ----- Skip the current tar name
       
  1199             if ($v_filename == $this->_tarname) {
       
  1200                 continue;
       
  1201             }
       
  1202 
       
  1203             if ($v_filename == '') {
       
  1204                 continue;
       
  1205             }
       
  1206 
       
  1207             // ----- ignore files and directories matching the ignore regular expression
       
  1208             if ($this->_ignore_regexp && preg_match($this->_ignore_regexp, '/' . $v_filename)) {
       
  1209                 $this->_warning("File '$v_filename' ignored");
       
  1210                 continue;
       
  1211             }
       
  1212 
       
  1213             if (!file_exists($v_filename) && !is_link($v_filename)) {
       
  1214                 $this->_warning("File '$v_filename' does not exist");
       
  1215                 continue;
       
  1216             }
       
  1217 
       
  1218             // ----- Add the file or directory header
       
  1219             if (!$this->_addFile($v_filename, $v_header, $p_add_dir, $p_remove_dir)) {
       
  1220                 return false;
       
  1221             }
       
  1222 
       
  1223             if (@is_dir($v_filename) && !@is_link($v_filename)) {
       
  1224                 if (!($p_hdir = opendir($v_filename))) {
       
  1225                     $this->_warning("Directory '$v_filename' can not be read");
       
  1226                     continue;
       
  1227                 }
       
  1228                 while (false !== ($p_hitem = readdir($p_hdir))) {
       
  1229                     if (($p_hitem != '.') && ($p_hitem != '..')) {
       
  1230                         if ($v_filename != ".") {
       
  1231                             $p_temp_list[0] = $v_filename . '/' . $p_hitem;
       
  1232                         } else {
       
  1233                             $p_temp_list[0] = $p_hitem;
       
  1234                         }
       
  1235 
       
  1236                         $v_result = $this->_addList(
       
  1237                             $p_temp_list,
       
  1238                             $p_add_dir,
       
  1239                             $p_remove_dir
       
  1240                         );
       
  1241                     }
       
  1242                 }
       
  1243 
       
  1244                 unset($p_temp_list);
       
  1245                 unset($p_hdir);
       
  1246                 unset($p_hitem);
       
  1247             }
       
  1248         }
       
  1249 
       
  1250         return $v_result;
       
  1251     }
       
  1252 
       
  1253     /**
       
  1254      * @param string $p_filename
       
  1255      * @param mixed $p_header
       
  1256      * @param string $p_add_dir
       
  1257      * @param string $p_remove_dir
       
  1258      * @param null $v_stored_filename
       
  1259      * @return bool
       
  1260      */
       
  1261     public function _addFile($p_filename, &$p_header, $p_add_dir, $p_remove_dir, $v_stored_filename = null)
       
  1262     {
       
  1263         if (!$this->_file) {
       
  1264             $this->_error('Invalid file descriptor');
       
  1265             return false;
       
  1266         }
       
  1267 
       
  1268         if ($p_filename == '') {
       
  1269             $this->_error('Invalid file name');
       
  1270             return false;
       
  1271         }
       
  1272 
       
  1273         if (is_null($v_stored_filename)) {
       
  1274             // ----- Calculate the stored filename
       
  1275             $p_filename = $this->_translateWinPath($p_filename, false);
       
  1276             $v_stored_filename = $p_filename;
       
  1277 
       
  1278             if (strcmp($p_filename, $p_remove_dir) == 0) {
       
  1279                 return true;
       
  1280             }
       
  1281 
       
  1282             if ($p_remove_dir != '') {
       
  1283                 if (substr($p_remove_dir, -1) != '/') {
       
  1284                     $p_remove_dir .= '/';
       
  1285                 }
       
  1286 
       
  1287                 if (substr($p_filename, 0, strlen($p_remove_dir)) == $p_remove_dir) {
       
  1288                     $v_stored_filename = substr($p_filename, strlen($p_remove_dir));
       
  1289                 }
       
  1290             }
       
  1291 
       
  1292             $v_stored_filename = $this->_translateWinPath($v_stored_filename);
       
  1293             if ($p_add_dir != '') {
       
  1294                 if (substr($p_add_dir, -1) == '/') {
       
  1295                     $v_stored_filename = $p_add_dir . $v_stored_filename;
       
  1296                 } else {
       
  1297                     $v_stored_filename = $p_add_dir . '/' . $v_stored_filename;
       
  1298                 }
       
  1299             }
       
  1300 
       
  1301             $v_stored_filename = $this->_pathReduction($v_stored_filename);
       
  1302         }
       
  1303 
       
  1304         if ($this->_isArchive($p_filename)) {
       
  1305             if (($v_file = @fopen($p_filename, "rb")) == 0) {
       
  1306                 $this->_warning(
       
  1307                     "Unable to open file '" . $p_filename
       
  1308                     . "' in binary read mode"
       
  1309                 );
       
  1310                 return true;
       
  1311             }
       
  1312 
       
  1313             if (!$this->_writeHeader($p_filename, $v_stored_filename)) {
       
  1314                 return false;
       
  1315             }
       
  1316 
       
  1317             while (($v_buffer = fread($v_file, 512)) != '') {
       
  1318                 $v_binary_data = pack("a512", "$v_buffer");
       
  1319                 $this->_writeBlock($v_binary_data);
       
  1320             }
       
  1321 
       
  1322             fclose($v_file);
       
  1323         } else {
       
  1324             // ----- Only header for dir
       
  1325             if (!$this->_writeHeader($p_filename, $v_stored_filename)) {
       
  1326                 return false;
       
  1327             }
       
  1328         }
       
  1329 
       
  1330         return true;
       
  1331     }
       
  1332 
       
  1333     /**
       
  1334      * @param string $p_filename
       
  1335      * @param string $p_string
       
  1336      * @param bool $p_datetime
       
  1337      * @param array $p_params
       
  1338      * @return bool
       
  1339      */
       
  1340     public function _addString($p_filename, $p_string, $p_datetime = false, $p_params = array())
       
  1341     {
       
  1342         $p_stamp = @$p_params["stamp"] ? $p_params["stamp"] : ($p_datetime ? $p_datetime : time());
       
  1343         $p_mode = @$p_params["mode"] ? $p_params["mode"] : 0600;
       
  1344         $p_type = @$p_params["type"] ? $p_params["type"] : "";
       
  1345         $p_uid = @$p_params["uid"] ? $p_params["uid"] : 0;
       
  1346         $p_gid = @$p_params["gid"] ? $p_params["gid"] : 0;
       
  1347         if (!$this->_file) {
       
  1348             $this->_error('Invalid file descriptor');
       
  1349             return false;
       
  1350         }
       
  1351 
       
  1352         if ($p_filename == '') {
       
  1353             $this->_error('Invalid file name');
       
  1354             return false;
       
  1355         }
       
  1356 
       
  1357         // ----- Calculate the stored filename
       
  1358         $p_filename = $this->_translateWinPath($p_filename, false);
       
  1359 
       
  1360         // ----- If datetime is not specified, set current time
       
  1361         if ($p_datetime === false) {
       
  1362             $p_datetime = time();
       
  1363         }
       
  1364 
       
  1365         if (!$this->_writeHeaderBlock(
       
  1366             $p_filename,
       
  1367             strlen($p_string),
       
  1368             $p_stamp,
       
  1369             $p_mode,
       
  1370             $p_type,
       
  1371             $p_uid,
       
  1372             $p_gid
       
  1373         )
       
  1374         ) {
       
  1375             return false;
       
  1376         }
       
  1377 
       
  1378         $i = 0;
       
  1379         while (($v_buffer = substr($p_string, (($i++) * 512), 512)) != '') {
       
  1380             $v_binary_data = pack("a512", $v_buffer);
       
  1381             $this->_writeBlock($v_binary_data);
       
  1382         }
       
  1383 
       
  1384         return true;
       
  1385     }
       
  1386 
       
  1387     /**
       
  1388      * @param string $p_filename
       
  1389      * @param string $p_stored_filename
       
  1390      * @return bool
       
  1391      */
       
  1392     public function _writeHeader($p_filename, $p_stored_filename)
       
  1393     {
       
  1394         if ($p_stored_filename == '') {
       
  1395             $p_stored_filename = $p_filename;
       
  1396         }
       
  1397         $v_reduce_filename = $this->_pathReduction($p_stored_filename);
       
  1398 
       
  1399         if (strlen($v_reduce_filename) > 99) {
       
  1400             if (!$this->_writeLongHeader($v_reduce_filename)) {
       
  1401                 return false;
       
  1402             }
       
  1403         }
       
  1404 
       
  1405         $v_info = lstat($p_filename);
       
  1406         $v_uid = sprintf("%07s", DecOct($v_info[4]));
       
  1407         $v_gid = sprintf("%07s", DecOct($v_info[5]));
       
  1408         $v_perms = sprintf("%07s", DecOct($v_info['mode'] & 000777));
       
  1409 
       
  1410         $v_mtime = sprintf("%011s", DecOct($v_info['mtime']));
       
  1411 
       
  1412         $v_linkname = '';
       
  1413 
       
  1414         if (@is_link($p_filename)) {
       
  1415             $v_typeflag = '2';
       
  1416             $v_linkname = readlink($p_filename);
       
  1417             $v_size = sprintf("%011s", DecOct(0));
       
  1418         } elseif (@is_dir($p_filename)) {
       
  1419             $v_typeflag = "5";
       
  1420             $v_size = sprintf("%011s", DecOct(0));
       
  1421         } else {
       
  1422             $v_typeflag = '0';
       
  1423             clearstatcache();
       
  1424             $v_size = sprintf("%011s", DecOct($v_info['size']));
       
  1425         }
       
  1426 
       
  1427         $v_magic = 'ustar ';
       
  1428 
       
  1429         $v_version = ' ';
       
  1430 
       
  1431         if (function_exists('posix_getpwuid')) {
       
  1432             $userinfo = posix_getpwuid($v_info[4]);
       
  1433             $groupinfo = posix_getgrgid($v_info[5]);
       
  1434 
       
  1435             $v_uname = $userinfo['name'];
       
  1436             $v_gname = $groupinfo['name'];
       
  1437         } else {
       
  1438             $v_uname = '';
       
  1439             $v_gname = '';
       
  1440         }
       
  1441 
       
  1442         $v_devmajor = '';
       
  1443 
       
  1444         $v_devminor = '';
       
  1445 
       
  1446         $v_prefix = '';
       
  1447 
       
  1448         $v_binary_data_first = pack(
       
  1449             "a100a8a8a8a12a12",
       
  1450             $v_reduce_filename,
       
  1451             $v_perms,
       
  1452             $v_uid,
       
  1453             $v_gid,
       
  1454             $v_size,
       
  1455             $v_mtime
       
  1456         );
       
  1457         $v_binary_data_last = pack(
       
  1458             "a1a100a6a2a32a32a8a8a155a12",
       
  1459             $v_typeflag,
       
  1460             $v_linkname,
       
  1461             $v_magic,
       
  1462             $v_version,
       
  1463             $v_uname,
       
  1464             $v_gname,
       
  1465             $v_devmajor,
       
  1466             $v_devminor,
       
  1467             $v_prefix,
       
  1468             ''
       
  1469         );
       
  1470 
       
  1471         // ----- Calculate the checksum
       
  1472         $v_checksum = 0;
       
  1473         // ..... First part of the header
       
  1474         for ($i = 0; $i < 148; $i++) {
       
  1475             $v_checksum += ord(substr($v_binary_data_first, $i, 1));
       
  1476         }
       
  1477         // ..... Ignore the checksum value and replace it by ' ' (space)
       
  1478         for ($i = 148; $i < 156; $i++) {
       
  1479             $v_checksum += ord(' ');
       
  1480         }
       
  1481         // ..... Last part of the header
       
  1482         for ($i = 156, $j = 0; $i < 512; $i++, $j++) {
       
  1483             $v_checksum += ord(substr($v_binary_data_last, $j, 1));
       
  1484         }
       
  1485 
       
  1486         // ----- Write the first 148 bytes of the header in the archive
       
  1487         $this->_writeBlock($v_binary_data_first, 148);
       
  1488 
       
  1489         // ----- Write the calculated checksum
       
  1490         $v_checksum = sprintf("%06s ", DecOct($v_checksum));
       
  1491         $v_binary_data = pack("a8", $v_checksum);
       
  1492         $this->_writeBlock($v_binary_data, 8);
       
  1493 
       
  1494         // ----- Write the last 356 bytes of the header in the archive
       
  1495         $this->_writeBlock($v_binary_data_last, 356);
       
  1496 
       
  1497         return true;
       
  1498     }
       
  1499 
       
  1500     /**
       
  1501      * @param string $p_filename
       
  1502      * @param int $p_size
       
  1503      * @param int $p_mtime
       
  1504      * @param int $p_perms
       
  1505      * @param string $p_type
       
  1506      * @param int $p_uid
       
  1507      * @param int $p_gid
       
  1508      * @return bool
       
  1509      */
       
  1510     public function _writeHeaderBlock(
       
  1511         $p_filename,
       
  1512         $p_size,
       
  1513         $p_mtime = 0,
       
  1514         $p_perms = 0,
       
  1515         $p_type = '',
       
  1516         $p_uid = 0,
       
  1517         $p_gid = 0
       
  1518     ) {
       
  1519         $p_filename = $this->_pathReduction($p_filename);
       
  1520 
       
  1521         if (strlen($p_filename) > 99) {
       
  1522             if (!$this->_writeLongHeader($p_filename)) {
       
  1523                 return false;
       
  1524             }
       
  1525         }
       
  1526 
       
  1527         if ($p_type == "5") {
       
  1528             $v_size = sprintf("%011s", DecOct(0));
       
  1529         } else {
       
  1530             $v_size = sprintf("%011s", DecOct($p_size));
       
  1531         }
       
  1532 
       
  1533         $v_uid = sprintf("%07s", DecOct($p_uid));
       
  1534         $v_gid = sprintf("%07s", DecOct($p_gid));
       
  1535         $v_perms = sprintf("%07s", DecOct($p_perms & 000777));
       
  1536 
       
  1537         $v_mtime = sprintf("%11s", DecOct($p_mtime));
       
  1538 
       
  1539         $v_linkname = '';
       
  1540 
       
  1541         $v_magic = 'ustar ';
       
  1542 
       
  1543         $v_version = ' ';
       
  1544 
       
  1545         if (function_exists('posix_getpwuid')) {
       
  1546             $userinfo = posix_getpwuid($p_uid);
       
  1547             $groupinfo = posix_getgrgid($p_gid);
       
  1548 
       
  1549             $v_uname = $userinfo['name'];
       
  1550             $v_gname = $groupinfo['name'];
       
  1551         } else {
       
  1552             $v_uname = '';
       
  1553             $v_gname = '';
       
  1554         }
       
  1555 
       
  1556         $v_devmajor = '';
       
  1557 
       
  1558         $v_devminor = '';
       
  1559 
       
  1560         $v_prefix = '';
       
  1561 
       
  1562         $v_binary_data_first = pack(
       
  1563             "a100a8a8a8a12A12",
       
  1564             $p_filename,
       
  1565             $v_perms,
       
  1566             $v_uid,
       
  1567             $v_gid,
       
  1568             $v_size,
       
  1569             $v_mtime
       
  1570         );
       
  1571         $v_binary_data_last = pack(
       
  1572             "a1a100a6a2a32a32a8a8a155a12",
       
  1573             $p_type,
       
  1574             $v_linkname,
       
  1575             $v_magic,
       
  1576             $v_version,
       
  1577             $v_uname,
       
  1578             $v_gname,
       
  1579             $v_devmajor,
       
  1580             $v_devminor,
       
  1581             $v_prefix,
       
  1582             ''
       
  1583         );
       
  1584 
       
  1585         // ----- Calculate the checksum
       
  1586         $v_checksum = 0;
       
  1587         // ..... First part of the header
       
  1588         for ($i = 0; $i < 148; $i++) {
       
  1589             $v_checksum += ord(substr($v_binary_data_first, $i, 1));
       
  1590         }
       
  1591         // ..... Ignore the checksum value and replace it by ' ' (space)
       
  1592         for ($i = 148; $i < 156; $i++) {
       
  1593             $v_checksum += ord(' ');
       
  1594         }
       
  1595         // ..... Last part of the header
       
  1596         for ($i = 156, $j = 0; $i < 512; $i++, $j++) {
       
  1597             $v_checksum += ord(substr($v_binary_data_last, $j, 1));
       
  1598         }
       
  1599 
       
  1600         // ----- Write the first 148 bytes of the header in the archive
       
  1601         $this->_writeBlock($v_binary_data_first, 148);
       
  1602 
       
  1603         // ----- Write the calculated checksum
       
  1604         $v_checksum = sprintf("%06s ", DecOct($v_checksum));
       
  1605         $v_binary_data = pack("a8", $v_checksum);
       
  1606         $this->_writeBlock($v_binary_data, 8);
       
  1607 
       
  1608         // ----- Write the last 356 bytes of the header in the archive
       
  1609         $this->_writeBlock($v_binary_data_last, 356);
       
  1610 
       
  1611         return true;
       
  1612     }
       
  1613 
       
  1614     /**
       
  1615      * @param string $p_filename
       
  1616      * @return bool
       
  1617      */
       
  1618     public function _writeLongHeader($p_filename)
       
  1619     {
       
  1620         $v_size = sprintf("%11s ", DecOct(strlen($p_filename)));
       
  1621 
       
  1622         $v_typeflag = 'L';
       
  1623 
       
  1624         $v_linkname = '';
       
  1625 
       
  1626         $v_magic = '';
       
  1627 
       
  1628         $v_version = '';
       
  1629 
       
  1630         $v_uname = '';
       
  1631 
       
  1632         $v_gname = '';
       
  1633 
       
  1634         $v_devmajor = '';
       
  1635 
       
  1636         $v_devminor = '';
       
  1637 
       
  1638         $v_prefix = '';
       
  1639 
       
  1640         $v_binary_data_first = pack(
       
  1641             "a100a8a8a8a12a12",
       
  1642             '././@LongLink',
       
  1643             0,
       
  1644             0,
       
  1645             0,
       
  1646             $v_size,
       
  1647             0
       
  1648         );
       
  1649         $v_binary_data_last = pack(
       
  1650             "a1a100a6a2a32a32a8a8a155a12",
       
  1651             $v_typeflag,
       
  1652             $v_linkname,
       
  1653             $v_magic,
       
  1654             $v_version,
       
  1655             $v_uname,
       
  1656             $v_gname,
       
  1657             $v_devmajor,
       
  1658             $v_devminor,
       
  1659             $v_prefix,
       
  1660             ''
       
  1661         );
       
  1662 
       
  1663         // ----- Calculate the checksum
       
  1664         $v_checksum = 0;
       
  1665         // ..... First part of the header
       
  1666         for ($i = 0; $i < 148; $i++) {
       
  1667             $v_checksum += ord(substr($v_binary_data_first, $i, 1));
       
  1668         }
       
  1669         // ..... Ignore the checksum value and replace it by ' ' (space)
       
  1670         for ($i = 148; $i < 156; $i++) {
       
  1671             $v_checksum += ord(' ');
       
  1672         }
       
  1673         // ..... Last part of the header
       
  1674         for ($i = 156, $j = 0; $i < 512; $i++, $j++) {
       
  1675             $v_checksum += ord(substr($v_binary_data_last, $j, 1));
       
  1676         }
       
  1677 
       
  1678         // ----- Write the first 148 bytes of the header in the archive
       
  1679         $this->_writeBlock($v_binary_data_first, 148);
       
  1680 
       
  1681         // ----- Write the calculated checksum
       
  1682         $v_checksum = sprintf("%06s ", DecOct($v_checksum));
       
  1683         $v_binary_data = pack("a8", $v_checksum);
       
  1684         $this->_writeBlock($v_binary_data, 8);
       
  1685 
       
  1686         // ----- Write the last 356 bytes of the header in the archive
       
  1687         $this->_writeBlock($v_binary_data_last, 356);
       
  1688 
       
  1689         // ----- Write the filename as content of the block
       
  1690         $i = 0;
       
  1691         while (($v_buffer = substr($p_filename, (($i++) * 512), 512)) != '') {
       
  1692             $v_binary_data = pack("a512", "$v_buffer");
       
  1693             $this->_writeBlock($v_binary_data);
       
  1694         }
       
  1695 
       
  1696         return true;
       
  1697     }
       
  1698 
       
  1699     /**
       
  1700      * @param mixed $v_binary_data
       
  1701      * @param mixed $v_header
       
  1702      * @return bool
       
  1703      */
       
  1704     public function _readHeader($v_binary_data, &$v_header)
       
  1705     {
       
  1706         if (strlen($v_binary_data) == 0) {
       
  1707             $v_header['filename'] = '';
       
  1708             return true;
       
  1709         }
       
  1710 
       
  1711         if (strlen($v_binary_data) != 512) {
       
  1712             $v_header['filename'] = '';
       
  1713             $this->_error('Invalid block size : ' . strlen($v_binary_data));
       
  1714             return false;
       
  1715         }
       
  1716 
       
  1717         if (!is_array($v_header)) {
       
  1718             $v_header = array();
       
  1719         }
       
  1720         // ----- Calculate the checksum
       
  1721         $v_checksum = 0;
       
  1722         // ..... First part of the header
       
  1723         for ($i = 0; $i < 148; $i++) {
       
  1724             $v_checksum += ord(substr($v_binary_data, $i, 1));
       
  1725         }
       
  1726         // ..... Ignore the checksum value and replace it by ' ' (space)
       
  1727         for ($i = 148; $i < 156; $i++) {
       
  1728             $v_checksum += ord(' ');
       
  1729         }
       
  1730         // ..... Last part of the header
       
  1731         for ($i = 156; $i < 512; $i++) {
       
  1732             $v_checksum += ord(substr($v_binary_data, $i, 1));
       
  1733         }
       
  1734 
       
  1735         if (version_compare(PHP_VERSION, "5.5.0-dev") < 0) {
       
  1736             $fmt = "a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/" .
       
  1737                 "a8checksum/a1typeflag/a100link/a6magic/a2version/" .
       
  1738                 "a32uname/a32gname/a8devmajor/a8devminor/a131prefix";
       
  1739         } else {
       
  1740             $fmt = "Z100filename/Z8mode/Z8uid/Z8gid/Z12size/Z12mtime/" .
       
  1741                 "Z8checksum/Z1typeflag/Z100link/Z6magic/Z2version/" .
       
  1742                 "Z32uname/Z32gname/Z8devmajor/Z8devminor/Z131prefix";
       
  1743         }
       
  1744         $v_data = unpack($fmt, $v_binary_data);
       
  1745 
       
  1746         if (strlen($v_data["prefix"]) > 0) {
       
  1747             $v_data["filename"] = "$v_data[prefix]/$v_data[filename]";
       
  1748         }
       
  1749 
       
  1750         // ----- Extract the checksum
       
  1751         $v_header['checksum'] = OctDec(trim($v_data['checksum']));
       
  1752         if ($v_header['checksum'] != $v_checksum) {
       
  1753             $v_header['filename'] = '';
       
  1754 
       
  1755             // ----- Look for last block (empty block)
       
  1756             if (($v_checksum == 256) && ($v_header['checksum'] == 0)) {
       
  1757                 return true;
       
  1758             }
       
  1759 
       
  1760             $this->_error(
       
  1761                 'Invalid checksum for file "' . $v_data['filename']
       
  1762                 . '" : ' . $v_checksum . ' calculated, '
       
  1763                 . $v_header['checksum'] . ' expected'
       
  1764             );
       
  1765             return false;
       
  1766         }
       
  1767 
       
  1768         // ----- Extract the properties
       
  1769         $v_header['filename'] = rtrim($v_data['filename'], "\0");
       
  1770         if ($this->_maliciousFilename($v_header['filename'])) {
       
  1771             $this->_error(
       
  1772                 'Malicious .tar detected, file "' . $v_header['filename'] .
       
  1773                 '" will not install in desired directory tree'
       
  1774             );
       
  1775             return false;
       
  1776         }
       
  1777         $v_header['mode'] = OctDec(trim($v_data['mode']));
       
  1778         $v_header['uid'] = OctDec(trim($v_data['uid']));
       
  1779         $v_header['gid'] = OctDec(trim($v_data['gid']));
       
  1780         $v_header['size'] = OctDec(trim($v_data['size']));
       
  1781         $v_header['mtime'] = OctDec(trim($v_data['mtime']));
       
  1782         if (($v_header['typeflag'] = $v_data['typeflag']) == "5") {
       
  1783             $v_header['size'] = 0;
       
  1784         }
       
  1785         $v_header['link'] = trim($v_data['link']);
       
  1786         /* ----- All these fields are removed form the header because
       
  1787         they do not carry interesting info
       
  1788         $v_header[magic] = trim($v_data[magic]);
       
  1789         $v_header[version] = trim($v_data[version]);
       
  1790         $v_header[uname] = trim($v_data[uname]);
       
  1791         $v_header[gname] = trim($v_data[gname]);
       
  1792         $v_header[devmajor] = trim($v_data[devmajor]);
       
  1793         $v_header[devminor] = trim($v_data[devminor]);
       
  1794         */
       
  1795 
       
  1796         return true;
       
  1797     }
       
  1798 
       
  1799     /**
       
  1800      * Detect and report a malicious file name
       
  1801      *
       
  1802      * @param string $file
       
  1803      *
       
  1804      * @return bool
       
  1805      */
       
  1806     private function _maliciousFilename($file)
       
  1807     {
       
  1808         if (strpos($file, '/../') !== false) {
       
  1809             return true;
       
  1810         }
       
  1811         if (strpos($file, '../') === 0) {
       
  1812             return true;
       
  1813         }
       
  1814         return false;
       
  1815     }
       
  1816 
       
  1817     /**
       
  1818      * @param $v_header
       
  1819      * @return bool
       
  1820      */
       
  1821     public function _readLongHeader(&$v_header)
       
  1822     {
       
  1823         $v_filename = '';
       
  1824         $v_filesize = $v_header['size'];
       
  1825         $n = floor($v_header['size'] / 512);
       
  1826         for ($i = 0; $i < $n; $i++) {
       
  1827             $v_content = $this->_readBlock();
       
  1828             $v_filename .= $v_content;
       
  1829         }
       
  1830         if (($v_header['size'] % 512) != 0) {
       
  1831             $v_content = $this->_readBlock();
       
  1832             $v_filename .= $v_content;
       
  1833         }
       
  1834 
       
  1835         // ----- Read the next header
       
  1836         $v_binary_data = $this->_readBlock();
       
  1837 
       
  1838         if (!$this->_readHeader($v_binary_data, $v_header)) {
       
  1839             return false;
       
  1840         }
       
  1841 
       
  1842         $v_filename = rtrim(substr($v_filename, 0, $v_filesize), "\0");
       
  1843         $v_header['filename'] = $v_filename;
       
  1844         if ($this->_maliciousFilename($v_filename)) {
       
  1845             $this->_error(
       
  1846                 'Malicious .tar detected, file "' . $v_filename .
       
  1847                 '" will not install in desired directory tree'
       
  1848             );
       
  1849             return false;
       
  1850         }
       
  1851 
       
  1852         return true;
       
  1853     }
       
  1854 
       
  1855     /**
       
  1856      * This method extract from the archive one file identified by $p_filename.
       
  1857      * The return value is a string with the file content, or null on error.
       
  1858      *
       
  1859      * @param string $p_filename The path of the file to extract in a string.
       
  1860      *
       
  1861      * @return a string with the file content or null.
       
  1862      */
       
  1863     private function _extractInString($p_filename)
       
  1864     {
       
  1865         $v_result_str = "";
       
  1866 
       
  1867         while (strlen($v_binary_data = $this->_readBlock()) != 0) {
       
  1868             if (!$this->_readHeader($v_binary_data, $v_header)) {
       
  1869                 return null;
       
  1870             }
       
  1871 
       
  1872             if ($v_header['filename'] == '') {
       
  1873                 continue;
       
  1874             }
       
  1875 
       
  1876             // ----- Look for long filename
       
  1877             if ($v_header['typeflag'] == 'L') {
       
  1878                 if (!$this->_readLongHeader($v_header)) {
       
  1879                     return null;
       
  1880                 }
       
  1881             }
       
  1882 
       
  1883             if ($v_header['filename'] == $p_filename) {
       
  1884                 if ($v_header['typeflag'] == "5") {
       
  1885                     $this->_error(
       
  1886                         'Unable to extract in string a directory '
       
  1887                         . 'entry {' . $v_header['filename'] . '}'
       
  1888                     );
       
  1889                     return null;
       
  1890                 } else {
       
  1891                     $n = floor($v_header['size'] / 512);
       
  1892                     for ($i = 0; $i < $n; $i++) {
       
  1893                         $v_result_str .= $this->_readBlock();
       
  1894                     }
       
  1895                     if (($v_header['size'] % 512) != 0) {
       
  1896                         $v_content = $this->_readBlock();
       
  1897                         $v_result_str .= substr(
       
  1898                             $v_content,
       
  1899                             0,
       
  1900                             ($v_header['size'] % 512)
       
  1901                         );
       
  1902                     }
       
  1903                     return $v_result_str;
       
  1904                 }
       
  1905             } else {
       
  1906                 $this->_jumpBlock(ceil(($v_header['size'] / 512)));
       
  1907             }
       
  1908         }
       
  1909 
       
  1910         return null;
       
  1911     }
       
  1912 
       
  1913     /**
       
  1914      * @param string $p_path
       
  1915      * @param string $p_list_detail
       
  1916      * @param string $p_mode
       
  1917      * @param string $p_file_list
       
  1918      * @param string $p_remove_path
       
  1919      * @param bool $p_preserve
       
  1920      * @return bool
       
  1921      */
       
  1922     public function _extractList(
       
  1923         $p_path,
       
  1924         &$p_list_detail,
       
  1925         $p_mode,
       
  1926         $p_file_list,
       
  1927         $p_remove_path,
       
  1928         $p_preserve = false
       
  1929     ) {
       
  1930         $v_result = true;
       
  1931         $v_nb = 0;
       
  1932         $v_extract_all = true;
       
  1933         $v_listing = false;
       
  1934 
       
  1935         $p_path = $this->_translateWinPath($p_path, false);
       
  1936         if ($p_path == '' || (substr($p_path, 0, 1) != '/'
       
  1937                 && substr($p_path, 0, 3) != "../" && !strpos($p_path, ':'))
       
  1938         ) {
       
  1939             $p_path = "./" . $p_path;
       
  1940         }
       
  1941         $p_remove_path = $this->_translateWinPath($p_remove_path);
       
  1942 
       
  1943         // ----- Look for path to remove format (should end by /)
       
  1944         if (($p_remove_path != '') && (substr($p_remove_path, -1) != '/')) {
       
  1945             $p_remove_path .= '/';
       
  1946         }
       
  1947         $p_remove_path_size = strlen($p_remove_path);
       
  1948 
       
  1949         switch ($p_mode) {
       
  1950             case "complete" :
       
  1951                 $v_extract_all = true;
       
  1952                 $v_listing = false;
       
  1953                 break;
       
  1954             case "partial" :
       
  1955                 $v_extract_all = false;
       
  1956                 $v_listing = false;
       
  1957                 break;
       
  1958             case "list" :
       
  1959                 $v_extract_all = false;
       
  1960                 $v_listing = true;
       
  1961                 break;
       
  1962             default :
       
  1963                 $this->_error('Invalid extract mode (' . $p_mode . ')');
       
  1964                 return false;
       
  1965         }
       
  1966 
       
  1967         clearstatcache();
       
  1968 
       
  1969         while (strlen($v_binary_data = $this->_readBlock()) != 0) {
       
  1970             $v_extract_file = false;
       
  1971             $v_extraction_stopped = 0;
       
  1972 
       
  1973             if (!$this->_readHeader($v_binary_data, $v_header)) {
       
  1974                 return false;
       
  1975             }
       
  1976 
       
  1977             if ($v_header['filename'] == '') {
       
  1978                 continue;
       
  1979             }
       
  1980 
       
  1981             // ----- Look for long filename
       
  1982             if ($v_header['typeflag'] == 'L') {
       
  1983                 if (!$this->_readLongHeader($v_header)) {
       
  1984                     return false;
       
  1985                 }
       
  1986             }
       
  1987 
       
  1988             // ignore extended / pax headers
       
  1989             if ($v_header['typeflag'] == 'x' || $v_header['typeflag'] == 'g') {
       
  1990                 $this->_jumpBlock(ceil(($v_header['size'] / 512)));
       
  1991                 continue;
       
  1992             }
       
  1993 
       
  1994             if ((!$v_extract_all) && (is_array($p_file_list))) {
       
  1995                 // ----- By default no unzip if the file is not found
       
  1996                 $v_extract_file = false;
       
  1997 
       
  1998                 for ($i = 0; $i < sizeof($p_file_list); $i++) {
       
  1999                     // ----- Look if it is a directory
       
  2000                     if (substr($p_file_list[$i], -1) == '/') {
       
  2001                         // ----- Look if the directory is in the filename path
       
  2002                         if ((strlen($v_header['filename']) > strlen($p_file_list[$i]))
       
  2003                             && (substr($v_header['filename'], 0, strlen($p_file_list[$i]))
       
  2004                                 == $p_file_list[$i])
       
  2005                         ) {
       
  2006                             $v_extract_file = true;
       
  2007                             break;
       
  2008                         }
       
  2009                     } // ----- It is a file, so compare the file names
       
  2010                     elseif ($p_file_list[$i] == $v_header['filename']) {
       
  2011                         $v_extract_file = true;
       
  2012                         break;
       
  2013                     }
       
  2014                 }
       
  2015             } else {
       
  2016                 $v_extract_file = true;
       
  2017             }
       
  2018 
       
  2019             // ----- Look if this file need to be extracted
       
  2020             if (($v_extract_file) && (!$v_listing)) {
       
  2021                 if (($p_remove_path != '')
       
  2022                     && (substr($v_header['filename'] . '/', 0, $p_remove_path_size)
       
  2023                         == $p_remove_path)
       
  2024                 ) {
       
  2025                     $v_header['filename'] = substr(
       
  2026                         $v_header['filename'],
       
  2027                         $p_remove_path_size
       
  2028                     );
       
  2029                     if ($v_header['filename'] == '') {
       
  2030                         continue;
       
  2031                     }
       
  2032                 }
       
  2033                 if (($p_path != './') && ($p_path != '/')) {
       
  2034                     while (substr($p_path, -1) == '/') {
       
  2035                         $p_path = substr($p_path, 0, strlen($p_path) - 1);
       
  2036                     }
       
  2037 
       
  2038                     if (substr($v_header['filename'], 0, 1) == '/') {
       
  2039                         $v_header['filename'] = $p_path . $v_header['filename'];
       
  2040                     } else {
       
  2041                         $v_header['filename'] = $p_path . '/' . $v_header['filename'];
       
  2042                     }
       
  2043                 }
       
  2044                 if (file_exists($v_header['filename'])) {
       
  2045                     if ((@is_dir($v_header['filename']))
       
  2046                         && ($v_header['typeflag'] == '')
       
  2047                     ) {
       
  2048                         $this->_error(
       
  2049                             'File ' . $v_header['filename']
       
  2050                             . ' already exists as a directory'
       
  2051                         );
       
  2052                         return false;
       
  2053                     }
       
  2054                     if (($this->_isArchive($v_header['filename']))
       
  2055                         && ($v_header['typeflag'] == "5")
       
  2056                     ) {
       
  2057                         $this->_error(
       
  2058                             'Directory ' . $v_header['filename']
       
  2059                             . ' already exists as a file'
       
  2060                         );
       
  2061                         return false;
       
  2062                     }
       
  2063                     if (!is_writeable($v_header['filename'])) {
       
  2064                         $this->_error(
       
  2065                             'File ' . $v_header['filename']
       
  2066                             . ' already exists and is write protected'
       
  2067                         );
       
  2068                         return false;
       
  2069                     }
       
  2070                     if (filemtime($v_header['filename']) > $v_header['mtime']) {
       
  2071                         // To be completed : An error or silent no replace ?
       
  2072                     }
       
  2073                 } // ----- Check the directory availability and create it if necessary
       
  2074                 elseif (($v_result
       
  2075                         = $this->_dirCheck(
       
  2076                         ($v_header['typeflag'] == "5"
       
  2077                             ? $v_header['filename']
       
  2078                             : dirname($v_header['filename']))
       
  2079                     )) != 1
       
  2080                 ) {
       
  2081                     $this->_error('Unable to create path for ' . $v_header['filename']);
       
  2082                     return false;
       
  2083                 }
       
  2084 
       
  2085                 if ($v_extract_file) {
       
  2086                     if ($v_header['typeflag'] == "5") {
       
  2087                         if (!@file_exists($v_header['filename'])) {
       
  2088                             if (!@mkdir($v_header['filename'], 0777)) {
       
  2089                                 $this->_error(
       
  2090                                     'Unable to create directory {'
       
  2091                                     . $v_header['filename'] . '}'
       
  2092                                 );
       
  2093                                 return false;
       
  2094                             }
       
  2095                         }
       
  2096                     } elseif ($v_header['typeflag'] == "2") {
       
  2097                         if (@file_exists($v_header['filename'])) {
       
  2098                             @drupal_unlink($v_header['filename']);
       
  2099                         }
       
  2100                         if (!@symlink($v_header['link'], $v_header['filename'])) {
       
  2101                             $this->_error(
       
  2102                                 'Unable to extract symbolic link {'
       
  2103                                 . $v_header['filename'] . '}'
       
  2104                             );
       
  2105                             return false;
       
  2106                         }
       
  2107                     } else {
       
  2108                         if (($v_dest_file = @fopen($v_header['filename'], "wb")) == 0) {
       
  2109                             $this->_error(
       
  2110                                 'Error while opening {' . $v_header['filename']
       
  2111                                 . '} in write binary mode'
       
  2112                             );
       
  2113                             return false;
       
  2114                         } else {
       
  2115                             $n = floor($v_header['size'] / 512);
       
  2116                             for ($i = 0; $i < $n; $i++) {
       
  2117                                 $v_content = $this->_readBlock();
       
  2118                                 fwrite($v_dest_file, $v_content, 512);
       
  2119                             }
       
  2120                             if (($v_header['size'] % 512) != 0) {
       
  2121                                 $v_content = $this->_readBlock();
       
  2122                                 fwrite($v_dest_file, $v_content, ($v_header['size'] % 512));
       
  2123                             }
       
  2124 
       
  2125                             @fclose($v_dest_file);
       
  2126 
       
  2127                             if ($p_preserve) {
       
  2128                                 @chown($v_header['filename'], $v_header['uid']);
       
  2129                                 @chgrp($v_header['filename'], $v_header['gid']);
       
  2130                             }
       
  2131 
       
  2132                             // ----- Change the file mode, mtime
       
  2133                             @touch($v_header['filename'], $v_header['mtime']);
       
  2134                             if ($v_header['mode'] & 0111) {
       
  2135                                 // make file executable, obey umask
       
  2136                                 $mode = fileperms($v_header['filename']) | (~umask() & 0111);
       
  2137                                 @chmod($v_header['filename'], $mode);
       
  2138                             }
       
  2139                         }
       
  2140 
       
  2141                         // ----- Check the file size
       
  2142                         clearstatcache();
       
  2143                         if (!is_file($v_header['filename'])) {
       
  2144                             $this->_error(
       
  2145                                 'Extracted file ' . $v_header['filename']
       
  2146                                 . 'does not exist. Archive may be corrupted.'
       
  2147                             );
       
  2148                             return false;
       
  2149                         }
       
  2150 
       
  2151                         $filesize = filesize($v_header['filename']);
       
  2152                         if ($filesize != $v_header['size']) {
       
  2153                             $this->_error(
       
  2154                                 'Extracted file ' . $v_header['filename']
       
  2155                                 . ' does not have the correct file size \''
       
  2156                                 . $filesize
       
  2157                                 . '\' (' . $v_header['size']
       
  2158                                 . ' expected). Archive may be corrupted.'
       
  2159                             );
       
  2160                             return false;
       
  2161                         }
       
  2162                     }
       
  2163                 } else {
       
  2164                     $this->_jumpBlock(ceil(($v_header['size'] / 512)));
       
  2165                 }
       
  2166             } else {
       
  2167                 $this->_jumpBlock(ceil(($v_header['size'] / 512)));
       
  2168             }
       
  2169 
       
  2170             /* TBC : Seems to be unused ...
       
  2171             if ($this->_compress)
       
  2172               $v_end_of_file = @gzeof($this->_file);
       
  2173             else
       
  2174               $v_end_of_file = @feof($this->_file);
       
  2175               */
       
  2176 
       
  2177             if ($v_listing || $v_extract_file || $v_extraction_stopped) {
       
  2178                 // ----- Log extracted files
       
  2179                 if (($v_file_dir = dirname($v_header['filename']))
       
  2180                     == $v_header['filename']
       
  2181                 ) {
       
  2182                     $v_file_dir = '';
       
  2183                 }
       
  2184                 if ((substr($v_header['filename'], 0, 1) == '/') && ($v_file_dir == '')) {
       
  2185                     $v_file_dir = '/';
       
  2186                 }
       
  2187 
       
  2188                 $p_list_detail[$v_nb++] = $v_header;
       
  2189                 if (is_array($p_file_list) && (count($p_list_detail) == count($p_file_list))) {
       
  2190                     return true;
       
  2191                 }
       
  2192             }
       
  2193         }
       
  2194 
       
  2195         return true;
       
  2196     }
       
  2197 
       
  2198     /**
       
  2199      * @return bool
       
  2200      */
       
  2201     public function _openAppend()
       
  2202     {
       
  2203         if (filesize($this->_tarname) == 0) {
       
  2204             return $this->_openWrite();
       
  2205         }
       
  2206 
       
  2207         if ($this->_compress) {
       
  2208             $this->_close();
       
  2209 
       
  2210             if (!@rename($this->_tarname, $this->_tarname . ".tmp")) {
       
  2211                 $this->_error(
       
  2212                     'Error while renaming \'' . $this->_tarname
       
  2213                     . '\' to temporary file \'' . $this->_tarname
       
  2214                     . '.tmp\''
       
  2215                 );
       
  2216                 return false;
       
  2217             }
       
  2218 
       
  2219             if ($this->_compress_type == 'gz') {
       
  2220                 $v_temp_tar = @gzopen($this->_tarname . ".tmp", "rb");
       
  2221             } elseif ($this->_compress_type == 'bz2') {
       
  2222                 $v_temp_tar = @bzopen($this->_tarname . ".tmp", "r");
       
  2223             } elseif ($this->_compress_type == 'lzma2') {
       
  2224                 $v_temp_tar = @xzopen($this->_tarname . ".tmp", "r");
       
  2225             }
       
  2226 
       
  2227 
       
  2228             if ($v_temp_tar == 0) {
       
  2229                 $this->_error(
       
  2230                     'Unable to open file \'' . $this->_tarname
       
  2231                     . '.tmp\' in binary read mode'
       
  2232                 );
       
  2233                 @rename($this->_tarname . ".tmp", $this->_tarname);
       
  2234                 return false;
       
  2235             }
       
  2236 
       
  2237             if (!$this->_openWrite()) {
       
  2238                 @rename($this->_tarname . ".tmp", $this->_tarname);
       
  2239                 return false;
       
  2240             }
       
  2241 
       
  2242             if ($this->_compress_type == 'gz') {
       
  2243                 $end_blocks = 0;
       
  2244 
       
  2245                 while (!@gzeof($v_temp_tar)) {
       
  2246                     $v_buffer = @gzread($v_temp_tar, 512);
       
  2247                     if ($v_buffer == ARCHIVE_TAR_END_BLOCK || strlen($v_buffer) == 0) {
       
  2248                         $end_blocks++;
       
  2249                         // do not copy end blocks, we will re-make them
       
  2250                         // after appending
       
  2251                         continue;
       
  2252                     } elseif ($end_blocks > 0) {
       
  2253                         for ($i = 0; $i < $end_blocks; $i++) {
       
  2254                             $this->_writeBlock(ARCHIVE_TAR_END_BLOCK);
       
  2255                         }
       
  2256                         $end_blocks = 0;
       
  2257                     }
       
  2258                     $v_binary_data = pack("a512", $v_buffer);
       
  2259                     $this->_writeBlock($v_binary_data);
       
  2260                 }
       
  2261 
       
  2262                 @gzclose($v_temp_tar);
       
  2263             } elseif ($this->_compress_type == 'bz2') {
       
  2264                 $end_blocks = 0;
       
  2265 
       
  2266                 while (strlen($v_buffer = @bzread($v_temp_tar, 512)) > 0) {
       
  2267                     if ($v_buffer == ARCHIVE_TAR_END_BLOCK || strlen($v_buffer) == 0) {
       
  2268                         $end_blocks++;
       
  2269                         // do not copy end blocks, we will re-make them
       
  2270                         // after appending
       
  2271                         continue;
       
  2272                     } elseif ($end_blocks > 0) {
       
  2273                         for ($i = 0; $i < $end_blocks; $i++) {
       
  2274                             $this->_writeBlock(ARCHIVE_TAR_END_BLOCK);
       
  2275                         }
       
  2276                         $end_blocks = 0;
       
  2277                     }
       
  2278                     $v_binary_data = pack("a512", $v_buffer);
       
  2279                     $this->_writeBlock($v_binary_data);
       
  2280                 }
       
  2281 
       
  2282                 @bzclose($v_temp_tar);
       
  2283             } elseif ($this->_compress_type == 'lzma2') {
       
  2284                 $end_blocks = 0;
       
  2285 
       
  2286                 while (strlen($v_buffer = @xzread($v_temp_tar, 512)) > 0) {
       
  2287                     if ($v_buffer == ARCHIVE_TAR_END_BLOCK || strlen($v_buffer) == 0) {
       
  2288                         $end_blocks++;
       
  2289                         // do not copy end blocks, we will re-make them
       
  2290                         // after appending
       
  2291                         continue;
       
  2292                     } elseif ($end_blocks > 0) {
       
  2293                         for ($i = 0; $i < $end_blocks; $i++) {
       
  2294                             $this->_writeBlock(ARCHIVE_TAR_END_BLOCK);
       
  2295                         }
       
  2296                         $end_blocks = 0;
       
  2297                     }
       
  2298                     $v_binary_data = pack("a512", $v_buffer);
       
  2299                     $this->_writeBlock($v_binary_data);
       
  2300                 }
       
  2301 
       
  2302                 @xzclose($v_temp_tar);
       
  2303             }
       
  2304 
       
  2305             if (!@drupal_unlink($this->_tarname . ".tmp")) {
       
  2306                 $this->_error(
       
  2307                     'Error while deleting temporary file \''
       
  2308                     . $this->_tarname . '.tmp\''
       
  2309                 );
       
  2310             }
       
  2311         } else {
       
  2312             // ----- For not compressed tar, just add files before the last
       
  2313             //       one or two 512 bytes block
       
  2314             if (!$this->_openReadWrite()) {
       
  2315                 return false;
       
  2316             }
       
  2317 
       
  2318             clearstatcache();
       
  2319             $v_size = filesize($this->_tarname);
       
  2320 
       
  2321             // We might have zero, one or two end blocks.
       
  2322             // The standard is two, but we should try to handle
       
  2323             // other cases.
       
  2324             fseek($this->_file, $v_size - 1024);
       
  2325             if (fread($this->_file, 512) == ARCHIVE_TAR_END_BLOCK) {
       
  2326                 fseek($this->_file, $v_size - 1024);
       
  2327             } elseif (fread($this->_file, 512) == ARCHIVE_TAR_END_BLOCK) {
       
  2328                 fseek($this->_file, $v_size - 512);
       
  2329             }
       
  2330         }
       
  2331 
       
  2332         return true;
       
  2333     }
       
  2334 
       
  2335     /**
       
  2336      * @param $p_filelist
       
  2337      * @param string $p_add_dir
       
  2338      * @param string $p_remove_dir
       
  2339      * @return bool
       
  2340      */
       
  2341     public function _append($p_filelist, $p_add_dir = '', $p_remove_dir = '')
       
  2342     {
       
  2343         if (!$this->_openAppend()) {
       
  2344             return false;
       
  2345         }
       
  2346 
       
  2347         if ($this->_addList($p_filelist, $p_add_dir, $p_remove_dir)) {
       
  2348             $this->_writeFooter();
       
  2349         }
       
  2350 
       
  2351         $this->_close();
       
  2352 
       
  2353         return true;
       
  2354     }
       
  2355 
       
  2356     /**
       
  2357      * Check if a directory exists and create it (including parent
       
  2358      * dirs) if not.
       
  2359      *
       
  2360      * @param string $p_dir directory to check
       
  2361      *
       
  2362      * @return bool true if the directory exists or was created
       
  2363      */
       
  2364     public function _dirCheck($p_dir)
       
  2365     {
       
  2366         clearstatcache();
       
  2367         if ((@is_dir($p_dir)) || ($p_dir == '')) {
       
  2368             return true;
       
  2369         }
       
  2370 
       
  2371         $p_parent_dir = dirname($p_dir);
       
  2372 
       
  2373         if (($p_parent_dir != $p_dir) &&
       
  2374             ($p_parent_dir != '') &&
       
  2375             (!$this->_dirCheck($p_parent_dir))
       
  2376         ) {
       
  2377             return false;
       
  2378         }
       
  2379 
       
  2380         if (!@mkdir($p_dir, 0777)) {
       
  2381             $this->_error("Unable to create directory '$p_dir'");
       
  2382             return false;
       
  2383         }
       
  2384 
       
  2385         return true;
       
  2386     }
       
  2387 
       
  2388     /**
       
  2389      * Compress path by changing for example "/dir/foo/../bar" to "/dir/bar",
       
  2390      * rand emove double slashes.
       
  2391      *
       
  2392      * @param string $p_dir path to reduce
       
  2393      *
       
  2394      * @return string reduced path
       
  2395      */
       
  2396     private function _pathReduction($p_dir)
       
  2397     {
       
  2398         $v_result = '';
       
  2399 
       
  2400         // ----- Look for not empty path
       
  2401         if ($p_dir != '') {
       
  2402             // ----- Explode path by directory names
       
  2403             $v_list = explode('/', $p_dir);
       
  2404 
       
  2405             // ----- Study directories from last to first
       
  2406             for ($i = sizeof($v_list) - 1; $i >= 0; $i--) {
       
  2407                 // ----- Look for current path
       
  2408                 if ($v_list[$i] == ".") {
       
  2409                     // ----- Ignore this directory
       
  2410                     // Should be the first $i=0, but no check is done
       
  2411                 } else {
       
  2412                     if ($v_list[$i] == "..") {
       
  2413                         // ----- Ignore it and ignore the $i-1
       
  2414                         $i--;
       
  2415                     } else {
       
  2416                         if (($v_list[$i] == '')
       
  2417                             && ($i != (sizeof($v_list) - 1))
       
  2418                             && ($i != 0)
       
  2419                         ) {
       
  2420                             // ----- Ignore only the double '//' in path,
       
  2421                             // but not the first and last /
       
  2422                         } else {
       
  2423                             $v_result = $v_list[$i] . ($i != (sizeof($v_list) - 1) ? '/'
       
  2424                                     . $v_result : '');
       
  2425                         }
       
  2426                     }
       
  2427                 }
       
  2428             }
       
  2429         }
       
  2430 
       
  2431         if (defined('OS_WINDOWS') && OS_WINDOWS) {
       
  2432             $v_result = strtr($v_result, '\\', '/');
       
  2433         }
       
  2434 
       
  2435         return $v_result;
       
  2436     }
       
  2437 
       
  2438     /**
       
  2439      * @param $p_path
       
  2440      * @param bool $p_remove_disk_letter
       
  2441      * @return string
       
  2442      */
       
  2443     public function _translateWinPath($p_path, $p_remove_disk_letter = true)
       
  2444     {
       
  2445         if (defined('OS_WINDOWS') && OS_WINDOWS) {
       
  2446             // ----- Look for potential disk letter
       
  2447             if (($p_remove_disk_letter)
       
  2448                 && (($v_position = strpos($p_path, ':')) != false)
       
  2449             ) {
       
  2450                 $p_path = substr($p_path, $v_position + 1);
       
  2451             }
       
  2452             // ----- Change potential windows directory separator
       
  2453             if ((strpos($p_path, '\\') > 0) || (substr($p_path, 0, 1) == '\\')) {
       
  2454                 $p_path = strtr($p_path, '\\', '/');
       
  2455             }
       
  2456         }
       
  2457         return $p_path;
       
  2458     }
       
  2459 }