source: branches/version-2_13-dev/data/module/Archive/Tar.php @ 23600

Revision 23600, 79.6 KB checked in by kimoto, 10 years ago (diff)

#2609 モジュールのバグfix

XML_Util 1.2.3
Mobile_Detect 2.8.3
Cache_Lite 1.7.16
Archive_Tar 1.3.12

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