source: temp/trunk/html/admin/system/Tar.php @ 5620

Revision 5620, 57.8 KB checked in by kakinaka, 20 years ago (diff)

blank

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