annotate vendor/pear/archive_tar/Archive/Tar.php @ 19:fa3358dc1485 tip

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