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