[ Index ] |
|
Code source de PHP PEAR 1.4.5 |
1 <?php 2 /* vim: set ts=4 sw=4: */ 3 // +----------------------------------------------------------------------+ 4 // | PHP Version 4 | 5 // +----------------------------------------------------------------------+ 6 // | Copyright (c) 1997-2003 The PHP Group | 7 // +----------------------------------------------------------------------+ 8 // | This source file is subject to version 3.0 of the PHP license, | 9 // | that is bundled with this package in the file LICENSE, and is | 10 // | available through the world-wide-web at the following url: | 11 // | http://www.php.net/license/3_0.txt. | 12 // | If you did not receive a copy of the PHP license and are unable to | 13 // | obtain it through the world-wide-web, please send a note to | 14 // | license@php.net so we can mail you a copy immediately. | 15 // +----------------------------------------------------------------------+ 16 // | Author: Vincent Blavet <vincent@phpconcept.net> | 17 // +----------------------------------------------------------------------+ 18 // 19 // $Id: Tar.php,v 1.39 2006/12/22 19:20:08 cellog Exp $ 20 21 require_once 'PEAR.php'; 22 23 24 define ('ARCHIVE_TAR_ATT_SEPARATOR', 90001); 25 define ('ARCHIVE_TAR_END_BLOCK', pack("a512", '')); 26 27 /** 28 * Creates a (compressed) Tar archive 29 * 30 * @author Vincent Blavet <vincent@phpconcept.net> 31 * @version $Revision: 1.39 $ 32 * @package Archive 33 */ 34 class Archive_Tar extends PEAR 35 { 36 /** 37 * @var string Name of the Tar 38 */ 39 var $_tarname=''; 40 41 /** 42 * @var boolean if true, the Tar file will be gzipped 43 */ 44 var $_compress=false; 45 46 /** 47 * @var string Type of compression : 'none', 'gz' or 'bz2' 48 */ 49 var $_compress_type='none'; 50 51 /** 52 * @var string Explode separator 53 */ 54 var $_separator=' '; 55 56 /** 57 * @var file descriptor 58 */ 59 var $_file=0; 60 61 /** 62 * @var string Local Tar name of a remote Tar (http:// or ftp://) 63 */ 64 var $_temp_tarname=''; 65 66 // {{{ constructor 67 /** 68 * Archive_Tar Class constructor. This flavour of the constructor only 69 * declare a new Archive_Tar object, identifying it by the name of the 70 * tar file. 71 * If the compress argument is set the tar will be read or created as a 72 * gzip or bz2 compressed TAR file. 73 * 74 * @param string $p_tarname The name of the tar archive to create 75 * @param string $p_compress can be null, 'gz' or 'bz2'. This 76 * parameter indicates if gzip or bz2 compression 77 * is required. For compatibility reason the 78 * boolean value 'true' means 'gz'. 79 * @access public 80 */ 81 function Archive_Tar($p_tarname, $p_compress = null) 82 { 83 $this->PEAR(); 84 $this->_compress = false; 85 $this->_compress_type = 'none'; 86 if (($p_compress === null) || ($p_compress == '')) { 87 if (@file_exists($p_tarname)) { 88 if ($fp = @fopen($p_tarname, "rb")) { 89 // look for gzip magic cookie 90 $data = fread($fp, 2); 91 fclose($fp); 92 if ($data == "\37\213") { 93 $this->_compress = true; 94 $this->_compress_type = 'gz'; 95 // No sure it's enought for a magic code .... 96 } elseif ($data == "BZ") { 97 $this->_compress = true; 98 $this->_compress_type = 'bz2'; 99 } 100 } 101 } else { 102 // probably a remote file or some file accessible 103 // through a stream interface 104 if (substr($p_tarname, -2) == 'gz') { 105 $this->_compress = true; 106 $this->_compress_type = 'gz'; 107 } elseif ((substr($p_tarname, -3) == 'bz2') || 108 (substr($p_tarname, -2) == 'bz')) { 109 $this->_compress = true; 110 $this->_compress_type = 'bz2'; 111 } 112 } 113 } else { 114 if (($p_compress === true) || ($p_compress == 'gz')) { 115 $this->_compress = true; 116 $this->_compress_type = 'gz'; 117 } else if ($p_compress == 'bz2') { 118 $this->_compress = true; 119 $this->_compress_type = 'bz2'; 120 } else { 121 die("Unsupported compression type '$p_compress'\n". 122 "Supported types are 'gz' and 'bz2'.\n"); 123 return false; 124 } 125 } 126 $this->_tarname = $p_tarname; 127 if ($this->_compress) { // assert zlib or bz2 extension support 128 if ($this->_compress_type == 'gz') 129 $extname = 'zlib'; 130 else if ($this->_compress_type == 'bz2') 131 $extname = 'bz2'; 132 133 if (!extension_loaded($extname)) { 134 PEAR::loadExtension($extname); 135 } 136 if (!extension_loaded($extname)) { 137 die("The extension '$extname' couldn't be found.\n". 138 "Please make sure your version of PHP was built ". 139 "with '$extname' support.\n"); 140 return false; 141 } 142 } 143 } 144 // }}} 145 146 // {{{ destructor 147 function _Archive_Tar() 148 { 149 $this->_close(); 150 // ----- Look for a local copy to delete 151 if ($this->_temp_tarname != '') 152 @unlink($this->_temp_tarname); 153 $this->_PEAR(); 154 } 155 // }}} 156 157 // {{{ create() 158 /** 159 * This method creates the archive file and add the files / directories 160 * that are listed in $p_filelist. 161 * If a file with the same name exist and is writable, it is replaced 162 * by the new tar. 163 * The method return false and a PEAR error text. 164 * The $p_filelist parameter can be an array of string, each string 165 * representing a filename or a directory name with their path if 166 * needed. It can also be a single string with names separated by a 167 * single blank. 168 * For each directory added in the archive, the files and 169 * sub-directories are also added. 170 * See also createModify() method for more details. 171 * 172 * @param array $p_filelist An array of filenames and directory names, or a 173 * single string with names separated by a single 174 * blank space. 175 * @return true on success, false on error. 176 * @see createModify() 177 * @access public 178 */ 179 function create($p_filelist) 180 { 181 return $this->createModify($p_filelist, '', ''); 182 } 183 // }}} 184 185 // {{{ add() 186 /** 187 * This method add the files / directories that are listed in $p_filelist in 188 * the archive. If the archive does not exist it is created. 189 * The method return false and a PEAR error text. 190 * The files and directories listed are only added at the end of the archive, 191 * even if a file with the same name is already archived. 192 * See also createModify() method for more details. 193 * 194 * @param array $p_filelist An array of filenames and directory names, or a 195 * single string with names separated by a single 196 * blank space. 197 * @return true on success, false on error. 198 * @see createModify() 199 * @access public 200 */ 201 function add($p_filelist) 202 { 203 return $this->addModify($p_filelist, '', ''); 204 } 205 // }}} 206 207 // {{{ extract() 208 function extract($p_path='') 209 { 210 return $this->extractModify($p_path, ''); 211 } 212 // }}} 213 214 // {{{ listContent() 215 function listContent() 216 { 217 $v_list_detail = array(); 218 219 if ($this->_openRead()) { 220 if (!$this->_extractList('', $v_list_detail, "list", '', '')) { 221 unset($v_list_detail); 222 $v_list_detail = 0; 223 } 224 $this->_close(); 225 } 226 227 return $v_list_detail; 228 } 229 // }}} 230 231 // {{{ createModify() 232 /** 233 * This method creates the archive file and add the files / directories 234 * that are listed in $p_filelist. 235 * If the file already exists and is writable, it is replaced by the 236 * new tar. It is a create and not an add. If the file exists and is 237 * read-only or is a directory it is not replaced. The method return 238 * false and a PEAR error text. 239 * The $p_filelist parameter can be an array of string, each string 240 * representing a filename or a directory name with their path if 241 * needed. It can also be a single string with names separated by a 242 * single blank. 243 * The path indicated in $p_remove_dir will be removed from the 244 * memorized path of each file / directory listed when this path 245 * exists. By default nothing is removed (empty path '') 246 * The path indicated in $p_add_dir will be added at the beginning of 247 * the memorized path of each file / directory listed. However it can 248 * be set to empty ''. The adding of a path is done after the removing 249 * of path. 250 * The path add/remove ability enables the user to prepare an archive 251 * for extraction in a different path than the origin files are. 252 * See also addModify() method for file adding properties. 253 * 254 * @param array $p_filelist An array of filenames and directory names, 255 * or a single string with names separated by 256 * a single blank space. 257 * @param string $p_add_dir A string which contains a path to be added 258 * to the memorized path of each element in 259 * the list. 260 * @param string $p_remove_dir A string which contains a path to be 261 * removed from the memorized path of each 262 * element in the list, when relevant. 263 * @return boolean true on success, false on error. 264 * @access public 265 * @see addModify() 266 */ 267 function createModify($p_filelist, $p_add_dir, $p_remove_dir='') 268 { 269 $v_result = true; 270 271 if (!$this->_openWrite()) 272 return false; 273 274 if ($p_filelist != '') { 275 if (is_array($p_filelist)) 276 $v_list = $p_filelist; 277 elseif (is_string($p_filelist)) 278 $v_list = explode($this->_separator, $p_filelist); 279 else { 280 $this->_cleanFile(); 281 $this->_error('Invalid file list'); 282 return false; 283 } 284 285 $v_result = $this->_addList($v_list, $p_add_dir, $p_remove_dir); 286 } 287 288 if ($v_result) { 289 $this->_writeFooter(); 290 $this->_close(); 291 } else 292 $this->_cleanFile(); 293 294 return $v_result; 295 } 296 // }}} 297 298 // {{{ addModify() 299 /** 300 * This method add the files / directories listed in $p_filelist at the 301 * end of the existing archive. If the archive does not yet exists it 302 * is created. 303 * The $p_filelist parameter can be an array of string, each string 304 * representing a filename or a directory name with their path if 305 * needed. It can also be a single string with names separated by a 306 * single blank. 307 * The path indicated in $p_remove_dir will be removed from the 308 * memorized path of each file / directory listed when this path 309 * exists. By default nothing is removed (empty path '') 310 * The path indicated in $p_add_dir will be added at the beginning of 311 * the memorized path of each file / directory listed. However it can 312 * be set to empty ''. The adding of a path is done after the removing 313 * of path. 314 * The path add/remove ability enables the user to prepare an archive 315 * for extraction in a different path than the origin files are. 316 * If a file/dir is already in the archive it will only be added at the 317 * end of the archive. There is no update of the existing archived 318 * file/dir. However while extracting the archive, the last file will 319 * replace the first one. This results in a none optimization of the 320 * archive size. 321 * If a file/dir does not exist the file/dir is ignored. However an 322 * error text is send to PEAR error. 323 * If a file/dir is not readable the file/dir is ignored. However an 324 * error text is send to PEAR error. 325 * 326 * @param array $p_filelist An array of filenames and directory 327 * names, or a single string with names 328 * separated by a single blank space. 329 * @param string $p_add_dir A string which contains a path to be 330 * added to the memorized path of each 331 * element in the list. 332 * @param string $p_remove_dir A string which contains a path to be 333 * removed from the memorized path of 334 * each element in the list, when 335 * relevant. 336 * @return true on success, false on error. 337 * @access public 338 */ 339 function addModify($p_filelist, $p_add_dir, $p_remove_dir='') 340 { 341 $v_result = true; 342 343 if (!$this->_isArchive()) 344 $v_result = $this->createModify($p_filelist, $p_add_dir, 345 $p_remove_dir); 346 else { 347 if (is_array($p_filelist)) 348 $v_list = $p_filelist; 349 elseif (is_string($p_filelist)) 350 $v_list = explode($this->_separator, $p_filelist); 351 else { 352 $this->_error('Invalid file list'); 353 return false; 354 } 355 356 $v_result = $this->_append($v_list, $p_add_dir, $p_remove_dir); 357 } 358 359 return $v_result; 360 } 361 // }}} 362 363 // {{{ addString() 364 /** 365 * This method add a single string as a file at the 366 * end of the existing archive. If the archive does not yet exists it 367 * is created. 368 * 369 * @param string $p_filename A string which contains the full 370 * filename path that will be associated 371 * with the string. 372 * @param string $p_string The content of the file added in 373 * the archive. 374 * @return true on success, false on error. 375 * @access public 376 */ 377 function addString($p_filename, $p_string) 378 { 379 $v_result = true; 380 381 if (!$this->_isArchive()) { 382 if (!$this->_openWrite()) { 383 return false; 384 } 385 $this->_close(); 386 } 387 388 if (!$this->_openAppend()) 389 return false; 390 391 // Need to check the get back to the temporary file ? .... 392 $v_result = $this->_addString($p_filename, $p_string); 393 394 $this->_writeFooter(); 395 396 $this->_close(); 397 398 return $v_result; 399 } 400 // }}} 401 402 // {{{ extractModify() 403 /** 404 * This method extract all the content of the archive in the directory 405 * indicated by $p_path. When relevant the memorized path of the 406 * files/dir can be modified by removing the $p_remove_path path at the 407 * beginning of the file/dir path. 408 * While extracting a file, if the directory path does not exists it is 409 * created. 410 * While extracting a file, if the file already exists it is replaced 411 * without looking for last modification date. 412 * While extracting a file, if the file already exists and is write 413 * protected, the extraction is aborted. 414 * While extracting a file, if a directory with the same name already 415 * exists, the extraction is aborted. 416 * While extracting a directory, if a file with the same name already 417 * exists, the extraction is aborted. 418 * While extracting a file/directory if the destination directory exist 419 * and is write protected, or does not exist but can not be created, 420 * the extraction is aborted. 421 * If after extraction an extracted file does not show the correct 422 * stored file size, the extraction is aborted. 423 * When the extraction is aborted, a PEAR error text is set and false 424 * is returned. However the result can be a partial extraction that may 425 * need to be manually cleaned. 426 * 427 * @param string $p_path The path of the directory where the 428 * files/dir need to by extracted. 429 * @param string $p_remove_path Part of the memorized path that can be 430 * removed if present at the beginning of 431 * the file/dir path. 432 * @return boolean true on success, false on error. 433 * @access public 434 * @see extractList() 435 */ 436 function extractModify($p_path, $p_remove_path) 437 { 438 $v_result = true; 439 $v_list_detail = array(); 440 441 if ($v_result = $this->_openRead()) { 442 $v_result = $this->_extractList($p_path, $v_list_detail, 443 "complete", 0, $p_remove_path); 444 $this->_close(); 445 } 446 447 return $v_result; 448 } 449 // }}} 450 451 // {{{ extractInString() 452 /** 453 * This method extract from the archive one file identified by $p_filename. 454 * The return value is a string with the file content, or NULL on error. 455 * @param string $p_filename The path of the file to extract in a string. 456 * @return a string with the file content or NULL. 457 * @access public 458 */ 459 function extractInString($p_filename) 460 { 461 if ($this->_openRead()) { 462 $v_result = $this->_extractInString($p_filename); 463 $this->_close(); 464 } else { 465 $v_result = NULL; 466 } 467 468 return $v_result; 469 } 470 // }}} 471 472 // {{{ extractList() 473 /** 474 * This method extract from the archive only the files indicated in the 475 * $p_filelist. These files are extracted in the current directory or 476 * in the directory indicated by the optional $p_path parameter. 477 * If indicated the $p_remove_path can be used in the same way as it is 478 * used in extractModify() method. 479 * @param array $p_filelist An array of filenames and directory names, 480 * or a single string with names separated 481 * by a single blank space. 482 * @param string $p_path The path of the directory where the 483 * files/dir need to by extracted. 484 * @param string $p_remove_path Part of the memorized path that can be 485 * removed if present at the beginning of 486 * the file/dir path. 487 * @return true on success, false on error. 488 * @access public 489 * @see extractModify() 490 */ 491 function extractList($p_filelist, $p_path='', $p_remove_path='') 492 { 493 $v_result = true; 494 $v_list_detail = array(); 495 496 if (is_array($p_filelist)) 497 $v_list = $p_filelist; 498 elseif (is_string($p_filelist)) 499 $v_list = explode($this->_separator, $p_filelist); 500 else { 501 $this->_error('Invalid string list'); 502 return false; 503 } 504 505 if ($v_result = $this->_openRead()) { 506 $v_result = $this->_extractList($p_path, $v_list_detail, "partial", 507 $v_list, $p_remove_path); 508 $this->_close(); 509 } 510 511 return $v_result; 512 } 513 // }}} 514 515 // {{{ setAttribute() 516 /** 517 * This method set specific attributes of the archive. It uses a variable 518 * list of parameters, in the format attribute code + attribute values : 519 * $arch->setAttribute(ARCHIVE_TAR_ATT_SEPARATOR, ','); 520 * @param mixed $argv variable list of attributes and values 521 * @return true on success, false on error. 522 * @access public 523 */ 524 function setAttribute() 525 { 526 $v_result = true; 527 528 // ----- Get the number of variable list of arguments 529 if (($v_size = func_num_args()) == 0) { 530 return true; 531 } 532 533 // ----- Get the arguments 534 $v_att_list = &func_get_args(); 535 536 // ----- Read the attributes 537 $i=0; 538 while ($i<$v_size) { 539 540 // ----- Look for next option 541 switch ($v_att_list[$i]) { 542 // ----- Look for options that request a string value 543 case ARCHIVE_TAR_ATT_SEPARATOR : 544 // ----- Check the number of parameters 545 if (($i+1) >= $v_size) { 546 $this->_error('Invalid number of parameters for ' 547 .'attribute ARCHIVE_TAR_ATT_SEPARATOR'); 548 return false; 549 } 550 551 // ----- Get the value 552 $this->_separator = $v_att_list[$i+1]; 553 $i++; 554 break; 555 556 default : 557 $this->_error('Unknow attribute code '.$v_att_list[$i].''); 558 return false; 559 } 560 561 // ----- Next attribute 562 $i++; 563 } 564 565 return $v_result; 566 } 567 // }}} 568 569 // {{{ _error() 570 function _error($p_message) 571 { 572 // ----- To be completed 573 $this->raiseError($p_message); 574 } 575 // }}} 576 577 // {{{ _warning() 578 function _warning($p_message) 579 { 580 // ----- To be completed 581 $this->raiseError($p_message); 582 } 583 // }}} 584 585 // {{{ _isArchive() 586 function _isArchive($p_filename=NULL) 587 { 588 if ($p_filename == NULL) { 589 $p_filename = $this->_tarname; 590 } 591 clearstatcache(); 592 return @is_file($p_filename); 593 } 594 // }}} 595 596 // {{{ _openWrite() 597 function _openWrite() 598 { 599 if ($this->_compress_type == 'gz') 600 $this->_file = @gzopen($this->_tarname, "wb9"); 601 else if ($this->_compress_type == 'bz2') 602 $this->_file = @bzopen($this->_tarname, "wb"); 603 else if ($this->_compress_type == 'none') 604 $this->_file = @fopen($this->_tarname, "wb"); 605 else 606 $this->_error('Unknown or missing compression type (' 607 .$this->_compress_type.')'); 608 609 if ($this->_file == 0) { 610 $this->_error('Unable to open in write mode \'' 611 .$this->_tarname.'\''); 612 return false; 613 } 614 615 return true; 616 } 617 // }}} 618 619 // {{{ _openRead() 620 function _openRead() 621 { 622 if (strtolower(substr($this->_tarname, 0, 7)) == 'http://') { 623 624 // ----- Look if a local copy need to be done 625 if ($this->_temp_tarname == '') { 626 $this->_temp_tarname = uniqid('tar').'.tmp'; 627 if (!$v_file_from = @fopen($this->_tarname, 'rb')) { 628 $this->_error('Unable to open in read mode \'' 629 .$this->_tarname.'\''); 630 $this->_temp_tarname = ''; 631 return false; 632 } 633 if (!$v_file_to = @fopen($this->_temp_tarname, 'wb')) { 634 $this->_error('Unable to open in write mode \'' 635 .$this->_temp_tarname.'\''); 636 $this->_temp_tarname = ''; 637 return false; 638 } 639 while ($v_data = @fread($v_file_from, 1024)) 640 @fwrite($v_file_to, $v_data); 641 @fclose($v_file_from); 642 @fclose($v_file_to); 643 } 644 645 // ----- File to open if the local copy 646 $v_filename = $this->_temp_tarname; 647 648 } else 649 // ----- File to open if the normal Tar file 650 $v_filename = $this->_tarname; 651 652 if ($this->_compress_type == 'gz') 653 $this->_file = @gzopen($v_filename, "rb"); 654 else if ($this->_compress_type == 'bz2') 655 $this->_file = @bzopen($v_filename, "rb"); 656 else if ($this->_compress_type == 'none') 657 $this->_file = @fopen($v_filename, "rb"); 658 else 659 $this->_error('Unknown or missing compression type (' 660 .$this->_compress_type.')'); 661 662 if ($this->_file == 0) { 663 $this->_error('Unable to open in read mode \''.$v_filename.'\''); 664 return false; 665 } 666 667 return true; 668 } 669 // }}} 670 671 // {{{ _openReadWrite() 672 function _openReadWrite() 673 { 674 if ($this->_compress_type == 'gz') 675 $this->_file = @gzopen($this->_tarname, "r+b"); 676 else if ($this->_compress_type == 'bz2') 677 $this->_file = @bzopen($this->_tarname, "r+b"); 678 else if ($this->_compress_type == 'none') 679 $this->_file = @fopen($this->_tarname, "r+b"); 680 else 681 $this->_error('Unknown or missing compression type (' 682 .$this->_compress_type.')'); 683 684 if ($this->_file == 0) { 685 $this->_error('Unable to open in read/write mode \'' 686 .$this->_tarname.'\''); 687 return false; 688 } 689 690 return true; 691 } 692 // }}} 693 694 // {{{ _close() 695 function _close() 696 { 697 //if (isset($this->_file)) { 698 if (is_resource($this->_file)) { 699 if ($this->_compress_type == 'gz') 700 @gzclose($this->_file); 701 else if ($this->_compress_type == 'bz2') 702 @bzclose($this->_file); 703 else if ($this->_compress_type == 'none') 704 @fclose($this->_file); 705 else 706 $this->_error('Unknown or missing compression type (' 707 .$this->_compress_type.')'); 708 709 $this->_file = 0; 710 } 711 712 // ----- Look if a local copy need to be erase 713 // Note that it might be interesting to keep the url for a time : ToDo 714 if ($this->_temp_tarname != '') { 715 @unlink($this->_temp_tarname); 716 $this->_temp_tarname = ''; 717 } 718 719 return true; 720 } 721 // }}} 722 723 // {{{ _cleanFile() 724 function _cleanFile() 725 { 726 $this->_close(); 727 728 // ----- Look for a local copy 729 if ($this->_temp_tarname != '') { 730 // ----- Remove the local copy but not the remote tarname 731 @unlink($this->_temp_tarname); 732 $this->_temp_tarname = ''; 733 } else { 734 // ----- Remove the local tarname file 735 @unlink($this->_tarname); 736 } 737 $this->_tarname = ''; 738 739 return true; 740 } 741 // }}} 742 743 // {{{ _writeBlock() 744 function _writeBlock($p_binary_data, $p_len=null) 745 { 746 if (is_resource($this->_file)) { 747 if ($p_len === null) { 748 if ($this->_compress_type == 'gz') 749 @gzputs($this->_file, $p_binary_data); 750 else if ($this->_compress_type == 'bz2') 751 @bzwrite($this->_file, $p_binary_data); 752 else if ($this->_compress_type == 'none') 753 @fputs($this->_file, $p_binary_data); 754 else 755 $this->_error('Unknown or missing compression type (' 756 .$this->_compress_type.')'); 757 } else { 758 if ($this->_compress_type == 'gz') 759 @gzputs($this->_file, $p_binary_data, $p_len); 760 else if ($this->_compress_type == 'bz2') 761 @bzwrite($this->_file, $p_binary_data, $p_len); 762 else if ($this->_compress_type == 'none') 763 @fputs($this->_file, $p_binary_data, $p_len); 764 else 765 $this->_error('Unknown or missing compression type (' 766 .$this->_compress_type.')'); 767 768 } 769 } 770 return true; 771 } 772 // }}} 773 774 // {{{ _readBlock() 775 function _readBlock() 776 { 777 $v_block = null; 778 if (is_resource($this->_file)) { 779 if ($this->_compress_type == 'gz') 780 $v_block = @gzread($this->_file, 512); 781 else if ($this->_compress_type == 'bz2') 782 $v_block = @bzread($this->_file, 512); 783 else if ($this->_compress_type == 'none') 784 $v_block = @fread($this->_file, 512); 785 else 786 $this->_error('Unknown or missing compression type (' 787 .$this->_compress_type.')'); 788 } 789 return $v_block; 790 } 791 // }}} 792 793 // {{{ _jumpBlock() 794 function _jumpBlock($p_len=null) 795 { 796 if (is_resource($this->_file)) { 797 if ($p_len === null) 798 $p_len = 1; 799 800 if ($this->_compress_type == 'gz') { 801 @gzseek($this->_file, gztell($this->_file)+($p_len*512)); 802 } 803 else if ($this->_compress_type == 'bz2') { 804 // ----- Replace missing bztell() and bzseek() 805 for ($i=0; $i<$p_len; $i++) 806 $this->_readBlock(); 807 } else if ($this->_compress_type == 'none') 808 @fseek($this->_file, ftell($this->_file)+($p_len*512)); 809 else 810 $this->_error('Unknown or missing compression type (' 811 .$this->_compress_type.')'); 812 813 } 814 return true; 815 } 816 // }}} 817 818 // {{{ _writeFooter() 819 function _writeFooter() 820 { 821 if (is_resource($this->_file)) { 822 // ----- Write the last 0 filled block for end of archive 823 $v_binary_data = pack('a1024', ''); 824 $this->_writeBlock($v_binary_data); 825 } 826 return true; 827 } 828 // }}} 829 830 // {{{ _addList() 831 function _addList($p_list, $p_add_dir, $p_remove_dir) 832 { 833 $v_result=true; 834 $v_header = array(); 835 836 // ----- Remove potential windows directory separator 837 $p_add_dir = $this->_translateWinPath($p_add_dir); 838 $p_remove_dir = $this->_translateWinPath($p_remove_dir, false); 839 840 if (!$this->_file) { 841 $this->_error('Invalid file descriptor'); 842 return false; 843 } 844 845 if (sizeof($p_list) == 0) 846 return true; 847 848 foreach ($p_list as $v_filename) { 849 if (!$v_result) { 850 break; 851 } 852 853 // ----- Skip the current tar name 854 if ($v_filename == $this->_tarname) 855 continue; 856 857 if ($v_filename == '') 858 continue; 859 860 if (!file_exists($v_filename)) { 861 $this->_warning("File '$v_filename' does not exist"); 862 continue; 863 } 864 865 // ----- Add the file or directory header 866 if (!$this->_addFile($v_filename, $v_header, $p_add_dir, $p_remove_dir)) 867 return false; 868 869 if (@is_dir($v_filename)) { 870 if (!($p_hdir = opendir($v_filename))) { 871 $this->_warning("Directory '$v_filename' can not be read"); 872 continue; 873 } 874 while (false !== ($p_hitem = readdir($p_hdir))) { 875 if (($p_hitem != '.') && ($p_hitem != '..')) { 876 if ($v_filename != ".") 877 $p_temp_list[0] = $v_filename.'/'.$p_hitem; 878 else 879 $p_temp_list[0] = $p_hitem; 880 881 $v_result = $this->_addList($p_temp_list, 882 $p_add_dir, 883 $p_remove_dir); 884 } 885 } 886 887 unset($p_temp_list); 888 unset($p_hdir); 889 unset($p_hitem); 890 } 891 } 892 893 return $v_result; 894 } 895 // }}} 896 897 // {{{ _addFile() 898 function _addFile($p_filename, &$p_header, $p_add_dir, $p_remove_dir) 899 { 900 if (!$this->_file) { 901 $this->_error('Invalid file descriptor'); 902 return false; 903 } 904 905 if ($p_filename == '') { 906 $this->_error('Invalid file name'); 907 return false; 908 } 909 910 // ----- Calculate the stored filename 911 $p_filename = $this->_translateWinPath($p_filename, false);; 912 $v_stored_filename = $p_filename; 913 if (strcmp($p_filename, $p_remove_dir) == 0) { 914 return true; 915 } 916 if ($p_remove_dir != '') { 917 if (substr($p_remove_dir, -1) != '/') 918 $p_remove_dir .= '/'; 919 920 if (substr($p_filename, 0, strlen($p_remove_dir)) == $p_remove_dir) 921 $v_stored_filename = substr($p_filename, strlen($p_remove_dir)); 922 } 923 $v_stored_filename = $this->_translateWinPath($v_stored_filename); 924 if ($p_add_dir != '') { 925 if (substr($p_add_dir, -1) == '/') 926 $v_stored_filename = $p_add_dir.$v_stored_filename; 927 else 928 $v_stored_filename = $p_add_dir.'/'.$v_stored_filename; 929 } 930 931 $v_stored_filename = $this->_pathReduction($v_stored_filename); 932 933 if ($this->_isArchive($p_filename)) { 934 if (($v_file = @fopen($p_filename, "rb")) == 0) { 935 $this->_warning("Unable to open file '".$p_filename 936 ."' in binary read mode"); 937 return true; 938 } 939 940 if (!$this->_writeHeader($p_filename, $v_stored_filename)) 941 return false; 942 943 while (($v_buffer = fread($v_file, 512)) != '') { 944 $v_binary_data = pack("a512", "$v_buffer"); 945 $this->_writeBlock($v_binary_data); 946 } 947 948 fclose($v_file); 949 950 } else { 951 // ----- Only header for dir 952 if (!$this->_writeHeader($p_filename, $v_stored_filename)) 953 return false; 954 } 955 956 return true; 957 } 958 // }}} 959 960 // {{{ _addString() 961 function _addString($p_filename, $p_string) 962 { 963 if (!$this->_file) { 964 $this->_error('Invalid file descriptor'); 965 return false; 966 } 967 968 if ($p_filename == '') { 969 $this->_error('Invalid file name'); 970 return false; 971 } 972 973 // ----- Calculate the stored filename 974 $p_filename = $this->_translateWinPath($p_filename, false);; 975 976 if (!$this->_writeHeaderBlock($p_filename, strlen($p_string), 977 time(), 384, "", 0, 0)) 978 return false; 979 980 $i=0; 981 while (($v_buffer = substr($p_string, (($i++)*512), 512)) != '') { 982 $v_binary_data = pack("a512", $v_buffer); 983 $this->_writeBlock($v_binary_data); 984 } 985 986 return true; 987 } 988 // }}} 989 990 // {{{ _writeHeader() 991 function _writeHeader($p_filename, $p_stored_filename) 992 { 993 if ($p_stored_filename == '') 994 $p_stored_filename = $p_filename; 995 $v_reduce_filename = $this->_pathReduction($p_stored_filename); 996 997 if (strlen($v_reduce_filename) > 99) { 998 if (!$this->_writeLongHeader($v_reduce_filename)) 999 return false; 1000 } 1001 1002 $v_info = stat($p_filename); 1003 $v_uid = sprintf("%6s ", DecOct($v_info[4])); 1004 $v_gid = sprintf("%6s ", DecOct($v_info[5])); 1005 $v_perms = sprintf("%6s ", DecOct(fileperms($p_filename))); 1006 1007 $v_mtime = sprintf("%11s", DecOct(filemtime($p_filename))); 1008 1009 if (@is_dir($p_filename)) { 1010 $v_typeflag = "5"; 1011 $v_size = sprintf("%11s ", DecOct(0)); 1012 } else { 1013 $v_typeflag = ''; 1014 clearstatcache(); 1015 $v_size = sprintf("%11s ", DecOct(filesize($p_filename))); 1016 } 1017 1018 $v_linkname = ''; 1019 1020 $v_magic = ''; 1021 1022 $v_version = ''; 1023 1024 $v_uname = ''; 1025 1026 $v_gname = ''; 1027 1028 $v_devmajor = ''; 1029 1030 $v_devminor = ''; 1031 1032 $v_prefix = ''; 1033 1034 $v_binary_data_first = pack("a100a8a8a8a12A12", 1035 $v_reduce_filename, $v_perms, $v_uid, 1036 $v_gid, $v_size, $v_mtime); 1037 $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12", 1038 $v_typeflag, $v_linkname, $v_magic, 1039 $v_version, $v_uname, $v_gname, 1040 $v_devmajor, $v_devminor, $v_prefix, ''); 1041 1042 // ----- Calculate the checksum 1043 $v_checksum = 0; 1044 // ..... First part of the header 1045 for ($i=0; $i<148; $i++) 1046 $v_checksum += ord(substr($v_binary_data_first,$i,1)); 1047 // ..... Ignore the checksum value and replace it by ' ' (space) 1048 for ($i=148; $i<156; $i++) 1049 $v_checksum += ord(' '); 1050 // ..... Last part of the header 1051 for ($i=156, $j=0; $i<512; $i++, $j++) 1052 $v_checksum += ord(substr($v_binary_data_last,$j,1)); 1053 1054 // ----- Write the first 148 bytes of the header in the archive 1055 $this->_writeBlock($v_binary_data_first, 148); 1056 1057 // ----- Write the calculated checksum 1058 $v_checksum = sprintf("%6s ", DecOct($v_checksum)); 1059 $v_binary_data = pack("a8", $v_checksum); 1060 $this->_writeBlock($v_binary_data, 8); 1061 1062 // ----- Write the last 356 bytes of the header in the archive 1063 $this->_writeBlock($v_binary_data_last, 356); 1064 1065 return true; 1066 } 1067 // }}} 1068 1069 // {{{ _writeHeaderBlock() 1070 function _writeHeaderBlock($p_filename, $p_size, $p_mtime=0, $p_perms=0, 1071 $p_type='', $p_uid=0, $p_gid=0) 1072 { 1073 $p_filename = $this->_pathReduction($p_filename); 1074 1075 if (strlen($p_filename) > 99) { 1076 if (!$this->_writeLongHeader($p_filename)) 1077 return false; 1078 } 1079 1080 if ($p_type == "5") { 1081 $v_size = sprintf("%11s ", DecOct(0)); 1082 } else { 1083 $v_size = sprintf("%11s ", DecOct($p_size)); 1084 } 1085 1086 $v_uid = sprintf("%6s ", DecOct($p_uid)); 1087 $v_gid = sprintf("%6s ", DecOct($p_gid)); 1088 $v_perms = sprintf("%6s ", DecOct($p_perms)); 1089 1090 $v_mtime = sprintf("%11s", DecOct($p_mtime)); 1091 1092 $v_linkname = ''; 1093 1094 $v_magic = ''; 1095 1096 $v_version = ''; 1097 1098 $v_uname = ''; 1099 1100 $v_gname = ''; 1101 1102 $v_devmajor = ''; 1103 1104 $v_devminor = ''; 1105 1106 $v_prefix = ''; 1107 1108 $v_binary_data_first = pack("a100a8a8a8a12A12", 1109 $p_filename, $v_perms, $v_uid, $v_gid, 1110 $v_size, $v_mtime); 1111 $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12", 1112 $p_type, $v_linkname, $v_magic, 1113 $v_version, $v_uname, $v_gname, 1114 $v_devmajor, $v_devminor, $v_prefix, ''); 1115 1116 // ----- Calculate the checksum 1117 $v_checksum = 0; 1118 // ..... First part of the header 1119 for ($i=0; $i<148; $i++) 1120 $v_checksum += ord(substr($v_binary_data_first,$i,1)); 1121 // ..... Ignore the checksum value and replace it by ' ' (space) 1122 for ($i=148; $i<156; $i++) 1123 $v_checksum += ord(' '); 1124 // ..... Last part of the header 1125 for ($i=156, $j=0; $i<512; $i++, $j++) 1126 $v_checksum += ord(substr($v_binary_data_last,$j,1)); 1127 1128 // ----- Write the first 148 bytes of the header in the archive 1129 $this->_writeBlock($v_binary_data_first, 148); 1130 1131 // ----- Write the calculated checksum 1132 $v_checksum = sprintf("%6s ", DecOct($v_checksum)); 1133 $v_binary_data = pack("a8", $v_checksum); 1134 $this->_writeBlock($v_binary_data, 8); 1135 1136 // ----- Write the last 356 bytes of the header in the archive 1137 $this->_writeBlock($v_binary_data_last, 356); 1138 1139 return true; 1140 } 1141 // }}} 1142 1143 // {{{ _writeLongHeader() 1144 function _writeLongHeader($p_filename) 1145 { 1146 $v_size = sprintf("%11s ", DecOct(strlen($p_filename))); 1147 1148 $v_typeflag = 'L'; 1149 1150 $v_linkname = ''; 1151 1152 $v_magic = ''; 1153 1154 $v_version = ''; 1155 1156 $v_uname = ''; 1157 1158 $v_gname = ''; 1159 1160 $v_devmajor = ''; 1161 1162 $v_devminor = ''; 1163 1164 $v_prefix = ''; 1165 1166 $v_binary_data_first = pack("a100a8a8a8a12A12", 1167 '././@LongLink', 0, 0, 0, $v_size, 0); 1168 $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12", 1169 $v_typeflag, $v_linkname, $v_magic, 1170 $v_version, $v_uname, $v_gname, 1171 $v_devmajor, $v_devminor, $v_prefix, ''); 1172 1173 // ----- Calculate the checksum 1174 $v_checksum = 0; 1175 // ..... First part of the header 1176 for ($i=0; $i<148; $i++) 1177 $v_checksum += ord(substr($v_binary_data_first,$i,1)); 1178 // ..... Ignore the checksum value and replace it by ' ' (space) 1179 for ($i=148; $i<156; $i++) 1180 $v_checksum += ord(' '); 1181 // ..... Last part of the header 1182 for ($i=156, $j=0; $i<512; $i++, $j++) 1183 $v_checksum += ord(substr($v_binary_data_last,$j,1)); 1184 1185 // ----- Write the first 148 bytes of the header in the archive 1186 $this->_writeBlock($v_binary_data_first, 148); 1187 1188 // ----- Write the calculated checksum 1189 $v_checksum = sprintf("%6s ", DecOct($v_checksum)); 1190 $v_binary_data = pack("a8", $v_checksum); 1191 $this->_writeBlock($v_binary_data, 8); 1192 1193 // ----- Write the last 356 bytes of the header in the archive 1194 $this->_writeBlock($v_binary_data_last, 356); 1195 1196 // ----- Write the filename as content of the block 1197 $i=0; 1198 while (($v_buffer = substr($p_filename, (($i++)*512), 512)) != '') { 1199 $v_binary_data = pack("a512", "$v_buffer"); 1200 $this->_writeBlock($v_binary_data); 1201 } 1202 1203 return true; 1204 } 1205 // }}} 1206 1207 // {{{ _readHeader() 1208 function _readHeader($v_binary_data, &$v_header) 1209 { 1210 if (strlen($v_binary_data)==0) { 1211 $v_header['filename'] = ''; 1212 return true; 1213 } 1214 1215 if (strlen($v_binary_data) != 512) { 1216 $v_header['filename'] = ''; 1217 $this->_error('Invalid block size : '.strlen($v_binary_data)); 1218 return false; 1219 } 1220 1221 if (!is_array($v_header)) { 1222 $v_header = array(); 1223 } 1224 // ----- Calculate the checksum 1225 $v_checksum = 0; 1226 // ..... First part of the header 1227 for ($i=0; $i<148; $i++) 1228 $v_checksum+=ord(substr($v_binary_data,$i,1)); 1229 // ..... Ignore the checksum value and replace it by ' ' (space) 1230 for ($i=148; $i<156; $i++) 1231 $v_checksum += ord(' '); 1232 // ..... Last part of the header 1233 for ($i=156; $i<512; $i++) 1234 $v_checksum+=ord(substr($v_binary_data,$i,1)); 1235 1236 $v_data = unpack("a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/" 1237 ."a8checksum/a1typeflag/a100link/a6magic/a2version/" 1238 ."a32uname/a32gname/a8devmajor/a8devminor", 1239 $v_binary_data); 1240 1241 // ----- Extract the checksum 1242 $v_header['checksum'] = OctDec(trim($v_data['checksum'])); 1243 if ($v_header['checksum'] != $v_checksum) { 1244 $v_header['filename'] = ''; 1245 1246 // ----- Look for last block (empty block) 1247 if (($v_checksum == 256) && ($v_header['checksum'] == 0)) 1248 return true; 1249 1250 $this->_error('Invalid checksum for file "'.$v_data['filename'] 1251 .'" : '.$v_checksum.' calculated, ' 1252 .$v_header['checksum'].' expected'); 1253 return false; 1254 } 1255 1256 // ----- Extract the properties 1257 $v_header['filename'] = trim($v_data['filename']); 1258 if ($this->_maliciousFilename($v_header['filename'])) { 1259 $this->_error('Malicious .tar detected, file "' . $v_header['filename'] . 1260 '" will not install in desired directory tree'); 1261 return false; 1262 } 1263 $v_header['mode'] = OctDec(trim($v_data['mode'])); 1264 $v_header['uid'] = OctDec(trim($v_data['uid'])); 1265 $v_header['gid'] = OctDec(trim($v_data['gid'])); 1266 $v_header['size'] = OctDec(trim($v_data['size'])); 1267 $v_header['mtime'] = OctDec(trim($v_data['mtime'])); 1268 if (($v_header['typeflag'] = $v_data['typeflag']) == "5") { 1269 $v_header['size'] = 0; 1270 } 1271 $v_header['link'] = trim($v_data['link']); 1272 /* ----- All these fields are removed form the header because 1273 they do not carry interesting info 1274 $v_header[magic] = trim($v_data[magic]); 1275 $v_header[version] = trim($v_data[version]); 1276 $v_header[uname] = trim($v_data[uname]); 1277 $v_header[gname] = trim($v_data[gname]); 1278 $v_header[devmajor] = trim($v_data[devmajor]); 1279 $v_header[devminor] = trim($v_data[devminor]); 1280 */ 1281 1282 return true; 1283 } 1284 // }}} 1285 1286 // {{{ _maliciousFilename() 1287 /** 1288 * Detect and report a malicious file name 1289 * 1290 * @param string $file 1291 * @return bool 1292 * @access private 1293 */ 1294 function _maliciousFilename($file) 1295 { 1296 if (strpos($file, '/../') !== false) { 1297 return true; 1298 } 1299 if (strpos($file, '../') === 0) { 1300 return true; 1301 } 1302 return false; 1303 } 1304 // }}} 1305 1306 // {{{ _readLongHeader() 1307 function _readLongHeader(&$v_header) 1308 { 1309 $v_filename = ''; 1310 $n = floor($v_header['size']/512); 1311 for ($i=0; $i<$n; $i++) { 1312 $v_content = $this->_readBlock(); 1313 $v_filename .= $v_content; 1314 } 1315 if (($v_header['size'] % 512) != 0) { 1316 $v_content = $this->_readBlock(); 1317 $v_filename .= $v_content; 1318 } 1319 1320 // ----- Read the next header 1321 $v_binary_data = $this->_readBlock(); 1322 1323 if (!$this->_readHeader($v_binary_data, $v_header)) 1324 return false; 1325 1326 $v_header['filename'] = $v_filename; 1327 if ($this->_maliciousFilename($v_filename)) { 1328 $this->_error('Malicious .tar detected, file "' . $v_filename . 1329 '" will not install in desired directory tree'); 1330 return false; 1331 } 1332 1333 return true; 1334 } 1335 // }}} 1336 1337 // {{{ _extractInString() 1338 /** 1339 * This method extract from the archive one file identified by $p_filename. 1340 * The return value is a string with the file content, or NULL on error. 1341 * @param string $p_filename The path of the file to extract in a string. 1342 * @return a string with the file content or NULL. 1343 * @access private 1344 */ 1345 function _extractInString($p_filename) 1346 { 1347 $v_result_str = ""; 1348 1349 While (strlen($v_binary_data = $this->_readBlock()) != 0) 1350 { 1351 if (!$this->_readHeader($v_binary_data, $v_header)) 1352 return NULL; 1353 1354 if ($v_header['filename'] == '') 1355 continue; 1356 1357 // ----- Look for long filename 1358 if ($v_header['typeflag'] == 'L') { 1359 if (!$this->_readLongHeader($v_header)) 1360 return NULL; 1361 } 1362 1363 if ($v_header['filename'] == $p_filename) { 1364 if ($v_header['typeflag'] == "5") { 1365 $this->_error('Unable to extract in string a directory ' 1366 .'entry {'.$v_header['filename'].'}'); 1367 return NULL; 1368 } else { 1369 $n = floor($v_header['size']/512); 1370 for ($i=0; $i<$n; $i++) { 1371 $v_result_str .= $this->_readBlock(); 1372 } 1373 if (($v_header['size'] % 512) != 0) { 1374 $v_content = $this->_readBlock(); 1375 $v_result_str .= substr($v_content, 0, 1376 ($v_header['size'] % 512)); 1377 } 1378 return $v_result_str; 1379 } 1380 } else { 1381 $this->_jumpBlock(ceil(($v_header['size']/512))); 1382 } 1383 } 1384 1385 return NULL; 1386 } 1387 // }}} 1388 1389 // {{{ _extractList() 1390 function _extractList($p_path, &$p_list_detail, $p_mode, 1391 $p_file_list, $p_remove_path) 1392 { 1393 $v_result=true; 1394 $v_nb = 0; 1395 $v_extract_all = true; 1396 $v_listing = false; 1397 1398 $p_path = $this->_translateWinPath($p_path, false); 1399 if ($p_path == '' || (substr($p_path, 0, 1) != '/' 1400 && substr($p_path, 0, 3) != "../" && !strpos($p_path, ':'))) { 1401 $p_path = "./".$p_path; 1402 } 1403 $p_remove_path = $this->_translateWinPath($p_remove_path); 1404 1405 // ----- Look for path to remove format (should end by /) 1406 if (($p_remove_path != '') && (substr($p_remove_path, -1) != '/')) 1407 $p_remove_path .= '/'; 1408 $p_remove_path_size = strlen($p_remove_path); 1409 1410 switch ($p_mode) { 1411 case "complete" : 1412 $v_extract_all = TRUE; 1413 $v_listing = FALSE; 1414 break; 1415 case "partial" : 1416 $v_extract_all = FALSE; 1417 $v_listing = FALSE; 1418 break; 1419 case "list" : 1420 $v_extract_all = FALSE; 1421 $v_listing = TRUE; 1422 break; 1423 default : 1424 $this->_error('Invalid extract mode ('.$p_mode.')'); 1425 return false; 1426 } 1427 1428 clearstatcache(); 1429 1430 while (strlen($v_binary_data = $this->_readBlock()) != 0) 1431 { 1432 $v_extract_file = FALSE; 1433 $v_extraction_stopped = 0; 1434 1435 if (!$this->_readHeader($v_binary_data, $v_header)) 1436 return false; 1437 1438 if ($v_header['filename'] == '') { 1439 continue; 1440 } 1441 1442 // ----- Look for long filename 1443 if ($v_header['typeflag'] == 'L') { 1444 if (!$this->_readLongHeader($v_header)) 1445 return false; 1446 } 1447 1448 if ((!$v_extract_all) && (is_array($p_file_list))) { 1449 // ----- By default no unzip if the file is not found 1450 $v_extract_file = false; 1451 1452 for ($i=0; $i<sizeof($p_file_list); $i++) { 1453 // ----- Look if it is a directory 1454 if (substr($p_file_list[$i], -1) == '/') { 1455 // ----- Look if the directory is in the filename path 1456 if ((strlen($v_header['filename']) > strlen($p_file_list[$i])) 1457 && (substr($v_header['filename'], 0, strlen($p_file_list[$i])) 1458 == $p_file_list[$i])) { 1459 $v_extract_file = TRUE; 1460 break; 1461 } 1462 } 1463 1464 // ----- It is a file, so compare the file names 1465 elseif ($p_file_list[$i] == $v_header['filename']) { 1466 $v_extract_file = TRUE; 1467 break; 1468 } 1469 } 1470 } else { 1471 $v_extract_file = TRUE; 1472 } 1473 1474 // ----- Look if this file need to be extracted 1475 if (($v_extract_file) && (!$v_listing)) 1476 { 1477 if (($p_remove_path != '') 1478 && (substr($v_header['filename'], 0, $p_remove_path_size) 1479 == $p_remove_path)) 1480 $v_header['filename'] = substr($v_header['filename'], 1481 $p_remove_path_size); 1482 if (($p_path != './') && ($p_path != '/')) { 1483 while (substr($p_path, -1) == '/') 1484 $p_path = substr($p_path, 0, strlen($p_path)-1); 1485 1486 if (substr($v_header['filename'], 0, 1) == '/') 1487 $v_header['filename'] = $p_path.$v_header['filename']; 1488 else 1489 $v_header['filename'] = $p_path.'/'.$v_header['filename']; 1490 } 1491 if (file_exists($v_header['filename'])) { 1492 if ( (@is_dir($v_header['filename'])) 1493 && ($v_header['typeflag'] == '')) { 1494 $this->_error('File '.$v_header['filename'] 1495 .' already exists as a directory'); 1496 return false; 1497 } 1498 if ( ($this->_isArchive($v_header['filename'])) 1499 && ($v_header['typeflag'] == "5")) { 1500 $this->_error('Directory '.$v_header['filename'] 1501 .' already exists as a file'); 1502 return false; 1503 } 1504 if (!is_writeable($v_header['filename'])) { 1505 $this->_error('File '.$v_header['filename'] 1506 .' already exists and is write protected'); 1507 return false; 1508 } 1509 if (filemtime($v_header['filename']) > $v_header['mtime']) { 1510 // To be completed : An error or silent no replace ? 1511 } 1512 } 1513 1514 // ----- Check the directory availability and create it if necessary 1515 elseif (($v_result 1516 = $this->_dirCheck(($v_header['typeflag'] == "5" 1517 ?$v_header['filename'] 1518 :dirname($v_header['filename'])))) != 1) { 1519 $this->_error('Unable to create path for '.$v_header['filename']); 1520 return false; 1521 } 1522 1523 if ($v_extract_file) { 1524 if ($v_header['typeflag'] == "5") { 1525 if (!@file_exists($v_header['filename'])) { 1526 if (!@mkdir($v_header['filename'], 0777)) { 1527 $this->_error('Unable to create directory {' 1528 .$v_header['filename'].'}'); 1529 return false; 1530 } 1531 } 1532 } elseif ($v_header['typeflag'] == "2") { 1533 if (!@symlink($v_header['link'], $v_header['filename'])) { 1534 $this->_error('Unable to extract symbolic link {' 1535 .$v_header['filename'].'}'); 1536 return false; 1537 } 1538 } else { 1539 if (($v_dest_file = @fopen($v_header['filename'], "wb")) == 0) { 1540 $this->_error('Error while opening {'.$v_header['filename'] 1541 .'} in write binary mode'); 1542 return false; 1543 } else { 1544 $n = floor($v_header['size']/512); 1545 for ($i=0; $i<$n; $i++) { 1546 $v_content = $this->_readBlock(); 1547 fwrite($v_dest_file, $v_content, 512); 1548 } 1549 if (($v_header['size'] % 512) != 0) { 1550 $v_content = $this->_readBlock(); 1551 fwrite($v_dest_file, $v_content, ($v_header['size'] % 512)); 1552 } 1553 1554 @fclose($v_dest_file); 1555 1556 // ----- Change the file mode, mtime 1557 @touch($v_header['filename'], $v_header['mtime']); 1558 if ($v_header['mode'] & 0111) { 1559 // make file executable, obey umask 1560 $mode = fileperms($v_header['filename']) | (~umask() & 0111); 1561 @chmod($v_header['filename'], $mode); 1562 } 1563 } 1564 1565 // ----- Check the file size 1566 clearstatcache(); 1567 if (filesize($v_header['filename']) != $v_header['size']) { 1568 $this->_error('Extracted file '.$v_header['filename'] 1569 .' does not have the correct file size \'' 1570 .filesize($v_header['filename']) 1571 .'\' ('.$v_header['size'] 1572 .' expected). Archive may be corrupted.'); 1573 return false; 1574 } 1575 } 1576 } else { 1577 $this->_jumpBlock(ceil(($v_header['size']/512))); 1578 } 1579 } else { 1580 $this->_jumpBlock(ceil(($v_header['size']/512))); 1581 } 1582 1583 /* TBC : Seems to be unused ... 1584 if ($this->_compress) 1585 $v_end_of_file = @gzeof($this->_file); 1586 else 1587 $v_end_of_file = @feof($this->_file); 1588 */ 1589 1590 if ($v_listing || $v_extract_file || $v_extraction_stopped) { 1591 // ----- Log extracted files 1592 if (($v_file_dir = dirname($v_header['filename'])) 1593 == $v_header['filename']) 1594 $v_file_dir = ''; 1595 if ((substr($v_header['filename'], 0, 1) == '/') && ($v_file_dir == '')) 1596 $v_file_dir = '/'; 1597 1598 $p_list_detail[$v_nb++] = $v_header; 1599 if (is_array($p_file_list) && (count($p_list_detail) == count($p_file_list))) { 1600 return true; 1601 } 1602 } 1603 } 1604 1605 return true; 1606 } 1607 // }}} 1608 1609 // {{{ _openAppend() 1610 function _openAppend() 1611 { 1612 if (filesize($this->_tarname) == 0) 1613 return $this->_openWrite(); 1614 1615 if ($this->_compress) { 1616 $this->_close(); 1617 1618 if (!@rename($this->_tarname, $this->_tarname.".tmp")) { 1619 $this->_error('Error while renaming \''.$this->_tarname 1620 .'\' to temporary file \''.$this->_tarname 1621 .'.tmp\''); 1622 return false; 1623 } 1624 1625 if ($this->_compress_type == 'gz') 1626 $v_temp_tar = @gzopen($this->_tarname.".tmp", "rb"); 1627 elseif ($this->_compress_type == 'bz2') 1628 $v_temp_tar = @bzopen($this->_tarname.".tmp", "rb"); 1629 1630 if ($v_temp_tar == 0) { 1631 $this->_error('Unable to open file \''.$this->_tarname 1632 .'.tmp\' in binary read mode'); 1633 @rename($this->_tarname.".tmp", $this->_tarname); 1634 return false; 1635 } 1636 1637 if (!$this->_openWrite()) { 1638 @rename($this->_tarname.".tmp", $this->_tarname); 1639 return false; 1640 } 1641 1642 if ($this->_compress_type == 'gz') { 1643 while (!@gzeof($v_temp_tar)) { 1644 $v_buffer = @gzread($v_temp_tar, 512); 1645 if ($v_buffer == ARCHIVE_TAR_END_BLOCK) { 1646 // do not copy end blocks, we will re-make them 1647 // after appending 1648 continue; 1649 } 1650 $v_binary_data = pack("a512", $v_buffer); 1651 $this->_writeBlock($v_binary_data); 1652 } 1653 1654 @gzclose($v_temp_tar); 1655 } 1656 elseif ($this->_compress_type == 'bz2') { 1657 while (strlen($v_buffer = @bzread($v_temp_tar, 512)) > 0) { 1658 if ($v_buffer == ARCHIVE_TAR_END_BLOCK) { 1659 continue; 1660 } 1661 $v_binary_data = pack("a512", $v_buffer); 1662 $this->_writeBlock($v_binary_data); 1663 } 1664 1665 @bzclose($v_temp_tar); 1666 } 1667 1668 if (!@unlink($this->_tarname.".tmp")) { 1669 $this->_error('Error while deleting temporary file \'' 1670 .$this->_tarname.'.tmp\''); 1671 } 1672 1673 } else { 1674 // ----- For not compressed tar, just add files before the last 1675 // one or two 512 bytes block 1676 if (!$this->_openReadWrite()) 1677 return false; 1678 1679 clearstatcache(); 1680 $v_size = filesize($this->_tarname); 1681 1682 // We might have zero, one or two end blocks. 1683 // The standard is two, but we should try to handle 1684 // other cases. 1685 fseek($this->_file, $v_size - 1024); 1686 if (fread($this->_file, 512) == ARCHIVE_TAR_END_BLOCK) { 1687 fseek($this->_file, $v_size - 1024); 1688 } 1689 elseif (fread($this->_file, 512) == ARCHIVE_TAR_END_BLOCK) { 1690 fseek($this->_file, $v_size - 512); 1691 } 1692 } 1693 1694 return true; 1695 } 1696 // }}} 1697 1698 // {{{ _append() 1699 function _append($p_filelist, $p_add_dir='', $p_remove_dir='') 1700 { 1701 if (!$this->_openAppend()) 1702 return false; 1703 1704 if ($this->_addList($p_filelist, $p_add_dir, $p_remove_dir)) 1705 $this->_writeFooter(); 1706 1707 $this->_close(); 1708 1709 return true; 1710 } 1711 // }}} 1712 1713 // {{{ _dirCheck() 1714 1715 /** 1716 * Check if a directory exists and create it (including parent 1717 * dirs) if not. 1718 * 1719 * @param string $p_dir directory to check 1720 * 1721 * @return bool TRUE if the directory exists or was created 1722 */ 1723 function _dirCheck($p_dir) 1724 { 1725 clearstatcache(); 1726 if ((@is_dir($p_dir)) || ($p_dir == '')) 1727 return true; 1728 1729 $p_parent_dir = dirname($p_dir); 1730 1731 if (($p_parent_dir != $p_dir) && 1732 ($p_parent_dir != '') && 1733 (!$this->_dirCheck($p_parent_dir))) 1734 return false; 1735 1736 if (!@mkdir($p_dir, 0777)) { 1737 $this->_error("Unable to create directory '$p_dir'"); 1738 return false; 1739 } 1740 1741 return true; 1742 } 1743 1744 // }}} 1745 1746 // {{{ _pathReduction() 1747 1748 /** 1749 * Compress path by changing for example "/dir/foo/../bar" to "/dir/bar", 1750 * rand emove double slashes. 1751 * 1752 * @param string $p_dir path to reduce 1753 * 1754 * @return string reduced path 1755 * 1756 * @access private 1757 * 1758 */ 1759 function _pathReduction($p_dir) 1760 { 1761 $v_result = ''; 1762 1763 // ----- Look for not empty path 1764 if ($p_dir != '') { 1765 // ----- Explode path by directory names 1766 $v_list = explode('/', $p_dir); 1767 1768 // ----- Study directories from last to first 1769 for ($i=sizeof($v_list)-1; $i>=0; $i--) { 1770 // ----- Look for current path 1771 if ($v_list[$i] == ".") { 1772 // ----- Ignore this directory 1773 // Should be the first $i=0, but no check is done 1774 } 1775 else if ($v_list[$i] == "..") { 1776 // ----- Ignore it and ignore the $i-1 1777 $i--; 1778 } 1779 else if ( ($v_list[$i] == '') 1780 && ($i!=(sizeof($v_list)-1)) 1781 && ($i!=0)) { 1782 // ----- Ignore only the double '//' in path, 1783 // but not the first and last / 1784 } else { 1785 $v_result = $v_list[$i].($i!=(sizeof($v_list)-1)?'/' 1786 .$v_result:''); 1787 } 1788 } 1789 } 1790 $v_result = strtr($v_result, '\\', '/'); 1791 return $v_result; 1792 } 1793 1794 // }}} 1795 1796 // {{{ _translateWinPath() 1797 function _translateWinPath($p_path, $p_remove_disk_letter=true) 1798 { 1799 if (defined('OS_WINDOWS') && OS_WINDOWS) { 1800 // ----- Look for potential disk letter 1801 if ( ($p_remove_disk_letter) 1802 && (($v_position = strpos($p_path, ':')) != false)) { 1803 $p_path = substr($p_path, $v_position+1); 1804 } 1805 // ----- Change potential windows directory separator 1806 if ((strpos($p_path, '\\') > 0) || (substr($p_path, 0,1) == '\\')) { 1807 $p_path = strtr($p_path, '\\', '/'); 1808 } 1809 } 1810 return $p_path; 1811 } 1812 // }}} 1813 1814 } 1815 ?>
titre
Description
Corps
titre
Description
Corps
titre
Description
Corps
titre
Corps
Généré le : Sun Feb 25 14:08:00 2007 | par Balluche grâce à PHPXref 0.7 |