Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

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