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