class.em_unzip.php

Go to the documentation of this file.
00001 <?php
00002 /***************************************************************
00003 *  Copyright notice
00004 *
00005 *  (c) Vincent Blavet <vincent@phpconcept.net>
00006 *  (c) 2005-2008 Karsten Dambekalns <karsten@typo3.org>
00007 *  All rights reserved
00008 *
00009 *  This library is free software; you can redistribute it and/or
00010 *  modify it under the terms of the GNU Lesser General Public
00011 *  License as published by the Free Software Foundation; either
00012 *  version 2.1 of the License, or (at your option) any later version.
00013 *
00014 *  This library is distributed in the hope that it will be useful,
00015 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00016 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00017 *  Lesser General Public License for more details.
00018 *
00019 *  You should have received a copy of the GNU Lesser General Public
00020 *  License along with this library; if not, write to the Free Software
00021 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
00022 *  MA  02110-1301  USA
00023 *
00024 *  This copyright notice MUST APPEAR in all copies of the script!
00025 ***************************************************************/
00026 /**
00027  * Module: Extension manager
00028  *
00029  * $Id: class.em_unzip.php 3439 2008-03-16 19:16:51Z flyguide $
00030  *
00031  * @author  Vincent Blavet <vincent@phpconcept.net>
00032  * @author  Karsten Dambekalns <karsten@typo3.org>
00033  */
00034 
00035 
00036 // Constants
00037 define( 'ARCHIVE_ZIP_READ_BLOCK_SIZE', 2048 );
00038 
00039 // File list separator
00040 define( 'ARCHIVE_ZIP_SEPARATOR', ',' );
00041 
00042 define( 'ARCHIVE_ZIP_TEMPORARY_DIR', '' );
00043 
00044 // Error codes
00045 define( 'ARCHIVE_ZIP_ERR_NO_ERROR', 0 );
00046 define( 'ARCHIVE_ZIP_ERR_WRITE_OPEN_FAIL', -1 );
00047 define( 'ARCHIVE_ZIP_ERR_READ_OPEN_FAIL', -2 );
00048 define( 'ARCHIVE_ZIP_ERR_INVALID_PARAMETER', -3 );
00049 define( 'ARCHIVE_ZIP_ERR_MISSING_FILE', -4 );
00050 define( 'ARCHIVE_ZIP_ERR_FILENAME_TOO_LONG', -5 );
00051 define( 'ARCHIVE_ZIP_ERR_INVALID_ZIP', -6 );
00052 define( 'ARCHIVE_ZIP_ERR_BAD_EXTRACTED_FILE', -7 );
00053 define( 'ARCHIVE_ZIP_ERR_DIR_CREATE_FAIL', -8 );
00054 define( 'ARCHIVE_ZIP_ERR_BAD_EXTENSION', -9 );
00055 define( 'ARCHIVE_ZIP_ERR_BAD_FORMAT', -10 );
00056 define( 'ARCHIVE_ZIP_ERR_DELETE_FILE_FAIL', -11 );
00057 define( 'ARCHIVE_ZIP_ERR_RENAME_FILE_FAIL', -12 );
00058 define( 'ARCHIVE_ZIP_ERR_BAD_CHECKSUM', -13 );
00059 define( 'ARCHIVE_ZIP_ERR_INVALID_ARCHIVE_ZIP', -14 );
00060 define( 'ARCHIVE_ZIP_ERR_MISSING_OPTION_VALUE', -15 );
00061 define( 'ARCHIVE_ZIP_ERR_INVALID_PARAM_VALUE', -16 );
00062 
00063 // Warning codes
00064 define( 'ARCHIVE_ZIP_WARN_NO_WARNING', 0 );
00065 define( 'ARCHIVE_ZIP_WARN_FILE_EXIST', 1 );
00066 
00067 // Methods parameters
00068 define( 'ARCHIVE_ZIP_PARAM_PATH', 'path' );
00069 define( 'ARCHIVE_ZIP_PARAM_ADD_PATH', 'add_path' );
00070 define( 'ARCHIVE_ZIP_PARAM_REMOVE_PATH', 'remove_path' );
00071 define( 'ARCHIVE_ZIP_PARAM_REMOVE_ALL_PATH', 'remove_all_path' );
00072 define( 'ARCHIVE_ZIP_PARAM_SET_CHMOD', 'set_chmod' );
00073 define( 'ARCHIVE_ZIP_PARAM_EXTRACT_AS_STRING', 'extract_as_string' );
00074 define( 'ARCHIVE_ZIP_PARAM_NO_COMPRESSION', 'no_compression' );
00075 
00076 define( 'ARCHIVE_ZIP_PARAM_PRE_EXTRACT', 'callback_pre_extract' );
00077 define( 'ARCHIVE_ZIP_PARAM_POST_EXTRACT', 'callback_post_extract' );
00078 define( 'ARCHIVE_ZIP_PARAM_PRE_ADD', 'callback_pre_add' );
00079 define( 'ARCHIVE_ZIP_PARAM_POST_ADD', 'callback_post_add' );
00080 
00081 
00082 
00083 /**
00084 * Class for unpacking zip archive files
00085 *
00086 * @author   Vincent Blavet <vincent@blavet.net>
00087 * @author   Karsten Dambekalns <karsten@typo3.org>
00088 */
00089 class em_unzip {
00090     /**
00091     * The filename of the zip archive.
00092     *
00093     * @var string Name of the Zip file
00094     */
00095     var $_zipname='';
00096 
00097     /**
00098     * File descriptor of the opened Zip file.
00099     *
00100     * @var int Internal zip file descriptor
00101     */
00102     var $_zip_fd=0;
00103 
00104     /**
00105     * @var int last error code
00106     */
00107     var $_error_code=1;
00108 
00109     /**
00110     * @var string Last error description
00111     */
00112     var $_error_string='';
00113 
00114     /**
00115     * em_unzip Class constructor. This flavour of the constructor only
00116     * declare a new em_unzip object, identifying it by the name of the
00117     * zip file.
00118     *
00119     * @param    string  $p_zipname  The name of the zip archive to create
00120     * @access public
00121     */
00122     function em_unzip($p_zipname) {
00123 
00124         // Check the zlib
00125         if (!extension_loaded('zlib')) {
00126             die("The extension 'zlib' couldn't be found.\n".
00127             "Please make sure your version of PHP was built ".
00128             "with 'zlib' support.\n");
00129         }
00130 
00131         // Set the attributes
00132         $this->_zipname = $p_zipname;
00133         $this->_zip_fd = 0;
00134 
00135         return;
00136     }
00137 
00138 
00139 
00140     /**
00141     * This method extract the files and folders which are in the zip archive.
00142     * It can extract all the archive or a part of the archive by using filter
00143     * feature (extract by name, by index, by ereg, by preg). The extraction
00144     * can occur in the current path or an other path.
00145     * All the advanced features are activated by the use of variable
00146         * parameters.
00147         * The return value is an array of entry descriptions which gives
00148         * information on extracted files (See listContent()).
00149         * The method may return a success value (an array) even if some files
00150         * are not correctly extracted (see the file status in listContent()).
00151     * The supported variable parameters for this method are :
00152     *   'add_path' : Path where the files and directories are to be extracted
00153     *
00154     * @access public
00155     * @param    mixed  $p_params  An array of variable parameters and values.
00156     * @return mixed An array of file description on success,
00157     *               0 on an unrecoverable failure, an error code is logged.
00158     */
00159     function extract($p_params=0) {
00160         $this->_errorReset();
00161 
00162         // Check archive
00163         if (!$this->_checkFormat()) {
00164             return(0);
00165         }
00166 
00167         // Set default values
00168         if ($p_params === 0) {
00169             $p_params = array();
00170         }
00171         if ($this->_check_parameters($p_params,
00172         array ('extract_as_string' => false,
00173         'add_path' => '',
00174         'remove_path' => '',
00175         'remove_all_path' => false,
00176         'callback_pre_extract' => '',
00177         'callback_post_extract' => '',
00178         'set_chmod' => 0) ) != 1) {
00179             return 0;
00180         }
00181 
00182         // Call the extracting fct
00183         $v_list = array();
00184         if ($this->_extractByRule($v_list, $p_params) != 1) {
00185             unset($v_list);
00186             return(0);
00187         }
00188 
00189         return $v_list;
00190     }
00191 
00192     /**
00193     * Method that gives the lastest error code.
00194     *
00195     * @access public
00196     * @return integer The error code value.
00197     */
00198     function errorCode() {
00199         return($this->_error_code);
00200     }
00201 
00202     /**
00203     * This method gives the latest error code name.
00204     *
00205     * @access public
00206     * @param  boolean $p_with_code  If true, gives the name and the int value.
00207     * @return string The error name.
00208     */
00209     function errorName($p_with_code=false) {
00210         $v_const_list = get_defined_constants();
00211 
00212         // Extract error constants from all const.
00213         for (reset($v_const_list);
00214         list($v_key, $v_value) = each($v_const_list);) {
00215             if (substr($v_key, 0, strlen('ARCHIVE_ZIP_ERR_')) =='ARCHIVE_ZIP_ERR_') {
00216                 $v_error_list[$v_key] = $v_value;
00217             }
00218         }
00219 
00220         // Search the name form the code value
00221         $v_key=array_search($this->_error_code, $v_error_list, true);
00222         if ($v_key!=false) {
00223             $v_value = $v_key;
00224         } else {
00225             $v_value = 'NoName';
00226         }
00227 
00228         if ($p_with_code) {
00229             return($v_value.' ('.$this->_error_code.')');
00230         } else {
00231             return($v_value);
00232         }
00233     }
00234 
00235     /**
00236     * This method returns the description associated with the latest error.
00237     *
00238     * @access public
00239     * @param  boolean $p_full If set to true gives the description with the
00240     *                         error code, the name and the description.
00241     *                         If set to false gives only the description
00242     *                         and the error code.
00243     * @return string The error description.
00244     */
00245     function errorInfo($p_full=false) {
00246         if ($p_full) {
00247             return($this->errorName(true)." : ".$this->_error_string);
00248         } else {
00249             return($this->_error_string." [code ".$this->_error_code."]");
00250         }
00251     }
00252 
00253 
00254     /**
00255   * em_unzip::_checkFormat()
00256   *
00257   * { Description }
00258   *
00259   * @param integer $p_level
00260   */
00261     function _checkFormat($p_level=0) {
00262         $v_result = true;
00263 
00264         // Reset the error handler
00265         $this->_errorReset();
00266 
00267         // Look if the file exits
00268         if (!is_file($this->_zipname)) {
00269             // Error log
00270             $this->_errorLog(ARCHIVE_ZIP_ERR_MISSING_FILE,
00271             "Missing archive file '".$this->_zipname."'");
00272             return(false);
00273         }
00274 
00275         // Check that the file is readeable
00276         if (!is_readable($this->_zipname)) {
00277             // Error log
00278             $this->_errorLog(ARCHIVE_ZIP_ERR_READ_OPEN_FAIL,
00279             "Unable to read archive '".$this->_zipname."'");
00280             return(false);
00281         }
00282 
00283         // Check the magic code
00284         // TBC
00285 
00286         // Check the central header
00287         // TBC
00288 
00289         // Check each file header
00290         // TBC
00291 
00292         // Return
00293         return $v_result;
00294     }
00295 
00296 
00297     /**
00298   * em_unzip::_openFd()
00299   *
00300   * { Description }
00301   *
00302   */
00303     function _openFd($p_mode) {
00304         $v_result=1;
00305 
00306         // Look if already open
00307         if ($this->_zip_fd != 0) {
00308             $this->_errorLog(ARCHIVE_ZIP_ERR_READ_OPEN_FAIL,
00309             'Zip file \''.$this->_zipname.'\' already open');
00310             return em_unzip::errorCode();
00311         }
00312 
00313         // Open the zip file
00314         if (($this->_zip_fd = @fopen($this->_zipname, $p_mode)) == 0) {
00315             $this->_errorLog(ARCHIVE_ZIP_ERR_READ_OPEN_FAIL,
00316             'Unable to open archive \''.$this->_zipname
00317             .'\' in '.$p_mode.' mode');
00318             return em_unzip::errorCode();
00319         }
00320 
00321         // Return
00322         return $v_result;
00323     }
00324 
00325     /**
00326   * em_unzip::_closeFd()
00327   *
00328   * { Description }
00329   *
00330   */
00331     function _closeFd() {
00332         $v_result=1;
00333 
00334         if ($this->_zip_fd != 0)
00335         @fclose($this->_zip_fd);
00336         $this->_zip_fd = 0;
00337 
00338         // Return
00339         return $v_result;
00340     }
00341 
00342 
00343 
00344     /**
00345   * em_unzip::_convertHeader2FileInfo()
00346   *
00347   * { Description }
00348   *
00349   */
00350     function _convertHeader2FileInfo($p_header, &$p_info) {
00351         $v_result=1;
00352 
00353         // Get the interesting attributes
00354         $p_info['filename'] = $p_header['filename'];
00355         $p_info['stored_filename'] = $p_header['stored_filename'];
00356         $p_info['size'] = $p_header['size'];
00357         $p_info['compressed_size'] = $p_header['compressed_size'];
00358         $p_info['mtime'] = $p_header['mtime'];
00359         $p_info['comment'] = $p_header['comment'];
00360         $p_info['folder'] = (($p_header['external']&0x00000010)==0x00000010);
00361         $p_info['index'] = $p_header['index'];
00362         $p_info['status'] = $p_header['status'];
00363 
00364         // Return
00365         return $v_result;
00366     }
00367 
00368 
00369     // Function : _extractByRule()
00370     // Description :
00371     //   Extract a file or directory depending of rules (by index, by name, ...)
00372     // Parameters :
00373     //   $p_file_list : An array where will be placed the properties of each
00374     //                  extracted file
00375     //   $p_path : Path to add while writing the extracted files
00376     //   $p_remove_path : Path to remove (from the file memorized path) while writing the
00377     //                    extracted files. If the path does not match the file path,
00378     //                    the file is extracted with its memorized path.
00379     //                    $p_remove_path does not apply to 'list' mode.
00380     //                    $p_path and $p_remove_path are commulative.
00381     // Return Values :
00382     //   1 on success,0 or less on error (see error code list)
00383 
00384     /**
00385   * em_unzip::_extractByRule()
00386   *
00387   * { Description }
00388   *
00389   */
00390     function _extractByRule(&$p_file_list, &$p_params)
00391     {
00392         $v_result=1;
00393 
00394         $p_path = $p_params['add_path'];
00395         $p_remove_path = $p_params['remove_path'];
00396         $p_remove_all_path = $p_params['remove_all_path'];
00397 
00398         // Check the path
00399         if (($p_path == "")
00400         || ((substr($p_path, 0, 1) != "/")
00401         && (substr($p_path, 0, 3) != "../") && (substr($p_path,1,2)!=":/")))
00402         $p_path = "./".$p_path;
00403 
00404         // Reduce the path last (and duplicated) '/'
00405         if (($p_path != "./") && ($p_path != "/")) {
00406             // Look for the path end '/'
00407             while (substr($p_path, -1) == "/") {
00408                 $p_path = substr($p_path, 0, strlen($p_path)-1);
00409             }
00410         }
00411 
00412         // Open the zip file
00413         if (($v_result = $this->_openFd('rb')) != 1)
00414         {
00415             return $v_result;
00416         }
00417 
00418         // Read the central directory informations
00419         $v_central_dir = array();
00420         if (($v_result = $this->_readEndCentralDir($v_central_dir)) != 1)
00421         {
00422             // Close the zip file
00423             $this->_closeFd();
00424 
00425             return $v_result;
00426         }
00427 
00428         // Start at beginning of Central Dir
00429         $v_pos_entry = $v_central_dir['offset'];
00430 
00431         // Read each entry
00432         $j_start = 0;
00433         for ($i=0, $v_nb_extracted=0; $i<$v_central_dir['entries']; $i++) {
00434             // Read next Central dir entry
00435             @rewind($this->_zip_fd);
00436             if (@fseek($this->_zip_fd, $v_pos_entry)) {
00437                 $this->_closeFd();
00438 
00439                 $this->_errorLog(ARCHIVE_ZIP_ERR_INVALID_ARCHIVE_ZIP,
00440                 'Invalid archive size');
00441 
00442                 return em_unzip::errorCode();
00443             }
00444 
00445             // Read the file header
00446             $v_header = array();
00447             if (($v_result = $this->_readCentralFileHeader($v_header)) != 1) {
00448                 $this->_closeFd();
00449 
00450                 return $v_result;
00451             }
00452 
00453             // Store the index
00454             $v_header['index'] = $i;
00455 
00456             // Store the file position
00457             $v_pos_entry = ftell($this->_zip_fd);
00458 
00459 
00460             // Go to the file position
00461             @rewind($this->_zip_fd);
00462             if (@fseek($this->_zip_fd, $v_header['offset']))
00463             {
00464                 // Close the zip file
00465                 $this->_closeFd();
00466 
00467                 // Error log
00468                 $this->_errorLog(ARCHIVE_ZIP_ERR_INVALID_ARCHIVE_ZIP, 'Invalid archive size');
00469 
00470                 // Return
00471                 return em_unzip::errorCode();
00472             }
00473 
00474             // Extracting the file
00475             if (($v_result = $this->_extractFile($v_header, $p_path, $p_remove_path, $p_remove_all_path, $p_params)) != 1)
00476             {
00477                 // Close the zip file
00478                 $this->_closeFd();
00479 
00480                 return $v_result;
00481             }
00482 
00483             // Get the only interesting attributes
00484             if (($v_result = $this->_convertHeader2FileInfo($v_header, $p_file_list[$v_nb_extracted++])) != 1)
00485             {
00486                 // Close the zip file
00487                 $this->_closeFd();
00488 
00489                 return $v_result;
00490             }
00491         }
00492 
00493         // Close the zip file
00494         $this->_closeFd();
00495 
00496         // Return
00497         return $v_result;
00498     }
00499 
00500     /**
00501   * em_unzip::_extractFile()
00502   *
00503   * { Description }
00504   *
00505   */
00506     function _extractFile(&$p_entry, $p_path, $p_remove_path, $p_remove_all_path, &$p_params) {
00507         $v_result=1;
00508 
00509         // Read the file header
00510         $v_header = '';
00511         if (($v_result = $this->_readFileHeader($v_header)) != 1)
00512         {
00513             // Return
00514             return $v_result;
00515         }
00516 
00517 
00518         // Check that the file header is coherent with $p_entry info
00519         // TBC
00520 
00521         // Look for all path to remove
00522         if ($p_remove_all_path == true) {
00523             // Get the basename of the path
00524             $p_entry['filename'] = basename($p_entry['filename']);
00525         }
00526 
00527         // Look for path to remove
00528         else if ($p_remove_path != "")
00529         {
00530             //if (strcmp($p_remove_path, $p_entry['filename'])==0)
00531             if ($this->_tool_PathInclusion($p_remove_path, $p_entry['filename']) == 2)
00532             {
00533 
00534                 // Change the file status
00535                 $p_entry['status'] = "filtered";
00536 
00537                 // Return
00538                 return $v_result;
00539             }
00540 
00541             $p_remove_path_size = strlen($p_remove_path);
00542             if (substr($p_entry['filename'], 0, $p_remove_path_size) == $p_remove_path)
00543             {
00544 
00545                 // Remove the path
00546                 $p_entry['filename'] = substr($p_entry['filename'], $p_remove_path_size);
00547 
00548             }
00549         }
00550 
00551         // Add the path
00552         if ($p_path != '')
00553         {
00554             $p_entry['filename'] = $p_path."/".$p_entry['filename'];
00555         }
00556 
00557         // Look for pre-extract callback
00558         if (   (isset($p_params[ARCHIVE_ZIP_PARAM_PRE_EXTRACT]))
00559         && ($p_params[ARCHIVE_ZIP_PARAM_PRE_EXTRACT] != '')) {
00560 
00561             // Generate a local information
00562             $v_local_header = array();
00563             $this->_convertHeader2FileInfo($p_entry, $v_local_header);
00564 
00565             // Call the callback
00566             // Here I do not use call_user_func() because I need to send a reference to the
00567             // header.
00568             eval('$v_result = '.$p_params[ARCHIVE_ZIP_PARAM_PRE_EXTRACT].'(ARCHIVE_ZIP_PARAM_PRE_EXTRACT, $v_local_header);');
00569             if ($v_result == 0) {
00570                 // Change the file status
00571                 $p_entry['status'] = "skipped";
00572                 $v_result = 1;
00573             }
00574 
00575             // Update the informations
00576             // Only some fields can be modified
00577             $p_entry['filename'] = $v_local_header['filename'];
00578         }
00579 
00580         // Trace
00581 
00582         // Look if extraction should be done
00583         if ($p_entry['status'] == 'ok') {
00584 
00585             // Look for specific actions while the file exist
00586             if (file_exists($p_entry['filename'])) {
00587                 // Look if file is a directory
00588                 if (is_dir($p_entry['filename'])) {
00589                     // Change the file status
00590                     $p_entry['status'] = "already_a_directory";
00591 
00592                     // Return
00593                     //return $v_result;
00594                 }
00595                 // Look if file is write protected
00596                 else if (!is_writeable($p_entry['filename'])) {
00597                     // Change the file status
00598                     $p_entry['status'] = "write_protected";
00599 
00600                     // Return
00601                     //return $v_result;
00602                 }
00603 
00604                 // Look if the extracted file is older
00605                 else if (filemtime($p_entry['filename']) > $p_entry['mtime']) {
00606                     // Change the file status
00607                     $p_entry['status'] = "newer_exist";
00608 
00609                     // Return
00610                     //return $v_result;
00611                 }
00612             }
00613 
00614             // Check the directory availability and create it if necessary
00615             else {
00616                 if ((($p_entry['external']&0x00000010)==0x00000010) || (substr($p_entry['filename'], -1) == '/'))
00617                 $v_dir_to_check = $p_entry['filename'];
00618                 else if (!strstr($p_entry['filename'], "/"))
00619                 $v_dir_to_check = "";
00620                 else
00621                 $v_dir_to_check = dirname($p_entry['filename']);
00622 
00623                 if (($v_result = $this->_dirCheck($v_dir_to_check, (($p_entry['external']&0x00000010)==0x00000010))) != 1) {
00624                     // Change the file status
00625                     $p_entry['status'] = "path_creation_fail";
00626 
00627                     // Return
00628                     //return $v_result;
00629                     $v_result = 1;
00630                 }
00631             }
00632         }
00633 
00634         // Look if extraction should be done
00635         if ($p_entry['status'] == 'ok') {
00636             // Do the extraction (if not a folder)
00637             if (!(($p_entry['external']&0x00000010)==0x00000010)) {
00638                 // Look for not compressed file
00639                 if ($p_entry['compressed_size'] == $p_entry['size']) {
00640                     // Opening destination file
00641                     if (($v_dest_file = @fopen($p_entry['filename'], 'wb')) == 0) {
00642                         // Change the file status
00643                         $p_entry['status'] = "write_error";
00644 
00645                         // Return
00646                         return $v_result;
00647                     }
00648 
00649 
00650                     // Read the file by ARCHIVE_ZIP_READ_BLOCK_SIZE octets blocks
00651                     $v_size = $p_entry['compressed_size'];
00652                     while ($v_size != 0) {
00653                         $v_read_size = ($v_size < ARCHIVE_ZIP_READ_BLOCK_SIZE ? $v_size : ARCHIVE_ZIP_READ_BLOCK_SIZE);
00654                         $v_buffer = fread($this->_zip_fd, $v_read_size);
00655                         $v_binary_data = pack('a'.$v_read_size, $v_buffer);
00656                         @fwrite($v_dest_file, $v_binary_data, $v_read_size);
00657                         $v_size -= $v_read_size;
00658                     }
00659 
00660                     // Closing the destination file
00661                     fclose($v_dest_file);
00662 
00663                     // Change the file mtime
00664                     touch($p_entry['filename'], $p_entry['mtime']);
00665                 } else {
00666                     // Trace
00667 
00668                     // Opening destination file
00669                     if (($v_dest_file = @fopen($p_entry['filename'], 'wb')) == 0) {
00670 
00671                         // Change the file status
00672                         $p_entry['status'] = "write_error";
00673 
00674                         return $v_result;
00675                     }
00676 
00677 
00678                     // Read the compressed file in a buffer (one shot)
00679                     $v_buffer = @fread($this->_zip_fd, $p_entry['compressed_size']);
00680 
00681                     // Decompress the file
00682                     $v_file_content = gzinflate($v_buffer);
00683                     unset($v_buffer);
00684 
00685                     // Write the uncompressed data
00686                     @fwrite($v_dest_file, $v_file_content, $p_entry['size']);
00687                     unset($v_file_content);
00688 
00689                     // Closing the destination file
00690                     @fclose($v_dest_file);
00691 
00692                     // Change the file mtime
00693                     @touch($p_entry['filename'], $p_entry['mtime']);
00694                 }
00695 
00696                 // Look for chmod option
00697                 if (   (isset($p_params[ARCHIVE_ZIP_PARAM_SET_CHMOD]))
00698                 && ($p_params[ARCHIVE_ZIP_PARAM_SET_CHMOD] != 0)) {
00699 
00700                     // Change the mode of the file
00701                     chmod($p_entry['filename'], $p_params[ARCHIVE_ZIP_PARAM_SET_CHMOD]);
00702                 }
00703 
00704             }
00705         }
00706 
00707         // Look for post-extract callback
00708         if (   (isset($p_params[ARCHIVE_ZIP_PARAM_POST_EXTRACT]))
00709         && ($p_params[ARCHIVE_ZIP_PARAM_POST_EXTRACT] != '')) {
00710 
00711             // Generate a local information
00712             $v_local_header = array();
00713             $this->_convertHeader2FileInfo($p_entry, $v_local_header);
00714 
00715             // Call the callback
00716             // Here I do not use call_user_func() because I need to send a reference to the
00717             // header.
00718             eval('$v_result = '.$p_params[ARCHIVE_ZIP_PARAM_POST_EXTRACT].'(ARCHIVE_ZIP_PARAM_POST_EXTRACT, $v_local_header);');
00719         }
00720 
00721         // Return
00722         return $v_result;
00723     }
00724 
00725     /**
00726   * em_unzip::_readFileHeader()
00727   *
00728   * { Description }
00729   *
00730   */
00731     function _readFileHeader(&$p_header) {
00732         $v_result=1;
00733 
00734         // Read the 4 bytes signature
00735         $v_binary_data = @fread($this->_zip_fd, 4);
00736         $v_data = unpack('Vid', $v_binary_data);
00737 
00738         // Check signature
00739         if ($v_data['id'] != 0x04034b50) {
00740 
00741             // Error log
00742             $this->_errorLog(ARCHIVE_ZIP_ERR_BAD_FORMAT, 'Invalid archive structure');
00743 
00744             // Return
00745             return em_unzip::errorCode();
00746         }
00747 
00748         // Read the first 42 bytes of the header
00749         $v_binary_data = fread($this->_zip_fd, 26);
00750 
00751         // Look for invalid block size
00752         if (strlen($v_binary_data) != 26) {
00753             $p_header['filename'] = "";
00754             $p_header['status'] = "invalid_header";
00755 
00756             // Error log
00757             $this->_errorLog(ARCHIVE_ZIP_ERR_BAD_FORMAT, "Invalid block size : ".strlen($v_binary_data));
00758 
00759             // Return
00760             return em_unzip::errorCode();
00761         }
00762 
00763         // Extract the values
00764         $v_data = unpack('vversion/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len', $v_binary_data);
00765 
00766         // Get filename
00767         $p_header['filename'] = fread($this->_zip_fd, $v_data['filename_len']);
00768 
00769         // Get extra_fields
00770         if ($v_data['extra_len'] != 0) {
00771             $p_header['extra'] = fread($this->_zip_fd, $v_data['extra_len']);
00772         }
00773         else {
00774             $p_header['extra'] = '';
00775         }
00776 
00777         // Extract properties
00778         $p_header['compression'] = $v_data['compression'];
00779         $p_header['size'] = $v_data['size'];
00780         $p_header['compressed_size'] = $v_data['compressed_size'];
00781         $p_header['crc'] = $v_data['crc'];
00782         $p_header['flag'] = $v_data['flag'];
00783 
00784         // Recuperate date in UNIX format
00785         $p_header['mdate'] = $v_data['mdate'];
00786         $p_header['mtime'] = $v_data['mtime'];
00787         if ($p_header['mdate'] && $p_header['mtime']) {
00788             // Extract time
00789             $v_hour = ($p_header['mtime'] & 0xF800) >> 11;
00790             $v_minute = ($p_header['mtime'] & 0x07E0) >> 5;
00791             $v_seconde = ($p_header['mtime'] & 0x001F)*2;
00792 
00793             // Extract date
00794             $v_year = (($p_header['mdate'] & 0xFE00) >> 9) + 1980;
00795             $v_month = ($p_header['mdate'] & 0x01E0) >> 5;
00796             $v_day = $p_header['mdate'] & 0x001F;
00797 
00798             // Get UNIX date format
00799             $p_header['mtime'] = mktime($v_hour, $v_minute, $v_seconde, $v_month, $v_day, $v_year);
00800 
00801         } else {
00802             $p_header['mtime'] = time();
00803         }
00804 
00805         // Other informations
00806 
00807         // TBC
00808         //for(reset($v_data); $key = key($v_data); next($v_data)) {
00809         //}
00810 
00811         // Set the stored filename
00812         $p_header['stored_filename'] = $p_header['filename'];
00813 
00814         // Set the status field
00815         $p_header['status'] = "ok";
00816 
00817         // Return
00818         return $v_result;
00819     }
00820 
00821     /**
00822   * em_unzip::_readCentralFileHeader()
00823   *
00824   * { Description }
00825   *
00826   */
00827     function _readCentralFileHeader(&$p_header) {
00828         $v_result=1;
00829 
00830         // Read the 4 bytes signature
00831         $v_binary_data = @fread($this->_zip_fd, 4);
00832         $v_data = unpack('Vid', $v_binary_data);
00833 
00834         // Check signature
00835         if ($v_data['id'] != 0x02014b50) {
00836 
00837             // Error log
00838             $this->_errorLog(ARCHIVE_ZIP_ERR_BAD_FORMAT, 'Invalid archive structure');
00839 
00840             // Return
00841             return em_unzip::errorCode();
00842         }
00843 
00844         // Read the first 42 bytes of the header
00845         $v_binary_data = fread($this->_zip_fd, 42);
00846 
00847         // Look for invalid block size
00848         if (strlen($v_binary_data) != 42) {
00849             $p_header['filename'] = "";
00850             $p_header['status'] = "invalid_header";
00851 
00852             // Error log
00853             $this->_errorLog(ARCHIVE_ZIP_ERR_BAD_FORMAT, "Invalid block size : ".strlen($v_binary_data));
00854 
00855             // Return
00856             return em_unzip::errorCode();
00857         }
00858 
00859         // Extract the values
00860         $p_header = unpack('vversion/vversion_extracted/vflag/vcompression/vmtime/vmdate/Vcrc/Vcompressed_size/Vsize/vfilename_len/vextra_len/vcomment_len/vdisk/vinternal/Vexternal/Voffset', $v_binary_data);
00861 
00862         // Get filename
00863         if ($p_header['filename_len'] != 0)
00864         $p_header['filename'] = fread($this->_zip_fd, $p_header['filename_len']);
00865         else
00866         $p_header['filename'] = '';
00867 
00868         // Get extra
00869         if ($p_header['extra_len'] != 0)
00870         $p_header['extra'] = fread($this->_zip_fd, $p_header['extra_len']);
00871         else
00872         $p_header['extra'] = '';
00873 
00874         // Get comment
00875         if ($p_header['comment_len'] != 0)
00876         $p_header['comment'] = fread($this->_zip_fd, $p_header['comment_len']);
00877         else
00878         $p_header['comment'] = '';
00879 
00880         // Extract properties
00881 
00882         // Recuperate date in UNIX format
00883         if ($p_header['mdate'] && $p_header['mtime']) {
00884             // Extract time
00885             $v_hour = ($p_header['mtime'] & 0xF800) >> 11;
00886             $v_minute = ($p_header['mtime'] & 0x07E0) >> 5;
00887             $v_seconde = ($p_header['mtime'] & 0x001F)*2;
00888 
00889             // Extract date
00890             $v_year = (($p_header['mdate'] & 0xFE00) >> 9) + 1980;
00891             $v_month = ($p_header['mdate'] & 0x01E0) >> 5;
00892             $v_day = $p_header['mdate'] & 0x001F;
00893 
00894             // Get UNIX date format
00895             $p_header['mtime'] = mktime($v_hour, $v_minute, $v_seconde, $v_month, $v_day, $v_year);
00896 
00897         } else {
00898             $p_header['mtime'] = time();
00899         }
00900 
00901         // Set the stored filename
00902         $p_header['stored_filename'] = $p_header['filename'];
00903 
00904         // Set default status to ok
00905         $p_header['status'] = 'ok';
00906 
00907         // Look if it is a directory
00908         if (substr($p_header['filename'], -1) == '/') {
00909             $p_header['external'] = 0x41FF0010;
00910         }
00911 
00912 
00913         // Return
00914         return $v_result;
00915     }
00916 
00917     /**
00918   * em_unzip::_readEndCentralDir()
00919   *
00920   * { Description }
00921   *
00922   */
00923     function _readEndCentralDir(&$p_central_dir) {
00924         $v_result=1;
00925 
00926         // Go to the end of the zip file
00927         $v_size = filesize($this->_zipname);
00928         @fseek($this->_zip_fd, $v_size);
00929         if (@ftell($this->_zip_fd) != $v_size) {
00930             $this->_errorLog(ARCHIVE_ZIP_ERR_BAD_FORMAT,
00931             'Unable to go to the end of the archive \''
00932             .$this->_zipname.'\'');
00933             return em_unzip::errorCode();
00934         }
00935 
00936         // First try : look if this is an archive with no commentaries
00937         // (most of the time)
00938         // in this case the end of central dir is at 22 bytes of the file end
00939         $v_found = 0;
00940         if ($v_size > 26) {
00941             @fseek($this->_zip_fd, $v_size-22);
00942             if (($v_pos = @ftell($this->_zip_fd)) != ($v_size-22)) {
00943                 $this->_errorLog(ARCHIVE_ZIP_ERR_BAD_FORMAT,
00944                 'Unable to seek back to the middle of the archive \''
00945                 .$this->_zipname.'\'');
00946                 return em_unzip::errorCode();
00947             }
00948 
00949             // Read for bytes
00950             $v_binary_data = @fread($this->_zip_fd, 4);
00951             $v_data = unpack('Vid', $v_binary_data);
00952 
00953             // Check signature
00954             if ($v_data['id'] == 0x06054b50) {
00955                 $v_found = 1;
00956             }
00957 
00958             $v_pos = ftell($this->_zip_fd);
00959         }
00960 
00961         // Go back to the maximum possible size of the Central Dir End Record
00962         if (!$v_found) {
00963             $v_maximum_size = 65557; // 0xFFFF + 22;
00964             if ($v_maximum_size > $v_size)
00965             $v_maximum_size = $v_size;
00966             @fseek($this->_zip_fd, $v_size-$v_maximum_size);
00967             if (@ftell($this->_zip_fd) != ($v_size-$v_maximum_size)) {
00968                 $this->_errorLog(ARCHIVE_ZIP_ERR_BAD_FORMAT,
00969                 'Unable to seek back to the middle of the archive \''
00970                 .$this->_zipname.'\'');
00971                 return em_unzip::errorCode();
00972             }
00973 
00974             // Read byte per byte in order to find the signature
00975             $v_pos = ftell($this->_zip_fd);
00976             $v_bytes = 0x00000000;
00977             while ($v_pos < $v_size) {
00978                 // Read a byte
00979                 $v_byte = @fread($this->_zip_fd, 1);
00980 
00981                 //  Add the byte
00982                 $v_bytes = ($v_bytes << 8) | Ord($v_byte);
00983 
00984                 // Compare the bytes
00985                 if ($v_bytes == 0x504b0506) {
00986                     $v_pos++;
00987                     break;
00988                 }
00989 
00990                 $v_pos++;
00991             }
00992 
00993             // Look if not found end of central dir
00994             if ($v_pos == $v_size) {
00995                 $this->_errorLog(ARCHIVE_ZIP_ERR_BAD_FORMAT,
00996                 "Unable to find End of Central Dir Record signature");
00997                 return em_unzip::errorCode();
00998             }
00999         }
01000 
01001         // Read the first 18 bytes of the header
01002         $v_binary_data = fread($this->_zip_fd, 18);
01003 
01004         // Look for invalid block size
01005         if (strlen($v_binary_data) != 18) {
01006             $this->_errorLog(ARCHIVE_ZIP_ERR_BAD_FORMAT,
01007             "Invalid End of Central Dir Record size : "
01008             .strlen($v_binary_data));
01009             return em_unzip::errorCode();
01010         }
01011 
01012         // Extract the values
01013         $v_data = unpack('vdisk/vdisk_start/vdisk_entries/ventries/Vsize/Voffset/vcomment_size', $v_binary_data);
01014 
01015         // Check the global size
01016         if (($v_pos + $v_data['comment_size'] + 18) != $v_size) {
01017             $this->_errorLog(ARCHIVE_ZIP_ERR_BAD_FORMAT,
01018             "Fail to find the right signature");
01019             return em_unzip::errorCode();
01020         }
01021 
01022         // Get comment
01023         if ($v_data['comment_size'] != 0)
01024         $p_central_dir['comment'] = fread($this->_zip_fd, $v_data['comment_size']);
01025         else
01026         $p_central_dir['comment'] = '';
01027 
01028         $p_central_dir['entries'] = $v_data['entries'];
01029         $p_central_dir['disk_entries'] = $v_data['disk_entries'];
01030         $p_central_dir['offset'] = $v_data['offset'];
01031         $p_central_dir['size'] = $v_data['size'];
01032         $p_central_dir['disk'] = $v_data['disk'];
01033         $p_central_dir['disk_start'] = $v_data['disk_start'];
01034 
01035         // Return
01036         return $v_result;
01037     }
01038 
01039     /**
01040   * em_unzip::_dirCheck()
01041   *
01042   * { Description }
01043   *
01044   * @param [type] $p_is_dir
01045   */
01046     function _dirCheck($p_dir, $p_is_dir=false) {
01047         $v_result = 1;
01048 
01049         // Remove the final '/'
01050         if (($p_is_dir) && (substr($p_dir, -1)=='/')) {
01051             $p_dir = substr($p_dir, 0, strlen($p_dir)-1);
01052         }
01053 
01054         // Check the directory availability
01055         if ((is_dir($p_dir)) || ($p_dir == "")) {
01056             return 1;
01057         }
01058 
01059         // Extract parent directory
01060         $p_parent_dir = dirname($p_dir);
01061 
01062         // Just a check
01063         if ($p_parent_dir != $p_dir) {
01064             // Look for parent directory
01065             if ($p_parent_dir != "") {
01066                 if (($v_result = $this->_dirCheck($p_parent_dir)) != 1) {
01067                     return $v_result;
01068                 }
01069             }
01070         }
01071 
01072         // Create the directory
01073         if (!@mkdir($p_dir, 0777)) {
01074             $this->_errorLog(ARCHIVE_ZIP_ERR_DIR_CREATE_FAIL,
01075             "Unable to create directory '$p_dir'");
01076             return em_unzip::errorCode();
01077         }
01078 
01079         // Return
01080         return $v_result;
01081     }
01082 
01083     /**
01084   * em_unzip::_check_parameters()
01085   *
01086   * { Description }
01087   *
01088   * @param integer $p_error_code
01089   * @param string $p_error_string
01090   */
01091     function _check_parameters(&$p_params, $p_default) {
01092 
01093         // Check that param is an array
01094         if (!is_array($p_params)) {
01095             $this->_errorLog(ARCHIVE_ZIP_ERR_INVALID_PARAMETER,
01096             'Unsupported parameter, waiting for an array');
01097             return em_unzip::errorCode();
01098         }
01099 
01100         // Check that all the params are valid
01101         for (reset($p_params); list($v_key, $v_value) = each($p_params); ) {
01102             if (!isset($p_default[$v_key])) {
01103                 $this->_errorLog(ARCHIVE_ZIP_ERR_INVALID_PARAMETER,
01104                 'Unsupported parameter with key \''.$v_key.'\'');
01105 
01106                 return em_unzip::errorCode();
01107             }
01108         }
01109 
01110         // Set the default values
01111         for (reset($p_default); list($v_key, $v_value) = each($p_default); ) {
01112             if (!isset($p_params[$v_key])) {
01113                 $p_params[$v_key] = $p_default[$v_key];
01114             }
01115         }
01116 
01117         // Check specific parameters
01118         $v_callback_list = array ('callback_pre_add','callback_post_add',
01119         'callback_pre_extract','callback_post_extract');
01120         for ($i=0; $i<sizeof($v_callback_list); $i++) {
01121             $v_key=$v_callback_list[$i];
01122             if (   (isset($p_params[$v_key])) && ($p_params[$v_key] != '')) {
01123                 if (!function_exists($p_params[$v_key])) {
01124                     $this->_errorLog(ARCHIVE_ZIP_ERR_INVALID_PARAM_VALUE,
01125                     "Callback '".$p_params[$v_key]
01126                     ."()' is not an existing function for "
01127                     ."parameter '".$v_key."'");
01128                     return em_unzip::errorCode();
01129                 }
01130             }
01131         }
01132 
01133         return(1);
01134     }
01135 
01136     /**
01137   * em_unzip::_errorLog()
01138   *
01139   * { Description }
01140   *
01141   * @param integer $p_error_code
01142   * @param string $p_error_string
01143   */
01144     function _errorLog($p_error_code=0, $p_error_string='') {
01145         $this->_error_code = $p_error_code;
01146         $this->_error_string = $p_error_string;
01147     }
01148 
01149     /**
01150   * em_unzip::_errorReset()
01151   *
01152   * { Description }
01153   *
01154   */
01155     function _errorReset() {
01156         $this->_error_code = 1;
01157         $this->_error_string = '';
01158     }
01159 
01160     /**
01161   * _tool_PathReduction()
01162   *
01163   * { Description }
01164   *
01165   */
01166     function _tool_PathReduction($p_dir) {
01167         $v_result = "";
01168 
01169         // Look for not empty path
01170         if ($p_dir != "") {
01171             // Explode path by directory names
01172             $v_list = explode("/", $p_dir);
01173 
01174             // Study directories from last to first
01175             for ($i=sizeof($v_list)-1; $i>=0; $i--) {
01176                 // Look for current path
01177                 if ($v_list[$i] == ".") {
01178                     // Ignore this directory
01179                     // Should be the first $i=0, but no check is done
01180                 } else if ($v_list[$i] == "..") {
01181                     // Ignore it and ignore the $i-1
01182                     $i--;
01183                 } else if (($v_list[$i] == "") && ($i!=(sizeof($v_list)-1)) && ($i!=0)) {
01184                     // Ignore only the double '//' in path,
01185                     // but not the first and last '/'
01186                 } else {
01187                     $v_result = $v_list[$i].($i!=(sizeof($v_list)-1)?"/".$v_result:"");
01188                 }
01189             }
01190         }
01191 
01192         // Return
01193         return $v_result;
01194     }
01195 
01196     /**
01197   * _tool_PathInclusion()
01198   *
01199   * { Description }
01200   *
01201   */
01202     function _tool_PathInclusion($p_dir, $p_path) {
01203         $v_result = 1;
01204 
01205         // Explode dir and path by directory separator
01206         $v_list_dir = explode("/", $p_dir);
01207         $v_list_dir_size = sizeof($v_list_dir);
01208         $v_list_path = explode("/", $p_path);
01209         $v_list_path_size = sizeof($v_list_path);
01210 
01211         // Study directories paths
01212         $i = 0;
01213         $j = 0;
01214         while (($i < $v_list_dir_size) && ($j < $v_list_path_size) && ($v_result)) {
01215             // Look for empty dir (path reduction)
01216             if ($v_list_dir[$i] == '') {
01217                 $i++;
01218                 continue;
01219             }
01220             if ($v_list_path[$j] == '') {
01221                 $j++;
01222                 continue;
01223             }
01224 
01225             // Compare the items
01226             if (   ($v_list_dir[$i] != $v_list_path[$j]) && ($v_list_dir[$i] != '') && ( $v_list_path[$j] != ''))  {
01227                 $v_result = 0;
01228             }
01229 
01230             // Next items
01231             $i++;
01232             $j++;
01233         }
01234 
01235         // Look if everything seems to be the same
01236         if ($v_result) {
01237             // Skip all the empty items
01238             while (($j < $v_list_path_size) && ($v_list_path[$j] == '')) $j++;
01239             while (($i < $v_list_dir_size) && ($v_list_dir[$i] == '')) $i++;
01240 
01241             if (($i >= $v_list_dir_size) && ($j >= $v_list_path_size)) {
01242                 // There are exactly the same
01243                 $v_result = 2;
01244             } else if ($i < $v_list_dir_size) {
01245                 // The path is shorter than the dir
01246                 $v_result = 0;
01247             }
01248         }
01249 
01250         // Return
01251         return $v_result;
01252     }
01253 
01254     /**
01255   * _tool_CopyBlock()
01256   *
01257   * { Description }
01258   *
01259   * @param integer $p_mode
01260   */
01261     function _tool_CopyBlock($p_src, $p_dest, $p_size, $p_mode=0) {
01262         $v_result = 1;
01263 
01264         if ($p_mode==0) {
01265             while ($p_size != 0) {
01266                 $v_read_size = ($p_size < ARCHIVE_ZIP_READ_BLOCK_SIZE
01267                 ? $p_size : ARCHIVE_ZIP_READ_BLOCK_SIZE);
01268                 $v_buffer = @fread($p_src, $v_read_size);
01269                 @fwrite($p_dest, $v_buffer, $v_read_size);
01270                 $p_size -= $v_read_size;
01271             }
01272         } else if ($p_mode==1) {
01273             while ($p_size != 0) {
01274                 $v_read_size = ($p_size < ARCHIVE_ZIP_READ_BLOCK_SIZE
01275                 ? $p_size : ARCHIVE_ZIP_READ_BLOCK_SIZE);
01276                 $v_buffer = @gzread($p_src, $v_read_size);
01277                 @fwrite($p_dest, $v_buffer, $v_read_size);
01278                 $p_size -= $v_read_size;
01279             }
01280         } else if ($p_mode==2) {
01281             while ($p_size != 0) {
01282                 $v_read_size = ($p_size < ARCHIVE_ZIP_READ_BLOCK_SIZE
01283                 ? $p_size : ARCHIVE_ZIP_READ_BLOCK_SIZE);
01284                 $v_buffer = @fread($p_src, $v_read_size);
01285                 @gzwrite($p_dest, $v_buffer, $v_read_size);
01286                 $p_size -= $v_read_size;
01287             }
01288         }
01289         else if ($p_mode==3) {
01290             while ($p_size != 0) {
01291                 $v_read_size = ($p_size < ARCHIVE_ZIP_READ_BLOCK_SIZE
01292                 ? $p_size : ARCHIVE_ZIP_READ_BLOCK_SIZE);
01293                 $v_buffer = @gzread($p_src, $v_read_size);
01294                 @gzwrite($p_dest, $v_buffer, $v_read_size);
01295                 $p_size -= $v_read_size;
01296             }
01297         }
01298 
01299         // Return
01300         return $v_result;
01301     }
01302 
01303     /**
01304   * _tool_Rename()
01305   *
01306   * { Description }
01307   *
01308   */
01309     function _tool_Rename($p_src, $p_dest) {
01310         $v_result = 1;
01311 
01312         // Try to rename the files
01313         if (!@rename($p_src, $p_dest)) {
01314 
01315             // Try to copy & unlink the src
01316             if (!@copy($p_src, $p_dest)) {
01317                 $v_result = 0;
01318             } else if (!@unlink($p_src)) {
01319                 $v_result = 0;
01320             }
01321         }
01322 
01323         // Return
01324         return $v_result;
01325     }
01326 
01327     /**
01328   * _tool_TranslateWinPath()
01329   *
01330   * { Description }
01331   *
01332   * @param [type] $p_remove_disk_letter
01333   */
01334     function _tool_TranslateWinPath($p_path, $p_remove_disk_letter=true) {
01335         if (stristr(php_uname(), 'windows')) {
01336             // Look for potential disk letter
01337             if (   ($p_remove_disk_letter)
01338             && (($v_position = strpos($p_path, ':')) != false)) {
01339                 $p_path = substr($p_path, $v_position+1);
01340             }
01341             // Change potential windows directory separator
01342             if ((strpos($p_path, '\\') > 0) || (substr($p_path, 0,1) == '\\')) {
01343                 $p_path = strtr($p_path, '\\', '/');
01344             }
01345         }
01346         return $p_path;
01347     }
01348 
01349 }
01350 
01351 ?>

Generated on Sat Jan 3 04:23:29 2009 for TYPO3 API by  doxygen 1.4.7