[ Index ] |
|
Code source de PRADO 3.0.6 |
1 <?php 2 /** 3 * TTarFileExtractor class file 4 * 5 * @author Vincent Blavet <vincent@phpconcept.net> 6 * @copyright Copyright © 1997-2003 The PHP Group 7 * @version $Id: TTarFileExtractor.php 1397 2006-09-07 07:55:53Z wei $ 8 * @package System.IO 9 */ 10 11 /* vim: set ts=4 sw=4: */ 12 // +----------------------------------------------------------------------+ 13 // | PHP Version 4 | 14 // +----------------------------------------------------------------------+ 15 // | Copyright (c) 1997-2003 The PHP Group | 16 // +----------------------------------------------------------------------+ 17 // | This source file is subject to version 3.0 of the PHP license, | 18 // | that is bundled with this package in the file LICENSE, and is | 19 // | available through the world-wide-web at the following url: | 20 // | http://www.php.net/license/3_0.txt. | 21 // | If you did not receive a copy of the PHP license and are unable to | 22 // | obtain it through the world-wide-web, please send a note to | 23 // | license@php.net so we can mail you a copy immediately. | 24 // +----------------------------------------------------------------------+ 25 // | Author: Vincent Blavet <vincent@phpconcept.net> | 26 // +----------------------------------------------------------------------+ 27 // 28 // $Id: TTarFileExtractor.php 1397 2006-09-07 07:55:53Z wei $ 29 30 /** 31 * TTarFileExtractor class 32 * 33 * @author Vincent Blavet <vincent@phpconcept.net> 34 * @version $Id: TTarFileExtractor.php 1397 2006-09-07 07:55:53Z wei $ 35 * @package System.IO 36 * @since 3.0 37 */ 38 class TTarFileExtractor 39 { 40 /** 41 * @var string Name of the Tar 42 */ 43 private $_tarname=''; 44 45 /** 46 * @var file descriptor 47 */ 48 private $_file=0; 49 50 /** 51 * @var string Local Tar name of a remote Tar (http:// or ftp://) 52 */ 53 private $_temp_tarname=''; 54 55 /** 56 * Archive_Tar Class constructor. This flavour of the constructor only 57 * declare a new Archive_Tar object, identifying it by the name of the 58 * tar file. 59 * 60 * @param string $p_tarname The name of the tar archive to create 61 * @access public 62 */ 63 public function __construct($p_tarname) 64 { 65 $this->_tarname = $p_tarname; 66 } 67 68 public function __destruct() 69 { 70 $this->_close(); 71 // ----- Look for a local copy to delete 72 if ($this->_temp_tarname != '') 73 @unlink($this->_temp_tarname); 74 } 75 76 public function extract($p_path='') 77 { 78 return $this->extractModify($p_path, ''); 79 } 80 81 /** 82 * This method extract all the content of the archive in the directory 83 * indicated by $p_path. When relevant the memorized path of the 84 * files/dir can be modified by removing the $p_remove_path path at the 85 * beginning of the file/dir path. 86 * While extracting a file, if the directory path does not exists it is 87 * created. 88 * While extracting a file, if the file already exists it is replaced 89 * without looking for last modification date. 90 * While extracting a file, if the file already exists and is write 91 * protected, the extraction is aborted. 92 * While extracting a file, if a directory with the same name already 93 * exists, the extraction is aborted. 94 * While extracting a directory, if a file with the same name already 95 * exists, the extraction is aborted. 96 * While extracting a file/directory if the destination directory exist 97 * and is write protected, or does not exist but can not be created, 98 * the extraction is aborted. 99 * If after extraction an extracted file does not show the correct 100 * stored file size, the extraction is aborted. 101 * When the extraction is aborted, a PEAR error text is set and false 102 * is returned. However the result can be a partial extraction that may 103 * need to be manually cleaned. 104 * 105 * @param string $p_path The path of the directory where the 106 * files/dir need to by extracted. 107 * @param string $p_remove_path Part of the memorized path that can be 108 * removed if present at the beginning of 109 * the file/dir path. 110 * @return boolean true on success, false on error. 111 * @access public 112 */ 113 protected function extractModify($p_path, $p_remove_path) 114 { 115 $v_result = true; 116 $v_list_detail = array(); 117 118 if ($v_result = $this->_openRead()) { 119 $v_result = $this->_extractList($p_path, $v_list_detail, 120 "complete", 0, $p_remove_path); 121 $this->_close(); 122 } 123 124 return $v_result; 125 } 126 127 protected function _error($p_message) 128 { 129 throw new Exception($p_message); 130 } 131 132 private function _isArchive($p_filename=null) 133 { 134 if ($p_filename == null) { 135 $p_filename = $this->_tarname; 136 } 137 clearstatcache(); 138 return @is_file($p_filename); 139 } 140 141 private function _openRead() 142 { 143 if (strtolower(substr($this->_tarname, 0, 7)) == 'http://') { 144 145 // ----- Look if a local copy need to be done 146 if ($this->_temp_tarname == '') { 147 $this->_temp_tarname = uniqid('tar').'.tmp'; 148 if (!$v_file_from = @fopen($this->_tarname, 'rb')) { 149 $this->_error('Unable to open in read mode \'' 150 .$this->_tarname.'\''); 151 $this->_temp_tarname = ''; 152 return false; 153 } 154 if (!$v_file_to = @fopen($this->_temp_tarname, 'wb')) { 155 $this->_error('Unable to open in write mode \'' 156 .$this->_temp_tarname.'\''); 157 $this->_temp_tarname = ''; 158 return false; 159 } 160 while ($v_data = @fread($v_file_from, 1024)) 161 @fwrite($v_file_to, $v_data); 162 @fclose($v_file_from); 163 @fclose($v_file_to); 164 } 165 166 // ----- File to open if the local copy 167 $v_filename = $this->_temp_tarname; 168 169 } else 170 // ----- File to open if the normal Tar file 171 $v_filename = $this->_tarname; 172 173 $this->_file = @fopen($v_filename, "rb"); 174 175 if ($this->_file == 0) { 176 $this->_error('Unable to open in read mode \''.$v_filename.'\''); 177 return false; 178 } 179 180 return true; 181 } 182 183 private function _close() 184 { 185 //if (isset($this->_file)) { 186 if (is_resource($this->_file)) 187 { 188 @fclose($this->_file); 189 $this->_file = 0; 190 } 191 192 // ----- Look if a local copy need to be erase 193 // Note that it might be interesting to keep the url for a time : ToDo 194 if ($this->_temp_tarname != '') { 195 @unlink($this->_temp_tarname); 196 $this->_temp_tarname = ''; 197 } 198 199 return true; 200 } 201 202 private function _cleanFile() 203 { 204 $this->_close(); 205 206 // ----- Look for a local copy 207 if ($this->_temp_tarname != '') { 208 // ----- Remove the local copy but not the remote tarname 209 @unlink($this->_temp_tarname); 210 $this->_temp_tarname = ''; 211 } else { 212 // ----- Remove the local tarname file 213 @unlink($this->_tarname); 214 } 215 $this->_tarname = ''; 216 217 return true; 218 } 219 220 private function _readBlock() 221 { 222 $v_block = null; 223 if (is_resource($this->_file)) { 224 $v_block = @fread($this->_file, 512); 225 } 226 return $v_block; 227 } 228 229 private function _jumpBlock($p_len=null) 230 { 231 if (is_resource($this->_file)) { 232 if ($p_len === null) 233 $p_len = 1; 234 235 @fseek($this->_file, @ftell($this->_file)+($p_len*512)); 236 } 237 return true; 238 } 239 240 private function _readHeader($v_binary_data, &$v_header) 241 { 242 if (strlen($v_binary_data)==0) { 243 $v_header['filename'] = ''; 244 return true; 245 } 246 247 if (strlen($v_binary_data) != 512) { 248 $v_header['filename'] = ''; 249 $this->_error('Invalid block size : '.strlen($v_binary_data)); 250 return false; 251 } 252 253 // ----- Calculate the checksum 254 $v_checksum = 0; 255 // ..... First part of the header 256 for ($i=0; $i<148; $i++) 257 $v_checksum+=ord(substr($v_binary_data,$i,1)); 258 // ..... Ignore the checksum value and replace it by ' ' (space) 259 for ($i=148; $i<156; $i++) 260 $v_checksum += ord(' '); 261 // ..... Last part of the header 262 for ($i=156; $i<512; $i++) 263 $v_checksum+=ord(substr($v_binary_data,$i,1)); 264 265 $v_data = unpack("a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/" 266 ."a8checksum/a1typeflag/a100link/a6magic/a2version/" 267 ."a32uname/a32gname/a8devmajor/a8devminor", 268 $v_binary_data); 269 270 // ----- Extract the checksum 271 $v_header['checksum'] = OctDec(trim($v_data['checksum'])); 272 if ($v_header['checksum'] != $v_checksum) { 273 $v_header['filename'] = ''; 274 275 // ----- Look for last block (empty block) 276 if (($v_checksum == 256) && ($v_header['checksum'] == 0)) 277 return true; 278 279 $this->_error('Invalid checksum for file "'.$v_data['filename'] 280 .'" : '.$v_checksum.' calculated, ' 281 .$v_header['checksum'].' expected'); 282 return false; 283 } 284 285 // ----- Extract the properties 286 $v_header['filename'] = trim($v_data['filename']); 287 $v_header['mode'] = OctDec(trim($v_data['mode'])); 288 $v_header['uid'] = OctDec(trim($v_data['uid'])); 289 $v_header['gid'] = OctDec(trim($v_data['gid'])); 290 $v_header['size'] = OctDec(trim($v_data['size'])); 291 $v_header['mtime'] = OctDec(trim($v_data['mtime'])); 292 if (($v_header['typeflag'] = $v_data['typeflag']) == "5") { 293 $v_header['size'] = 0; 294 } 295 return true; 296 } 297 298 private function _readLongHeader(&$v_header) 299 { 300 $v_filename = ''; 301 $n = floor($v_header['size']/512); 302 for ($i=0; $i<$n; $i++) { 303 $v_content = $this->_readBlock(); 304 $v_filename .= $v_content; 305 } 306 if (($v_header['size'] % 512) != 0) { 307 $v_content = $this->_readBlock(); 308 $v_filename .= $v_content; 309 } 310 311 // ----- Read the next header 312 $v_binary_data = $this->_readBlock(); 313 314 if (!$this->_readHeader($v_binary_data, $v_header)) 315 return false; 316 317 $v_header['filename'] = $v_filename; 318 319 return true; 320 } 321 322 protected function _extractList($p_path, &$p_list_detail, $p_mode, 323 $p_file_list, $p_remove_path) 324 { 325 $v_result=true; 326 $v_nb = 0; 327 $v_extract_all = true; 328 $v_listing = false; 329 330 $p_path = $this->_translateWinPath($p_path, false); 331 if ($p_path == '' || (substr($p_path, 0, 1) != '/' 332 && substr($p_path, 0, 3) != "../" && !strpos($p_path, ':'))) { 333 $p_path = "./".$p_path; 334 } 335 $p_remove_path = $this->_translateWinPath($p_remove_path); 336 337 // ----- Look for path to remove format (should end by /) 338 if (($p_remove_path != '') && (substr($p_remove_path, -1) != '/')) 339 $p_remove_path .= '/'; 340 $p_remove_path_size = strlen($p_remove_path); 341 342 switch ($p_mode) { 343 case "complete" : 344 $v_extract_all = true; 345 $v_listing = false; 346 break; 347 case "partial" : 348 $v_extract_all = false; 349 $v_listing = false; 350 break; 351 case "list" : 352 $v_extract_all = false; 353 $v_listing = true; 354 break; 355 default : 356 $this->_error('Invalid extract mode ('.$p_mode.')'); 357 return false; 358 } 359 360 clearstatcache(); 361 362 while (strlen($v_binary_data = $this->_readBlock()) != 0) 363 { 364 $v_extract_file = false; 365 $v_extraction_stopped = 0; 366 367 if (!$this->_readHeader($v_binary_data, $v_header)) 368 return false; 369 370 if ($v_header['filename'] == '') { 371 continue; 372 } 373 374 // ----- Look for long filename 375 if ($v_header['typeflag'] == 'L') { 376 if (!$this->_readLongHeader($v_header)) 377 return false; 378 } 379 380 if ((!$v_extract_all) && (is_array($p_file_list))) { 381 // ----- By default no unzip if the file is not found 382 $v_extract_file = false; 383 384 for ($i=0; $i<sizeof($p_file_list); $i++) { 385 // ----- Look if it is a directory 386 if (substr($p_file_list[$i], -1) == '/') { 387 // ----- Look if the directory is in the filename path 388 if ((strlen($v_header['filename']) > strlen($p_file_list[$i])) 389 && (substr($v_header['filename'], 0, strlen($p_file_list[$i])) 390 == $p_file_list[$i])) { 391 $v_extract_file = true; 392 break; 393 } 394 } 395 396 // ----- It is a file, so compare the file names 397 elseif ($p_file_list[$i] == $v_header['filename']) { 398 $v_extract_file = true; 399 break; 400 } 401 } 402 } else { 403 $v_extract_file = true; 404 } 405 406 // ----- Look if this file need to be extracted 407 if (($v_extract_file) && (!$v_listing)) 408 { 409 if (($p_remove_path != '') 410 && (substr($v_header['filename'], 0, $p_remove_path_size) 411 == $p_remove_path)) 412 $v_header['filename'] = substr($v_header['filename'], 413 $p_remove_path_size); 414 if (($p_path != './') && ($p_path != '/')) { 415 while (substr($p_path, -1) == '/') 416 $p_path = substr($p_path, 0, strlen($p_path)-1); 417 418 if (substr($v_header['filename'], 0, 1) == '/') 419 $v_header['filename'] = $p_path.$v_header['filename']; 420 else 421 $v_header['filename'] = $p_path.'/'.$v_header['filename']; 422 } 423 if (file_exists($v_header['filename'])) { 424 if ( (@is_dir($v_header['filename'])) 425 && ($v_header['typeflag'] == '')) { 426 $this->_error('File '.$v_header['filename'] 427 .' already exists as a directory'); 428 return false; 429 } 430 if ( ($this->_isArchive($v_header['filename'])) 431 && ($v_header['typeflag'] == "5")) { 432 $this->_error('Directory '.$v_header['filename'] 433 .' already exists as a file'); 434 return false; 435 } 436 if (!is_writeable($v_header['filename'])) { 437 $this->_error('File '.$v_header['filename'] 438 .' already exists and is write protected'); 439 return false; 440 } 441 if (filemtime($v_header['filename']) > $v_header['mtime']) { 442 // To be completed : An error or silent no replace ? 443 } 444 } 445 446 // ----- Check the directory availability and create it if necessary 447 elseif (($v_result 448 = $this->_dirCheck(($v_header['typeflag'] == "5" 449 ?$v_header['filename'] 450 :dirname($v_header['filename'])))) != 1) { 451 $this->_error('Unable to create path for '.$v_header['filename']); 452 return false; 453 } 454 455 if ($v_extract_file) { 456 if ($v_header['typeflag'] == "5") { 457 if (!@file_exists($v_header['filename'])) { 458 if (!@mkdir($v_header['filename'], 0777)) { 459 $this->_error('Unable to create directory {' 460 .$v_header['filename'].'}'); 461 return false; 462 } 463 chmod($v_header['filename'], 0777); 464 } 465 } else { 466 if (($v_dest_file = @fopen($v_header['filename'], "wb")) == 0) { 467 $this->_error('Error while opening {'.$v_header['filename'] 468 .'} in write binary mode'); 469 return false; 470 } else { 471 $n = floor($v_header['size']/512); 472 for ($i=0; $i<$n; $i++) { 473 $v_content = $this->_readBlock(); 474 fwrite($v_dest_file, $v_content, 512); 475 } 476 if (($v_header['size'] % 512) != 0) { 477 $v_content = $this->_readBlock(); 478 fwrite($v_dest_file, $v_content, ($v_header['size'] % 512)); 479 } 480 481 @fclose($v_dest_file); 482 483 // ----- Change the file mode, mtime 484 @touch($v_header['filename'], $v_header['mtime']); 485 // To be completed 486 //chmod($v_header[filename], DecOct($v_header[mode])); 487 } 488 489 // ----- Check the file size 490 clearstatcache(); 491 if (filesize($v_header['filename']) != $v_header['size']) { 492 $this->_error('Extracted file '.$v_header['filename'] 493 .' does not have the correct file size \'' 494 .filesize($v_header['filename']) 495 .'\' ('.$v_header['size'] 496 .' expected). Archive may be corrupted.'); 497 return false; 498 } 499 } 500 } else { 501 $this->_jumpBlock(ceil(($v_header['size']/512))); 502 } 503 } else { 504 $this->_jumpBlock(ceil(($v_header['size']/512))); 505 } 506 507 /* TBC : Seems to be unused ... 508 if ($this->_compress) 509 $v_end_of_file = @gzeof($this->_file); 510 else 511 $v_end_of_file = @feof($this->_file); 512 */ 513 514 if ($v_listing || $v_extract_file || $v_extraction_stopped) { 515 // ----- Log extracted files 516 if (($v_file_dir = dirname($v_header['filename'])) 517 == $v_header['filename']) 518 $v_file_dir = ''; 519 if ((substr($v_header['filename'], 0, 1) == '/') && ($v_file_dir == '')) 520 $v_file_dir = '/'; 521 522 $p_list_detail[$v_nb++] = $v_header; 523 } 524 } 525 526 return true; 527 } 528 529 /** 530 * Check if a directory exists and create it (including parent 531 * dirs) if not. 532 * 533 * @param string $p_dir directory to check 534 * 535 * @return bool true if the directory exists or was created 536 */ 537 protected function _dirCheck($p_dir) 538 { 539 if ((@is_dir($p_dir)) || ($p_dir == '')) 540 return true; 541 542 $p_parent_dir = dirname($p_dir); 543 544 if (($p_parent_dir != $p_dir) && 545 ($p_parent_dir != '') && 546 (!$this->_dirCheck($p_parent_dir))) 547 return false; 548 549 if (!@mkdir($p_dir, 0777)) { 550 $this->_error("Unable to create directory '$p_dir'"); 551 return false; 552 } 553 chmod($p_dir,0777); 554 555 return true; 556 } 557 558 protected function _translateWinPath($p_path, $p_remove_disk_letter=true) 559 { 560 if (substr(PHP_OS, 0, 3) == 'WIN') { 561 // ----- Look for potential disk letter 562 if ( ($p_remove_disk_letter) 563 && (($v_position = strpos($p_path, ':')) != false)) { 564 $p_path = substr($p_path, $v_position+1); 565 } 566 // ----- Change potential windows directory separator 567 if ((strpos($p_path, '\\') > 0) || (substr($p_path, 0,1) == '\\')) { 568 $p_path = strtr($p_path, '\\', '/'); 569 } 570 } 571 return $p_path; 572 } 573 } 574 ?>
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
Généré le : Sun Feb 25 21:07:04 2007 | par Balluche grâce à PHPXref 0.7 |