comparison core/lib/Drupal/Core/Archiver/ArchiveTar.php @ 0:c75dbcec494b

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