Mercurial > hg > cmmr2012-drupal-site
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 } |