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 * @category File_Formats 34 * @package Archive_Tar 35 * @author Vincent Blavet <vincent@phpconcept.net> 36 * @copyright 1997-2010 The Authors 37 * @license http://www.opensource.org/licenses/bsd-license.php New BSD License 38 * @version CVS: $Id$ 39 * @link http://pear.php.net/package/Archive_Tar 40 */ 41 42// If the PEAR class cannot be loaded via the autoloader, 43// then try to require_once it from the PHP include path. 44if (!class_exists('PEAR')) { 45 require_once 'PEAR.php'; 46} 47 48define('ARCHIVE_TAR_ATT_SEPARATOR', 90001); 49define('ARCHIVE_TAR_END_BLOCK', pack("a512", '')); 50 51if (!function_exists('gzopen') && function_exists('gzopen64')) { 52 function gzopen($filename, $mode, $use_include_path = 0) 53 { 54 return gzopen64($filename, $mode, $use_include_path); 55 } 56} 57 58if (!function_exists('gztell') && function_exists('gztell64')) { 59 function gztell($zp) 60 { 61 return gztell64($zp); 62 } 63} 64 65if (!function_exists('gzseek') && function_exists('gzseek64')) { 66 function gzseek($zp, $offset, $whence = SEEK_SET) 67 { 68 return gzseek64($zp, $offset, $whence); 69 } 70} 71 72/** 73 * Creates a (compressed) Tar archive 74 * 75 * @package Archive_Tar 76 * @author Vincent Blavet <vincent@phpconcept.net> 77 * @license http://www.opensource.org/licenses/bsd-license.php New BSD License 78 * @version $Revision$ 79 */ 80class Archive_Tar extends PEAR 81{ 82 /** 83 * @var string Name of the Tar 84 */ 85 public $_tarname = ''; 86 87 /** 88 * @var boolean if true, the Tar file will be gzipped 89 */ 90 public $_compress = false; 91 92 /** 93 * @var string Type of compression : 'none', 'gz', 'bz2' or 'lzma2' 94 */ 95 public $_compress_type = 'none'; 96 97 /** 98 * @var string Explode separator 99 */ 100 public $_separator = ' '; 101 102 /** 103 * @var file descriptor 104 */ 105 public $_file = 0; 106 107 /** 108 * @var string Local Tar name of a remote Tar (http:// or ftp://) 109 */ 110 public $_temp_tarname = ''; 111 112 /** 113 * @var string regular expression for ignoring files or directories 114 */ 115 public $_ignore_regexp = ''; 116 117 /** 118 * @var object PEAR_Error object 119 */ 120 public $error_object = null; 121 122 /** 123 * Format for data extraction 124 * 125 * @var string 126 */ 127 public $_fmt = ''; 128 129 /** 130 * @var int Length of the read buffer in bytes 131 */ 132 protected $buffer_length; 133 134 /** 135 * Archive_Tar Class constructor. This flavour of the constructor only 136 * declare a new Archive_Tar object, identifying it by the name of the 137 * tar file. 138 * If the compress argument is set the tar will be read or created as a 139 * gzip or bz2 compressed TAR file. 140 * 141 * @param string $p_tarname The name of the tar archive to create 142 * @param string $p_compress can be null, 'gz', 'bz2' or 'lzma2'. This 143 * parameter indicates if gzip, bz2 or lzma2 compression 144 * is required. For compatibility reason the 145 * boolean value 'true' means 'gz'. 146 * @param int $buffer_length Length of the read buffer in bytes 147 * 148 * @return bool 149 */ 150 public function __construct($p_tarname, $p_compress = null, $buffer_length = 512) 151 { 152 parent::__construct(); 153 154 $this->_compress = false; 155 $this->_compress_type = 'none'; 156 if (($p_compress === null) || ($p_compress == '')) { 157 if (@file_exists($p_tarname)) { 158 if ($fp = @fopen($p_tarname, "rb")) { 159 // look for gzip magic cookie 160 $data = fread($fp, 2); 161 fclose($fp); 162 if ($data == "\37\213") { 163 $this->_compress = true; 164 $this->_compress_type = 'gz'; 165 // No sure it's enought for a magic code .... 166 } elseif ($data == "BZ") { 167 $this->_compress = true; 168 $this->_compress_type = 'bz2'; 169 } elseif (file_get_contents($p_tarname, false, null, 1, 4) == '7zXZ') { 170 $this->_compress = true; 171 $this->_compress_type = 'lzma2'; 172 } 173 } 174 } else { 175 // probably a remote file or some file accessible 176 // through a stream interface 177 if (substr($p_tarname, -2) == 'gz') { 178 $this->_compress = true; 179 $this->_compress_type = 'gz'; 180 } elseif ((substr($p_tarname, -3) == 'bz2') || 181 (substr($p_tarname, -2) == 'bz') 182 ) { 183 $this->_compress = true; 184 $this->_compress_type = 'bz2'; 185 } else { 186 if (substr($p_tarname, -2) == 'xz') { 187 $this->_compress = true; 188 $this->_compress_type = 'lzma2'; 189 } 190 } 191 } 192 } else { 193 if (($p_compress === true) || ($p_compress == 'gz')) { 194 $this->_compress = true; 195 $this->_compress_type = 'gz'; 196 } else { 197 if ($p_compress == 'bz2') { 198 $this->_compress = true; 199 $this->_compress_type = 'bz2'; 200 } else { 201 if ($p_compress == 'lzma2') { 202 $this->_compress = true; 203 $this->_compress_type = 'lzma2'; 204 } else { 205 $this->_error( 206 "Unsupported compression type '$p_compress'\n" . 207 "Supported types are 'gz', 'bz2' and 'lzma2'.\n" 208 ); 209 return false; 210 } 211 } 212 } 213 } 214 $this->_tarname = $p_tarname; 215 if ($this->_compress) { // assert zlib or bz2 or xz extension support 216 if ($this->_compress_type == 'gz') { 217 $extname = 'zlib'; 218 } else { 219 if ($this->_compress_type == 'bz2') { 220 $extname = 'bz2'; 221 } else { 222 if ($this->_compress_type == 'lzma2') { 223 $extname = 'xz'; 224 } 225 } 226 } 227 228 if (!extension_loaded($extname)) { 229 PEAR::loadExtension($extname); 230 } 231 if (!extension_loaded($extname)) { 232 $this->_error( 233 "The extension '$extname' couldn't be found.\n" . 234 "Please make sure your version of PHP was built " . 235 "with '$extname' support.\n" 236 ); 237 return false; 238 } 239 } 240 241 242 if (version_compare(PHP_VERSION, "5.5.0-dev") < 0) { 243 $this->_fmt = "a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/" . 244 "a8checksum/a1typeflag/a100link/a6magic/a2version/" . 245 "a32uname/a32gname/a8devmajor/a8devminor/a131prefix"; 246 } else { 247 $this->_fmt = "Z100filename/Z8mode/Z8uid/Z8gid/Z12size/Z12mtime/" . 248 "Z8checksum/Z1typeflag/Z100link/Z6magic/Z2version/" . 249 "Z32uname/Z32gname/Z8devmajor/Z8devminor/Z131prefix"; 250 } 251 252 253 $this->buffer_length = $buffer_length; 254 } 255 256 public function __destruct() 257 { 258 $this->_close(); 259 // ----- Look for a local copy to delete 260 if ($this->_temp_tarname != '') { 261 @unlink($this->_temp_tarname); 262 } 263 } 264 265 /** 266 * This method creates the archive file and add the files / directories 267 * that are listed in $p_filelist. 268 * If a file with the same name exist and is writable, it is replaced 269 * by the new tar. 270 * The method return false and a PEAR error text. 271 * The $p_filelist parameter can be an array of string, each string 272 * representing a filename or a directory name with their path if 273 * needed. It can also be a single string with names separated by a 274 * single blank. 275 * For each directory added in the archive, the files and 276 * sub-directories are also added. 277 * See also createModify() method for more details. 278 * 279 * @param array $p_filelist An array of filenames and directory names, or a 280 * single string with names separated by a single 281 * blank space. 282 * 283 * @return true on success, false on error. 284 * @see createModify() 285 */ 286 public function create($p_filelist) 287 { 288 return $this->createModify($p_filelist, '', ''); 289 } 290 291 /** 292 * This method add the files / directories that are listed in $p_filelist in 293 * the archive. If the archive does not exist it is created. 294 * The method return false and a PEAR error text. 295 * The files and directories listed are only added at the end of the archive, 296 * even if a file with the same name is already archived. 297 * See also createModify() method for more details. 298 * 299 * @param array $p_filelist An array of filenames and directory names, or a 300 * single string with names separated by a single 301 * blank space. 302 * 303 * @return true on success, false on error. 304 * @see createModify() 305 * @access public 306 */ 307 public function add($p_filelist) 308 { 309 return $this->addModify($p_filelist, '', ''); 310 } 311 312 /** 313 * @param string $p_path 314 * @param bool $p_preserve 315 * @param bool $p_symlinks 316 * @return bool 317 */ 318 public function extract($p_path = '', $p_preserve = false, $p_symlinks = true) 319 { 320 return $this->extractModify($p_path, '', $p_preserve, $p_symlinks); 321 } 322 323 /** 324 * @return array|int 325 */ 326 public function listContent() 327 { 328 $v_list_detail = array(); 329 330 if ($this->_openRead()) { 331 if (!$this->_extractList('', $v_list_detail, "list", '', '')) { 332 unset($v_list_detail); 333 $v_list_detail = 0; 334 } 335 $this->_close(); 336 } 337 338 return $v_list_detail; 339 } 340 341 /** 342 * This method creates the archive file and add the files / directories 343 * that are listed in $p_filelist. 344 * If the file already exists and is writable, it is replaced by the 345 * new tar. It is a create and not an add. If the file exists and is 346 * read-only or is a directory it is not replaced. The method return 347 * false and a PEAR error text. 348 * The $p_filelist parameter can be an array of string, each string 349 * representing a filename or a directory name with their path if 350 * needed. It can also be a single string with names separated by a 351 * single blank. 352 * The path indicated in $p_remove_dir will be removed from the 353 * memorized path of each file / directory listed when this path 354 * exists. By default nothing is removed (empty path '') 355 * The path indicated in $p_add_dir will be added at the beginning of 356 * the memorized path of each file / directory listed. However it can 357 * be set to empty ''. The adding of a path is done after the removing 358 * of path. 359 * The path add/remove ability enables the user to prepare an archive 360 * for extraction in a different path than the origin files are. 361 * See also addModify() method for file adding properties. 362 * 363 * @param array $p_filelist An array of filenames and directory names, 364 * or a single string with names separated by 365 * a single blank space. 366 * @param string $p_add_dir A string which contains a path to be added 367 * to the memorized path of each element in 368 * the list. 369 * @param string $p_remove_dir A string which contains a path to be 370 * removed from the memorized path of each 371 * element in the list, when relevant. 372 * 373 * @return boolean true on success, false on error. 374 * @see addModify() 375 */ 376 public function createModify($p_filelist, $p_add_dir, $p_remove_dir = '') 377 { 378 $v_result = true; 379 380 if (!$this->_openWrite()) { 381 return false; 382 } 383 384 if ($p_filelist != '') { 385 if (is_array($p_filelist)) { 386 $v_list = $p_filelist; 387 } elseif (is_string($p_filelist)) { 388 $v_list = explode($this->_separator, $p_filelist); 389 } else { 390 $this->_cleanFile(); 391 $this->_error('Invalid file list'); 392 return false; 393 } 394 395 $v_result = $this->_addList($v_list, $p_add_dir, $p_remove_dir); 396 } 397 398 if ($v_result) { 399 $this->_writeFooter(); 400 $this->_close(); 401 } else { 402 $this->_cleanFile(); 403 } 404 405 return $v_result; 406 } 407 408 /** 409 * This method add the files / directories listed in $p_filelist at the 410 * end of the existing archive. If the archive does not yet exists it 411 * is created. 412 * The $p_filelist parameter can be an array of string, each string 413 * representing a filename or a directory name with their path if 414 * needed. It can also be a single string with names separated by a 415 * single blank. 416 * The path indicated in $p_remove_dir will be removed from the 417 * memorized path of each file / directory listed when this path 418 * exists. By default nothing is removed (empty path '') 419 * The path indicated in $p_add_dir will be added at the beginning of 420 * the memorized path of each file / directory listed. However it can 421 * be set to empty ''. The adding of a path is done after the removing 422 * of path. 423 * The path add/remove ability enables the user to prepare an archive 424 * for extraction in a different path than the origin files are. 425 * If a file/dir is already in the archive it will only be added at the 426 * end of the archive. There is no update of the existing archived 427 * file/dir. However while extracting the archive, the last file will 428 * replace the first one. This results in a none optimization of the 429 * archive size. 430 * If a file/dir does not exist the file/dir is ignored. However an 431 * error text is send to PEAR error. 432 * If a file/dir is not readable the file/dir is ignored. However an 433 * error text is send to PEAR error. 434 * 435 * @param array $p_filelist An array of filenames and directory 436 * names, or a single string with names 437 * separated by a single blank space. 438 * @param string $p_add_dir A string which contains a path to be 439 * added to the memorized path of each 440 * element in the list. 441 * @param string $p_remove_dir A string which contains a path to be 442 * removed from the memorized path of 443 * each element in the list, when 444 * relevant. 445 * 446 * @return true on success, false on error. 447 */ 448 public function addModify($p_filelist, $p_add_dir, $p_remove_dir = '') 449 { 450 $v_result = true; 451 452 if (!$this->_isArchive()) { 453 $v_result = $this->createModify( 454 $p_filelist, 455 $p_add_dir, 456 $p_remove_dir 457 ); 458 } else { 459 if (is_array($p_filelist)) { 460 $v_list = $p_filelist; 461 } elseif (is_string($p_filelist)) { 462 $v_list = explode($this->_separator, $p_filelist); 463 } else { 464 $this->_error('Invalid file list'); 465 return false; 466 } 467 468 $v_result = $this->_append($v_list, $p_add_dir, $p_remove_dir); 469 } 470 471 return $v_result; 472 } 473 474 /** 475 * This method add a single string as a file at the 476 * end of the existing archive. If the archive does not yet exists it 477 * is created. 478 * 479 * @param string $p_filename A string which contains the full 480 * filename path that will be associated 481 * with the string. 482 * @param string $p_string The content of the file added in 483 * the archive. 484 * @param bool|int $p_datetime A custom date/time (unix timestamp) 485 * for the file (optional). 486 * @param array $p_params An array of optional params: 487 * stamp => the datetime (replaces 488 * datetime above if it exists) 489 * mode => the permissions on the 490 * file (600 by default) 491 * type => is this a link? See the 492 * tar specification for details. 493 * (default = regular file) 494 * uid => the user ID of the file 495 * (default = 0 = root) 496 * gid => the group ID of the file 497 * (default = 0 = root) 498 * 499 * @return true on success, false on error. 500 */ 501 public function addString($p_filename, $p_string, $p_datetime = false, $p_params = array()) 502 { 503 $p_stamp = @$p_params["stamp"] ? $p_params["stamp"] : ($p_datetime ? $p_datetime : time()); 504 $p_mode = @$p_params["mode"] ? $p_params["mode"] : 0600; 505 $p_type = @$p_params["type"] ? $p_params["type"] : ""; 506 $p_uid = @$p_params["uid"] ? $p_params["uid"] : ""; 507 $p_gid = @$p_params["gid"] ? $p_params["gid"] : ""; 508 $v_result = true; 509 510 if (!$this->_isArchive()) { 511 if (!$this->_openWrite()) { 512 return false; 513 } 514 $this->_close(); 515 } 516 517 if (!$this->_openAppend()) { 518 return false; 519 } 520 521 // Need to check the get back to the temporary file ? .... 522 $v_result = $this->_addString($p_filename, $p_string, $p_datetime, $p_params); 523 524 $this->_writeFooter(); 525 526 $this->_close(); 527 528 return $v_result; 529 } 530 531 /** 532 * This method extract all the content of the archive in the directory 533 * indicated by $p_path. When relevant the memorized path of the 534 * files/dir can be modified by removing the $p_remove_path path at the 535 * beginning of the file/dir path. 536 * While extracting a file, if the directory path does not exists it is 537 * created. 538 * While extracting a file, if the file already exists it is replaced 539 * without looking for last modification date. 540 * While extracting a file, if the file already exists and is write 541 * protected, the extraction is aborted. 542 * While extracting a file, if a directory with the same name already 543 * exists, the extraction is aborted. 544 * While extracting a directory, if a file with the same name already 545 * exists, the extraction is aborted. 546 * While extracting a file/directory if the destination directory exist 547 * and is write protected, or does not exist but can not be created, 548 * the extraction is aborted. 549 * If after extraction an extracted file does not show the correct 550 * stored file size, the extraction is aborted. 551 * When the extraction is aborted, a PEAR error text is set and false 552 * is returned. However the result can be a partial extraction that may 553 * need to be manually cleaned. 554 * 555 * @param string $p_path The path of the directory where the 556 * files/dir need to by extracted. 557 * @param string $p_remove_path Part of the memorized path that can be 558 * removed if present at the beginning of 559 * the file/dir path. 560 * @param boolean $p_preserve Preserve user/group ownership of files 561 * @param boolean $p_symlinks Allow symlinks. 562 * 563 * @return boolean true on success, false on error. 564 * @see extractList() 565 */ 566 public function extractModify($p_path, $p_remove_path, $p_preserve = false, $p_symlinks = true) 567 { 568 $v_result = true; 569 $v_list_detail = array(); 570 571 if ($v_result = $this->_openRead()) { 572 $v_result = $this->_extractList( 573 $p_path, 574 $v_list_detail, 575 "complete", 576 0, 577 $p_remove_path, 578 $p_preserve, 579 $p_symlinks 580 ); 581 $this->_close(); 582 } 583 584 return $v_result; 585 } 586 587 /** 588 * This method extract from the archive one file identified by $p_filename. 589 * The return value is a string with the file content, or NULL on error. 590 * 591 * @param string $p_filename The path of the file to extract in a string. 592 * 593 * @return a string with the file content or NULL. 594 */ 595 public function extractInString($p_filename) 596 { 597 if ($this->_openRead()) { 598 $v_result = $this->_extractInString($p_filename); 599 $this->_close(); 600 } else { 601 $v_result = null; 602 } 603 604 return $v_result; 605 } 606 607 /** 608 * This method extract from the archive only the files indicated in the 609 * $p_filelist. These files are extracted in the current directory or 610 * in the directory indicated by the optional $p_path parameter. 611 * If indicated the $p_remove_path can be used in the same way as it is 612 * used in extractModify() method. 613 * 614 * @param array $p_filelist An array of filenames and directory names, 615 * or a single string with names separated 616 * by a single blank space. 617 * @param string $p_path The path of the directory where the 618 * files/dir need to by extracted. 619 * @param string $p_remove_path Part of the memorized path that can be 620 * removed if present at the beginning of 621 * the file/dir path. 622 * @param boolean $p_preserve Preserve user/group ownership of files 623 * @param boolean $p_symlinks Allow symlinks. 624 * 625 * @return true on success, false on error. 626 * @see extractModify() 627 */ 628 public function extractList($p_filelist, $p_path = '', $p_remove_path = '', $p_preserve = false, $p_symlinks = true) 629 { 630 $v_result = true; 631 $v_list_detail = array(); 632 633 if (is_array($p_filelist)) { 634 $v_list = $p_filelist; 635 } elseif (is_string($p_filelist)) { 636 $v_list = explode($this->_separator, $p_filelist); 637 } else { 638 $this->_error('Invalid string list'); 639 return false; 640 } 641 642 if ($v_result = $this->_openRead()) { 643 $v_result = $this->_extractList( 644 $p_path, 645 $v_list_detail, 646 "partial", 647 $v_list, 648 $p_remove_path, 649 $p_preserve, 650 $p_symlinks 651 ); 652 $this->_close(); 653 } 654 655 return $v_result; 656 } 657 658 /** 659 * This method set specific attributes of the archive. It uses a variable 660 * list of parameters, in the format attribute code + attribute values : 661 * $arch->setAttribute(ARCHIVE_TAR_ATT_SEPARATOR, ','); 662 * 663 * @return true on success, false on error. 664 */ 665 public function setAttribute() 666 { 667 $v_result = true; 668 669 // ----- Get the number of variable list of arguments 670 if (($v_size = func_num_args()) == 0) { 671 return true; 672 } 673 674 // ----- Get the arguments 675 $v_att_list = func_get_args(); 676 677 // ----- Read the attributes 678 $i = 0; 679 while ($i < $v_size) { 680 681 // ----- Look for next option 682 switch ($v_att_list[$i]) { 683 // ----- Look for options that request a string value 684 case ARCHIVE_TAR_ATT_SEPARATOR : 685 // ----- Check the number of parameters 686 if (($i + 1) >= $v_size) { 687 $this->_error( 688 'Invalid number of parameters for ' 689 . 'attribute ARCHIVE_TAR_ATT_SEPARATOR' 690 ); 691 return false; 692 } 693 694 // ----- Get the value 695 $this->_separator = $v_att_list[$i + 1]; 696 $i++; 697 break; 698 699 default : 700 $this->_error('Unknown attribute code ' . $v_att_list[$i] . ''); 701 return false; 702 } 703 704 // ----- Next attribute 705 $i++; 706 } 707 708 return $v_result; 709 } 710 711 /** 712 * This method sets the regular expression for ignoring files and directories 713 * at import, for example: 714 * $arch->setIgnoreRegexp("#CVS|\.svn#"); 715 * 716 * @param string $regexp regular expression defining which files or directories to ignore 717 */ 718 public function setIgnoreRegexp($regexp) 719 { 720 $this->_ignore_regexp = $regexp; 721 } 722 723 /** 724 * This method sets the regular expression for ignoring all files and directories 725 * matching the filenames in the array list at import, for example: 726 * $arch->setIgnoreList(array('CVS', '.svn', 'bin/tool')); 727 * 728 * @param array $list a list of file or directory names to ignore 729 * 730 * @access public 731 */ 732 public function setIgnoreList($list) 733 { 734 $list = str_replace(array('#', '.', '^', '$'), array('\#', '\.', '\^', '\$'), $list); 735 $regexp = '#/' . join('$|/', $list) . '#'; 736 $this->setIgnoreRegexp($regexp); 737 } 738 739 /** 740 * @param string $p_message 741 */ 742 public function _error($p_message) 743 { 744 $this->error_object = $this->raiseError($p_message); 745 } 746 747 /** 748 * @param string $p_message 749 */ 750 public function _warning($p_message) 751 { 752 $this->error_object = $this->raiseError($p_message); 753 } 754 755 /** 756 * @param string $p_filename 757 * @return bool 758 */ 759 public function _isArchive($p_filename = null) 760 { 761 if ($p_filename == null) { 762 $p_filename = $this->_tarname; 763 } 764 clearstatcache(); 765 return @is_file($p_filename) && !@is_link($p_filename); 766 } 767 768 /** 769 * @return bool 770 */ 771 public function _openWrite() 772 { 773 if ($this->_compress_type == 'gz' && function_exists('gzopen')) { 774 $this->_file = @gzopen($this->_tarname, "wb9"); 775 } else { 776 if ($this->_compress_type == 'bz2' && function_exists('bzopen')) { 777 $this->_file = @bzopen($this->_tarname, "w"); 778 } else { 779 if ($this->_compress_type == 'lzma2' && function_exists('xzopen')) { 780 $this->_file = @xzopen($this->_tarname, 'w'); 781 } else { 782 if ($this->_compress_type == 'none') { 783 $this->_file = @fopen($this->_tarname, "wb"); 784 } else { 785 $this->_error( 786 'Unknown or missing compression type (' 787 . $this->_compress_type . ')' 788 ); 789 return false; 790 } 791 } 792 } 793 } 794 795 if ($this->_file == 0) { 796 $this->_error( 797 'Unable to open in write mode \'' 798 . $this->_tarname . '\'' 799 ); 800 return false; 801 } 802 803 return true; 804 } 805 806 /** 807 * @return bool 808 */ 809 public function _openRead() 810 { 811 if (strtolower(substr($this->_tarname, 0, 7)) == 'http://') { 812 813 // ----- Look if a local copy need to be done 814 if ($this->_temp_tarname == '') { 815 $this->_temp_tarname = uniqid('tar') . '.tmp'; 816 if (!$v_file_from = @fopen($this->_tarname, 'rb')) { 817 $this->_error( 818 'Unable to open in read mode \'' 819 . $this->_tarname . '\'' 820 ); 821 $this->_temp_tarname = ''; 822 return false; 823 } 824 if (!$v_file_to = @fopen($this->_temp_tarname, 'wb')) { 825 $this->_error( 826 'Unable to open in write mode \'' 827 . $this->_temp_tarname . '\'' 828 ); 829 $this->_temp_tarname = ''; 830 return false; 831 } 832 while ($v_data = @fread($v_file_from, 1024)) { 833 @fwrite($v_file_to, $v_data); 834 } 835 @fclose($v_file_from); 836 @fclose($v_file_to); 837 } 838 839 // ----- File to open if the local copy 840 $v_filename = $this->_temp_tarname; 841 } else { 842 // ----- File to open if the normal Tar file 843 844 $v_filename = $this->_tarname; 845 } 846 847 if ($this->_compress_type == 'gz' && function_exists('gzopen')) { 848 $this->_file = @gzopen($v_filename, "rb"); 849 } else { 850 if ($this->_compress_type == 'bz2' && function_exists('bzopen')) { 851 $this->_file = @bzopen($v_filename, "r"); 852 } else { 853 if ($this->_compress_type == 'lzma2' && function_exists('xzopen')) { 854 $this->_file = @xzopen($v_filename, "r"); 855 } else { 856 if ($this->_compress_type == 'none') { 857 $this->_file = @fopen($v_filename, "rb"); 858 } else { 859 $this->_error( 860 'Unknown or missing compression type (' 861 . $this->_compress_type . ')' 862 ); 863 return false; 864 } 865 } 866 } 867 } 868 869 if ($this->_file == 0) { 870 $this->_error('Unable to open in read mode \'' . $v_filename . '\''); 871 return false; 872 } 873 874 return true; 875 } 876 877 /** 878 * @return bool 879 */ 880 public function _openReadWrite() 881 { 882 if ($this->_compress_type == 'gz') { 883 $this->_file = @gzopen($this->_tarname, "r+b"); 884 } else { 885 if ($this->_compress_type == 'bz2') { 886 $this->_error( 887 'Unable to open bz2 in read/write mode \'' 888 . $this->_tarname . '\' (limitation of bz2 extension)' 889 ); 890 return false; 891 } else { 892 if ($this->_compress_type == 'lzma2') { 893 $this->_error( 894 'Unable to open lzma2 in read/write mode \'' 895 . $this->_tarname . '\' (limitation of lzma2 extension)' 896 ); 897 return false; 898 } else { 899 if ($this->_compress_type == 'none') { 900 $this->_file = @fopen($this->_tarname, "r+b"); 901 } else { 902 $this->_error( 903 'Unknown or missing compression type (' 904 . $this->_compress_type . ')' 905 ); 906 return false; 907 } 908 } 909 } 910 } 911 912 if ($this->_file == 0) { 913 $this->_error( 914 'Unable to open in read/write mode \'' 915 . $this->_tarname . '\'' 916 ); 917 return false; 918 } 919 920 return true; 921 } 922 923 /** 924 * @return bool 925 */ 926 public function _close() 927 { 928 //if (isset($this->_file)) { 929 if (is_resource($this->_file)) { 930 if ($this->_compress_type == 'gz') { 931 @gzclose($this->_file); 932 } else { 933 if ($this->_compress_type == 'bz2') { 934 @bzclose($this->_file); 935 } else { 936 if ($this->_compress_type == 'lzma2') { 937 @xzclose($this->_file); 938 } else { 939 if ($this->_compress_type == 'none') { 940 @fclose($this->_file); 941 } else { 942 $this->_error( 943 'Unknown or missing compression type (' 944 . $this->_compress_type . ')' 945 ); 946 } 947 } 948 } 949 } 950 951 $this->_file = 0; 952 } 953 954 // ----- Look if a local copy need to be erase 955 // Note that it might be interesting to keep the url for a time : ToDo 956 if ($this->_temp_tarname != '') { 957 @unlink($this->_temp_tarname); 958 $this->_temp_tarname = ''; 959 } 960 961 return true; 962 } 963 964 /** 965 * @return bool 966 */ 967 public function _cleanFile() 968 { 969 $this->_close(); 970 971 // ----- Look for a local copy 972 if ($this->_temp_tarname != '') { 973 // ----- Remove the local copy but not the remote tarname 974 @unlink($this->_temp_tarname); 975 $this->_temp_tarname = ''; 976 } else { 977 // ----- Remove the local tarname file 978 @unlink($this->_tarname); 979 } 980 $this->_tarname = ''; 981 982 return true; 983 } 984 985 /** 986 * @param mixed $p_binary_data 987 * @param integer $p_len 988 * @return bool 989 */ 990 public function _writeBlock($p_binary_data, $p_len = null) 991 { 992 if (is_resource($this->_file)) { 993 if ($p_len === null) { 994 if ($this->_compress_type == 'gz') { 995 @gzputs($this->_file, $p_binary_data); 996 } else { 997 if ($this->_compress_type == 'bz2') { 998 @bzwrite($this->_file, $p_binary_data); 999 } else { 1000 if ($this->_compress_type == 'lzma2') { 1001 @xzwrite($this->_file, $p_binary_data); 1002 } else { 1003 if ($this->_compress_type == 'none') { 1004 @fputs($this->_file, $p_binary_data); 1005 } else { 1006 $this->_error( 1007 'Unknown or missing compression type (' 1008 . $this->_compress_type . ')' 1009 ); 1010 } 1011 } 1012 } 1013 } 1014 } else { 1015 if ($this->_compress_type == 'gz') { 1016 @gzputs($this->_file, $p_binary_data, $p_len); 1017 } else { 1018 if ($this->_compress_type == 'bz2') { 1019 @bzwrite($this->_file, $p_binary_data, $p_len); 1020 } else { 1021 if ($this->_compress_type == 'lzma2') { 1022 @xzwrite($this->_file, $p_binary_data, $p_len); 1023 } else { 1024 if ($this->_compress_type == 'none') { 1025 @fputs($this->_file, $p_binary_data, $p_len); 1026 } else { 1027 $this->_error( 1028 'Unknown or missing compression type (' 1029 . $this->_compress_type . ')' 1030 ); 1031 } 1032 } 1033 } 1034 } 1035 } 1036 } 1037 return true; 1038 } 1039 1040 /** 1041 * @return null|string 1042 */ 1043 public function _readBlock() 1044 { 1045 $v_block = null; 1046 if (is_resource($this->_file)) { 1047 if ($this->_compress_type == 'gz') { 1048 $v_block = @gzread($this->_file, 512); 1049 } else { 1050 if ($this->_compress_type == 'bz2') { 1051 $v_block = @bzread($this->_file, 512); 1052 } else { 1053 if ($this->_compress_type == 'lzma2') { 1054 $v_block = @xzread($this->_file, 512); 1055 } else { 1056 if ($this->_compress_type == 'none') { 1057 $v_block = @fread($this->_file, 512); 1058 } else { 1059 $this->_error( 1060 'Unknown or missing compression type (' 1061 . $this->_compress_type . ')' 1062 ); 1063 } 1064 } 1065 } 1066 } 1067 } 1068 return $v_block; 1069 } 1070 1071 /** 1072 * @param null $p_len 1073 * @return bool 1074 */ 1075 public function _jumpBlock($p_len = null) 1076 { 1077 if (is_resource($this->_file)) { 1078 if ($p_len === null) { 1079 $p_len = 1; 1080 } 1081 1082 if ($this->_compress_type == 'gz') { 1083 @gzseek($this->_file, gztell($this->_file) + ($p_len * 512)); 1084 } else { 1085 if ($this->_compress_type == 'bz2') { 1086 // ----- Replace missing bztell() and bzseek() 1087 for ($i = 0; $i < $p_len; $i++) { 1088 $this->_readBlock(); 1089 } 1090 } else { 1091 if ($this->_compress_type == 'lzma2') { 1092 // ----- Replace missing xztell() and xzseek() 1093 for ($i = 0; $i < $p_len; $i++) { 1094 $this->_readBlock(); 1095 } 1096 } else { 1097 if ($this->_compress_type == 'none') { 1098 @fseek($this->_file, $p_len * 512, SEEK_CUR); 1099 } else { 1100 $this->_error( 1101 'Unknown or missing compression type (' 1102 . $this->_compress_type . ')' 1103 ); 1104 } 1105 } 1106 } 1107 } 1108 } 1109 return true; 1110 } 1111 1112 /** 1113 * @return bool 1114 */ 1115 public function _writeFooter() 1116 { 1117 if (is_resource($this->_file)) { 1118 // ----- Write the last 0 filled block for end of archive 1119 $v_binary_data = pack('a1024', ''); 1120 $this->_writeBlock($v_binary_data); 1121 } 1122 return true; 1123 } 1124 1125 /** 1126 * @param array $p_list 1127 * @param string $p_add_dir 1128 * @param string $p_remove_dir 1129 * @return bool 1130 */ 1131 public function _addList($p_list, $p_add_dir, $p_remove_dir) 1132 { 1133 $v_result = true; 1134 $v_header = array(); 1135 1136 // ----- Remove potential windows directory separator 1137 $p_add_dir = $this->_translateWinPath($p_add_dir); 1138 $p_remove_dir = $this->_translateWinPath($p_remove_dir, false); 1139 1140 if (!$this->_file) { 1141 $this->_error('Invalid file descriptor'); 1142 return false; 1143 } 1144 1145 if (sizeof($p_list) == 0) { 1146 return true; 1147 } 1148 1149 foreach ($p_list as $v_filename) { 1150 if (!$v_result) { 1151 break; 1152 } 1153 1154 // ----- Skip the current tar name 1155 if ($v_filename == $this->_tarname) { 1156 continue; 1157 } 1158 1159 if ($v_filename == '') { 1160 continue; 1161 } 1162 1163 // ----- ignore files and directories matching the ignore regular expression 1164 if ($this->_ignore_regexp && preg_match($this->_ignore_regexp, '/' . $v_filename)) { 1165 $this->_warning("File '$v_filename' ignored"); 1166 continue; 1167 } 1168 1169 if (!file_exists($v_filename) && !is_link($v_filename)) { 1170 $this->_warning("File '$v_filename' does not exist"); 1171 continue; 1172 } 1173 1174 // ----- Add the file or directory header 1175 if (!$this->_addFile($v_filename, $v_header, $p_add_dir, $p_remove_dir)) { 1176 return false; 1177 } 1178 1179 if (@is_dir($v_filename) && !@is_link($v_filename)) { 1180 if (!($p_hdir = opendir($v_filename))) { 1181 $this->_warning("Directory '$v_filename' can not be read"); 1182 continue; 1183 } 1184 while (false !== ($p_hitem = readdir($p_hdir))) { 1185 if (($p_hitem != '.') && ($p_hitem != '..')) { 1186 if ($v_filename != ".") { 1187 $p_temp_list[0] = $v_filename . '/' . $p_hitem; 1188 } else { 1189 $p_temp_list[0] = $p_hitem; 1190 } 1191 1192 $v_result = $this->_addList( 1193 $p_temp_list, 1194 $p_add_dir, 1195 $p_remove_dir 1196 ); 1197 } 1198 } 1199 1200 unset($p_temp_list); 1201 unset($p_hdir); 1202 unset($p_hitem); 1203 } 1204 } 1205 1206 return $v_result; 1207 } 1208 1209 /** 1210 * @param string $p_filename 1211 * @param mixed $p_header 1212 * @param string $p_add_dir 1213 * @param string $p_remove_dir 1214 * @param null $v_stored_filename 1215 * @return bool 1216 */ 1217 public function _addFile($p_filename, &$p_header, $p_add_dir, $p_remove_dir, $v_stored_filename = null) 1218 { 1219 if (!$this->_file) { 1220 $this->_error('Invalid file descriptor'); 1221 return false; 1222 } 1223 1224 if ($p_filename == '') { 1225 $this->_error('Invalid file name'); 1226 return false; 1227 } 1228 1229 if (is_null($v_stored_filename)) { 1230 // ----- Calculate the stored filename 1231 $p_filename = $this->_translateWinPath($p_filename, false); 1232 $v_stored_filename = $p_filename; 1233 1234 if (strcmp($p_filename, $p_remove_dir) == 0) { 1235 return true; 1236 } 1237 1238 if ($p_remove_dir != '') { 1239 if (substr($p_remove_dir, -1) != '/') { 1240 $p_remove_dir .= '/'; 1241 } 1242 1243 if (substr($p_filename, 0, strlen($p_remove_dir)) == $p_remove_dir) { 1244 $v_stored_filename = substr($p_filename, strlen($p_remove_dir)); 1245 } 1246 } 1247 1248 $v_stored_filename = $this->_translateWinPath($v_stored_filename); 1249 if ($p_add_dir != '') { 1250 if (substr($p_add_dir, -1) == '/') { 1251 $v_stored_filename = $p_add_dir . $v_stored_filename; 1252 } else { 1253 $v_stored_filename = $p_add_dir . '/' . $v_stored_filename; 1254 } 1255 } 1256 1257 $v_stored_filename = $this->_pathReduction($v_stored_filename); 1258 } 1259 1260 if ($this->_isArchive($p_filename)) { 1261 if (($v_file = @fopen($p_filename, "rb")) == 0) { 1262 $this->_warning( 1263 "Unable to open file '" . $p_filename 1264 . "' in binary read mode" 1265 ); 1266 return true; 1267 } 1268 1269 if (!$this->_writeHeader($p_filename, $v_stored_filename)) { 1270 return false; 1271 } 1272 1273 while (($v_buffer = fread($v_file, $this->buffer_length)) != '') { 1274 $buffer_length = strlen("$v_buffer"); 1275 if ($buffer_length != $this->buffer_length) { 1276 $pack_size = ((int)($buffer_length / 512) + ($buffer_length % 512 !== 0 ? 1 : 0)) * 512; 1277 $pack_format = sprintf('a%d', $pack_size); 1278 } else { 1279 $pack_format = sprintf('a%d', $this->buffer_length); 1280 } 1281 $v_binary_data = pack($pack_format, "$v_buffer"); 1282 $this->_writeBlock($v_binary_data); 1283 } 1284 1285 fclose($v_file); 1286 } else { 1287 // ----- Only header for dir 1288 if (!$this->_writeHeader($p_filename, $v_stored_filename)) { 1289 return false; 1290 } 1291 } 1292 1293 return true; 1294 } 1295 1296 /** 1297 * @param string $p_filename 1298 * @param string $p_string 1299 * @param bool $p_datetime 1300 * @param array $p_params 1301 * @return bool 1302 */ 1303 public function _addString($p_filename, $p_string, $p_datetime = false, $p_params = array()) 1304 { 1305 $p_stamp = @$p_params["stamp"] ? $p_params["stamp"] : ($p_datetime ? $p_datetime : time()); 1306 $p_mode = @$p_params["mode"] ? $p_params["mode"] : 0600; 1307 $p_type = @$p_params["type"] ? $p_params["type"] : ""; 1308 $p_uid = @$p_params["uid"] ? $p_params["uid"] : 0; 1309 $p_gid = @$p_params["gid"] ? $p_params["gid"] : 0; 1310 if (!$this->_file) { 1311 $this->_error('Invalid file descriptor'); 1312 return false; 1313 } 1314 1315 if ($p_filename == '') { 1316 $this->_error('Invalid file name'); 1317 return false; 1318 } 1319 1320 // ----- Calculate the stored filename 1321 $p_filename = $this->_translateWinPath($p_filename, false); 1322 1323 // ----- If datetime is not specified, set current time 1324 if ($p_datetime === false) { 1325 $p_datetime = time(); 1326 } 1327 1328 if (!$this->_writeHeaderBlock( 1329 $p_filename, 1330 strlen($p_string), 1331 $p_stamp, 1332 $p_mode, 1333 $p_type, 1334 $p_uid, 1335 $p_gid 1336 ) 1337 ) { 1338 return false; 1339 } 1340 1341 $i = 0; 1342 while (($v_buffer = substr($p_string, (($i++) * 512), 512)) != '') { 1343 $v_binary_data = pack("a512", $v_buffer); 1344 $this->_writeBlock($v_binary_data); 1345 } 1346 1347 return true; 1348 } 1349 1350 /** 1351 * @param string $p_filename 1352 * @param string $p_stored_filename 1353 * @return bool 1354 */ 1355 public function _writeHeader($p_filename, $p_stored_filename) 1356 { 1357 if ($p_stored_filename == '') { 1358 $p_stored_filename = $p_filename; 1359 } 1360 1361 $v_reduced_filename = $this->_pathReduction($p_stored_filename); 1362 1363 if (strlen($v_reduced_filename) > 99) { 1364 if (!$this->_writeLongHeader($v_reduced_filename, false)) { 1365 return false; 1366 } 1367 } 1368 1369 $v_linkname = ''; 1370 if (@is_link($p_filename)) { 1371 $v_linkname = readlink($p_filename); 1372 } 1373 1374 if (strlen($v_linkname) > 99) { 1375 if (!$this->_writeLongHeader($v_linkname, true)) { 1376 return false; 1377 } 1378 } 1379 1380 $v_info = lstat($p_filename); 1381 $v_uid = sprintf("%07s", DecOct($v_info[4])); 1382 $v_gid = sprintf("%07s", DecOct($v_info[5])); 1383 $v_perms = sprintf("%07s", DecOct($v_info['mode'] & 000777)); 1384 $v_mtime = sprintf("%011s", DecOct($v_info['mtime'])); 1385 1386 if (@is_link($p_filename)) { 1387 $v_typeflag = '2'; 1388 $v_size = sprintf("%011s", DecOct(0)); 1389 } elseif (@is_dir($p_filename)) { 1390 $v_typeflag = "5"; 1391 $v_size = sprintf("%011s", DecOct(0)); 1392 } else { 1393 $v_typeflag = '0'; 1394 clearstatcache(); 1395 $v_size = sprintf("%011s", DecOct($v_info['size'])); 1396 } 1397 1398 $v_magic = 'ustar '; 1399 $v_version = ' '; 1400 1401 if (function_exists('posix_getpwuid')) { 1402 $userinfo = posix_getpwuid($v_info[4]); 1403 $groupinfo = posix_getgrgid($v_info[5]); 1404 1405 $v_uname = $userinfo['name']; 1406 $v_gname = $groupinfo['name']; 1407 } else { 1408 $v_uname = ''; 1409 $v_gname = ''; 1410 } 1411 1412 $v_devmajor = ''; 1413 $v_devminor = ''; 1414 $v_prefix = ''; 1415 1416 $v_binary_data_first = pack( 1417 "a100a8a8a8a12a12", 1418 $v_reduced_filename, 1419 $v_perms, 1420 $v_uid, 1421 $v_gid, 1422 $v_size, 1423 $v_mtime 1424 ); 1425 $v_binary_data_last = pack( 1426 "a1a100a6a2a32a32a8a8a155a12", 1427 $v_typeflag, 1428 $v_linkname, 1429 $v_magic, 1430 $v_version, 1431 $v_uname, 1432 $v_gname, 1433 $v_devmajor, 1434 $v_devminor, 1435 $v_prefix, 1436 '' 1437 ); 1438 1439 // ----- Calculate the checksum 1440 $v_checksum = 0; 1441 // ..... First part of the header 1442 for ($i = 0; $i < 148; $i++) { 1443 $v_checksum += ord(substr($v_binary_data_first, $i, 1)); 1444 } 1445 // ..... Ignore the checksum value and replace it by ' ' (space) 1446 for ($i = 148; $i < 156; $i++) { 1447 $v_checksum += ord(' '); 1448 } 1449 // ..... Last part of the header 1450 for ($i = 156, $j = 0; $i < 512; $i++, $j++) { 1451 $v_checksum += ord(substr($v_binary_data_last, $j, 1)); 1452 } 1453 1454 // ----- Write the first 148 bytes of the header in the archive 1455 $this->_writeBlock($v_binary_data_first, 148); 1456 1457 // ----- Write the calculated checksum 1458 $v_checksum = sprintf("%06s\0 ", DecOct($v_checksum)); 1459 $v_binary_data = pack("a8", $v_checksum); 1460 $this->_writeBlock($v_binary_data, 8); 1461 1462 // ----- Write the last 356 bytes of the header in the archive 1463 $this->_writeBlock($v_binary_data_last, 356); 1464 1465 return true; 1466 } 1467 1468 /** 1469 * @param string $p_filename 1470 * @param int $p_size 1471 * @param int $p_mtime 1472 * @param int $p_perms 1473 * @param string $p_type 1474 * @param int $p_uid 1475 * @param int $p_gid 1476 * @return bool 1477 */ 1478 public function _writeHeaderBlock( 1479 $p_filename, 1480 $p_size, 1481 $p_mtime = 0, 1482 $p_perms = 0, 1483 $p_type = '', 1484 $p_uid = 0, 1485 $p_gid = 0 1486 ) 1487 { 1488 $p_filename = $this->_pathReduction($p_filename); 1489 1490 if (strlen($p_filename) > 99) { 1491 if (!$this->_writeLongHeader($p_filename, false)) { 1492 return false; 1493 } 1494 } 1495 1496 if ($p_type == "5") { 1497 $v_size = sprintf("%011s", DecOct(0)); 1498 } else { 1499 $v_size = sprintf("%011s", DecOct($p_size)); 1500 } 1501 1502 $v_uid = sprintf("%07s", DecOct($p_uid)); 1503 $v_gid = sprintf("%07s", DecOct($p_gid)); 1504 $v_perms = sprintf("%07s", DecOct($p_perms & 000777)); 1505 1506 $v_mtime = sprintf("%11s", DecOct($p_mtime)); 1507 1508 $v_linkname = ''; 1509 1510 $v_magic = 'ustar '; 1511 1512 $v_version = ' '; 1513 1514 if (function_exists('posix_getpwuid')) { 1515 $userinfo = posix_getpwuid($p_uid); 1516 $groupinfo = posix_getgrgid($p_gid); 1517 1518 if ($userinfo === false || $groupinfo === false) { 1519 $v_uname = ''; 1520 $v_gname = ''; 1521 } else { 1522 $v_uname = $userinfo['name']; 1523 $v_gname = $groupinfo['name']; 1524 } 1525 } else { 1526 $v_uname = ''; 1527 $v_gname = ''; 1528 } 1529 1530 $v_devmajor = ''; 1531 1532 $v_devminor = ''; 1533 1534 $v_prefix = ''; 1535 1536 $v_binary_data_first = pack( 1537 "a100a8a8a8a12A12", 1538 $p_filename, 1539 $v_perms, 1540 $v_uid, 1541 $v_gid, 1542 $v_size, 1543 $v_mtime 1544 ); 1545 $v_binary_data_last = pack( 1546 "a1a100a6a2a32a32a8a8a155a12", 1547 $p_type, 1548 $v_linkname, 1549 $v_magic, 1550 $v_version, 1551 $v_uname, 1552 $v_gname, 1553 $v_devmajor, 1554 $v_devminor, 1555 $v_prefix, 1556 '' 1557 ); 1558 1559 // ----- Calculate the checksum 1560 $v_checksum = 0; 1561 // ..... First part of the header 1562 for ($i = 0; $i < 148; $i++) { 1563 $v_checksum += ord(substr($v_binary_data_first, $i, 1)); 1564 } 1565 // ..... Ignore the checksum value and replace it by ' ' (space) 1566 for ($i = 148; $i < 156; $i++) { 1567 $v_checksum += ord(' '); 1568 } 1569 // ..... Last part of the header 1570 for ($i = 156, $j = 0; $i < 512; $i++, $j++) { 1571 $v_checksum += ord(substr($v_binary_data_last, $j, 1)); 1572 } 1573 1574 // ----- Write the first 148 bytes of the header in the archive 1575 $this->_writeBlock($v_binary_data_first, 148); 1576 1577 // ----- Write the calculated checksum 1578 $v_checksum = sprintf("%06s ", DecOct($v_checksum)); 1579 $v_binary_data = pack("a8", $v_checksum); 1580 $this->_writeBlock($v_binary_data, 8); 1581 1582 // ----- Write the last 356 bytes of the header in the archive 1583 $this->_writeBlock($v_binary_data_last, 356); 1584 1585 return true; 1586 } 1587 1588 /** 1589 * @param string $p_filename 1590 * @return bool 1591 */ 1592 public function _writeLongHeader($p_filename, $is_link = false) 1593 { 1594 $v_uid = sprintf("%07s", 0); 1595 $v_gid = sprintf("%07s", 0); 1596 $v_perms = sprintf("%07s", 0); 1597 $v_size = sprintf("%'011s", DecOct(strlen($p_filename))); 1598 $v_mtime = sprintf("%011s", 0); 1599 $v_typeflag = ($is_link ? 'K' : 'L'); 1600 $v_linkname = ''; 1601 $v_magic = 'ustar '; 1602 $v_version = ' '; 1603 $v_uname = ''; 1604 $v_gname = ''; 1605 $v_devmajor = ''; 1606 $v_devminor = ''; 1607 $v_prefix = ''; 1608 1609 $v_binary_data_first = pack( 1610 "a100a8a8a8a12a12", 1611 '././@LongLink', 1612 $v_perms, 1613 $v_uid, 1614 $v_gid, 1615 $v_size, 1616 $v_mtime 1617 ); 1618 $v_binary_data_last = pack( 1619 "a1a100a6a2a32a32a8a8a155a12", 1620 $v_typeflag, 1621 $v_linkname, 1622 $v_magic, 1623 $v_version, 1624 $v_uname, 1625 $v_gname, 1626 $v_devmajor, 1627 $v_devminor, 1628 $v_prefix, 1629 '' 1630 ); 1631 1632 // ----- Calculate the checksum 1633 $v_checksum = 0; 1634 // ..... First part of the header 1635 for ($i = 0; $i < 148; $i++) { 1636 $v_checksum += ord(substr($v_binary_data_first, $i, 1)); 1637 } 1638 // ..... Ignore the checksum value and replace it by ' ' (space) 1639 for ($i = 148; $i < 156; $i++) { 1640 $v_checksum += ord(' '); 1641 } 1642 // ..... Last part of the header 1643 for ($i = 156, $j = 0; $i < 512; $i++, $j++) { 1644 $v_checksum += ord(substr($v_binary_data_last, $j, 1)); 1645 } 1646 1647 // ----- Write the first 148 bytes of the header in the archive 1648 $this->_writeBlock($v_binary_data_first, 148); 1649 1650 // ----- Write the calculated checksum 1651 $v_checksum = sprintf("%06s\0 ", DecOct($v_checksum)); 1652 $v_binary_data = pack("a8", $v_checksum); 1653 $this->_writeBlock($v_binary_data, 8); 1654 1655 // ----- Write the last 356 bytes of the header in the archive 1656 $this->_writeBlock($v_binary_data_last, 356); 1657 1658 // ----- Write the filename as content of the block 1659 $i = 0; 1660 while (($v_buffer = substr($p_filename, (($i++) * 512), 512)) != '') { 1661 $v_binary_data = pack("a512", "$v_buffer"); 1662 $this->_writeBlock($v_binary_data); 1663 } 1664 1665 return true; 1666 } 1667 1668 /** 1669 * @param mixed $v_binary_data 1670 * @param mixed $v_header 1671 * @return bool 1672 */ 1673 public function _readHeader($v_binary_data, &$v_header) 1674 { 1675 if (strlen($v_binary_data) == 0) { 1676 $v_header['filename'] = ''; 1677 return true; 1678 } 1679 1680 if (strlen($v_binary_data) != 512) { 1681 $v_header['filename'] = ''; 1682 $this->_error('Invalid block size : ' . strlen($v_binary_data)); 1683 return false; 1684 } 1685 1686 if (!is_array($v_header)) { 1687 $v_header = array(); 1688 } 1689 // ----- Calculate the checksum 1690 $v_checksum = 0; 1691 // ..... First part of the header 1692 $v_binary_split = str_split($v_binary_data); 1693 $v_checksum += array_sum(array_map('ord', array_slice($v_binary_split, 0, 148))); 1694 $v_checksum += array_sum(array_map('ord', array(' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',))); 1695 $v_checksum += array_sum(array_map('ord', array_slice($v_binary_split, 156, 512))); 1696 1697 1698 $v_data = unpack($this->_fmt, $v_binary_data); 1699 1700 if (strlen($v_data["prefix"]) > 0) { 1701 $v_data["filename"] = "$v_data[prefix]/$v_data[filename]"; 1702 } 1703 1704 // ----- Extract the checksum 1705 $v_data_checksum = trim($v_data['checksum']); 1706 if (!preg_match('/^[0-7]*$/', $v_data_checksum)) { 1707 $this->_error( 1708 'Invalid checksum for file "' . $v_data['filename'] 1709 . '" : ' . $v_data_checksum . ' extracted' 1710 ); 1711 return false; 1712 } 1713 1714 $v_header['checksum'] = OctDec($v_data_checksum); 1715 if ($v_header['checksum'] != $v_checksum) { 1716 $v_header['filename'] = ''; 1717 1718 // ----- Look for last block (empty block) 1719 if (($v_checksum == 256) && ($v_header['checksum'] == 0)) { 1720 return true; 1721 } 1722 1723 $this->_error( 1724 'Invalid checksum for file "' . $v_data['filename'] 1725 . '" : ' . $v_checksum . ' calculated, ' 1726 . $v_header['checksum'] . ' expected' 1727 ); 1728 return false; 1729 } 1730 1731 // ----- Extract the properties 1732 $v_header['filename'] = rtrim($v_data['filename'], "\0"); 1733 if ($this->_maliciousFilename($v_header['filename'])) { 1734 $this->_error( 1735 'Malicious .tar detected, file "' . $v_header['filename'] . 1736 '" will not install in desired directory tree' 1737 ); 1738 return false; 1739 } 1740 $v_header['mode'] = OctDec(trim($v_data['mode'])); 1741 $v_header['uid'] = OctDec(trim($v_data['uid'])); 1742 $v_header['gid'] = OctDec(trim($v_data['gid'])); 1743 $v_header['size'] = $this->_tarRecToSize($v_data['size']); 1744 $v_header['mtime'] = OctDec(trim($v_data['mtime'])); 1745 if (($v_header['typeflag'] = $v_data['typeflag']) == "5") { 1746 $v_header['size'] = 0; 1747 } 1748 $v_header['link'] = trim($v_data['link']); 1749 /* ----- All these fields are removed form the header because 1750 they do not carry interesting info 1751 $v_header[magic] = trim($v_data[magic]); 1752 $v_header[version] = trim($v_data[version]); 1753 $v_header[uname] = trim($v_data[uname]); 1754 $v_header[gname] = trim($v_data[gname]); 1755 $v_header[devmajor] = trim($v_data[devmajor]); 1756 $v_header[devminor] = trim($v_data[devminor]); 1757 */ 1758 1759 return true; 1760 } 1761 1762 /** 1763 * Convert Tar record size to actual size 1764 * 1765 * @param string $tar_size 1766 * @return size of tar record in bytes 1767 */ 1768 private function _tarRecToSize($tar_size) 1769 { 1770 /* 1771 * First byte of size has a special meaning if bit 7 is set. 1772 * 1773 * Bit 7 indicates base-256 encoding if set. 1774 * Bit 6 is the sign bit. 1775 * Bits 5:0 are most significant value bits. 1776 */ 1777 $ch = ord($tar_size[0]); 1778 if ($ch & 0x80) { 1779 // Full 12-bytes record is required. 1780 $rec_str = $tar_size . "\x00"; 1781 1782 $size = ($ch & 0x40) ? -1 : 0; 1783 $size = ($size << 6) | ($ch & 0x3f); 1784 1785 for ($num_ch = 1; $num_ch < 12; ++$num_ch) { 1786 $size = ($size * 256) + ord($rec_str[$num_ch]); 1787 } 1788 1789 return $size; 1790 1791 } else { 1792 return OctDec(trim($tar_size)); 1793 } 1794 } 1795 1796 /** 1797 * Detect and report a malicious file name 1798 * 1799 * @param string $file 1800 * 1801 * @return bool 1802 */ 1803 private function _maliciousFilename($file) 1804 { 1805 if (strpos($file, 'phar://') === 0) { 1806 return true; 1807 } 1808 if (strpos($file, '../') !== false || strpos($file, '..\\') !== false) { 1809 return true; 1810 } 1811 return false; 1812 } 1813 1814 /** 1815 * @param $v_header 1816 * @return bool 1817 */ 1818 public function _readLongHeader(&$v_header) 1819 { 1820 $v_filename = ''; 1821 $v_filesize = $v_header['size']; 1822 $n = floor($v_header['size'] / 512); 1823 for ($i = 0; $i < $n; $i++) { 1824 $v_content = $this->_readBlock(); 1825 $v_filename .= $v_content; 1826 } 1827 if (($v_header['size'] % 512) != 0) { 1828 $v_content = $this->_readBlock(); 1829 $v_filename .= $v_content; 1830 } 1831 1832 // ----- Read the next header 1833 $v_binary_data = $this->_readBlock(); 1834 1835 if (!$this->_readHeader($v_binary_data, $v_header)) { 1836 return false; 1837 } 1838 1839 $v_filename = rtrim(substr($v_filename, 0, $v_filesize), "\0"); 1840 $v_header['filename'] = $v_filename; 1841 if ($this->_maliciousFilename($v_filename)) { 1842 $this->_error( 1843 'Malicious .tar detected, file "' . $v_filename . 1844 '" will not install in desired directory tree' 1845 ); 1846 return false; 1847 } 1848 1849 return true; 1850 } 1851 1852 /** 1853 * This method extract from the archive one file identified by $p_filename. 1854 * The return value is a string with the file content, or null on error. 1855 * 1856 * @param string $p_filename The path of the file to extract in a string. 1857 * 1858 * @return a string with the file content or null. 1859 */ 1860 private function _extractInString($p_filename) 1861 { 1862 $v_result_str = ""; 1863 1864 while (strlen($v_binary_data = $this->_readBlock()) != 0) { 1865 if (!$this->_readHeader($v_binary_data, $v_header)) { 1866 return null; 1867 } 1868 1869 if ($v_header['filename'] == '') { 1870 continue; 1871 } 1872 1873 switch ($v_header['typeflag']) { 1874 case 'L': 1875 { 1876 if (!$this->_readLongHeader($v_header)) { 1877 return null; 1878 } 1879 } 1880 break; 1881 1882 case 'K': 1883 { 1884 $v_link_header = $v_header; 1885 if (!$this->_readLongHeader($v_link_header)) { 1886 return null; 1887 } 1888 $v_header['link'] = $v_link_header['filename']; 1889 } 1890 break; 1891 } 1892 1893 if ($v_header['filename'] == $p_filename) { 1894 if ($v_header['typeflag'] == "5") { 1895 $this->_error( 1896 'Unable to extract in string a directory ' 1897 . 'entry {' . $v_header['filename'] . '}' 1898 ); 1899 return null; 1900 } else { 1901 $n = floor($v_header['size'] / 512); 1902 for ($i = 0; $i < $n; $i++) { 1903 $v_result_str .= $this->_readBlock(); 1904 } 1905 if (($v_header['size'] % 512) != 0) { 1906 $v_content = $this->_readBlock(); 1907 $v_result_str .= substr( 1908 $v_content, 1909 0, 1910 ($v_header['size'] % 512) 1911 ); 1912 } 1913 return $v_result_str; 1914 } 1915 } else { 1916 $this->_jumpBlock(ceil(($v_header['size'] / 512))); 1917 } 1918 } 1919 1920 return null; 1921 } 1922 1923 /** 1924 * @param string $p_path 1925 * @param string $p_list_detail 1926 * @param string $p_mode 1927 * @param string $p_file_list 1928 * @param string $p_remove_path 1929 * @param bool $p_preserve 1930 * @param bool $p_symlinks 1931 * @return bool 1932 */ 1933 public function _extractList( 1934 $p_path, 1935 &$p_list_detail, 1936 $p_mode, 1937 $p_file_list, 1938 $p_remove_path, 1939 $p_preserve = false, 1940 $p_symlinks = true 1941 ) 1942 { 1943 $v_result = true; 1944 $v_nb = 0; 1945 $v_extract_all = true; 1946 $v_listing = false; 1947 1948 $p_path = $this->_translateWinPath($p_path, false); 1949 if ($p_path == '' || (substr($p_path, 0, 1) != '/' 1950 && substr($p_path, 0, 3) != "../" && !strpos($p_path, ':')) 1951 ) { 1952 $p_path = "./" . $p_path; 1953 } 1954 $p_remove_path = $this->_translateWinPath($p_remove_path); 1955 1956 // ----- Look for path to remove format (should end by /) 1957 if (($p_remove_path != '') && (substr($p_remove_path, -1) != '/')) { 1958 $p_remove_path .= '/'; 1959 } 1960 $p_remove_path_size = strlen($p_remove_path); 1961 1962 switch ($p_mode) { 1963 case "complete" : 1964 $v_extract_all = true; 1965 $v_listing = false; 1966 break; 1967 case "partial" : 1968 $v_extract_all = false; 1969 $v_listing = false; 1970 break; 1971 case "list" : 1972 $v_extract_all = false; 1973 $v_listing = true; 1974 break; 1975 default : 1976 $this->_error('Invalid extract mode (' . $p_mode . ')'); 1977 return false; 1978 } 1979 1980 clearstatcache(); 1981 1982 while (strlen($v_binary_data = $this->_readBlock()) != 0) { 1983 $v_extract_file = false; 1984 $v_extraction_stopped = 0; 1985 1986 if (!$this->_readHeader($v_binary_data, $v_header)) { 1987 return false; 1988 } 1989 1990 if ($v_header['filename'] == '') { 1991 continue; 1992 } 1993 1994 switch ($v_header['typeflag']) { 1995 case 'L': 1996 { 1997 if (!$this->_readLongHeader($v_header)) { 1998 return null; 1999 } 2000 } 2001 break; 2002 2003 case 'K': 2004 { 2005 $v_link_header = $v_header; 2006 if (!$this->_readLongHeader($v_link_header)) { 2007 return null; 2008 } 2009 $v_header['link'] = $v_link_header['filename']; 2010 } 2011 break; 2012 } 2013 2014 // ignore extended / pax headers 2015 if ($v_header['typeflag'] == 'x' || $v_header['typeflag'] == 'g') { 2016 $this->_jumpBlock(ceil(($v_header['size'] / 512))); 2017 continue; 2018 } 2019 2020 if ((!$v_extract_all) && (is_array($p_file_list))) { 2021 // ----- By default no unzip if the file is not found 2022 $v_extract_file = false; 2023 2024 for ($i = 0; $i < sizeof($p_file_list); $i++) { 2025 // ----- Look if it is a directory 2026 if (substr($p_file_list[$i], -1) == '/') { 2027 // ----- Look if the directory is in the filename path 2028 if ((strlen($v_header['filename']) > strlen($p_file_list[$i])) 2029 && (substr($v_header['filename'], 0, strlen($p_file_list[$i])) 2030 == $p_file_list[$i]) 2031 ) { 2032 $v_extract_file = true; 2033 break; 2034 } 2035 } // ----- It is a file, so compare the file names 2036 elseif ($p_file_list[$i] == $v_header['filename']) { 2037 $v_extract_file = true; 2038 break; 2039 } 2040 } 2041 } else { 2042 $v_extract_file = true; 2043 } 2044 2045 // ----- Look if this file need to be extracted 2046 if (($v_extract_file) && (!$v_listing)) { 2047 if (($p_remove_path != '') 2048 && (substr($v_header['filename'] . '/', 0, $p_remove_path_size) 2049 == $p_remove_path) 2050 ) { 2051 $v_header['filename'] = substr( 2052 $v_header['filename'], 2053 $p_remove_path_size 2054 ); 2055 if ($v_header['filename'] == '') { 2056 continue; 2057 } 2058 } 2059 if (($p_path != './') && ($p_path != '/')) { 2060 while (substr($p_path, -1) == '/') { 2061 $p_path = substr($p_path, 0, strlen($p_path) - 1); 2062 } 2063 2064 if (substr($v_header['filename'], 0, 1) == '/') { 2065 $v_header['filename'] = $p_path . $v_header['filename']; 2066 } else { 2067 $v_header['filename'] = $p_path . '/' . $v_header['filename']; 2068 } 2069 } 2070 if (file_exists($v_header['filename'])) { 2071 if ((@is_dir($v_header['filename'])) 2072 && ($v_header['typeflag'] == '') 2073 ) { 2074 $this->_error( 2075 'File ' . $v_header['filename'] 2076 . ' already exists as a directory' 2077 ); 2078 return false; 2079 } 2080 if (($this->_isArchive($v_header['filename'])) 2081 && ($v_header['typeflag'] == "5") 2082 ) { 2083 $this->_error( 2084 'Directory ' . $v_header['filename'] 2085 . ' already exists as a file' 2086 ); 2087 return false; 2088 } 2089 if (!is_writeable($v_header['filename'])) { 2090 $this->_error( 2091 'File ' . $v_header['filename'] 2092 . ' already exists and is write protected' 2093 ); 2094 return false; 2095 } 2096 if (filemtime($v_header['filename']) > $v_header['mtime']) { 2097 // To be completed : An error or silent no replace ? 2098 } 2099 } // ----- Check the directory availability and create it if necessary 2100 elseif (($v_result 2101 = $this->_dirCheck( 2102 ($v_header['typeflag'] == "5" 2103 ? $v_header['filename'] 2104 : dirname($v_header['filename'])) 2105 )) != 1 2106 ) { 2107 $this->_error('Unable to create path for ' . $v_header['filename']); 2108 return false; 2109 } 2110 2111 if ($v_extract_file) { 2112 if ($v_header['typeflag'] == "5") { 2113 if (!@file_exists($v_header['filename'])) { 2114 if (!@mkdir($v_header['filename'], 0777)) { 2115 $this->_error( 2116 'Unable to create directory {' 2117 . $v_header['filename'] . '}' 2118 ); 2119 return false; 2120 } 2121 } 2122 } elseif ($v_header['typeflag'] == "2") { 2123 if (!$p_symlinks) { 2124 $this->_warning('Symbolic links are not allowed. ' 2125 . 'Unable to extract {' 2126 . $v_header['filename'] . '}' 2127 ); 2128 return false; 2129 } 2130 if (@file_exists($v_header['filename'])) { 2131 @unlink($v_header['filename']); 2132 } 2133 if (!@symlink($v_header['link'], $v_header['filename'])) { 2134 $this->_error( 2135 'Unable to extract symbolic link {' 2136 . $v_header['filename'] . '}' 2137 ); 2138 return false; 2139 } 2140 } else { 2141 if (($v_dest_file = @fopen($v_header['filename'], "wb")) == 0) { 2142 $this->_error( 2143 'Error while opening {' . $v_header['filename'] 2144 . '} in write binary mode' 2145 ); 2146 return false; 2147 } else { 2148 $n = floor($v_header['size'] / 512); 2149 for ($i = 0; $i < $n; $i++) { 2150 $v_content = $this->_readBlock(); 2151 fwrite($v_dest_file, $v_content, 512); 2152 } 2153 if (($v_header['size'] % 512) != 0) { 2154 $v_content = $this->_readBlock(); 2155 fwrite($v_dest_file, $v_content, ($v_header['size'] % 512)); 2156 } 2157 2158 @fclose($v_dest_file); 2159 2160 if ($p_preserve) { 2161 @chown($v_header['filename'], $v_header['uid']); 2162 @chgrp($v_header['filename'], $v_header['gid']); 2163 } 2164 2165 // ----- Change the file mode, mtime 2166 @touch($v_header['filename'], $v_header['mtime']); 2167 if ($v_header['mode'] & 0111) { 2168 // make file executable, obey umask 2169 $mode = fileperms($v_header['filename']) | (~umask() & 0111); 2170 @chmod($v_header['filename'], $mode); 2171 } 2172 } 2173 2174 // ----- Check the file size 2175 clearstatcache(); 2176 if (!is_file($v_header['filename'])) { 2177 $this->_error( 2178 'Extracted file ' . $v_header['filename'] 2179 . 'does not exist. Archive may be corrupted.' 2180 ); 2181 return false; 2182 } 2183 2184 $filesize = filesize($v_header['filename']); 2185 if ($filesize != $v_header['size']) { 2186 $this->_error( 2187 'Extracted file ' . $v_header['filename'] 2188 . ' does not have the correct file size \'' 2189 . $filesize 2190 . '\' (' . $v_header['size'] 2191 . ' expected). Archive may be corrupted.' 2192 ); 2193 return false; 2194 } 2195 } 2196 } else { 2197 $this->_jumpBlock(ceil(($v_header['size'] / 512))); 2198 } 2199 } else { 2200 $this->_jumpBlock(ceil(($v_header['size'] / 512))); 2201 } 2202 2203 /* TBC : Seems to be unused ... 2204 if ($this->_compress) 2205 $v_end_of_file = @gzeof($this->_file); 2206 else 2207 $v_end_of_file = @feof($this->_file); 2208 */ 2209 2210 if ($v_listing || $v_extract_file || $v_extraction_stopped) { 2211 // ----- Log extracted files 2212 if (($v_file_dir = dirname($v_header['filename'])) 2213 == $v_header['filename'] 2214 ) { 2215 $v_file_dir = ''; 2216 } 2217 if ((substr($v_header['filename'], 0, 1) == '/') && ($v_file_dir == '')) { 2218 $v_file_dir = '/'; 2219 } 2220 2221 $p_list_detail[$v_nb++] = $v_header; 2222 if (is_array($p_file_list) && (count($p_list_detail) == count($p_file_list))) { 2223 return true; 2224 } 2225 } 2226 } 2227 2228 return true; 2229 } 2230 2231 /** 2232 * @return bool 2233 */ 2234 public function _openAppend() 2235 { 2236 if (filesize($this->_tarname) == 0) { 2237 return $this->_openWrite(); 2238 } 2239 2240 if ($this->_compress) { 2241 $this->_close(); 2242 2243 if (!@rename($this->_tarname, $this->_tarname . ".tmp")) { 2244 $this->_error( 2245 'Error while renaming \'' . $this->_tarname 2246 . '\' to temporary file \'' . $this->_tarname 2247 . '.tmp\'' 2248 ); 2249 return false; 2250 } 2251 2252 if ($this->_compress_type == 'gz') { 2253 $v_temp_tar = @gzopen($this->_tarname . ".tmp", "rb"); 2254 } elseif ($this->_compress_type == 'bz2') { 2255 $v_temp_tar = @bzopen($this->_tarname . ".tmp", "r"); 2256 } elseif ($this->_compress_type == 'lzma2') { 2257 $v_temp_tar = @xzopen($this->_tarname . ".tmp", "r"); 2258 } 2259 2260 2261 if ($v_temp_tar == 0) { 2262 $this->_error( 2263 'Unable to open file \'' . $this->_tarname 2264 . '.tmp\' in binary read mode' 2265 ); 2266 @rename($this->_tarname . ".tmp", $this->_tarname); 2267 return false; 2268 } 2269 2270 if (!$this->_openWrite()) { 2271 @rename($this->_tarname . ".tmp", $this->_tarname); 2272 return false; 2273 } 2274 2275 if ($this->_compress_type == 'gz') { 2276 $end_blocks = 0; 2277 2278 while (!@gzeof($v_temp_tar)) { 2279 $v_buffer = @gzread($v_temp_tar, 512); 2280 if ($v_buffer == ARCHIVE_TAR_END_BLOCK || strlen($v_buffer) == 0) { 2281 $end_blocks++; 2282 // do not copy end blocks, we will re-make them 2283 // after appending 2284 continue; 2285 } elseif ($end_blocks > 0) { 2286 for ($i = 0; $i < $end_blocks; $i++) { 2287 $this->_writeBlock(ARCHIVE_TAR_END_BLOCK); 2288 } 2289 $end_blocks = 0; 2290 } 2291 $v_binary_data = pack("a512", $v_buffer); 2292 $this->_writeBlock($v_binary_data); 2293 } 2294 2295 @gzclose($v_temp_tar); 2296 } elseif ($this->_compress_type == 'bz2') { 2297 $end_blocks = 0; 2298 2299 while (strlen($v_buffer = @bzread($v_temp_tar, 512)) > 0) { 2300 if ($v_buffer == ARCHIVE_TAR_END_BLOCK || strlen($v_buffer) == 0) { 2301 $end_blocks++; 2302 // do not copy end blocks, we will re-make them 2303 // after appending 2304 continue; 2305 } elseif ($end_blocks > 0) { 2306 for ($i = 0; $i < $end_blocks; $i++) { 2307 $this->_writeBlock(ARCHIVE_TAR_END_BLOCK); 2308 } 2309 $end_blocks = 0; 2310 } 2311 $v_binary_data = pack("a512", $v_buffer); 2312 $this->_writeBlock($v_binary_data); 2313 } 2314 2315 @bzclose($v_temp_tar); 2316 } elseif ($this->_compress_type == 'lzma2') { 2317 $end_blocks = 0; 2318 2319 while (strlen($v_buffer = @xzread($v_temp_tar, 512)) > 0) { 2320 if ($v_buffer == ARCHIVE_TAR_END_BLOCK || strlen($v_buffer) == 0) { 2321 $end_blocks++; 2322 // do not copy end blocks, we will re-make them 2323 // after appending 2324 continue; 2325 } elseif ($end_blocks > 0) { 2326 for ($i = 0; $i < $end_blocks; $i++) { 2327 $this->_writeBlock(ARCHIVE_TAR_END_BLOCK); 2328 } 2329 $end_blocks = 0; 2330 } 2331 $v_binary_data = pack("a512", $v_buffer); 2332 $this->_writeBlock($v_binary_data); 2333 } 2334 2335 @xzclose($v_temp_tar); 2336 } 2337 2338 if (!@unlink($this->_tarname . ".tmp")) { 2339 $this->_error( 2340 'Error while deleting temporary file \'' 2341 . $this->_tarname . '.tmp\'' 2342 ); 2343 } 2344 } else { 2345 // ----- For not compressed tar, just add files before the last 2346 // one or two 512 bytes block 2347 if (!$this->_openReadWrite()) { 2348 return false; 2349 } 2350 2351 clearstatcache(); 2352 $v_size = filesize($this->_tarname); 2353 2354 // We might have zero, one or two end blocks. 2355 // The standard is two, but we should try to handle 2356 // other cases. 2357 fseek($this->_file, $v_size - 1024); 2358 if (fread($this->_file, 512) == ARCHIVE_TAR_END_BLOCK) { 2359 fseek($this->_file, $v_size - 1024); 2360 } elseif (fread($this->_file, 512) == ARCHIVE_TAR_END_BLOCK) { 2361 fseek($this->_file, $v_size - 512); 2362 } 2363 } 2364 2365 return true; 2366 } 2367 2368 /** 2369 * @param $p_filelist 2370 * @param string $p_add_dir 2371 * @param string $p_remove_dir 2372 * @return bool 2373 */ 2374 public function _append($p_filelist, $p_add_dir = '', $p_remove_dir = '') 2375 { 2376 if (!$this->_openAppend()) { 2377 return false; 2378 } 2379 2380 if ($this->_addList($p_filelist, $p_add_dir, $p_remove_dir)) { 2381 $this->_writeFooter(); 2382 } 2383 2384 $this->_close(); 2385 2386 return true; 2387 } 2388 2389 /** 2390 * Check if a directory exists and create it (including parent 2391 * dirs) if not. 2392 * 2393 * @param string $p_dir directory to check 2394 * 2395 * @return bool true if the directory exists or was created 2396 */ 2397 public function _dirCheck($p_dir) 2398 { 2399 clearstatcache(); 2400 if ((@is_dir($p_dir)) || ($p_dir == '')) { 2401 return true; 2402 } 2403 2404 $p_parent_dir = dirname($p_dir); 2405 2406 if (($p_parent_dir != $p_dir) && 2407 ($p_parent_dir != '') && 2408 (!$this->_dirCheck($p_parent_dir)) 2409 ) { 2410 return false; 2411 } 2412 2413 if (!@mkdir($p_dir, 0777)) { 2414 $this->_error("Unable to create directory '$p_dir'"); 2415 return false; 2416 } 2417 2418 return true; 2419 } 2420 2421 /** 2422 * Compress path by changing for example "/dir/foo/../bar" to "/dir/bar", 2423 * rand emove double slashes. 2424 * 2425 * @param string $p_dir path to reduce 2426 * 2427 * @return string reduced path 2428 */ 2429 private function _pathReduction($p_dir) 2430 { 2431 $v_result = ''; 2432 2433 // ----- Look for not empty path 2434 if ($p_dir != '') { 2435 // ----- Explode path by directory names 2436 $v_list = explode('/', $p_dir); 2437 2438 // ----- Study directories from last to first 2439 for ($i = sizeof($v_list) - 1; $i >= 0; $i--) { 2440 // ----- Look for current path 2441 if ($v_list[$i] == ".") { 2442 // ----- Ignore this directory 2443 // Should be the first $i=0, but no check is done 2444 } else { 2445 if ($v_list[$i] == "..") { 2446 // ----- Ignore it and ignore the $i-1 2447 $i--; 2448 } else { 2449 if (($v_list[$i] == '') 2450 && ($i != (sizeof($v_list) - 1)) 2451 && ($i != 0) 2452 ) { 2453 // ----- Ignore only the double '//' in path, 2454 // but not the first and last / 2455 } else { 2456 $v_result = $v_list[$i] . ($i != (sizeof($v_list) - 1) ? '/' 2457 . $v_result : ''); 2458 } 2459 } 2460 } 2461 } 2462 } 2463 2464 if (defined('OS_WINDOWS') && OS_WINDOWS) { 2465 $v_result = strtr($v_result, '\\', '/'); 2466 } 2467 2468 return $v_result; 2469 } 2470 2471 /** 2472 * @param $p_path 2473 * @param bool $p_remove_disk_letter 2474 * @return string 2475 */ 2476 public function _translateWinPath($p_path, $p_remove_disk_letter = true) 2477 { 2478 if (defined('OS_WINDOWS') && OS_WINDOWS) { 2479 // ----- Look for potential disk letter 2480 if (($p_remove_disk_letter) 2481 && (($v_position = strpos($p_path, ':')) != false) 2482 ) { 2483 $p_path = substr($p_path, $v_position + 1); 2484 } 2485 // ----- Change potential windows directory separator 2486 if ((strpos($p_path, '\\') > 0) || (substr($p_path, 0, 1) == '\\')) { 2487 $p_path = strtr($p_path, '\\', '/'); 2488 } 2489 } 2490 return $p_path; 2491 } 2492} 2493