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 $v_uname = ''; 1401 $v_gname = ''; 1402 1403 if (function_exists('posix_getpwuid')) { 1404 $userinfo = posix_getpwuid($v_info[4]); 1405 $groupinfo = posix_getgrgid($v_info[5]); 1406 1407 if (isset($userinfo['name'])) { 1408 $v_uname = $userinfo['name']; 1409 } 1410 1411 if (isset($groupinfo['name'])) { 1412 $v_gname = $groupinfo['name']; 1413 } 1414 } 1415 1416 $v_devmajor = ''; 1417 $v_devminor = ''; 1418 $v_prefix = ''; 1419 1420 $v_binary_data_first = pack( 1421 "a100a8a8a8a12a12", 1422 $v_reduced_filename, 1423 $v_perms, 1424 $v_uid, 1425 $v_gid, 1426 $v_size, 1427 $v_mtime 1428 ); 1429 $v_binary_data_last = pack( 1430 "a1a100a6a2a32a32a8a8a155a12", 1431 $v_typeflag, 1432 $v_linkname, 1433 $v_magic, 1434 $v_version, 1435 $v_uname, 1436 $v_gname, 1437 $v_devmajor, 1438 $v_devminor, 1439 $v_prefix, 1440 '' 1441 ); 1442 1443 // ----- Calculate the checksum 1444 $v_checksum = 0; 1445 // ..... First part of the header 1446 for ($i = 0; $i < 148; $i++) { 1447 $v_checksum += ord(substr($v_binary_data_first, $i, 1)); 1448 } 1449 // ..... Ignore the checksum value and replace it by ' ' (space) 1450 for ($i = 148; $i < 156; $i++) { 1451 $v_checksum += ord(' '); 1452 } 1453 // ..... Last part of the header 1454 for ($i = 156, $j = 0; $i < 512; $i++, $j++) { 1455 $v_checksum += ord(substr($v_binary_data_last, $j, 1)); 1456 } 1457 1458 // ----- Write the first 148 bytes of the header in the archive 1459 $this->_writeBlock($v_binary_data_first, 148); 1460 1461 // ----- Write the calculated checksum 1462 $v_checksum = sprintf("%06s\0 ", DecOct($v_checksum)); 1463 $v_binary_data = pack("a8", $v_checksum); 1464 $this->_writeBlock($v_binary_data, 8); 1465 1466 // ----- Write the last 356 bytes of the header in the archive 1467 $this->_writeBlock($v_binary_data_last, 356); 1468 1469 return true; 1470 } 1471 1472 /** 1473 * @param string $p_filename 1474 * @param int $p_size 1475 * @param int $p_mtime 1476 * @param int $p_perms 1477 * @param string $p_type 1478 * @param int $p_uid 1479 * @param int $p_gid 1480 * @return bool 1481 */ 1482 public function _writeHeaderBlock( 1483 $p_filename, 1484 $p_size, 1485 $p_mtime = 0, 1486 $p_perms = 0, 1487 $p_type = '', 1488 $p_uid = 0, 1489 $p_gid = 0 1490 ) 1491 { 1492 $p_filename = $this->_pathReduction($p_filename); 1493 1494 if (strlen($p_filename) > 99) { 1495 if (!$this->_writeLongHeader($p_filename, false)) { 1496 return false; 1497 } 1498 } 1499 1500 if ($p_type == "5") { 1501 $v_size = sprintf("%011s", DecOct(0)); 1502 } else { 1503 $v_size = sprintf("%011s", DecOct($p_size)); 1504 } 1505 1506 $v_uid = sprintf("%07s", DecOct($p_uid)); 1507 $v_gid = sprintf("%07s", DecOct($p_gid)); 1508 $v_perms = sprintf("%07s", DecOct($p_perms & 000777)); 1509 1510 $v_mtime = sprintf("%11s", DecOct($p_mtime)); 1511 1512 $v_linkname = ''; 1513 1514 $v_magic = 'ustar '; 1515 1516 $v_version = ' '; 1517 1518 if (function_exists('posix_getpwuid')) { 1519 $userinfo = posix_getpwuid($p_uid); 1520 $groupinfo = posix_getgrgid($p_gid); 1521 1522 if ($userinfo === false || $groupinfo === false) { 1523 $v_uname = ''; 1524 $v_gname = ''; 1525 } else { 1526 $v_uname = $userinfo['name']; 1527 $v_gname = $groupinfo['name']; 1528 } 1529 } else { 1530 $v_uname = ''; 1531 $v_gname = ''; 1532 } 1533 1534 $v_devmajor = ''; 1535 1536 $v_devminor = ''; 1537 1538 $v_prefix = ''; 1539 1540 $v_binary_data_first = pack( 1541 "a100a8a8a8a12A12", 1542 $p_filename, 1543 $v_perms, 1544 $v_uid, 1545 $v_gid, 1546 $v_size, 1547 $v_mtime 1548 ); 1549 $v_binary_data_last = pack( 1550 "a1a100a6a2a32a32a8a8a155a12", 1551 $p_type, 1552 $v_linkname, 1553 $v_magic, 1554 $v_version, 1555 $v_uname, 1556 $v_gname, 1557 $v_devmajor, 1558 $v_devminor, 1559 $v_prefix, 1560 '' 1561 ); 1562 1563 // ----- Calculate the checksum 1564 $v_checksum = 0; 1565 // ..... First part of the header 1566 for ($i = 0; $i < 148; $i++) { 1567 $v_checksum += ord(substr($v_binary_data_first, $i, 1)); 1568 } 1569 // ..... Ignore the checksum value and replace it by ' ' (space) 1570 for ($i = 148; $i < 156; $i++) { 1571 $v_checksum += ord(' '); 1572 } 1573 // ..... Last part of the header 1574 for ($i = 156, $j = 0; $i < 512; $i++, $j++) { 1575 $v_checksum += ord(substr($v_binary_data_last, $j, 1)); 1576 } 1577 1578 // ----- Write the first 148 bytes of the header in the archive 1579 $this->_writeBlock($v_binary_data_first, 148); 1580 1581 // ----- Write the calculated checksum 1582 $v_checksum = sprintf("%06s ", DecOct($v_checksum)); 1583 $v_binary_data = pack("a8", $v_checksum); 1584 $this->_writeBlock($v_binary_data, 8); 1585 1586 // ----- Write the last 356 bytes of the header in the archive 1587 $this->_writeBlock($v_binary_data_last, 356); 1588 1589 return true; 1590 } 1591 1592 /** 1593 * @param string $p_filename 1594 * @return bool 1595 */ 1596 public function _writeLongHeader($p_filename, $is_link = false) 1597 { 1598 $v_uid = sprintf("%07s", 0); 1599 $v_gid = sprintf("%07s", 0); 1600 $v_perms = sprintf("%07s", 0); 1601 $v_size = sprintf("%'011s", DecOct(strlen($p_filename))); 1602 $v_mtime = sprintf("%011s", 0); 1603 $v_typeflag = ($is_link ? 'K' : 'L'); 1604 $v_linkname = ''; 1605 $v_magic = 'ustar '; 1606 $v_version = ' '; 1607 $v_uname = ''; 1608 $v_gname = ''; 1609 $v_devmajor = ''; 1610 $v_devminor = ''; 1611 $v_prefix = ''; 1612 1613 $v_binary_data_first = pack( 1614 "a100a8a8a8a12a12", 1615 '././@LongLink', 1616 $v_perms, 1617 $v_uid, 1618 $v_gid, 1619 $v_size, 1620 $v_mtime 1621 ); 1622 $v_binary_data_last = pack( 1623 "a1a100a6a2a32a32a8a8a155a12", 1624 $v_typeflag, 1625 $v_linkname, 1626 $v_magic, 1627 $v_version, 1628 $v_uname, 1629 $v_gname, 1630 $v_devmajor, 1631 $v_devminor, 1632 $v_prefix, 1633 '' 1634 ); 1635 1636 // ----- Calculate the checksum 1637 $v_checksum = 0; 1638 // ..... First part of the header 1639 for ($i = 0; $i < 148; $i++) { 1640 $v_checksum += ord(substr($v_binary_data_first, $i, 1)); 1641 } 1642 // ..... Ignore the checksum value and replace it by ' ' (space) 1643 for ($i = 148; $i < 156; $i++) { 1644 $v_checksum += ord(' '); 1645 } 1646 // ..... Last part of the header 1647 for ($i = 156, $j = 0; $i < 512; $i++, $j++) { 1648 $v_checksum += ord(substr($v_binary_data_last, $j, 1)); 1649 } 1650 1651 // ----- Write the first 148 bytes of the header in the archive 1652 $this->_writeBlock($v_binary_data_first, 148); 1653 1654 // ----- Write the calculated checksum 1655 $v_checksum = sprintf("%06s\0 ", DecOct($v_checksum)); 1656 $v_binary_data = pack("a8", $v_checksum); 1657 $this->_writeBlock($v_binary_data, 8); 1658 1659 // ----- Write the last 356 bytes of the header in the archive 1660 $this->_writeBlock($v_binary_data_last, 356); 1661 1662 // ----- Write the filename as content of the block 1663 $i = 0; 1664 while (($v_buffer = substr($p_filename, (($i++) * 512), 512)) != '') { 1665 $v_binary_data = pack("a512", "$v_buffer"); 1666 $this->_writeBlock($v_binary_data); 1667 } 1668 1669 return true; 1670 } 1671 1672 /** 1673 * @param mixed $v_binary_data 1674 * @param mixed $v_header 1675 * @return bool 1676 */ 1677 public function _readHeader($v_binary_data, &$v_header) 1678 { 1679 if (strlen($v_binary_data) == 0) { 1680 $v_header['filename'] = ''; 1681 return true; 1682 } 1683 1684 if (strlen($v_binary_data) != 512) { 1685 $v_header['filename'] = ''; 1686 $this->_error('Invalid block size : ' . strlen($v_binary_data)); 1687 return false; 1688 } 1689 1690 if (!is_array($v_header)) { 1691 $v_header = array(); 1692 } 1693 // ----- Calculate the checksum 1694 $v_checksum = 0; 1695 // ..... First part of the header 1696 $v_binary_split = str_split($v_binary_data); 1697 $v_checksum += array_sum(array_map('ord', array_slice($v_binary_split, 0, 148))); 1698 $v_checksum += array_sum(array_map('ord', array(' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ',))); 1699 $v_checksum += array_sum(array_map('ord', array_slice($v_binary_split, 156, 512))); 1700 1701 1702 $v_data = unpack($this->_fmt, $v_binary_data); 1703 1704 if (strlen($v_data["prefix"]) > 0) { 1705 $v_data["filename"] = "$v_data[prefix]/$v_data[filename]"; 1706 } 1707 1708 // ----- Extract the checksum 1709 $v_data_checksum = trim($v_data['checksum']); 1710 if (!preg_match('/^[0-7]*$/', $v_data_checksum)) { 1711 $this->_error( 1712 'Invalid checksum for file "' . $v_data['filename'] 1713 . '" : ' . $v_data_checksum . ' extracted' 1714 ); 1715 return false; 1716 } 1717 1718 $v_header['checksum'] = OctDec($v_data_checksum); 1719 if ($v_header['checksum'] != $v_checksum) { 1720 $v_header['filename'] = ''; 1721 1722 // ----- Look for last block (empty block) 1723 if (($v_checksum == 256) && ($v_header['checksum'] == 0)) { 1724 return true; 1725 } 1726 1727 $this->_error( 1728 'Invalid checksum for file "' . $v_data['filename'] 1729 . '" : ' . $v_checksum . ' calculated, ' 1730 . $v_header['checksum'] . ' expected' 1731 ); 1732 return false; 1733 } 1734 1735 // ----- Extract the properties 1736 $v_header['filename'] = rtrim($v_data['filename'], "\0"); 1737 if ($this->_isMaliciousFilename($v_header['filename'])) { 1738 $this->_error( 1739 'Malicious .tar detected, file "' . $v_header['filename'] . 1740 '" will not install in desired directory tree' 1741 ); 1742 return false; 1743 } 1744 $v_header['mode'] = OctDec(trim($v_data['mode'])); 1745 $v_header['uid'] = OctDec(trim($v_data['uid'])); 1746 $v_header['gid'] = OctDec(trim($v_data['gid'])); 1747 $v_header['size'] = $this->_tarRecToSize($v_data['size']); 1748 $v_header['mtime'] = OctDec(trim($v_data['mtime'])); 1749 if (($v_header['typeflag'] = $v_data['typeflag']) == "5") { 1750 $v_header['size'] = 0; 1751 } 1752 $v_header['link'] = trim($v_data['link']); 1753 /* ----- All these fields are removed form the header because 1754 they do not carry interesting info 1755 $v_header[magic] = trim($v_data[magic]); 1756 $v_header[version] = trim($v_data[version]); 1757 $v_header[uname] = trim($v_data[uname]); 1758 $v_header[gname] = trim($v_data[gname]); 1759 $v_header[devmajor] = trim($v_data[devmajor]); 1760 $v_header[devminor] = trim($v_data[devminor]); 1761 */ 1762 1763 return true; 1764 } 1765 1766 /** 1767 * Convert Tar record size to actual size 1768 * 1769 * @param string $tar_size 1770 * @return size of tar record in bytes 1771 */ 1772 private function _tarRecToSize($tar_size) 1773 { 1774 /* 1775 * First byte of size has a special meaning if bit 7 is set. 1776 * 1777 * Bit 7 indicates base-256 encoding if set. 1778 * Bit 6 is the sign bit. 1779 * Bits 5:0 are most significant value bits. 1780 */ 1781 $ch = ord($tar_size[0]); 1782 if ($ch & 0x80) { 1783 // Full 12-bytes record is required. 1784 $rec_str = $tar_size . "\x00"; 1785 1786 $size = ($ch & 0x40) ? -1 : 0; 1787 $size = ($size << 6) | ($ch & 0x3f); 1788 1789 for ($num_ch = 1; $num_ch < 12; ++$num_ch) { 1790 $size = ($size * 256) + ord($rec_str[$num_ch]); 1791 } 1792 1793 return $size; 1794 1795 } else { 1796 return OctDec(trim($tar_size)); 1797 } 1798 } 1799 1800 /** 1801 * Detect and report a malicious file name 1802 * 1803 * @param string $file 1804 * 1805 * @return bool 1806 */ 1807 private function _isMaliciousFilename($file) 1808 { 1809 if (strpos($file, '://') !== false) { 1810 return true; 1811 } 1812 if (strpos($file, '../') !== false || strpos($file, '..\\') !== false) { 1813 return true; 1814 } 1815 return false; 1816 } 1817 1818 /** 1819 * @param $v_header 1820 * @return bool 1821 */ 1822 public function _readLongHeader(&$v_header) 1823 { 1824 $v_filename = ''; 1825 $v_filesize = $v_header['size']; 1826 $n = floor($v_header['size'] / 512); 1827 for ($i = 0; $i < $n; $i++) { 1828 $v_content = $this->_readBlock(); 1829 $v_filename .= $v_content; 1830 } 1831 if (($v_header['size'] % 512) != 0) { 1832 $v_content = $this->_readBlock(); 1833 $v_filename .= $v_content; 1834 } 1835 1836 // ----- Read the next header 1837 $v_binary_data = $this->_readBlock(); 1838 1839 if (!$this->_readHeader($v_binary_data, $v_header)) { 1840 return false; 1841 } 1842 1843 $v_filename = rtrim(substr($v_filename, 0, $v_filesize), "\0"); 1844 $v_header['filename'] = $v_filename; 1845 if ($this->_isMaliciousFilename($v_filename)) { 1846 $this->_error( 1847 'Malicious .tar detected, file "' . $v_filename . 1848 '" will not install in desired directory tree' 1849 ); 1850 return false; 1851 } 1852 1853 return true; 1854 } 1855 1856 /** 1857 * This method extract from the archive one file identified by $p_filename. 1858 * The return value is a string with the file content, or null on error. 1859 * 1860 * @param string $p_filename The path of the file to extract in a string. 1861 * 1862 * @return a string with the file content or null. 1863 */ 1864 private function _extractInString($p_filename) 1865 { 1866 $v_result_str = ""; 1867 1868 while (strlen($v_binary_data = $this->_readBlock()) != 0) { 1869 if (!$this->_readHeader($v_binary_data, $v_header)) { 1870 return null; 1871 } 1872 1873 if ($v_header['filename'] == '') { 1874 continue; 1875 } 1876 1877 switch ($v_header['typeflag']) { 1878 case 'L': 1879 { 1880 if (!$this->_readLongHeader($v_header)) { 1881 return null; 1882 } 1883 } 1884 break; 1885 1886 case 'K': 1887 { 1888 $v_link_header = $v_header; 1889 if (!$this->_readLongHeader($v_link_header)) { 1890 return null; 1891 } 1892 $v_header['link'] = $v_link_header['filename']; 1893 } 1894 break; 1895 } 1896 1897 if ($v_header['filename'] == $p_filename) { 1898 if ($v_header['typeflag'] == "5") { 1899 $this->_error( 1900 'Unable to extract in string a directory ' 1901 . 'entry {' . $v_header['filename'] . '}' 1902 ); 1903 return null; 1904 } else { 1905 $n = floor($v_header['size'] / 512); 1906 for ($i = 0; $i < $n; $i++) { 1907 $v_result_str .= $this->_readBlock(); 1908 } 1909 if (($v_header['size'] % 512) != 0) { 1910 $v_content = $this->_readBlock(); 1911 $v_result_str .= substr( 1912 $v_content, 1913 0, 1914 ($v_header['size'] % 512) 1915 ); 1916 } 1917 return $v_result_str; 1918 } 1919 } else { 1920 $this->_jumpBlock(ceil(($v_header['size'] / 512))); 1921 } 1922 } 1923 1924 return null; 1925 } 1926 1927 /** 1928 * @param string $p_path 1929 * @param string $p_list_detail 1930 * @param string $p_mode 1931 * @param string $p_file_list 1932 * @param string $p_remove_path 1933 * @param bool $p_preserve 1934 * @param bool $p_symlinks 1935 * @return bool 1936 */ 1937 public function _extractList( 1938 $p_path, 1939 &$p_list_detail, 1940 $p_mode, 1941 $p_file_list, 1942 $p_remove_path, 1943 $p_preserve = false, 1944 $p_symlinks = true 1945 ) 1946 { 1947 $v_result = true; 1948 $v_nb = 0; 1949 $v_extract_all = true; 1950 $v_listing = false; 1951 1952 $p_path = $this->_translateWinPath($p_path, false); 1953 if ($p_path == '' || (substr($p_path, 0, 1) != '/' 1954 && substr($p_path, 0, 3) != "../" && !strpos($p_path, ':')) 1955 ) { 1956 $p_path = "./" . $p_path; 1957 } 1958 $p_remove_path = $this->_translateWinPath($p_remove_path); 1959 1960 // ----- Look for path to remove format (should end by /) 1961 if (($p_remove_path != '') && (substr($p_remove_path, -1) != '/')) { 1962 $p_remove_path .= '/'; 1963 } 1964 $p_remove_path_size = strlen($p_remove_path); 1965 1966 switch ($p_mode) { 1967 case "complete" : 1968 $v_extract_all = true; 1969 $v_listing = false; 1970 break; 1971 case "partial" : 1972 $v_extract_all = false; 1973 $v_listing = false; 1974 break; 1975 case "list" : 1976 $v_extract_all = false; 1977 $v_listing = true; 1978 break; 1979 default : 1980 $this->_error('Invalid extract mode (' . $p_mode . ')'); 1981 return false; 1982 } 1983 1984 clearstatcache(); 1985 1986 while (strlen($v_binary_data = $this->_readBlock()) != 0) { 1987 $v_extract_file = false; 1988 $v_extraction_stopped = 0; 1989 1990 if (!$this->_readHeader($v_binary_data, $v_header)) { 1991 return false; 1992 } 1993 1994 if ($v_header['filename'] == '') { 1995 continue; 1996 } 1997 1998 switch ($v_header['typeflag']) { 1999 case 'L': 2000 { 2001 if (!$this->_readLongHeader($v_header)) { 2002 return null; 2003 } 2004 } 2005 break; 2006 2007 case 'K': 2008 { 2009 $v_link_header = $v_header; 2010 if (!$this->_readLongHeader($v_link_header)) { 2011 return null; 2012 } 2013 $v_header['link'] = $v_link_header['filename']; 2014 } 2015 break; 2016 } 2017 2018 // ignore extended / pax headers 2019 if ($v_header['typeflag'] == 'x' || $v_header['typeflag'] == 'g') { 2020 $this->_jumpBlock(ceil(($v_header['size'] / 512))); 2021 continue; 2022 } 2023 2024 if ((!$v_extract_all) && (is_array($p_file_list))) { 2025 // ----- By default no unzip if the file is not found 2026 $v_extract_file = false; 2027 2028 for ($i = 0; $i < sizeof($p_file_list); $i++) { 2029 // ----- Look if it is a directory 2030 if (substr($p_file_list[$i], -1) == '/') { 2031 // ----- Look if the directory is in the filename path 2032 if ((strlen($v_header['filename']) > strlen($p_file_list[$i])) 2033 && (substr($v_header['filename'], 0, strlen($p_file_list[$i])) 2034 == $p_file_list[$i]) 2035 ) { 2036 $v_extract_file = true; 2037 break; 2038 } 2039 } // ----- It is a file, so compare the file names 2040 elseif ($p_file_list[$i] == $v_header['filename']) { 2041 $v_extract_file = true; 2042 break; 2043 } 2044 } 2045 } else { 2046 $v_extract_file = true; 2047 } 2048 2049 // ----- Look if this file need to be extracted 2050 if (($v_extract_file) && (!$v_listing)) { 2051 if (($p_remove_path != '') 2052 && (substr($v_header['filename'] . '/', 0, $p_remove_path_size) 2053 == $p_remove_path) 2054 ) { 2055 $v_header['filename'] = substr( 2056 $v_header['filename'], 2057 $p_remove_path_size 2058 ); 2059 if ($v_header['filename'] == '') { 2060 continue; 2061 } 2062 } 2063 if (($p_path != './') && ($p_path != '/')) { 2064 while (substr($p_path, -1) == '/') { 2065 $p_path = substr($p_path, 0, strlen($p_path) - 1); 2066 } 2067 2068 if (substr($v_header['filename'], 0, 1) == '/') { 2069 $v_header['filename'] = $p_path . $v_header['filename']; 2070 } else { 2071 $v_header['filename'] = $p_path . '/' . $v_header['filename']; 2072 } 2073 } 2074 if (file_exists($v_header['filename'])) { 2075 if ((@is_dir($v_header['filename'])) 2076 && ($v_header['typeflag'] == '') 2077 ) { 2078 $this->_error( 2079 'File ' . $v_header['filename'] 2080 . ' already exists as a directory' 2081 ); 2082 return false; 2083 } 2084 if (($this->_isArchive($v_header['filename'])) 2085 && ($v_header['typeflag'] == "5") 2086 ) { 2087 $this->_error( 2088 'Directory ' . $v_header['filename'] 2089 . ' already exists as a file' 2090 ); 2091 return false; 2092 } 2093 if (!is_writeable($v_header['filename'])) { 2094 $this->_error( 2095 'File ' . $v_header['filename'] 2096 . ' already exists and is write protected' 2097 ); 2098 return false; 2099 } 2100 if (filemtime($v_header['filename']) > $v_header['mtime']) { 2101 // To be completed : An error or silent no replace ? 2102 } 2103 } // ----- Check the directory availability and create it if necessary 2104 elseif (($v_result 2105 = $this->_dirCheck( 2106 ($v_header['typeflag'] == "5" 2107 ? $v_header['filename'] 2108 : dirname($v_header['filename'])) 2109 )) != 1 2110 ) { 2111 $this->_error('Unable to create path for ' . $v_header['filename']); 2112 return false; 2113 } 2114 2115 if ($v_extract_file) { 2116 if ($v_header['typeflag'] == "5") { 2117 if (!@file_exists($v_header['filename'])) { 2118 if (!@mkdir($v_header['filename'], 0777)) { 2119 $this->_error( 2120 'Unable to create directory {' 2121 . $v_header['filename'] . '}' 2122 ); 2123 return false; 2124 } 2125 } 2126 } elseif ($v_header['typeflag'] == "2") { 2127 if (strpos(realpath(dirname($v_header['link'])), realpath($p_path)) !== 0) { 2128 $this->_error( 2129 'Out-of-path file extraction {' 2130 . $v_header['filename'] . ' --> ' . 2131 $v_header['link'] . '}' 2132 ); 2133 return false; 2134 } 2135 if (!$p_symlinks) { 2136 $this->_warning('Symbolic links are not allowed. ' 2137 . 'Unable to extract {' 2138 . $v_header['filename'] . '}' 2139 ); 2140 return false; 2141 } 2142 if (@file_exists($v_header['filename'])) { 2143 @unlink($v_header['filename']); 2144 } 2145 if (!@symlink($v_header['link'], $v_header['filename'])) { 2146 $this->_error( 2147 'Unable to extract symbolic link {' 2148 . $v_header['filename'] . '}' 2149 ); 2150 return false; 2151 } 2152 } else { 2153 if (($v_dest_file = @fopen($v_header['filename'], "wb")) == 0) { 2154 $this->_error( 2155 'Error while opening {' . $v_header['filename'] 2156 . '} in write binary mode' 2157 ); 2158 return false; 2159 } else { 2160 $n = floor($v_header['size'] / 512); 2161 for ($i = 0; $i < $n; $i++) { 2162 $v_content = $this->_readBlock(); 2163 fwrite($v_dest_file, $v_content, 512); 2164 } 2165 if (($v_header['size'] % 512) != 0) { 2166 $v_content = $this->_readBlock(); 2167 fwrite($v_dest_file, $v_content, ($v_header['size'] % 512)); 2168 } 2169 2170 @fclose($v_dest_file); 2171 2172 if ($p_preserve) { 2173 @chown($v_header['filename'], $v_header['uid']); 2174 @chgrp($v_header['filename'], $v_header['gid']); 2175 } 2176 2177 // ----- Change the file mode, mtime 2178 @touch($v_header['filename'], $v_header['mtime']); 2179 if ($v_header['mode'] & 0111) { 2180 // make file executable, obey umask 2181 $mode = fileperms($v_header['filename']) | (~umask() & 0111); 2182 @chmod($v_header['filename'], $mode); 2183 } 2184 } 2185 2186 // ----- Check the file size 2187 clearstatcache(); 2188 if (!is_file($v_header['filename'])) { 2189 $this->_error( 2190 'Extracted file ' . $v_header['filename'] 2191 . 'does not exist. Archive may be corrupted.' 2192 ); 2193 return false; 2194 } 2195 2196 $filesize = filesize($v_header['filename']); 2197 if ($filesize != $v_header['size']) { 2198 $this->_error( 2199 'Extracted file ' . $v_header['filename'] 2200 . ' does not have the correct file size \'' 2201 . $filesize 2202 . '\' (' . $v_header['size'] 2203 . ' expected). Archive may be corrupted.' 2204 ); 2205 return false; 2206 } 2207 } 2208 } else { 2209 $this->_jumpBlock(ceil(($v_header['size'] / 512))); 2210 } 2211 } else { 2212 $this->_jumpBlock(ceil(($v_header['size'] / 512))); 2213 } 2214 2215 /* TBC : Seems to be unused ... 2216 if ($this->_compress) 2217 $v_end_of_file = @gzeof($this->_file); 2218 else 2219 $v_end_of_file = @feof($this->_file); 2220 */ 2221 2222 if ($v_listing || $v_extract_file || $v_extraction_stopped) { 2223 // ----- Log extracted files 2224 if (($v_file_dir = dirname($v_header['filename'])) 2225 == $v_header['filename'] 2226 ) { 2227 $v_file_dir = ''; 2228 } 2229 if ((substr($v_header['filename'], 0, 1) == '/') && ($v_file_dir == '')) { 2230 $v_file_dir = '/'; 2231 } 2232 2233 $p_list_detail[$v_nb++] = $v_header; 2234 if (is_array($p_file_list) && (count($p_list_detail) == count($p_file_list))) { 2235 return true; 2236 } 2237 } 2238 } 2239 2240 return true; 2241 } 2242 2243 /** 2244 * @return bool 2245 */ 2246 public function _openAppend() 2247 { 2248 if (filesize($this->_tarname) == 0) { 2249 return $this->_openWrite(); 2250 } 2251 2252 if ($this->_compress) { 2253 $this->_close(); 2254 2255 if (!@rename($this->_tarname, $this->_tarname . ".tmp")) { 2256 $this->_error( 2257 'Error while renaming \'' . $this->_tarname 2258 . '\' to temporary file \'' . $this->_tarname 2259 . '.tmp\'' 2260 ); 2261 return false; 2262 } 2263 2264 if ($this->_compress_type == 'gz') { 2265 $v_temp_tar = @gzopen($this->_tarname . ".tmp", "rb"); 2266 } elseif ($this->_compress_type == 'bz2') { 2267 $v_temp_tar = @bzopen($this->_tarname . ".tmp", "r"); 2268 } elseif ($this->_compress_type == 'lzma2') { 2269 $v_temp_tar = @xzopen($this->_tarname . ".tmp", "r"); 2270 } 2271 2272 2273 if ($v_temp_tar == 0) { 2274 $this->_error( 2275 'Unable to open file \'' . $this->_tarname 2276 . '.tmp\' in binary read mode' 2277 ); 2278 @rename($this->_tarname . ".tmp", $this->_tarname); 2279 return false; 2280 } 2281 2282 if (!$this->_openWrite()) { 2283 @rename($this->_tarname . ".tmp", $this->_tarname); 2284 return false; 2285 } 2286 2287 if ($this->_compress_type == 'gz') { 2288 $end_blocks = 0; 2289 2290 while (!@gzeof($v_temp_tar)) { 2291 $v_buffer = @gzread($v_temp_tar, 512); 2292 if ($v_buffer == ARCHIVE_TAR_END_BLOCK || strlen($v_buffer) == 0) { 2293 $end_blocks++; 2294 // do not copy end blocks, we will re-make them 2295 // after appending 2296 continue; 2297 } elseif ($end_blocks > 0) { 2298 for ($i = 0; $i < $end_blocks; $i++) { 2299 $this->_writeBlock(ARCHIVE_TAR_END_BLOCK); 2300 } 2301 $end_blocks = 0; 2302 } 2303 $v_binary_data = pack("a512", $v_buffer); 2304 $this->_writeBlock($v_binary_data); 2305 } 2306 2307 @gzclose($v_temp_tar); 2308 } elseif ($this->_compress_type == 'bz2') { 2309 $end_blocks = 0; 2310 2311 while (strlen($v_buffer = @bzread($v_temp_tar, 512)) > 0) { 2312 if ($v_buffer == ARCHIVE_TAR_END_BLOCK || strlen($v_buffer) == 0) { 2313 $end_blocks++; 2314 // do not copy end blocks, we will re-make them 2315 // after appending 2316 continue; 2317 } elseif ($end_blocks > 0) { 2318 for ($i = 0; $i < $end_blocks; $i++) { 2319 $this->_writeBlock(ARCHIVE_TAR_END_BLOCK); 2320 } 2321 $end_blocks = 0; 2322 } 2323 $v_binary_data = pack("a512", $v_buffer); 2324 $this->_writeBlock($v_binary_data); 2325 } 2326 2327 @bzclose($v_temp_tar); 2328 } elseif ($this->_compress_type == 'lzma2') { 2329 $end_blocks = 0; 2330 2331 while (strlen($v_buffer = @xzread($v_temp_tar, 512)) > 0) { 2332 if ($v_buffer == ARCHIVE_TAR_END_BLOCK || strlen($v_buffer) == 0) { 2333 $end_blocks++; 2334 // do not copy end blocks, we will re-make them 2335 // after appending 2336 continue; 2337 } elseif ($end_blocks > 0) { 2338 for ($i = 0; $i < $end_blocks; $i++) { 2339 $this->_writeBlock(ARCHIVE_TAR_END_BLOCK); 2340 } 2341 $end_blocks = 0; 2342 } 2343 $v_binary_data = pack("a512", $v_buffer); 2344 $this->_writeBlock($v_binary_data); 2345 } 2346 2347 @xzclose($v_temp_tar); 2348 } 2349 2350 if (!@unlink($this->_tarname . ".tmp")) { 2351 $this->_error( 2352 'Error while deleting temporary file \'' 2353 . $this->_tarname . '.tmp\'' 2354 ); 2355 } 2356 } else { 2357 // ----- For not compressed tar, just add files before the last 2358 // one or two 512 bytes block 2359 if (!$this->_openReadWrite()) { 2360 return false; 2361 } 2362 2363 clearstatcache(); 2364 $v_size = filesize($this->_tarname); 2365 2366 // We might have zero, one or two end blocks. 2367 // The standard is two, but we should try to handle 2368 // other cases. 2369 fseek($this->_file, $v_size - 1024); 2370 if (fread($this->_file, 512) == ARCHIVE_TAR_END_BLOCK) { 2371 fseek($this->_file, $v_size - 1024); 2372 } elseif (fread($this->_file, 512) == ARCHIVE_TAR_END_BLOCK) { 2373 fseek($this->_file, $v_size - 512); 2374 } 2375 } 2376 2377 return true; 2378 } 2379 2380 /** 2381 * @param $p_filelist 2382 * @param string $p_add_dir 2383 * @param string $p_remove_dir 2384 * @return bool 2385 */ 2386 public function _append($p_filelist, $p_add_dir = '', $p_remove_dir = '') 2387 { 2388 if (!$this->_openAppend()) { 2389 return false; 2390 } 2391 2392 if ($this->_addList($p_filelist, $p_add_dir, $p_remove_dir)) { 2393 $this->_writeFooter(); 2394 } 2395 2396 $this->_close(); 2397 2398 return true; 2399 } 2400 2401 /** 2402 * Check if a directory exists and create it (including parent 2403 * dirs) if not. 2404 * 2405 * @param string $p_dir directory to check 2406 * 2407 * @return bool true if the directory exists or was created 2408 */ 2409 public function _dirCheck($p_dir) 2410 { 2411 clearstatcache(); 2412 if ((@is_dir($p_dir)) || ($p_dir == '')) { 2413 return true; 2414 } 2415 2416 $p_parent_dir = dirname($p_dir); 2417 2418 if (($p_parent_dir != $p_dir) && 2419 ($p_parent_dir != '') && 2420 (!$this->_dirCheck($p_parent_dir)) 2421 ) { 2422 return false; 2423 } 2424 2425 if (!@mkdir($p_dir, 0777)) { 2426 $this->_error("Unable to create directory '$p_dir'"); 2427 return false; 2428 } 2429 2430 return true; 2431 } 2432 2433 /** 2434 * Compress path by changing for example "/dir/foo/../bar" to "/dir/bar", 2435 * rand emove double slashes. 2436 * 2437 * @param string $p_dir path to reduce 2438 * 2439 * @return string reduced path 2440 */ 2441 private function _pathReduction($p_dir) 2442 { 2443 $v_result = ''; 2444 2445 // ----- Look for not empty path 2446 if ($p_dir != '') { 2447 // ----- Explode path by directory names 2448 $v_list = explode('/', $p_dir); 2449 2450 // ----- Study directories from last to first 2451 for ($i = sizeof($v_list) - 1; $i >= 0; $i--) { 2452 // ----- Look for current path 2453 if ($v_list[$i] == ".") { 2454 // ----- Ignore this directory 2455 // Should be the first $i=0, but no check is done 2456 } else { 2457 if ($v_list[$i] == "..") { 2458 // ----- Ignore it and ignore the $i-1 2459 $i--; 2460 } else { 2461 if (($v_list[$i] == '') 2462 && ($i != (sizeof($v_list) - 1)) 2463 && ($i != 0) 2464 ) { 2465 // ----- Ignore only the double '//' in path, 2466 // but not the first and last / 2467 } else { 2468 $v_result = $v_list[$i] . ($i != (sizeof($v_list) - 1) ? '/' 2469 . $v_result : ''); 2470 } 2471 } 2472 } 2473 } 2474 } 2475 2476 if (defined('OS_WINDOWS') && OS_WINDOWS) { 2477 $v_result = strtr($v_result, '\\', '/'); 2478 } 2479 2480 return $v_result; 2481 } 2482 2483 /** 2484 * @param $p_path 2485 * @param bool $p_remove_disk_letter 2486 * @return string 2487 */ 2488 public function _translateWinPath($p_path, $p_remove_disk_letter = true) 2489 { 2490 if (defined('OS_WINDOWS') && OS_WINDOWS) { 2491 // ----- Look for potential disk letter 2492 if (($p_remove_disk_letter) 2493 && (($v_position = strpos($p_path, ':')) != false) 2494 ) { 2495 $p_path = substr($p_path, $v_position + 1); 2496 } 2497 // ----- Change potential windows directory separator 2498 if ((strpos($p_path, '\\') > 0) || (substr($p_path, 0, 1) == '\\')) { 2499 $p_path = strtr($p_path, '\\', '/'); 2500 } 2501 } 2502 return $p_path; 2503 } 2504} 2505