eZ Publish  [4.0]
eztararchivehandler.php
Go to the documentation of this file.
00001 <?php
00002 /* vim: set ts=4 sw=4: */
00003 // +----------------------------------------------------------------------+
00004 // | PHP Version 5                                                        |
00005 // +----------------------------------------------------------------------+
00006 // | Copyright (c) 1997-2004 The PHP Group                                |
00007 // +----------------------------------------------------------------------+
00008 // | This source file is subject to version 3.0 of the PHP license,       |
00009 // | that is bundled with this package in the file LICENSE, and is        |
00010 // | available through the world-wide-web at the following url:           |
00011 // | http://www.php.net/license/3_0.txt.                                  |
00012 // | If you did not receive a copy of the PHP license and are unable to   |
00013 // | obtain it through the world-wide-web, please send a note to          |
00014 // | license@php.net so we can mail you a copy immediately.               |
00015 // +----------------------------------------------------------------------+
00016 // | Author: Vincent Blavet <vincent@blavet.net>                          |
00017 // +----------------------------------------------------------------------+
00018 //
00019 // $Id: Tar.php,v 1.20 2004/01/08 17:33:09 sniper Exp $
00020 
00021 //include_once( 'lib/ezfile/classes/ezarchivehandler.php' );
00022 
00023 /**
00024 * Creates a (compressed) Tar archive
00025 *
00026 * @author   Vincent Blavet <vincent@blavet.net>
00027 * @version  $Revision: 1.20 $
00028 * @package  Archive
00029 */
00030 class eZTARArchiveHandler extends eZArchiveHandler
00031 {
00032     /**
00033     * @var string Name of the Tar
00034     */
00035     public $_tarname='';
00036 
00037     /**
00038     * @var boolean if true, the Tar file will be gzipped
00039     */
00040     public $_compress=false;
00041 
00042 //     /**
00043 //     * @var file descriptor
00044 //     */
00045 //     public $_file=0;
00046     /**
00047     * @var string Type of compression : 'none', 'gz' or 'bz2'
00048     */
00049     public $_compress_type='none';
00050 
00051     /**
00052     * @var string Local Tar name of a remote Tar (http:// or ftp://)
00053     */
00054     public $_temp_tarname='';
00055 
00056     // {{{ constructor
00057     /**
00058     * Archive_Tar Class constructor. This flavour of the constructor only
00059     * declare a new Archive_Tar object, identifying it by the name of the
00060     * tar file.
00061     * If the compress argument is set the tar will be read or created as a
00062     * gzip or bz2 compressed TAR file.
00063     *
00064     * @param    string  $p_tarname  The name of the tar archive to create
00065     * @param    string  $p_compress can be null, 'gz' or 'bz2'. This
00066     *                   parameter indicates if gzip or bz2 compression
00067     *                   is required.  For compatibility reason the
00068     *                   boolean value 'true' means 'gz'.
00069     * @access public
00070     */
00071     function eZTARArchiveHandler( &$fileHandler, $p_tarname, $p_compress = false )
00072     {
00073         $this->eZArchiveHandler( $fileHandler );
00074 //         $this->PEAR();
00075         $this->_tarname = $p_tarname;
00076 //         if ($p_compress) { // assert zlib extension support
00077 //             $extname = 'zlib';
00078 //             if (!extension_loaded($extname)) {
00079 //                 $dlext = (OS_WINDOWS) ? '.dll' : '.so';
00080 //                 @dl($extname . $dlext);
00081 //             }
00082 //             if (!extension_loaded($extname)) {
00083 //                 die("The extension '$extname' couldn't be loaded. ".
00084 //                     'Probably you don\'t have support in your PHP '.
00085 //                     'to this extension');
00086 //                 return false;
00087 //             }
00088 //         }
00089         $this->_compress = $p_compress;
00090     }
00091     // }}}
00092 
00093     // {{{ destructor
00094     function _eZTARArchiveHandler()
00095     {
00096         $this->_close();
00097         // ----- Look for a local copy to delete
00098         if ( $this->_temp_tarname != '' )
00099             $this->fileUnlink( $this->_temp_tarname );
00100 //         $this->_PEAR();
00101     }
00102     // }}}
00103 
00104     // {{{ create()
00105     /**
00106     * This method creates the archive file and add the files / directories
00107     * that are listed in $p_filelist.
00108     * If a file with the same name exist and is writable, it is replaced
00109     * by the new tar.
00110     * The method return false and a PEAR error text.
00111     * The $p_filelist parameter can be an array of string, each string
00112     * representing a filename or a directory name with their path if
00113     * needed. It can also be a single string with names separated by a
00114     * single blank.
00115     * For each directory added in the archive, the files and
00116     * sub-directories are also added.
00117     * See also createModify() method for more details.
00118     *
00119     * @param array  $p_filelist An array of filenames and directory names, or a single
00120     *                           string with names separated by a single blank space.
00121     * @return                   true on success, false on error.
00122     * @see createModify()
00123     * @access public
00124     */
00125     function create($p_filelist)
00126     {
00127         return $this->createModify($p_filelist, '', '');
00128     }
00129     // }}}
00130 
00131     // {{{ add()
00132     /**
00133     * This method add the files / directories that are listed in $p_filelist in
00134     * the archive. If the archive does not exist it is created.
00135     * The method return false and a PEAR error text.
00136     * The files and directories listed are only added at the end of the archive,
00137     * even if a file with the same name is already archived.
00138     * See also createModify() method for more details.
00139     *
00140     * @param array  $p_filelist An array of filenames and directory names, or a single
00141     *                           string with names separated by a single blank space.
00142     * @return                   true on success, false on error.
00143     * @see createModify()
00144     * @access public
00145     */
00146     function add($p_filelist)
00147     {
00148         return $this->addModify($p_filelist, '', '');
00149     }
00150     // }}}
00151 
00152     // {{{ extract()
00153     function extract($p_path='')
00154     {
00155         return $this->extractModify($p_path, '');
00156     }
00157     // }}}
00158 
00159     // {{{ listContent()
00160     function listContent()
00161     {
00162         $v_list_detail = array();
00163 
00164         if ($this->_openRead()) {
00165             if (!$this->_extractList('', $v_list_detail, "list", '', '')) {
00166                 unset($v_list_detail);
00167                 $v_list_detail = 0;
00168             }
00169             $this->_close();
00170         }
00171 
00172         return $v_list_detail;
00173     }
00174     // }}}
00175 
00176     // {{{ createModify()
00177     /**
00178     * This method creates the archive file and add the files / directories
00179     * that are listed in $p_filelist.
00180     * If the file already exists and is writable, it is replaced by the
00181     * new tar. It is a create and not an add. If the file exists and is
00182     * read-only or is a directory it is not replaced. The method return
00183     * false and a PEAR error text.
00184     * The $p_filelist parameter can be an array of string, each string
00185     * representing a filename or a directory name with their path if
00186     * needed. It can also be a single string with names separated by a
00187     * single blank.
00188     * The path indicated in $p_remove_dir will be removed from the
00189     * memorized path of each file / directory listed when this path
00190     * exists. By default nothing is removed (empty path '')
00191     * The path indicated in $p_add_dir will be added at the beginning of
00192     * the memorized path of each file / directory listed. However it can
00193     * be set to empty ''. The adding of a path is done after the removing
00194     * of path.
00195     * The path add/remove ability enables the user to prepare an archive
00196     * for extraction in a different path than the origin files are.
00197     * See also addModify() method for file adding properties.
00198     *
00199     * @param array  $p_filelist     An array of filenames and directory names, or a single
00200     *                               string with names separated by a single blank space.
00201     * @param string $p_add_dir      A string which contains a path to be added to the
00202     *                               memorized path of each element in the list.
00203     * @param string $p_remove_dir   A string which contains a path to be removed from
00204     *                               the memorized path of each element in the list, when
00205     *                               relevant.
00206     * @return boolean               true on success, false on error.
00207     * @access public
00208     * @see addModify()
00209     */
00210     function createModify($p_filelist, $p_add_dir, $p_remove_dir='')
00211     {
00212         $v_result = true;
00213 
00214         if (!$this->_openWrite())
00215             return false;
00216 
00217         if ($p_filelist != '') {
00218             if (is_array($p_filelist))
00219                 $v_list = $p_filelist;
00220             elseif (is_string($p_filelist))
00221                 $v_list = explode(" ", $p_filelist);
00222             else {
00223                 $this->_cleanFile();
00224                 $this->_error('Invalid file list');
00225                 return false;
00226             }
00227 
00228             $v_result = $this->_addList($v_list, $p_add_dir, $p_remove_dir);
00229         }
00230 
00231         if ($v_result) {
00232             $this->_writeFooter();
00233             $this->_close();
00234         } else
00235             $this->_cleanFile();
00236 
00237         return $v_result;
00238     }
00239     // }}}
00240 
00241     // {{{ addModify()
00242     /**
00243     * This method add the files / directories listed in $p_filelist at the
00244     * end of the existing archive. If the archive does not yet exists it
00245     * is created.
00246     * The $p_filelist parameter can be an array of string, each string
00247     * representing a filename or a directory name with their path if
00248     * needed. It can also be a single string with names separated by a
00249     * single blank.
00250     * The path indicated in $p_remove_dir will be removed from the
00251     * memorized path of each file / directory listed when this path
00252     * exists. By default nothing is removed (empty path '')
00253     * The path indicated in $p_add_dir will be added at the beginning of
00254     * the memorized path of each file / directory listed. However it can
00255     * be set to empty ''. The adding of a path is done after the removing
00256     * of path.
00257     * The path add/remove ability enables the user to prepare an archive
00258     * for extraction in a different path than the origin files are.
00259     * If a file/dir is already in the archive it will only be added at the
00260     * end of the archive. There is no update of the existing archived
00261     * file/dir. However while extracting the archive, the last file will
00262     * replace the first one. This results in a none optimization of the
00263     * archive size.
00264     * If a file/dir does not exist the file/dir is ignored. However an
00265     * error text is send to PEAR error.
00266     * If a file/dir is not readable the file/dir is ignored. However an
00267     * error text is send to PEAR error.
00268     *
00269     * @param array      $p_filelist     An array of filenames and directory names, or a single
00270     *                                   string with names separated by a single blank space.
00271     * @param string     $p_add_dir      A string which contains a path to be added to the
00272     *                                   memorized path of each element in the list.
00273     * @param string     $p_remove_dir   A string which contains a path to be removed from
00274     *                                   the memorized path of each element in the list, when
00275     *                                   relevant.
00276     * @return                           true on success, false on error.
00277     * @access public
00278     */
00279     function addModify($p_filelist, $p_add_dir, $p_remove_dir='')
00280     {
00281         $v_result = true;
00282 
00283         if (!@is_file($this->_tarname))
00284             $v_result = $this->createModify($p_filelist, $p_add_dir, $p_remove_dir);
00285         else {
00286             if (is_array($p_filelist))
00287                 $v_list = $p_filelist;
00288             elseif (is_string($p_filelist))
00289                 $v_list = explode(" ", $p_filelist);
00290             else {
00291                 $this->_error('Invalid file list');
00292                 return false;
00293             }
00294 
00295             $v_result = $this->_append($v_list, $p_add_dir, $p_remove_dir);
00296         }
00297 
00298         return $v_result;
00299     }
00300     // }}}
00301 
00302     // {{{ addString()
00303     /**
00304     * This method add a single string as a file at the
00305     * end of the existing archive. If the archive does not yet exists it
00306     * is created.
00307     *
00308     * @param string     $p_filename     A string which contains the full filename path
00309     *                                   that will be associated with the string.
00310     * @param string     $p_string       The content of the file added in the archive.
00311     * @return                           true on success, false on error.
00312     * @access public
00313     */
00314     function addString($p_filename, $p_string)
00315     {
00316         $v_result = true;
00317 
00318         if (!@is_file($this->_tarname)) {
00319             if (!$this->_openWrite()) {
00320                 return false;
00321             }
00322             $this->_close();
00323         }
00324 
00325         if (!$this->_openAppend())
00326             return false;
00327 
00328         // Need to check the get back to the temporary file ? ....
00329         $v_result = $this->_addString($p_filename, $p_string);
00330 
00331         $this->_writeFooter();
00332 
00333         $this->_close();
00334 
00335         return $v_result;
00336     }
00337     // }}}
00338 
00339     // {{{ extractModify()
00340     /**
00341     * This method extract all the content of the archive in the directory
00342     * indicated by $p_path. When relevant the memorized path of the
00343     * files/dir can be modified by removing the $p_remove_path path at the
00344     * beginning of the file/dir path.
00345     * While extracting a file, if the directory path does not exists it is
00346     * created.
00347     * While extracting a file, if the file already exists it is replaced
00348     * without looking for last modification date.
00349     * While extracting a file, if the file already exists and is write
00350     * protected, the extraction is aborted.
00351     * While extracting a file, if a directory with the same name already
00352     * exists, the extraction is aborted.
00353     * While extracting a directory, if a file with the same name already
00354     * exists, the extraction is aborted.
00355     * While extracting a file/directory if the destination directory exist
00356     * and is write protected, or does not exist but can not be created,
00357     * the extraction is aborted.
00358     * If after extraction an extracted file does not show the correct
00359     * stored file size, the extraction is aborted.
00360     * When the extraction is aborted, a PEAR error text is set and false
00361     * is returned. However the result can be a partial extraction that may
00362     * need to be manually cleaned.
00363     *
00364     * @param string $p_path         The path of the directory where the files/dir need to by
00365     *                               extracted.
00366     * @param string $p_remove_path  Part of the memorized path that can be removed if
00367     *                               present at the beginning of the file/dir path.
00368     * @return boolean               true on success, false on error.
00369     * @access public
00370     * @see extractList()
00371     */
00372     function extractModify($p_path, $p_remove_path)
00373     {
00374         $v_result = true;
00375         $v_list_detail = array();
00376 
00377         if ($v_result = $this->_openRead()) {
00378             $v_result = $this->_extractList($p_path, $v_list_detail, "complete", 0, $p_remove_path);
00379             $this->_close();
00380         }
00381 
00382         return $v_result;
00383     }
00384     // }}}
00385 
00386     // {{{ extractInString()
00387     /**
00388     * This method extract from the archive one file identified by $p_filename.
00389     * The return value is a string with the file content, or NULL on error.
00390     * @param string $p_filename     The path of the file to extract in a string.
00391     * @return                       a string with the file content or NULL.
00392     * @access public
00393     */
00394     function extractInString($p_filename)
00395     {
00396         if ($this->_openRead()) {
00397             $v_result = $this->_extractInString($p_filename);
00398             $this->_close();
00399         } else {
00400             $v_result = NULL;
00401         }
00402 
00403         return $v_result;
00404     }
00405     // }}}
00406 
00407     // {{{ extractList()
00408     /**
00409     * This method extract from the archive only the files indicated in the
00410     * $p_filelist. These files are extracted in the current directory or
00411     * in the directory indicated by the optional $p_path parameter.
00412     * If indicated the $p_remove_path can be used in the same way as it is
00413     * used in extractModify() method.
00414     * @param array  $p_filelist     An array of filenames and directory names, or a single
00415     *                               string with names separated by a single blank space.
00416     * @param string $p_path         The path of the directory where the files/dir need to by
00417     *                               extracted.
00418     * @param string $p_remove_path  Part of the memorized path that can be removed if
00419     *                               present at the beginning of the file/dir path.
00420     * @return                       true on success, false on error.
00421     * @access public
00422     * @see extractModify()
00423     */
00424     function extractList($p_filelist, $p_path='', $p_remove_path='')
00425     {
00426         $v_result = true;
00427         $v_list_detail = array();
00428 
00429         if (is_array($p_filelist))
00430             $v_list = $p_filelist;
00431         elseif (is_string($p_filelist))
00432             $v_list = explode(" ", $p_filelist);
00433         else {
00434             $this->_error('Invalid string list');
00435             return false;
00436         }
00437 
00438         if ($v_result = $this->_openRead()) {
00439             $v_result = $this->_extractList($p_path, $v_list_detail, "partial", $v_list, $p_remove_path);
00440             $this->_close();
00441         }
00442 
00443         return $v_result;
00444     }
00445     // }}}
00446 
00447     // {{{ _error()
00448     function _error($p_message)
00449     {
00450         // ----- To be completed
00451 //         $this->raiseError($p_message);
00452         eZDebug::writeError( $p_message, 'eZTARArchiveHandler' );
00453     }
00454     // }}}
00455 
00456     // {{{ _warning()
00457     function _warning($p_message)
00458     {
00459         // ----- To be completed
00460 //         $this->raiseError($p_message);
00461         eZDebug::writeWarning( $p_message, 'eZTARArchiveHandler' );
00462     }
00463     // }}}
00464 
00465     // {{{ _openWrite()
00466     function _openWrite()
00467     {
00468         $result = $this->fileOpen( $this->_tarname, 'w' );
00469 //         if ($this->_compress)
00470 //             $this->_file = @gzopen($this->_tarname, "w");
00471 //         else
00472 //             $this->_file = @fopen($this->_tarname, "w");
00473 
00474         if ( $result == 0 )
00475         {
00476             $this->_error('Unable to open in write mode \''.$this->_tarname.'\'');
00477             return false;
00478         }
00479 
00480         return true;
00481     }
00482     // }}}
00483 
00484     // {{{ _openRead()
00485     function _openRead()
00486     {
00487         if (strtolower(substr($this->_tarname, 0, 7)) == 'http://') {
00488 
00489           // ----- Look if a local copy need to be done
00490           if ($this->_temp_tarname == '')
00491           {
00492               $this->_temp_tarname = uniqid('tar').'.tmp';
00493               eZFileHandler::copy( $this->_tarname,
00494                                    $this->_temp_tarname );
00495 //               if (!$v_file_from = @fopen($this->_tarname, 'rb'))
00496 //               {
00497 //                 $this->_error('Unable to open in read mode \''.$this->_tarname.'\'');
00498 //                 $this->_temp_tarname = '';
00499 //                 return false;
00500 //               }
00501 //               if (!$v_file_to = @fopen($this->_temp_tarname, 'wb'))
00502 //               {
00503 //                 $this->_error('Unable to open in write mode \''.$this->_temp_tarname.'\'');
00504 //                 $this->_temp_tarname = '';
00505 //                 return false;
00506 //               }
00507 //               while ($v_data = @fread($v_file_from, 1024))
00508 //                   @fwrite($v_file_to, $v_data);
00509 //               @fclose($v_file_from);
00510 //               @fclose($v_file_to);
00511           }
00512 
00513           // ----- File to open if the local copy
00514           $v_filename = $this->_temp_tarname;
00515 
00516         } else
00517           // ----- File to open if the normal Tar file
00518           $v_filename = $this->_tarname;
00519 
00520         $result = $this->fileOpen( $v_filename, 'rb' );
00521 //         if ($this->_compress)
00522 //             $this->_file = @gzopen($v_filename, "rb");
00523 //         else
00524 //             $this->_file = @fopen($v_filename, "rb");
00525 
00526         if ( $result == 0 )
00527         {
00528             $this->_error('Unable to open in read mode \''.$v_filename.'\'');
00529             return false;
00530         }
00531 
00532         return true;
00533     }
00534     // }}}
00535 
00536     // {{{ _openReadWrite()
00537     function _openReadWrite()
00538     {
00539         $result = $this->fileOpen( $this->_tarname, 'r+b' );
00540 //         if ($this->_compress)
00541 //             $this->_file = @gzopen($this->_tarname, "r+b");
00542 //         else
00543 //             $this->_file = @fopen($this->_tarname, "r+b");
00544 
00545         if ( $result == 0 )
00546         {
00547             $this->_error('Unable to open in read/write mode \''.$this->_tarname.'\'');
00548             return false;
00549         }
00550 
00551         return true;
00552     }
00553     // }}}
00554 
00555     // {{{ _close()
00556     function _close()
00557     {
00558 //         if (isset($this->_file))
00559 //         {
00560 //             if ($this->_compress)
00561 //                 @gzclose($this->_file);
00562 //             else
00563 //                 @fclose($this->_file);
00564 
00565 //             $this->_file = 0;
00566 //         }
00567         $this->fileClose();
00568 
00569         // ----- Look if a local copy need to be erased
00570         // Note that it might be interesting to keep the url for a time : ToDo
00571         if ( $this->_temp_tarname != '' )
00572         {
00573             $this->fileUnlink( $this->_temp_tarname );
00574             $this->_temp_tarname = '';
00575         }
00576 
00577         return true;
00578     }
00579     // }}}
00580 
00581     // {{{ _cleanFile()
00582     function _cleanFile()
00583     {
00584         $this->_close();
00585 
00586         // ----- Look for a local copy
00587         if ($this->_temp_tarname != '')
00588         {
00589             // ----- Remove the local copy but not the remote tarname
00590             $this->fileUnlink( $this->_temp_tarname );
00591             $this->_temp_tarname = '';
00592         }
00593         else
00594         {
00595             // ----- Remove the local tarname file
00596             $this->fileUnlink();
00597         }
00598         $this->_tarname = '';
00599 
00600         return true;
00601     }
00602     // }}}
00603 
00604     // {{{ _writeBlock()
00605     function _writeBlock($p_binary_data, $p_len=null)
00606     {
00607       if ($this->_file) {
00608           if ($p_len === null) {
00609               if ($this->_compress_type == 'gz')
00610                   @gzputs($this->_file, $p_binary_data);
00611               else if ($this->_compress_type == 'bz2')
00612                   @bzwrite($this->_file, $p_binary_data);
00613               else if ($this->_compress_type == 'none')
00614                   @fputs($this->_file, $p_binary_data);
00615               else
00616                   $this->_error('Unknown or missing compression type ('.$this->_compress_type.')');
00617           } else {
00618               if ($this->_compress_type == 'gz')
00619                   @gzputs($this->_file, $p_binary_data, $p_len);
00620               else if ($this->_compress_type == 'bz2')
00621                   @bzwrite($this->_file, $p_binary_data, $p_len);
00622               else if ($this->_compress_type == 'none')
00623                   @fputs($this->_file, $p_binary_data, $p_len);
00624               else
00625                   $this->_error('Unknown or missing compression type ('.$this->_compress_type.')');
00626 
00627           }
00628       }
00629       return true;
00630     }
00631     // }}}
00632 
00633     // {{{ _readBlock()
00634     function _readBlock($p_len=null)
00635     {
00636       $v_block = null;
00637       if ($this->_file) {
00638           if ($p_len === null)
00639               $p_len = 512;
00640 
00641           if ($this->_compress_type == 'gz')
00642               $v_block = @gzread($this->_file, 512);
00643           else if ($this->_compress_type == 'bz2')
00644               $v_block = @bzread($this->_file, 512);
00645           else if ($this->_compress_type == 'none')
00646               $v_block = @fread($this->_file, 512);
00647           else
00648               $this->_error('Unknown or missing compression type ('.$this->_compress_type.')');
00649 
00650       }
00651       return $v_block;
00652     }
00653     // }}}
00654 
00655     // {{{ _jumpBlock()
00656     function _jumpBlock($p_len=null)
00657     {
00658       if ($this->_file) {
00659           if ($p_len === null)
00660               $p_len = 1;
00661 
00662           if ($this->_compress_type == 'gz')
00663               @gzseek($this->_file, @gztell($this->_file)+($p_len*512));
00664           else if ($this->_compress_type == 'bz2') {
00665               // ----- Replace missing bztell() and bzseek()
00666               for ($i=0; $i<$p_len; $i++)
00667                   $this->_readBlock();
00668           } else if ($this->_compress_type == 'none')
00669               @fseek($this->_file, @ftell($this->_file)+($p_len*512));
00670           else
00671               $this->_error('Unknown or missing compression type ('.$this->_compress_type.')');
00672 
00673       }
00674       return true;
00675     }
00676     // }}}
00677 
00678     // {{{ _addFile()
00679     function _addFile($p_filename, &$p_header, $p_add_dir, $p_remove_dir)
00680     {
00681 //       if (!$this->_file)
00682 //       {
00683 //           $this->_error('Invalid file descriptor');
00684 //           return false;
00685 //       }
00686       if ( !$this->fileIsOpen() )
00687       {
00688           $this->_error( 'File is not open' );
00689           return false;
00690       }
00691 
00692       if ($p_filename == '')
00693       {
00694           $this->_error('Invalid file name');
00695           return false;
00696       }
00697 
00698       // ----- Calculate the stored filename
00699       $p_filename = $this->_translateWinPath($p_filename, false);
00700       $v_stored_filename = $p_filename;
00701       if (strcmp($p_filename, $p_remove_dir) == 0)
00702       {
00703           return true;
00704       }
00705       if ($p_remove_dir != '')
00706       {
00707           if (substr($p_remove_dir, -1) != '/')
00708               $p_remove_dir .= '/';
00709 
00710           if (substr($p_filename, 0, strlen($p_remove_dir)) == $p_remove_dir)
00711               $v_stored_filename = substr($p_filename, strlen($p_remove_dir));
00712       }
00713       $v_stored_filename = $this->_translateWinPath($v_stored_filename);
00714       if ($p_add_dir != '') {
00715           if (substr($p_add_dir, -1) == '/')
00716               $v_stored_filename = $p_add_dir.$v_stored_filename;
00717           else
00718               $v_stored_filename = $p_add_dir.'/'.$v_stored_filename;
00719       }
00720 
00721       $isFile = $this->fileIsFile( $p_filename );
00722       if ( !$isFile )
00723       {
00724           // Make sure directory names end with a slash
00725           if ( $v_stored_filename[ strlen( $v_stored_filename ) - 1 ] != '/' )
00726               $v_stored_filename .= '/';
00727       }
00728 
00729       if (strlen($v_stored_filename) > 99)
00730       {
00731           $this->_warning("Stored file name is too long (max. 99) : '$v_stored_filename'");
00732           // Cannot do this, $v_file is not available
00733 //           fclose($v_file);
00734           return true;
00735       }
00736       $v_stored_filename = $this->_pathReduction($v_stored_filename);
00737 
00738       if ( $isFile )
00739       {
00740           if (($v_file = @fopen($p_filename, "rb")) == 0)
00741           {
00742               $this->_warning("Unable to open file '$p_filename' in binary read mode");
00743               return true;
00744           }
00745 
00746           if (!$this->_writeHeader($p_filename, $v_stored_filename))
00747               return false;
00748 
00749           while (($v_buffer = fread($v_file, 512)) != '')
00750           {
00751               $v_binary_data = pack("a512", "$v_buffer");
00752 //               if ($this->_compress)
00753 //                   @gzputs($this->_file, $v_binary_data);
00754 //               else
00755 //                   @fputs($this->_file, $v_binary_data);
00756               $this->fileWrite( $v_binary_data );
00757           }
00758 
00759           fclose($v_file);
00760 
00761       }
00762       else
00763       {
00764           // ----- Only header for dir
00765           if (!$this->_writeHeader($p_filename, $v_stored_filename))
00766               return false;
00767       }
00768 
00769       return true;
00770     }
00771     // }}}
00772 
00773     // {{{ _addString()
00774     function _addString($p_filename, $p_string)
00775     {
00776       if (!$this->_file) {
00777           $this->_error('Invalid file descriptor');
00778           return false;
00779       }
00780 
00781       if ($p_filename == '') {
00782           $this->_error('Invalid file name');
00783           return false;
00784       }
00785 
00786       // ----- Calculate the stored filename
00787       $p_filename = $this->_translateWinPath($p_filename, false);
00788 
00789       if (!$this->_writeHeaderBlock($p_filename, strlen($p_string), 0, 0, "", 0, 0))
00790           return false;
00791 
00792       $i=0;
00793       while (($v_buffer = substr($p_string, (($i++)*512), 512)) != '') {
00794           $v_binary_data = pack("a512", $v_buffer);
00795           $this->_writeBlock($v_binary_data);
00796       }
00797 
00798       return true;
00799     }
00800     // }}}
00801 
00802     // {{{ _writeFooter()
00803     function _writeFooter()
00804     {
00805       if ( $this->fileIsOpen() )
00806       {
00807           $endBlocks = ( filesize( $this->_tarname ) / 512 ) + 1;
00808           $blockPadding = 20;
00809           $modulo = $endBlocks % $blockPadding;
00810           if ( $modulo == 0 )
00811           {
00812               $blockCount = $blockPadding;
00813           }
00814           else
00815           {
00816               $blockCount = ( $blockPadding - $modulo ) + 1;
00817           }
00818           // ----- Write the last 0 filled block for end of archive and pad it to 20 blocks
00819           for ( $i = 0; $i < $blockCount; ++$i )
00820           {
00821               $v_binary_data = pack("a512", '');
00822               $this->fileWrite( $v_binary_data );
00823           }
00824       }
00825       return true;
00826     }
00827     // }}}
00828 
00829     // {{{ _addList()
00830     function _addList($p_list, $p_add_dir, $p_remove_dir)
00831     {
00832       $v_result=true;
00833       $v_header = array();
00834 
00835 //       if (!$this->_file )
00836 //       {
00837 //           $this->_error('Invalid file descriptor');
00838 //           return false;
00839 //       }
00840       if ( !$this->fileIsOpen() )
00841       {
00842           $this->_error( 'File is not open' );
00843       // ----- Remove potential windows directory separator
00844 //      $p_add_dir = $this->_translateWinPath($p_add_dir);
00845 //      $p_remove_dir = $this->_translateWinPath($p_remove_dir, false);
00846 //
00847 //      if (!$this->_file) {
00848 //          $this->_error('Invalid file descriptor');
00849           return false;
00850       }
00851 
00852       if ( sizeof($p_list) == 0 )
00853           return true;
00854 
00855       for ( $j = 0; ( $j < count( $p_list ) ) && ( $v_result ); $j++ )
00856       {
00857         $v_filename = $p_list[$j];
00858 
00859         // ----- Skip the current tar name
00860         if ($v_filename == $this->_tarname)
00861             continue;
00862 
00863         if ($v_filename == '')
00864             continue;
00865 
00866         if ( !$this->fileExists( $v_filename ) )
00867         {
00868             $this->_warning("File '$v_filename' does not exist");
00869             continue;
00870         }
00871 
00872         // ----- Add the file or directory header
00873         if (!$this->_addFile($v_filename, $v_header, $p_add_dir, $p_remove_dir))
00874             return false;
00875 
00876         if ( $this->fileIsDirectory( $v_filename ) )
00877         {
00878             if (!($p_hdir = opendir($v_filename)))
00879             {
00880                 $this->_warning("Directory '$v_filename' can not be read");
00881                 continue;
00882             }
00883             while (false !== ($p_hitem = readdir($p_hdir))) {
00884                 if ( $p_hitem == '.' || $p_hitem == '..' )
00885                     continue;
00886 
00887                 if ($v_filename != ".")
00888                     $p_temp_list[0] = $v_filename.'/'.$p_hitem;
00889                 else
00890                     $p_temp_list[0] = $p_hitem;
00891 
00892                 $v_result = $this->_addList($p_temp_list, $p_add_dir, $p_remove_dir);
00893             }
00894 
00895             unset($p_temp_list);
00896             unset($p_hdir);
00897             unset($p_hitem);
00898         }
00899       }
00900 
00901       return $v_result;
00902     }
00903     // }}}
00904 
00905     // {{{ _writeHeader()
00906     function _writeHeader($p_filename, $p_stored_filename)
00907     {
00908         if ($p_stored_filename == '')
00909             $p_stored_filename = $p_filename;
00910         $v_reduce_filename = $this->_pathReduction($p_stored_filename);
00911 
00912         $v_info = $this->fileStatistics( $p_filename );
00913         if (strlen($v_reduce_filename) > 99) {
00914           if (!$this->_writeLongHeader($v_reduce_filename))
00915             return false;
00916         }
00917 
00918         $v_info = stat($p_filename);
00919         $v_uid = sprintf("%6s ", DecOct($v_info[4]));
00920         $v_gid = sprintf("%6s ", DecOct($v_info[5]));
00921         $v_perms = sprintf("%6s ", DecOct(fileperms($p_filename)));
00922 
00923         $v_mtime = sprintf("%11s", DecOct(filemtime($p_filename)));
00924 
00925         if ( $this->fileIsDirectory( $p_filename ))
00926         {
00927           $v_typeflag = "5";
00928           $v_size = sprintf("%11s ", DecOct(0));
00929         }
00930         else
00931         {
00932             $v_typeflag = '';
00933             clearstatcache();
00934             $v_size = sprintf("%11s ", DecOct(filesize($p_filename)));
00935         }
00936 
00937         $v_linkname = '';
00938 
00939         $v_magic = '';
00940 
00941         $v_version = '';
00942 
00943         $v_uname = '';
00944 
00945         $v_gname = '';
00946 
00947         $v_devmajor = '';
00948 
00949         $v_devminor = '';
00950 
00951         $v_prefix = '';
00952 
00953         $v_binary_data_first = pack("a100a8a8a8a12A12", $v_reduce_filename, $v_perms, $v_uid, $v_gid, $v_size, $v_mtime);
00954         $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12", $v_typeflag, $v_linkname, $v_magic, $v_version, $v_uname, $v_gname, $v_devmajor, $v_devminor, $v_prefix, '');
00955 
00956         // ----- Calculate the checksum
00957         $v_checksum = 0;
00958         // ..... First part of the header
00959         for ($i=0; $i<148; $i++)
00960             $v_checksum += ord(substr($v_binary_data_first,$i,1));
00961         // ..... Ignore the checksum value and replace it by ' ' (space)
00962         for ($i=148; $i<156; $i++)
00963             $v_checksum += ord(' ');
00964         // ..... Last part of the header
00965         for ($i=156, $j=0; $i<512; $i++, $j++)
00966             $v_checksum += ord(substr($v_binary_data_last,$j,1));
00967 
00968         // ----- Write the first 148 bytes of the header in the archive
00969 //         if ($this->_compress)
00970 //             @gzputs($this->_file, $v_binary_data_first, 148);
00971 //         else
00972 //             @fputs($this->_file, $v_binary_data_first, 148);
00973         $this->fileWrite( $v_binary_data_first, 148 );
00974 
00975         // ----- Write the calculated checksum
00976         $v_checksum = sprintf("%6s ", DecOct($v_checksum));
00977         $v_binary_data = pack("a8", $v_checksum);
00978 //         if ($this->_compress)
00979 //           @gzputs($this->_file, $v_binary_data, 8);
00980 //         else
00981 //           @fputs($this->_file, $v_binary_data, 8);
00982         $this->fileWrite( $v_binary_data, 8 );
00983 
00984         // ----- Write the last 356 bytes of the header in the archive
00985 //         if ($this->_compress)
00986 //             @gzputs($this->_file, $v_binary_data_last, 356);
00987 //         else
00988 //             @fputs($this->_file, $v_binary_data_last, 356);
00989         $this->fileWrite( $v_binary_data_last, 356 );
00990 
00991         return true;
00992     }
00993     // }}}
00994 
00995     // {{{ _writeHeaderBlock()
00996     function _writeHeaderBlock($p_filename, $p_size, $p_mtime=0, $p_perms=0, $p_type='', $p_uid=0, $p_gid=0)
00997     {
00998         $p_filename = $this->_pathReduction($p_filename);
00999 
01000         if (strlen($p_filename) > 99) {
01001           if (!$this->_writeLongHeader($p_filename))
01002             return false;
01003         }
01004 
01005         if ($p_type == "5") {
01006           $v_size = sprintf("%11s ", DecOct(0));
01007         } else {
01008           $v_size = sprintf("%11s ", DecOct($p_size));
01009         }
01010 
01011         $v_uid = sprintf("%6s ", DecOct($p_uid));
01012         $v_gid = sprintf("%6s ", DecOct($p_gid));
01013         $v_perms = sprintf("%6s ", DecOct($p_perms));
01014 
01015         $v_mtime = sprintf("%11s", DecOct($p_mtime));
01016 
01017         $v_linkname = '';
01018 
01019         $v_magic = '';
01020 
01021         $v_version = '';
01022 
01023         $v_uname = '';
01024 
01025         $v_gname = '';
01026 
01027         $v_devmajor = '';
01028 
01029         $v_devminor = '';
01030 
01031         $v_prefix = '';
01032 
01033         $v_binary_data_first = pack("a100a8a8a8a12A12", $p_filename, $v_perms, $v_uid, $v_gid, $v_size, $v_mtime);
01034         $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12", $p_type, $v_linkname, $v_magic, $v_version, $v_uname, $v_gname, $v_devmajor, $v_devminor, $v_prefix, '');
01035 
01036         // ----- Calculate the checksum
01037         $v_checksum = 0;
01038         // ..... First part of the header
01039         for ($i=0; $i<148; $i++)
01040             $v_checksum += ord(substr($v_binary_data_first,$i,1));
01041         // ..... Ignore the checksum value and replace it by ' ' (space)
01042         for ($i=148; $i<156; $i++)
01043             $v_checksum += ord(' ');
01044         // ..... Last part of the header
01045         for ($i=156, $j=0; $i<512; $i++, $j++)
01046             $v_checksum += ord(substr($v_binary_data_last,$j,1));
01047 
01048         // ----- Write the first 148 bytes of the header in the archive
01049         $this->_writeBlock($v_binary_data_first, 148);
01050 
01051         // ----- Write the calculated checksum
01052         $v_checksum = sprintf("%6s ", DecOct($v_checksum));
01053         $v_binary_data = pack("a8", $v_checksum);
01054         $this->_writeBlock($v_binary_data, 8);
01055 
01056         // ----- Write the last 356 bytes of the header in the archive
01057         $this->_writeBlock($v_binary_data_last, 356);
01058 
01059         return true;
01060     }
01061     // }}}
01062 
01063     // {{{ _writeLongHeader()
01064     function _writeLongHeader($p_filename)
01065     {
01066         $v_size = sprintf("%11s ", DecOct(strlen($p_filename)));
01067 
01068         $v_typeflag = 'L';
01069 
01070         $v_linkname = '';
01071 
01072         $v_magic = '';
01073 
01074         $v_version = '';
01075 
01076         $v_uname = '';
01077 
01078         $v_gname = '';
01079 
01080         $v_devmajor = '';
01081 
01082         $v_devminor = '';
01083 
01084         $v_prefix = '';
01085 
01086         $v_binary_data_first = pack("a100a8a8a8a12A12", '././@LongLink', 0, 0, 0, $v_size, 0);
01087         $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12", $v_typeflag, $v_linkname, $v_magic, $v_version, $v_uname, $v_gname, $v_devmajor, $v_devminor, $v_prefix, '');
01088 
01089         // ----- Calculate the checksum
01090         $v_checksum = 0;
01091         // ..... First part of the header
01092         for ($i=0; $i<148; $i++)
01093             $v_checksum += ord(substr($v_binary_data_first,$i,1));
01094         // ..... Ignore the checksum value and replace it by ' ' (space)
01095         for ($i=148; $i<156; $i++)
01096             $v_checksum += ord(' ');
01097         // ..... Last part of the header
01098         for ($i=156, $j=0; $i<512; $i++, $j++)
01099             $v_checksum += ord(substr($v_binary_data_last,$j,1));
01100 
01101         // ----- Write the first 148 bytes of the header in the archive
01102         $this->_writeBlock($v_binary_data_first, 148);
01103 
01104         // ----- Write the calculated checksum
01105         $v_checksum = sprintf("%6s ", DecOct($v_checksum));
01106         $v_binary_data = pack("a8", $v_checksum);
01107         $this->_writeBlock($v_binary_data, 8);
01108 
01109         // ----- Write the last 356 bytes of the header in the archive
01110         $this->_writeBlock($v_binary_data_last, 356);
01111 
01112         // ----- Write the filename as content of the block
01113         $i=0;
01114         while (($v_buffer = substr($p_filename, (($i++)*512), 512)) != '') {
01115             $v_binary_data = pack("a512", "$v_buffer");
01116             $this->_writeBlock($v_binary_data);
01117         }
01118 
01119         return true;
01120     }
01121     // }}}
01122 
01123     // {{{ _readHeader()
01124     function _readHeader($v_binary_data, &$v_header)
01125     {
01126         if (strlen($v_binary_data)==0) {
01127             $v_header['filename'] = '';
01128             return true;
01129         }
01130 
01131         if (strlen($v_binary_data) != 512) {
01132             $v_header['filename'] = '';
01133             $this->_error('Invalid block size : '.strlen($v_binary_data));
01134             return false;
01135         }
01136 
01137         // ----- Calculate the checksum
01138         $v_checksum = 0;
01139         // ..... First part of the header
01140         for ($i=0; $i<148; $i++)
01141             $v_checksum+=ord(substr($v_binary_data,$i,1));
01142         // ..... Ignore the checksum value and replace it by ' ' (space)
01143         for ($i=148; $i<156; $i++)
01144             $v_checksum += ord(' ');
01145         // ..... Last part of the header
01146         for ($i=156; $i<512; $i++)
01147            $v_checksum+=ord(substr($v_binary_data,$i,1));
01148 
01149         $v_data = unpack("a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/a8checksum/a1typeflag/a100link/a6magic/a2version/a32uname/a32gname/a8devmajor/a8devminor", $v_binary_data);
01150 
01151         // ----- Extract the checksum
01152         $v_header['checksum'] = OctDec(trim($v_data['checksum']));
01153         if ($v_header['checksum'] != $v_checksum) {
01154             $v_header['filename'] = '';
01155 
01156             // ----- Look for last block (empty block)
01157             if (($v_checksum == 256) && ($v_header['checksum'] == 0))
01158                 return true;
01159 
01160             $this->_error('Invalid checksum for file "'.$v_data['filename'].'" : '.$v_checksum.' calculated, '.$v_header['checksum'].' expected');
01161             return false;
01162         }
01163 
01164         // ----- Extract the properties
01165         $v_header['filename'] = trim($v_data['filename']);
01166         $v_header['mode'] = OctDec(trim($v_data['mode']));
01167         $v_header['uid'] = OctDec(trim($v_data['uid']));
01168         $v_header['gid'] = OctDec(trim($v_data['gid']));
01169         $v_header['size'] = OctDec(trim($v_data['size']));
01170         $v_header['mtime'] = OctDec(trim($v_data['mtime']));
01171         if (($v_header['typeflag'] = $v_data['typeflag']) == "5") {
01172           $v_header['size'] = 0;
01173         }
01174         /* ----- All these fields are removed form the header because they do not carry interesting info
01175         $v_header[link] = trim($v_data[link]);
01176         $v_header[magic] = trim($v_data[magic]);
01177         $v_header[version] = trim($v_data[version]);
01178         $v_header[uname] = trim($v_data[uname]);
01179         $v_header[gname] = trim($v_data[gname]);
01180         $v_header[devmajor] = trim($v_data[devmajor]);
01181         $v_header[devminor] = trim($v_data[devminor]);
01182         */
01183 
01184         return true;
01185     }
01186     // }}}
01187 
01188     // {{{ _readLongHeader()
01189     function _readLongHeader(&$v_header)
01190     {
01191       $v_filename = '';
01192       $n = floor($v_header['size']/512);
01193       for ($i=0; $i<$n; $i++) {
01194         $v_content = $this->_readBlock();
01195         $v_filename .= $v_content;
01196       }
01197       if (($v_header['size'] % 512) != 0) {
01198         $v_content = $this->_readBlock();
01199         $v_filename .= $v_content;
01200       }
01201 
01202       // ----- Read the next header
01203       $v_binary_data = $this->_readBlock();
01204 
01205       if (!$this->_readHeader($v_binary_data, $v_header))
01206         return false;
01207 
01208       $v_header['filename'] = $v_filename;
01209 
01210       return true;
01211     }
01212     // }}}
01213 
01214     // {{{ _extractInString()
01215     /**
01216     * This method extract from the archive one file identified by $p_filename.
01217     * The return value is a string with the file content, or NULL on error.
01218     * @param string $p_filename     The path of the file to extract in a string.
01219     * @return                       a string with the file content or NULL.
01220     * @access private
01221     */
01222     function _extractInString($p_filename)
01223     {
01224         $v_result_str = "";
01225 
01226         While (strlen($v_binary_data = $this->_readBlock()) != 0)
01227         {
01228           if (!$this->_readHeader($v_binary_data, $v_header))
01229             return NULL;
01230 
01231           if ($v_header['filename'] == '')
01232             continue;
01233 
01234           // ----- Look for long filename
01235           if ($v_header['typeflag'] == 'L') {
01236             if (!$this->_readLongHeader($v_header))
01237               return NULL;
01238           }
01239 
01240           if ($v_header['filename'] == $p_filename) {
01241               if ($v_header['typeflag'] == "5") {
01242                   $this->_error('Unable to extract in string a directory entry {'.$v_header['filename'].'}');
01243                   return NULL;
01244               } else {
01245                   $n = floor($v_header['size']/512);
01246                   for ($i=0; $i<$n; $i++) {
01247                       $v_result_str .= $this->_readBlock();
01248                   }
01249                   if (($v_header['size'] % 512) != 0) {
01250                       $v_content = $this->_readBlock();
01251                       $v_result_str .= substr($v_content, 0, ($v_header['size'] % 512));
01252                   }
01253                   return $v_result_str;
01254               }
01255           } else {
01256               $this->_jumpBlock(ceil(($v_header['size']/512)));
01257           }
01258         }
01259 
01260         return NULL;
01261     }
01262     // }}}
01263 
01264     // {{{ _extractList()
01265     function _extractList($p_path, &$p_list_detail, $p_mode, $p_file_list, $p_remove_path)
01266     {
01267     $v_result=true;
01268     $v_nb = 0;
01269     $v_extract_all = true;
01270     $v_listing = false;
01271 
01272     // ----- Look for removing the WINDOW '\'
01273     if ( eZSys::osType() == 'win32' && strpos($p_path, '\\'))
01274     {
01275         str_replace('\\', '/', $p_path);
01276     }
01277 
01278     if ($p_path == '' || (substr($p_path, 0, 1) != '/' && substr($p_path, 0, 3) != "../" && !strpos($p_path, ':'))) {
01279       $p_path = "./".$p_path;
01280     }
01281     $p_remove_path = $this->_translateWinPath($p_remove_path);
01282 
01283     // ----- Look for path to remove format (should end by /)
01284     if (($p_remove_path != '') && (substr($p_remove_path, -1) != '/'))
01285       $p_remove_path .= '/';
01286     $p_remove_path_size = strlen($p_remove_path);
01287 
01288     switch ($p_mode) {
01289       case "complete" :
01290         $v_extract_all = TRUE;
01291         $v_listing = FALSE;
01292       break;
01293       case "partial" :
01294           $v_extract_all = FALSE;
01295           $v_listing = FALSE;
01296       break;
01297       case "list" :
01298           $v_extract_all = FALSE;
01299           $v_listing = TRUE;
01300       break;
01301       default :
01302         $this->_error('Invalid extract mode ('.$p_mode.')');
01303         return false;
01304     }
01305 
01306     clearstatcache();
01307 
01308 //     While (!($v_end_of_file = ($this->_compress?@gzeof($this->_file):@feof($this->_file))))
01309     while (!($v_end_of_file = ($this->fileEOF())))
01310     {
01311       $v_extract_file = FALSE;
01312       $v_extraction_stopped = 0;
01313 
01314 //       if ($this->_compress)
01315 //         $v_binary_data = @gzread($this->_file, 512);
01316 //       else
01317 //         $v_binary_data = @fread($this->_file, 512);
01318       $v_binary_data = $this->fileRead( 512 );
01319 
01320       if (!$this->_readHeader($v_binary_data, $v_header))
01321         return false;
01322 
01323       if ($v_header['filename'] == '')
01324         continue;
01325 
01326       // ----- Look for long filename
01327       if ($v_header['typeflag'] == 'L') {
01328         if (!$this->_readLongHeader($v_header))
01329           return false;
01330       }
01331 
01332       if ((!$v_extract_all) && (is_array($p_file_list))) {
01333         // ----- By default no unzip if the file is not found
01334         $v_extract_file = false;
01335 
01336         for ($i=0; $i<sizeof($p_file_list); $i++) {
01337           // ----- Look if it is a directory
01338           if (substr($p_file_list[$i], -1) == '/') {
01339             // ----- Look if the directory is in the filename path
01340             if ((strlen($v_header['filename']) > strlen($p_file_list[$i])) && (substr($v_header['filename'], 0, strlen($p_file_list[$i])) == $p_file_list[$i])) {
01341               $v_extract_file = TRUE;
01342               break;
01343             }
01344           }
01345 
01346           // ----- It is a file, so compare the file names
01347           elseif ($p_file_list[$i] == $v_header['filename']) {
01348             $v_extract_file = TRUE;
01349             break;
01350           }
01351         }
01352       } else {
01353         $v_extract_file = TRUE;
01354       }
01355 
01356       // ----- Look if this file need to be extracted
01357       if (($v_extract_file) && (!$v_listing))
01358       {
01359         if (($p_remove_path != '')
01360             && (substr($v_header['filename'], 0, $p_remove_path_size) == $p_remove_path))
01361           $v_header['filename'] = substr($v_header['filename'], $p_remove_path_size);
01362         if (($p_path != './') && ($p_path != '/')) {
01363           while (substr($p_path, -1) == '/')
01364             $p_path = substr($p_path, 0, strlen($p_path)-1);
01365 
01366           if (substr($v_header['filename'], 0, 1) == '/')
01367               $v_header['filename'] = $p_path.$v_header['filename'];
01368           else
01369             $v_header['filename'] = $p_path.'/'.$v_header['filename'];
01370         }
01371         if (file_exists($v_header['filename'])) {
01372           if ((@is_dir($v_header['filename'])) && ($v_header['typeflag'] == '')) {
01373             $this->_error('File '.$v_header['filename'].' already exists as a directory');
01374             return false;
01375           }
01376           if ((is_file($v_header['filename'])) && ($v_header['typeflag'] == "5")) {
01377             $this->_error('Directory '.$v_header['filename'].' already exists as a file');
01378             return false;
01379           }
01380           if (!is_writeable($v_header['filename'])) {
01381             $this->_error('File '.$v_header['filename'].' already exists and is write protected');
01382             return false;
01383           }
01384           if (filemtime($v_header['filename']) > $v_header['mtime']) {
01385             // To be completed : An error or silent no replace ?
01386           }
01387         }
01388 
01389         // ----- Check the directory availability and create it if necessary
01390         elseif (($v_result = $this->_dirCheck(($v_header['typeflag'] == "5"?$v_header['filename']:dirname($v_header['filename'])))) != 1) {
01391             $this->_error('Unable to create path for '.$v_header['filename']);
01392             return false;
01393         }
01394 
01395         if ($v_extract_file) {
01396           if ($v_header['typeflag'] == "5") {
01397             if (!@file_exists($v_header['filename'])) {
01398                 if (!@eZDir::mkdir($v_header['filename'] )) {
01399                     $this->_error('Unable to create directory {'.$v_header['filename'].'}');
01400                     return false;
01401                 }
01402             }
01403           } else {
01404               if (($v_dest_file = @fopen($v_header['filename'], "wb")) == 0) {
01405                   $this->_error('Error while opening {'.$v_header['filename'].'} in write binary mode');
01406                   return false;
01407               } else {
01408                   $n = floor($v_header['size']/512);
01409                   for ($i=0; $i<$n; $i++) {
01410 //                       if ($this->_compress)
01411 //                           $v_content = @gzread($this->_file, 512);
01412 //                       else
01413 //                           $v_content = @fread($this->_file, 512);
01414                       $v_content = $this->fileRead( 512 );
01415                       fwrite($v_dest_file, $v_content, 512);
01416                   }
01417             if (($v_header['size'] % 512) != 0) {
01418 //               if ($this->_compress)
01419 //                 $v_content = @gzread($this->_file, 512);
01420 //               else
01421 //                 $v_content = @fread($this->_file, 512);
01422                 $v_content = $this->fileRead( 512 );
01423               fwrite($v_dest_file, $v_content, ($v_header['size'] % 512));
01424             }
01425 
01426             @fclose($v_dest_file);
01427 
01428             // ----- Change the file mode, mtime
01429             @touch($v_header['filename'], $v_header['mtime']);
01430             if ($v_header['mode'] & 0111) {
01431                 // make file executable, obey umask
01432                 $mode = fileperms($v_header['filename']) | (~umask() & 0111);
01433                 @chmod($v_header['filename'], $mode);
01434             }
01435             // If we don`t force this, we can never delete installed packages
01436             @chmod( $v_header['filename'],
01437                 octdec( eZINI::instance()->variable( 'FileSettings', 'StorageFilePermissions' ) ) );
01438           }
01439 
01440           // ----- Check the file size
01441           clearstatcache();
01442           if (filesize($v_header['filename']) != $v_header['size']) {
01443               $this->_error('Extracted file '.$v_header['filename'].' does not have the correct file size \''.filesize($v_filename).'\' ('.$v_header['size'].' expected). Archive may be corrupted.');
01444               return false;
01445           }
01446           }
01447         } else {
01448           // ----- Jump to next file
01449 //           if ($this->_compress)
01450 //               @gzseek($this->_file, @gztell($this->_file)+(ceil(($v_header['size']/512))*512));
01451 //           else
01452 //               @fseek($this->_file, @ftell($this->_file)+(ceil(($v_header['size']/512))*512));
01453             $seekPosition = $this->fileTell() + ( ceil( ( $v_header['size'] / 512 ) ) * 512 );
01454             $this->fileSeek( $seekPosition );
01455         }
01456       } else {
01457         // ----- Jump to next file
01458 //         if ($this->_compress)
01459 //           @gzseek($this->_file, @gztell($this->_file)+(ceil(($v_header['size']/512))*512));
01460 //         else
01461 //           @fseek($this->_file, @ftell($this->_file)+(ceil(($v_header['size']/512))*512));
01462           $seekPosition = $this->fileTell() + ( ceil ( ( $v_header['size'] / 512 ) ) * 512 );
01463           $this->fileSeek( $seekPosition );
01464       }
01465 
01466 //       if ($this->_compress)
01467 //         $v_end_of_file = @gzeof($this->_file);
01468 //       else
01469 //         $v_end_of_file = @feof($this->_file);
01470       $v_end_of_file = $this->fileEOF();
01471 
01472       if ($v_listing || $v_extract_file || $v_extraction_stopped) {
01473         // ----- Log extracted files
01474         if (($v_file_dir = dirname($v_header['filename'])) == $v_header['filename'])
01475           $v_file_dir = '';
01476         if ((substr($v_header['filename'], 0, 1) == '/') && ($v_file_dir == ''))
01477           $v_file_dir = '/';
01478 
01479         $p_list_detail[$v_nb++] = $v_header;
01480       }
01481     }
01482 
01483         return true;
01484     }
01485     // }}}
01486 
01487     // {{{ _openAppend()
01488     function _openAppend()
01489     {
01490         if (filesize($this->_tarname) == 0)
01491           return $this->_openWrite();
01492 
01493         if ($this->_compress)
01494         {
01495             // Rename to temporary file.
01496             $this->fileClose();
01497             $temporaryHandler =& $this->detachHandler();
01498             $temporaryHandler->rename( $this->_tarname . '.tmp' );
01499             if ( !$this->_openWrite() )
01500             {
01501                 $temporaryHandler->rename( $this->_tarname );
01502 //                 @rename($this->_tarname.".tmp", $this->_tarname);
01503                 return false;
01504             }
01505             $temporaryHandler->fileOpen( false, 'rb' );
01506 
01507 //             $v_buffer = @gzread($v_temp_tar, 512);
01508             $v_buffer = $temporaryHandler->read( 512 );
01509 
01510             // ----- Read the following blocks but not the last one
01511 //             if (!@gzeof($v_temp_tar)) {
01512 //                 do{
01513 //                     $v_binary_data = pack("a512", "$v_buffer");
01514 //                     @gzputs($this->_file, $v_binary_data);
01515 //                     $v_buffer = @gzread($v_temp_tar, 512);
01516 
01517 //<<<<<<< .working
01518 //                 } while (!@gzeof($v_temp_tar));
01519 //             }
01520             if ( !$temporaryHandler->eof() )
01521             {
01522                 do
01523                 {
01524                     $v_binary_data = pack( 'a512', "$v_buffer" );
01525                     $this->fileWrite( $v_binary_data );
01526                     $v_buffer = $temporaryHandler->read( 512 );
01527                 } while( !$temporaryHandler->eof() );
01528             }
01529 //            elseif ($this->_compress_type == 'bz2') {
01530 //                $v_buffered_lines   = array();
01531 //                $v_buffered_lines[] = @bzread($v_temp_tar, 512);
01532 //
01533 //                // ----- Read the following blocks but not the last one
01534 //                while (strlen($v_buffered_lines[] = @bzread($v_temp_tar, 512)) > 0) {
01535 //                    $v_binary_data = pack("a512", array_shift($v_buffered_lines));
01536 //                    $this->_writeBlock($v_binary_data);
01537 //                }
01538 //                @bzclose($v_temp_tar);
01539 //            }
01540 
01541             $this->_close();
01542 
01543 //             if (!@unlink($this->_tarname.".tmp"))
01544             if ( !$temporaryHandler->unlink() )
01545             {
01546                 $this->_error('Error while deleting temporary file \''.$this->_tarname.'.tmp\'');
01547             }
01548         }
01549         else
01550         {
01551             // ----- For not compressed tar, just add files before the last 512 bytes block
01552             if (!$this->_openReadWrite())
01553                return false;
01554 
01555             clearstatcache();
01556             $v_size = filesize($this->_tarname);
01557 //          fseek($this->_file, $v_size-512);
01558             $this->fileSeek( $v_size - 512 );
01559 
01560         }
01561 
01562         return true;
01563     }
01564     // }}}
01565 
01566     // {{{ _append()
01567     function _append($p_filelist, $p_add_dir='', $p_remove_dir='')
01568     {
01569         if (!$this->_openAppend())
01570             return false;
01571 
01572         if ($this->_addList($p_filelist, $p_add_dir, $p_remove_dir))
01573            $this->_writeFooter();
01574 
01575         $this->_close();
01576 
01577         return true;
01578     }
01579     // }}}
01580 
01581     // {{{ _dirCheck()
01582 
01583     /**
01584      * Check if a directory exists and create it (including parent
01585      * dirs) if not.
01586      *
01587      * @param string $p_dir directory to check
01588      *
01589      * @return bool TRUE if the directory exists or was created
01590      */
01591     function _dirCheck($p_dir)
01592     {
01593         if ((@is_dir($p_dir)) || ($p_dir == ''))
01594             return true;
01595 
01596         $p_parent_dir = dirname($p_dir);
01597 
01598         if (($p_parent_dir != $p_dir) &&
01599             ($p_parent_dir != '') &&
01600             (!$this->_dirCheck($p_parent_dir)))
01601              return false;
01602 
01603         if (!@eZDir::mkdir( $p_dir )) {
01604             $this->_error("Unable to create directory '$p_dir'");
01605             return false;
01606         }
01607 
01608         return true;
01609     }
01610 
01611     // }}}
01612 
01613     // {{{ _pathReduction()
01614 
01615     /**
01616      * Compress path by changing for example "/dir/foo/../bar" to "/dir/bar", and
01617      * remove double slashes.
01618      *
01619      * @param string $p_dir path to reduce
01620      *
01621      * @return string reduced path
01622      *
01623      * @access private
01624      *
01625      */
01626     function _pathReduction($p_dir)
01627     {
01628         $v_result = '';
01629 
01630         // ----- Look for not empty path
01631         if ($p_dir != '') {
01632             // ----- Explode path by directory names
01633             $v_list = explode('/', $p_dir);
01634 
01635             // ----- Study directories from last to first
01636             for ($i=sizeof($v_list)-1; $i>=0; $i--) {
01637                 // ----- Look for current path
01638                 if ($v_list[$i] == ".") {
01639                     // ----- Ignore this directory
01640                     // Should be the first $i=0, but no check is done
01641                 }
01642                 else if ($v_list[$i] == "..") {
01643                     // ----- Ignore it and ignore the $i-1
01644                     $i--;
01645                 }
01646                 else if (($v_list[$i] == '') && ($i!=(sizeof($v_list)-1)) && ($i!=0)) {
01647                     // ----- Ignore only the double '//' in path,
01648                     // but not the first and last /
01649                 } else {
01650                     $v_result = $v_list[$i].($i!=(sizeof($v_list)-1)?'/'.$v_result:'');
01651                 }
01652             }
01653         }
01654         $v_result = strtr($v_result, '\\', '/');
01655         return $v_result;
01656     }
01657 
01658     // }}}
01659 
01660     // {{{ _translateWinPath()
01661     function _translateWinPath($p_path, $p_remove_disk_letter=true)
01662     {
01663       //if (OS_WINDOWS) {
01664       if ( eZSys::osType() == 'win32' )
01665       {
01666           // ----- Look for potential disk letter
01667           if (($p_remove_disk_letter) && (($v_position = strpos($p_path, ':')) != false)) {
01668               $p_path = substr($p_path, $v_position+1);
01669           }
01670           // ----- Change potential windows directory separator
01671           if ((strpos($p_path, '\\') > 0) || (substr($p_path, 0,1) == '\\')) {
01672               $p_path = strtr($p_path, '\\', '/');
01673           }
01674       }
01675       return $p_path;
01676     }
01677     // }}}
01678 
01679 }
01680 ?>