openrat-cms

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

ArchiveUnzip.class.php (17715B)


      1 <?php
      2 // 28/11/2005 (2.4)
      3 // - dUnzip2 is now compliant with wrong placed "Data Description", made by some compressors,
      4 //   like the classes ZipLib and ZipLib2 by 'Hasin Hayder'. Thanks to Ricardo Parreno for pointing it.
      5 // 09/11/2005 (2.3)
      6 // - Added optional parameter '$stopOnFile' on method 'getList()'.
      7 //   If given, file listing will stop when find given filename. (Useful to open and unzip an exact file)
      8 // 06/11/2005 (2.21)
      9 // - Added support to PK00 file format (Packed to Removable Disk) (thanks to Lito [PHPfileNavigator])
     10 // - Method 'getExtraInfo': If requested file doesn't exist, return FALSE instead of Array()
     11 // 31/10/2005 (2.2)
     12 // - Removed redundant 'file_name' on centralDirs declaration (thanks to Lito [PHPfileNavigator])
     13 // - Fixed redeclaration of file_put_contents when in PHP4 (not returning true)
     14 
     15 ##############################################################
     16 # Class dUnzip2 v2.4
     17 #
     18 #  Author: Alexandre Tedeschi (d)
     19 #  E-Mail: alexandrebr at gmail dot com
     20 #  Londrina - PR / Brazil
     21 #
     22 #  Objective:
     23 #    This class allows programmer to easily unzip files on the fly.
     24 #
     25 #  Requirements:
     26 #    This class requires extension ZLib Enabled. It is default
     27 #    for most site hosts around the world, and for the PHP Win32 dist.
     28 #
     29 #  To do:
     30 #   * Error handling
     31 #   * Write a PHP-Side gzinflate, to completely avoid any external extensions
     32 #   * Write other decompress algorithms
     33 #
     34 #  If you modify this class, or have any ideas to improve it, please contact me!
     35 #  You are allowed to redistribute this class, if you keep my name and contact e-mail on it.
     36 ##############################################################
     37 
     38 namespace util;
     39 class ArchiveUnzip
     40 {
     41 
     42 	// Public
     43 	var $files = array();
     44 	var $value = '';
     45 	var $fileName;
     46 	var $compressedList; // You will problably use only this one!
     47 	var $centralDirList; // Central dir list... It's a kind of 'extra attributes' for a set of files
     48 	var $endOfCentral;   // End of central dir, contains ZIP Comments
     49 	var $debug;
     50 
     51 	// Private
     52 	var $fh;
     53 	var $zipSignature = "\x50\x4b\x03\x04"; // local file header signature
     54 	var $dirSignature = "\x50\x4b\x01\x02"; // central dir header signature
     55 	var $dirSignatureE = "\x50\x4b\x05\x06"; // end of central dir signature
     56 
     57 	// Public
     58 	Function __construct()
     59 	{
     60 		$this->compressedList =
     61 		$this->centralDirList =
     62 		$this->endOfCentral = Array();
     63 	}
     64 
     65 	function open($value)
     66 	{
     67 		$this->fileName = tempnam('/tmp', 'unzip');
     68 //		echo $this->fileName;
     69 		$fo = fopen($this->fileName, 'w');
     70 		fwrite($fo, $value);
     71 		$this->unzipAll();
     72 	}
     73 
     74 
     75 	Function getList($stopOnFile = false)
     76 	{
     77 		if (sizeof($this->compressedList)) {
     78 			$this->debugMsg(1, "Returning already loaded file list.");
     79 			return $this->compressedList;
     80 		}
     81 
     82 		// Open file, and set file handler
     83 		$fh = fopen($this->fileName, "r");
     84 		$this->fh = &$fh;
     85 		if (!$fh) {
     86 			$this->debugMsg(2, "Failed to load file.");
     87 			return false;
     88 		}
     89 
     90 		// Loop the file, looking for files and folders
     91 		$ddTry = false;
     92 		fseek($fh, 0);
     93 		for (; ;) {
     94 			// Check if the signature is valid...
     95 			$signature = fread($fh, 4);
     96 			if (feof($fh)) {
     97 #				$this->debugMsg(1, "Reached end of file");
     98 				break;
     99 			}
    100 
    101 			// If signature is a 'Packed to Removable Disk', just ignore it and move to the next.
    102 			if ($signature == 'PK00') {
    103 				$this->debugMsg(1, "Found PK00: Packed to Removable Disk");
    104 				continue;
    105 			}
    106 
    107 			// If signature of a 'Local File Header'
    108 			if ($signature == $this->zipSignature) {
    109 				# $this->debugMsg(1, "Zip Signature!");
    110 
    111 				// Get information about the zipped file
    112 				$file['version_needed'] = unpack("v", fread($fh, 2)); // version needed to extract
    113 				$file['general_bit_flag'] = unpack("v", fread($fh, 2)); // general purpose bit flag
    114 				$file['compression_method'] = unpack("v", fread($fh, 2)); // compression method
    115 				$file['lastmod_time'] = unpack("v", fread($fh, 2)); // last mod file time
    116 				$file['lastmod_date'] = unpack("v", fread($fh, 2));  // last mod file date
    117 				$file['crc-32'] = fread($fh, 4);              // crc-32
    118 				$file['compressed_size'] = unpack("V", fread($fh, 4)); // compressed size
    119 				$file['uncompressed_size'] = unpack("V", fread($fh, 4)); // uncompressed size
    120 				$fileNameLength = unpack("v", fread($fh, 2)); // filename length
    121 				$extraFieldLength = unpack("v", fread($fh, 2)); // extra field length
    122 				$file['file_name'] = fread($fh, $fileNameLength[1]); // filename
    123 				$file['extra_field'] = $extraFieldLength[1] ? fread($fh, $extraFieldLength[1]) : ''; // extra field
    124 				$file['contents-startOffset'] = ftell($fh);
    125 
    126 				// Bypass the whole compressed contents, and look for the next file
    127 				fseek($fh, $file['compressed_size'][1], SEEK_CUR);
    128 
    129 				// Convert the date and time, from MS-DOS format to UNIX Timestamp
    130 				$BINlastmod_date = str_pad(decbin($file['lastmod_date'][1]), 16, '0', STR_PAD_LEFT);
    131 				$BINlastmod_time = str_pad(decbin($file['lastmod_time'][1]), 16, '0', STR_PAD_LEFT);
    132 				$lastmod_dateY = bindec(substr($BINlastmod_date, 0, 7)) + 1980;
    133 				$lastmod_dateM = bindec(substr($BINlastmod_date, 7, 4));
    134 				$lastmod_dateD = bindec(substr($BINlastmod_date, 11, 5));
    135 				$lastmod_timeH = bindec(substr($BINlastmod_time, 0, 5));
    136 				$lastmod_timeM = bindec(substr($BINlastmod_time, 5, 6));
    137 				$lastmod_timeS = bindec(substr($BINlastmod_time, 11, 5));
    138 
    139 				// Mount file table
    140 				$this->compressedList[$file['file_name']] = Array(
    141 					'file_name' => $file['file_name'],
    142 					'compression_method' => $file['compression_method'][1],
    143 					'version_needed' => $file['version_needed'][1],
    144 					'lastmod_datetime' => mktime($lastmod_timeH, $lastmod_timeM, $lastmod_timeS, $lastmod_dateM, $lastmod_dateD, $lastmod_dateY),
    145 					'crc-32' => str_pad(dechex(ord($file['crc-32'][3])), 2, '0', STR_PAD_LEFT) .
    146 						str_pad(dechex(ord($file['crc-32'][2])), 2, '0', STR_PAD_LEFT) .
    147 						str_pad(dechex(ord($file['crc-32'][1])), 2, '0', STR_PAD_LEFT) .
    148 						str_pad(dechex(ord($file['crc-32'][0])), 2, '0', STR_PAD_LEFT),
    149 					'compressed_size' => $file['compressed_size'][1],
    150 					'uncompressed_size' => $file['uncompressed_size'][1],
    151 					'extra_field' => $file['extra_field'],
    152 					'general_bit_flag' => str_pad(decbin($file['general_bit_flag'][1]), 8, '0', STR_PAD_LEFT),
    153 					'contents-startOffset' => $file['contents-startOffset']
    154 				);
    155 
    156 				if ($stopOnFile) if ($file['file_name'] == $stopOnFile) {
    157 					$this->debugMsg(1, "Stopping on file...");
    158 					break;
    159 				}
    160 			} // If signature of a 'Central Directory Structure'
    161 			elseif ($signature == $this->dirSignature) {
    162 				# $this->debugMsg(1, "Dir Signature!");
    163 
    164 				$dir['version_madeby'] = unpack("v", fread($fh, 2)); // version made by
    165 				$dir['version_needed'] = unpack("v", fread($fh, 2)); // version needed to extract
    166 				$dir['general_bit_flag'] = unpack("v", fread($fh, 2)); // general purpose bit flag
    167 				$dir['compression_method'] = unpack("v", fread($fh, 2)); // compression method
    168 				$dir['lastmod_time'] = unpack("v", fread($fh, 2)); // last mod file time
    169 				$dir['lastmod_date'] = unpack("v", fread($fh, 2)); // last mod file date
    170 				$dir['crc-32'] = fread($fh, 4);              // crc-32
    171 				$dir['compressed_size'] = unpack("V", fread($fh, 4)); // compressed size
    172 				$dir['uncompressed_size'] = unpack("V", fread($fh, 4)); // uncompressed size
    173 				$fileNameLength = unpack("v", fread($fh, 2)); // filename length
    174 				$extraFieldLength = unpack("v", fread($fh, 2)); // extra field length
    175 				$fileCommentLength = unpack("v", fread($fh, 2)); // file comment length
    176 				$dir['disk_number_start'] = unpack("v", fread($fh, 2)); // disk number start
    177 				$dir['internal_attributes'] = unpack("v", fread($fh, 2)); // internal file attributes-byte1
    178 				$dir['external_attributes1'] = unpack("v", fread($fh, 2)); // external file attributes-byte2
    179 				$dir['external_attributes2'] = unpack("v", fread($fh, 2)); // external file attributes
    180 				$dir['relative_offset'] = unpack("V", fread($fh, 4)); // relative offset of local header
    181 				$dir['file_name'] = fread($fh, $fileNameLength[1]);                             // filename
    182 				$dir['extra_field'] = $extraFieldLength[1] ? fread($fh, $extraFieldLength[1]) : '';   // extra field
    183 				$dir['file_comment'] = $fileCommentLength[1] ? fread($fh, $fileCommentLength[1]) : ''; // file comment
    184 
    185 				// Convert the date and time, from MS-DOS format to UNIX Timestamp
    186 				$BINlastmod_date = str_pad(decbin($file['lastmod_date'][1]), 16, '0', STR_PAD_LEFT);
    187 				$BINlastmod_time = str_pad(decbin($file['lastmod_time'][1]), 16, '0', STR_PAD_LEFT);
    188 				$lastmod_dateY = bindec(substr($BINlastmod_date, 0, 7)) + 1980;
    189 				$lastmod_dateM = bindec(substr($BINlastmod_date, 7, 4));
    190 				$lastmod_dateD = bindec(substr($BINlastmod_date, 11, 5));
    191 				$lastmod_timeH = bindec(substr($BINlastmod_time, 0, 5));
    192 				$lastmod_timeM = bindec(substr($BINlastmod_time, 5, 6));
    193 				$lastmod_timeS = bindec(substr($BINlastmod_time, 11, 5));
    194 
    195 				$this->centralDirList[$dir['file_name']] = Array(
    196 					'version_madeby' => $dir['version_madeby'][1],
    197 					'version_needed' => $dir['version_needed'][1],
    198 					'general_bit_flag' => str_pad(decbin($file['general_bit_flag'][1]), 8, '0', STR_PAD_LEFT),
    199 					'compression_method' => $dir['compression_method'][1],
    200 					'lastmod_datetime' => mktime($lastmod_timeH, $lastmod_timeM, $lastmod_timeS, $lastmod_dateM, $lastmod_dateD, $lastmod_dateY),
    201 					'crc-32' => str_pad(dechex(ord($file['crc-32'][3])), 2, '0', STR_PAD_LEFT) .
    202 						str_pad(dechex(ord($file['crc-32'][2])), 2, '0', STR_PAD_LEFT) .
    203 						str_pad(dechex(ord($file['crc-32'][1])), 2, '0', STR_PAD_LEFT) .
    204 						str_pad(dechex(ord($file['crc-32'][0])), 2, '0', STR_PAD_LEFT),
    205 					'compressed_size' => $dir['compressed_size'][1],
    206 					'uncompressed_size' => $dir['uncompressed_size'][1],
    207 					'disk_number_start' => $dir['disk_number_start'][1],
    208 					'internal_attributes' => $dir['internal_attributes'][1],
    209 					'external_attributes1' => $dir['external_attributes1'][1],
    210 					'external_attributes2' => $dir['external_attributes2'][1],
    211 					'relative_offset' => $dir['relative_offset'][1],
    212 					'file_name' => $dir['file_name'],
    213 					'extra_field' => $dir['extra_field'],
    214 					'file_comment' => $dir['file_comment'],
    215 				);
    216 			} elseif ($signature == $this->dirSignatureE) {
    217 				# $this->debugMsg(1, "EOF Dir Signature!");
    218 
    219 				$eodir['disk_number_this'] = unpack("v", fread($fh, 2)); // number of this disk
    220 				$eodir['disk_number'] = unpack("v", fread($fh, 2)); // number of the disk with the start of the central directory
    221 				$eodir['total_entries_this'] = unpack("v", fread($fh, 2)); // total number of entries in the central dir on this disk
    222 				$eodir['total_entries'] = unpack("v", fread($fh, 2)); // total number of entries in
    223 				$eodir['size_of_cd'] = unpack("V", fread($fh, 4)); // size of the central directory
    224 				$eodir['offset_start_cd'] = unpack("V", fread($fh, 4)); // offset of start of central directory with respect to the starting disk number
    225 				$zipFileCommentLenght = unpack("v", fread($fh, 2)); // zipfile comment length
    226 				$eodir['zipfile_comment'] = $zipFileCommentLenght[1] ? fread($fh, $zipFileCommentLenght[1]) : ''; // zipfile comment
    227 				$this->endOfCentral = Array(
    228 					'disk_number_this' => $eodir['disk_number_this'][1],
    229 					'disk_number' => $eodir['disk_number'][1],
    230 					'total_entries_this' => $eodir['total_entries_this'][1],
    231 					'total_entries' => $eodir['total_entries'][1],
    232 					'size_of_cd' => $eodir['size_of_cd'][1],
    233 					'offset_start_cd' => $eodir['offset_start_cd'][1],
    234 					'zipfile_comment' => $eodir['zipfile_comment'],
    235 				);
    236 			} else {
    237 				if (!$ddTry) {
    238 					$this->debugMsg(1, "Unexpected header. Trying to detect wrong placed 'Data Descriptor'...\n");
    239 					$ddTry = true;
    240 					fseek($fh, 12 - 4, SEEK_CUR); // Jump over 'crc-32'(4) 'compressed-size'(4), 'uncompressed-size'(4)
    241 					continue;
    242 				}
    243 				$this->debugMsg(1, "Unexpected header, ending loop at offset " . ftell($fh));
    244 				break;
    245 			}
    246 			$ddTry = false;
    247 		}
    248 
    249 		if ($this->debug) {
    250 			#------- Debug compressedList
    251 			$kkk = 0;
    252 			echo "<table border='0' style='font: 11px Verdana; border: 1px solid #000'>";
    253 			foreach ($this->compressedList as $fileName => $item) {
    254 				if (!$kkk && $kkk = 1) {
    255 					echo "<tr style='background: #ADA'>";
    256 					foreach ($item as $fieldName => $value)
    257 						echo "<td>$fieldName</td>";
    258 					echo '</tr>';
    259 				}
    260 				echo "<tr style='background: #CFC'>";
    261 				foreach ($item as $fieldName => $value) {
    262 					if ($fieldName == 'lastmod_datetime')
    263 						echo "<td title='$fieldName' nowrap='nowrap'>" . date("d/m/Y H:i:s", $value) . "</td>";
    264 					else
    265 						echo "<td title='$fieldName' nowrap='nowrap'>$value</td>";
    266 				}
    267 				echo "</tr>";
    268 			}
    269 			echo "</table>";
    270 
    271 			#------- Debug centralDirList
    272 			$kkk = 0;
    273 			if (sizeof($this->centralDirList)) {
    274 				echo "<table border='0' style='font: 11px Verdana; border: 1px solid #000'>";
    275 				foreach ($this->centralDirList as $fileName => $item) {
    276 					if (!$kkk && $kkk = 1) {
    277 						echo "<tr style='background: #AAD'>";
    278 						foreach ($item as $fieldName => $value)
    279 							echo "<td>$fieldName</td>";
    280 						echo '</tr>';
    281 					}
    282 					echo "<tr style='background: #CCF'>";
    283 					foreach ($item as $fieldName => $value) {
    284 						if ($fieldName == 'lastmod_datetime')
    285 							echo "<td title='$fieldName' nowrap='nowrap'>" . date("d/m/Y H:i:s", $value) . "</td>";
    286 						else
    287 							echo "<td title='$fieldName' nowrap='nowrap'>$value</td>";
    288 					}
    289 					echo "</tr>";
    290 				}
    291 				echo "</table>";
    292 			}
    293 
    294 			#------- Debug endOfCentral
    295 			$kkk = 0;
    296 			if (sizeof($this->endOfCentral)) {
    297 				echo "<table border='0' style='font: 11px Verdana' style='border: 1px solid #000'>";
    298 				echo "<tr style='background: #DAA'><td colspan='2'>dUnzip - End of file</td></tr>";
    299 				foreach ($this->endOfCentral as $field => $value) {
    300 					echo "<tr>";
    301 					echo "<td style='background: #FCC'>$field</td>";
    302 					echo "<td style='background: #FDD'>$value</td>";
    303 					echo "</tr>";
    304 				}
    305 				echo "</table>";
    306 			}
    307 		}
    308 
    309 		return $this->compressedList;
    310 	}
    311 
    312 
    313 	Function getExtraInfo($compressedFileName)
    314 	{
    315 		return
    316 			isset($this->centralDirList[$compressedFileName]) ?
    317 				$this->centralDirList[$compressedFileName] :
    318 				false;
    319 	}
    320 
    321 
    322 	Function getZipInfo($detail = false)
    323 	{
    324 		return $detail ?
    325 			$this->endOfCentral[$detail] :
    326 			$this->endOfCentral;
    327 	}
    328 
    329 
    330 	Function unzip($compressedFileName, $targetFileName = false)
    331 	{
    332 		$fdetails = &$this->compressedList[$compressedFileName];
    333 
    334 		if (!sizeof($this->compressedList)) {
    335 			$this->debugMsg(1, "Trying to unzip before loading file list... Loading it!");
    336 			$this->getList(false, $compressedFileName);
    337 		}
    338 		if (!isset($this->compressedList[$compressedFileName])) {
    339 			$this->debugMsg(2, "File '<b>$compressedFileName</b>' is not compressed in the zip.");
    340 			return false;
    341 		}
    342 		if (substr($compressedFileName, -1) == "/") {
    343 			$this->debugMsg(2, "Trying to unzip a folder name '<b>$compressedFileName</b>'.");
    344 			return false;
    345 		}
    346 		if (!$fdetails['uncompressed_size']) {
    347 			$this->debugMsg(1, "File '<b>$compressedFileName</b>' is empty.");
    348 			return "";
    349 		}
    350 
    351 		fseek($this->fh, $fdetails['contents-startOffset']);
    352 		return $this->uncompress(
    353 			fread($this->fh, $fdetails['compressed_size']),
    354 			$fdetails['compression_method'],
    355 			$fdetails['uncompressed_size']);
    356 	}
    357 
    358 
    359 	Function unzipAll($targetDir = false, $baseDir = "", $maintainStructure = true, $chmod = false)
    360 	{
    361 		if ($targetDir === false)
    362 			$targetDir = dirname(__FILE__) . "/";
    363 
    364 		$lista = $this->getList();
    365 		if (sizeof($lista)) foreach ($lista as $fileName => $trash) {
    366 			$dirname = dirname($fileName);
    367 			$outDN = "$targetDir/$dirname";
    368 
    369 			if (substr($dirname, 0, strlen($baseDir)) != $baseDir)
    370 				continue;
    371 
    372 			if (!is_dir($outDN) && $maintainStructure) {
    373 				$str = "";
    374 				$folders = explode("/", $dirname);
    375 				foreach ($folders as $folder) {
    376 					$str = $str ? "$str/$folder" : $folder;
    377 					if (!is_dir("$targetDir/$str")) {
    378 						$this->debugMsg(1, "Creating folder: $targetDir/$str");
    379 						mkdir("$targetDir/$str");
    380 						if ($chmod)
    381 							chmod("$targetDir/$str", $chmod);
    382 					}
    383 				}
    384 			}
    385 			if (substr($fileName, -1, 1) == "/")
    386 				continue;
    387 
    388 			$maintainStructure ?
    389 				$this->unzip($fileName, "$targetDir/$fileName") :
    390 				$this->unzip($fileName, "$targetDir/" . basename($fileName));
    391 
    392 			if ($chmod)
    393 				chmod($maintainStructure ? "$targetDir/$fileName" : "$targetDir/" . basename($fileName), $chmod);
    394 		}
    395 	}
    396 
    397 	Function close()
    398 	{ // Free the file resource
    399 		if ($this->fh)
    400 			fclose($this->fh);
    401 	}
    402 
    403 	// Private (you should NOT call these methods):
    404 	Function uncompress($content, $mode, $uncompressedSize, $targetFileName = false)
    405 	{
    406 		switch ($mode) {
    407 			case 0:
    408 				// Not compressed
    409 				return $content;
    410 			case 1:
    411 				$this->debugMsg(2, "Shrunk mode is not supported... yet?");
    412 				return false;
    413 			case 2:
    414 			case 3:
    415 			case 4:
    416 			case 5:
    417 				$this->debugMsg(2, "Compression factor " . ($mode - 1) . " is not supported... yet?");
    418 				return false;
    419 			case 6:
    420 				$this->debugMsg(2, "Implode is not supported... yet?");
    421 				return false;
    422 			case 7:
    423 				$this->debugMsg(2, "Tokenizing compression algorithm is not supported... yet?");
    424 				return false;
    425 			case 8:
    426 				// Deflate
    427 				return gzinflate($content, $uncompressedSize);
    428 			case 9:
    429 				$this->debugMsg(2, "Enhanced Deflating is not supported... yet?");
    430 				return false;
    431 			case 10:
    432 				$this->debugMsg(2, "PKWARE Date Compression Library Impoloding is not supported... yet?");
    433 				return false;
    434 			default:
    435 				$this->debugMsg(2, "Unknown uncompress method: $mode");
    436 				return false;
    437 		}
    438 	}
    439 
    440 
    441 	Function debugMsg($level, $string)
    442 	{
    443 		if ($this->debug)
    444 			if ($level == 1)
    445 				echo "<b style='color: #777'>dUnzip2:</b> $string<br>";
    446 		if ($level == 2)
    447 			echo "<b style='color: #F00'>dUnzip2:</b> $string<br>";
    448 	}
    449 }
    450 
    451 ?>