annotate core/lib/Drupal/Core/Archiver/ArchiveTar.php @ 17:129ea1e6d783

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