jablonka.czprosek.czf

websvn

Subversion Repositories:
[/] [lib/] [pear/] [Archive/] [Tar.php] - Blame information for rev 5

 

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

Powered by WebSVN 2.2.1