annotate core/lib/Drupal/Core/Archiver/ArchiveTar.php @ 0:4c8ae668cc8c

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