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