* @copyright Copyright © 2003, Holger Boskugel, Berlin, Germany * @license http://opensource.org/licenses/gpl-license.php GNU Public License * * @history * 2003-12-02 - HB : Patched : naming bug : Time/Size of file * Added : ZIP file comment * Added : Check BZIP2 support of PHP * 2003-11-29 - HB * Initial version */ /** * Unzip class, which retrieves entries from ZIP files. * * Supports only the compression modes * - 0 : Stored, * - 8 : Deflated and * - 12 : BZIP2 * * Based on :
*
* {@link http://www.pkware.com/products/enterprise/white_papers/appnote.html * * Official ZIP file format}
* {@link http://msdn.microsoft.com/library/en-us/w98ddk/hh/w98ddk/storage_5l4m.asp * * Microsoft DOS date/time format} * * @category phpPublic * @package File-Formats-ZIP * @subpackage Unzip * @version 1.0.1 * @author Holger Boskugel * @uses SimpleUnzipEntry * @example example.unzip.php Two examples */ class SimpleUnzip { // 2003-12-02 - HB > /** * Array to store file entries * * @var string * @access public * @see ReadFile() * @since 1.0.1 */ var $Comment = ''; // 2003-12-02 - HB < /** * Array to store file entries * * @var array * @access public * @see ReadFile() * @since 1.0 */ var $Entries = array(); /** * Name of the ZIP file * * @var string * @access public * @see ReadFile() * @since 1.0 */ var $Name = ''; /** * Size of the ZIP file * * @var integer * @access public * @see ReadFile() * @since 1.0 */ var $Size = 0; /** * Time of the ZIP file (unix timestamp) * * @var integer * @access public * @see ReadFile() * @since 1.0 */ var $Time = 0; /** * Contructor of the class * * @param string File name * @return SimpleUnzip Instanced class * @access public * @uses SimpleUnzip::ReadFile() Opens file on new if specified * @since 1.0 */ function SimpleUnzip($in_FileName = '') { if($in_FileName !== '') { SimpleUnzip::ReadFile($in_FileName); } } // end of the 'SimpleUnzip' constructor /** * Counts the entries * * @return integer Count of ZIP entries * @access public * @uses $Entries * @since 1.0 */ function Count() { return count($this->Entries); } // end of the 'Count()' method /** * Gets data of the specified ZIP entry * * @param integer Index of the ZIP entry * @return mixed Data for the ZIP entry * @uses SimpleUnzipEntry::$Data * @access public * @since 1.0 */ function GetData($in_Index) { return $this->Entries[$in_Index]->Data; } // end of the 'GetData()' method /** * Gets an entry of the ZIP file * * @param integer Index of the ZIP entry * @return SimpleUnzipEntry Entry of the ZIP file * @uses $Entries * @access public * @since 1.0 */ function GetEntry($in_Index) { return $this->Entries[$in_Index]; } // end of the 'GetEntry()' method /** * Gets error code for the specified ZIP entry * * @param integer Index of the ZIP entry * @return integer Error code for the ZIP entry * @uses SimpleUnzipEntry::$Error * @access public * @since 1.0 */ function GetError($in_Index) { return $this->Entries[$in_Index]->Error; } // end of the 'GetError()' method /** * Gets error message for the specified ZIP entry * * @param integer Index of the ZIP entry * @return string Error message for the ZIP entry * @uses SimpleUnzipEntry::$ErrorMsg * @access public * @since 1.0 */ function GetErrorMsg($in_Index) { return $this->Entries[$in_Index]->ErrorMsg; } // end of the 'GetErrorMsg()' method /** * Gets file name for the specified ZIP entry * * @param integer Index of the ZIP entry * @return string File name for the ZIP entry * @uses SimpleUnzipEntry::$Name * @access public * @since 1.0 */ function GetName($in_Index) { return $this->Entries[$in_Index]->Name; } // end of the 'GetName()' method /** * Gets path of the file for the specified ZIP entry * * @param integer Index of the ZIP entry * @return string Path of the file for the ZIP entry * @uses SimpleUnzipEntry::$Path * @access public * @since 1.0 */ function GetPath($in_Index) { return $this->Entries[$in_Index]->Path; } // end of the 'GetPath()' method /** * Gets file time for the specified ZIP entry * * @param integer Index of the ZIP entry * @return integer File time for the ZIP entry (unix timestamp) * @uses SimpleUnzipEntry::$Time * @access public * @since 1.0 */ function GetTime($in_Index) { return $this->Entries[$in_Index]->Time; } // end of the 'GetTime()' method /** * Reads ZIP file and extracts the entries * * @param string File name of the ZIP archive * @return array ZIP entry list (see also class variable {@link $Entries $Entries}) * @uses SimpleUnzipEntry For the entries * @access public * @since 1.0 */ function ReadFile($in_FileName) { $this->Entries = array(); // Get file parameters $this->Name = $in_FileName; $this->Time = filemtime($in_FileName); $this->Size = filesize($in_FileName); // Read file $oF = fopen($in_FileName, 'rb'); $vZ = fread($oF, $this->Size); fclose($oF); // 2003-12-02 - HB > // Cut end of central directory $aE = explode("\x50\x4b\x05\x06", $vZ); // Easiest way, but not sure if format changes //$this->Comment = substr($aE[1], 18); // Normal way $aP = unpack('x16/v1CL', $aE[1]); $this->Comment = substr($aE[1], 18, $aP['CL']); // Translates end of line from other operating systems $this->Comment = strtr($this->Comment, array("\r\n" => "\n", "\r" => "\n")); // 2003-12-02 - HB < // Cut the entries from the central directory $aE = explode("\x50\x4b\x01\x02", $vZ); // Explode to each part $aE = explode("\x50\x4b\x03\x04", $aE[0]); // Shift out spanning signature or empty entry array_shift($aE); // Loop through the entries foreach($aE as $vZ) { $aI = array(); $aI['E'] = 0; $aI['EM'] = ''; // Retrieving local file header information $aP = unpack('v1VN/v1GPF/v1CM/v1FT/v1FD/V1CRC/V1CS/V1UCS/v1FNL', $vZ); // Check if data is encrypted $bE = ($aP['GPF'] && 0x0001) ? TRUE : FALSE; $nF = $aP['FNL']; // Special case : value block after the compressed data if($aP['GPF'] & 0x0008) { $aP1 = unpack('V1CRC/V1CS/V1UCS', substr($vZ, -12)); $aP['CRC'] = $aP1['CRC']; $aP['CS'] = $aP1['CS']; $aP['UCS'] = $aP1['UCS']; $vZ = substr($vZ, 0, -12); } // Getting stored filename $aI['N'] = substr($vZ, 26, $nF); if(substr($aI['N'], -1) == '/') { // is a directory entry - will be skipped continue; } // Truncate full filename in path and filename $aI['P'] = dirname($aI['N']); $aI['P'] = $aI['P'] == '.' ? '' : $aI['P']; $aI['N'] = basename($aI['N']); $vZ = substr($vZ, 26 + $nF); if(strlen($vZ) != $aP['CS']) { $aI['E'] = 1; $aI['EM'] = 'Compressed size is not equal with the value in header information.'; } else { if($bE) { $aI['E'] = 5; $aI['EM'] = 'File is encrypted, which is not supported from this class.'; } else { switch($aP['CM']) { case 0: // Stored // Here is nothing to do, the file ist flat. break; case 8: // Deflated $vZ = gzinflate($vZ); break; case 12: // BZIP2 // 2003-12-02 - HB > if(! extension_loaded('bz2')) { if(strtoupper(substr(PHP_OS, 0, 3)) == 'WIN') { @dl('php_bz2.dll'); } else { @dl('bz2.so'); } } if(extension_loaded('bz2')) { // 2003-12-02 - HB < $vZ = bzdecompress($vZ); // 2003-12-02 - HB > } else { $aI['E'] = 7; $aI['EM'] = "PHP BZIP2 extension not available."; } // 2003-12-02 - HB < break; default: $aI['E'] = 6; $aI['EM'] = "De-/Compression method {$aP['CM']} is not supported."; } // 2003-12-02 - HB > if(! $aI['E']) { // 2003-12-02 - HB < if($vZ === FALSE) { $aI['E'] = 2; $aI['EM'] = 'Decompression of data failed.'; } else { if(strlen($vZ) != $aP['UCS']) { $aI['E'] = 3; $aI['EM'] = 'Uncompressed size is not equal with the value in header information.'; } else { if(crc32($vZ) != $aP['CRC']) { $aI['E'] = 4; $aI['EM'] = 'CRC32 checksum is not equal with the value in header information.'; } } } // 2003-12-02 - HB > } // 2003-12-02 - HB < } } $aI['D'] = $vZ; // DOS to UNIX timestamp $aI['T'] = mktime(($aP['FT'] & 0xf800) >> 11, ($aP['FT'] & 0x07e0) >> 5, ($aP['FT'] & 0x001f) << 1, ($aP['FD'] & 0x01e0) >> 5, ($aP['FD'] & 0x001f), (($aP['FD'] & 0xfe00) >> 9) + 1980); $this->Entries[] = &new SimpleUnzipEntry($aI); } // end for each entries return $this->Entries; } // end of the 'ReadFile()' method } // end of the 'SimpleUnzip' class /** * Entry of the ZIP file. * * @category phpPublic * @package File-Formats-ZIP * @subpackage Unzip * @version 1.0 * @author Holger Boskugel * @example example.unzip.php Two examples */ class SimpleUnzipEntry { /** * Data of the file entry * * @var mixed * @access public * @see SimpleUnzipEntry() * @since 1.0 */ var $Data = ''; /** * Error of the file entry * * - 0 : No error raised.
* - 1 : Compressed size is not equal with the value in header information.
* - 2 : Decompression of data failed.
* - 3 : Uncompressed size is not equal with the value in header information.
* - 4 : CRC32 checksum is not equal with the value in header information.
* - 5 : File is encrypted, which is not supported from this class.
* - 6 : De-/Compression method ... is not supported.
* - 7 : PHP BZIP2 extension not available. * * @var integer * @access public * @see SimpleUnzipEntry() * @since 1.0 */ var $Error = 0; /** * Error message of the file entry * * @var string * @access public * @see SimpleUnzipEntry() * @since 1.0 */ var $ErrorMsg = ''; /** * File name of the file entry * * @var string * @access public * @see SimpleUnzipEntry() * @since 1.0 */ var $Name = ''; /** * File path of the file entry * * @var string * @access public * @see SimpleUnzipEntry() * @since 1.0 */ var $Path = ''; /** * File time of the file entry (unix timestamp) * * @var integer * @access public * @see SimpleUnzipEntry() * @since 1.0 */ var $Time = 0; /** * Contructor of the class * * @param array Entry datas * @return SimpleUnzipEntry Instanced class * @access public * @since 1.0 */ function SimpleUnzipEntry($in_Entry) { $this->Data = $in_Entry['D']; $this->Error = $in_Entry['E']; $this->ErrorMsg = $in_Entry['EM']; $this->Name = $in_Entry['N']; $this->Path = $in_Entry['P']; $this->Time = $in_Entry['T']; } // end of the 'SimpleUnzipEntry' constructor } // end of the 'SimpleUnzipEntry' class ?>