/[drupal]/drupal/modules/system/system.tar.inc
ViewVC logotype

Contents of /drupal/modules/system/system.tar.inc

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.2 - (show annotations) (download) (as text)
Mon Aug 17 19:14:41 2009 UTC (3 months, 1 week ago) by webchick
Branch: MAIN
CVS Tags: DRUPAL-7-0-UNSTABLE-9, DRUPAL-7-0-UNSTABLE-10, HEAD
Changes since 1.1: +4 -4 lines
File MIME type: text/x-php
#517814 by jmstacey, justinrandell, pwolanin, drewish, Jody Lynn, aaron, dopry, and c960657: Converted File API to stream wrappers, for enhanced private/public file handling, and the ability to reference other storage mechanisms such as s3:// and flicker://.
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: system.tar.inc,v 1.1 2009/06/17 10:46:49 dries Exp $
40 * @link http://pear.php.net/package/Archive_Tar
41 */
42
43 define ('ARCHIVE_TAR_ATT_SEPARATOR', 90001);
44 define ('ARCHIVE_TAR_END_BLOCK', pack("a512", ''));
45
46 /**
47 * Creates a (compressed) Tar archive
48 *
49 * @author Vincent Blavet <vincent@phpconcept.net>
50 * @version $Revision: 1.1 $
51 * @license http://www.opensource.org/licenses/bsd-license.php New BSD License
52 * @package Archive_Tar
53 */
54 class Archive_Tar
55 {
56 /**
57 * @var string Name of the Tar
58 */
59 var $_tarname='';
60
61 /**
62 * @var boolean if true, the Tar file will be gzipped
63 */
64 var $_compress=false;
65
66 /**
67 * @var string Type of compression : 'none', 'gz' or 'bz2'
68 */
69 var $_compress_type='none';
70
71 /**
72 * @var string Explode separator
73 */
74 var $_separator=' ';
75
76 /**
77 * @var file descriptor
78 */
79 var $_file=0;
80
81 /**
82 * @var string Local Tar name of a remote Tar (http:// or ftp://)
83 */
84 var $_temp_tarname='';
85
86 // {{{ constructor
87 /**
88 * Archive_Tar Class constructor. This flavour of the constructor only
89 * declare a new Archive_Tar object, identifying it by the name of the
90 * tar file.
91 * If the compress argument is set the tar will be read or created as a
92 * gzip or bz2 compressed TAR file.
93 *
94 * @param string $p_tarname The name of the tar archive to create
95 * @param string $p_compress can be null, 'gz' or 'bz2'. This
96 * parameter indicates if gzip or bz2 compression
97 * is required. For compatibility reason the
98 * boolean value 'true' means 'gz'.
99 * @access public
100 */
101 function __construct($p_tarname, $p_compress = null)
102 {
103 $this->_compress = false;
104 $this->_compress_type = 'none';
105 if (($p_compress === null) || ($p_compress == '')) {
106 if (@file_exists($p_tarname)) {
107 if ($fp = @fopen($p_tarname, "rb")) {
108 // look for gzip magic cookie
109 $data = fread($fp, 2);
110 fclose($fp);
111 if ($data == "\37\213") {
112 $this->_compress = true;
113 $this->_compress_type = 'gz';
114 // No sure it's enought for a magic code ....
115 } elseif ($data == "BZ") {
116 $this->_compress = true;
117 $this->_compress_type = 'bz2';
118 }
119 }
120 } else {
121 // probably a remote file or some file accessible
122 // through a stream interface
123 if (substr($p_tarname, -2) == 'gz') {
124 $this->_compress = true;
125 $this->_compress_type = 'gz';
126 } elseif ((substr($p_tarname, -3) == 'bz2') ||
127 (substr($p_tarname, -2) == 'bz')) {
128 $this->_compress = true;
129 $this->_compress_type = 'bz2';
130 }
131 }
132 } else {
133 if (($p_compress === true) || ($p_compress == 'gz')) {
134 $this->_compress = true;
135 $this->_compress_type = 'gz';
136 } else if ($p_compress == 'bz2') {
137 $this->_compress = true;
138 $this->_compress_type = 'bz2';
139 } else {
140 die("Unsupported compression type '$p_compress'\n".
141 "Supported types are 'gz' and 'bz2'.\n");
142 return false;
143 }
144 }
145 $this->_tarname = $p_tarname;
146 if ($this->_compress) { // assert zlib or bz2 extension support
147 if ($this->_compress_type == 'gz')
148 $extname = 'zlib';
149 else if ($this->_compress_type == 'bz2')
150 $extname = 'bz2';
151
152 if (!extension_loaded($extname)) {
153 $this->loadExtension($extname);
154 }
155 if (!extension_loaded($extname)) {
156 die("The extension '$extname' couldn't be found.\n".
157 "Please make sure your version of PHP was built ".
158 "with '$extname' support.\n");
159 return false;
160 }
161 }
162 }
163 // }}}
164
165 /**
166 * OS independant PHP extension load. Remember to take care
167 * on the correct extension name for case sensitive OSes.
168 *
169 * @param string $ext The extension name
170 * @return bool Success or not on the dl() call
171 */
172 function loadExtension($ext)
173 {
174 if (!extension_loaded($ext)) {
175 // if either returns true dl() will produce a FATAL error, stop that
176 if ((ini_get('enable_dl') != 1) || (ini_get('safe_mode') == 1)) {
177 return false;
178 }
179
180 if (OS_WINDOWS) {
181 $suffix = '.dll';
182 } elseif (PHP_OS == 'HP-UX') {
183 $suffix = '.sl';
184 } elseif (PHP_OS == 'AIX') {
185 $suffix = '.a';
186 } elseif (PHP_OS == 'OSX') {
187 $suffix = '.bundle';
188 } else {
189 $suffix = '.so';
190 }
191
192 return @dl('php_'.$ext.$suffix) || @dl($ext.$suffix);
193 }
194
195 return true;
196 }
197
198
199 // {{{ destructor
200 function __destruct()
201 {
202 $this->_close();
203 // ----- Look for a local copy to delete
204 if ($this->_temp_tarname != '')
205 @unlink($this->_temp_tarname);
206 }
207 // }}}
208
209 // {{{ create()
210 /**
211 * This method creates the archive file and add the files / directories
212 * that are listed in $p_filelist.
213 * If a file with the same name exist and is writable, it is replaced
214 * by the new tar.
215 * The method return false and a PEAR error text.
216 * The $p_filelist parameter can be an array of string, each string
217 * representing a filename or a directory name with their path if
218 * needed. It can also be a single string with names separated by a
219 * single blank.
220 * For each directory added in the archive, the files and
221 * sub-directories are also added.
222 * See also createModify() method for more details.
223 *
224 * @param array $p_filelist An array of filenames and directory names, or a
225 * single string with names separated by a single
226 * blank space.
227 * @return true on success, false on error.
228 * @see createModify()
229 * @access public
230 */
231 function create($p_filelist)
232 {
233 return $this->createModify($p_filelist, '', '');
234 }
235 // }}}
236
237 // {{{ add()
238 /**
239 * This method add the files / directories that are listed in $p_filelist in
240 * the archive. If the archive does not exist it is created.
241 * The method return false and a PEAR error text.
242 * The files and directories listed are only added at the end of the archive,
243 * even if a file with the same name is already archived.
244 * See also createModify() method for more details.
245 *
246 * @param array $p_filelist An array of filenames and directory names, or a
247 * single string with names separated by a single
248 * blank space.
249 * @return true on success, false on error.
250 * @see createModify()
251 * @access public
252 */
253 function add($p_filelist)
254 {
255 return $this->addModify($p_filelist, '', '');
256 }
257 // }}}
258
259 // {{{ extract()
260 function extract($p_path='')
261 {
262 return $this->extractModify($p_path, '');
263 }
264 // }}}
265
266 // {{{ listContent()
267 function listContent()
268 {
269 $v_list_detail = array();
270
271 if ($this->_openRead()) {
272 if (!$this->_extractList('', $v_list_detail, "list", '', '')) {
273 unset($v_list_detail);
274 $v_list_detail = 0;
275 }
276 $this->_close();
277 }
278
279 return $v_list_detail;
280 }
281 // }}}
282
283 // {{{ createModify()
284 /**
285 * This method creates the archive file and add the files / directories
286 * that are listed in $p_filelist.
287 * If the file already exists and is writable, it is replaced by the
288 * new tar. It is a create and not an add. If the file exists and is
289 * read-only or is a directory it is not replaced. The method return
290 * false and a PEAR error text.
291 * The $p_filelist parameter can be an array of string, each string
292 * representing a filename or a directory name with their path if
293 * needed. It can also be a single string with names separated by a
294 * single blank.
295 * The path indicated in $p_remove_dir will be removed from the
296 * memorized path of each file / directory listed when this path
297 * exists. By default nothing is removed (empty path '')
298 * The path indicated in $p_add_dir will be added at the beginning of
299 * the memorized path of each file / directory listed. However it can
300 * be set to empty ''. The adding of a path is done after the removing
301 * of path.
302 * The path add/remove ability enables the user to prepare an archive
303 * for extraction in a different path than the origin files are.
304 * See also addModify() method for file adding properties.
305 *
306 * @param array $p_filelist An array of filenames and directory names,
307 * or a single string with names separated by
308 * a single blank space.
309 * @param string $p_add_dir A string which contains a path to be added
310 * to the memorized path of each element in
311 * the list.
312 * @param string $p_remove_dir A string which contains a path to be
313 * removed from the memorized path of each
314 * element in the list, when relevant.
315 * @return boolean true on success, false on error.
316 * @access public
317 * @see addModify()
318 */
319 function createModify($p_filelist, $p_add_dir, $p_remove_dir='')
320 {
321 $v_result = true;
322
323 if (!$this->_openWrite())
324 return false;
325
326 if ($p_filelist != '') {
327 if (is_array($p_filelist))
328 $v_list = $p_filelist;
329 elseif (is_string($p_filelist))
330 $v_list = explode($this->_separator, $p_filelist);
331 else {
332 $this->_cleanFile();
333 $this->_error('Invalid file list');
334 return false;
335 }
336
337 $v_result = $this->_addList($v_list, $p_add_dir, $p_remove_dir);
338 }
339
340 if ($v_result) {
341 $this->_writeFooter();
342 $this->_close();
343 } else
344 $this->_cleanFile();
345
346 return $v_result;
347 }
348 // }}}
349
350 // {{{ addModify()
351 /**
352 * This method add the files / directories listed in $p_filelist at the
353 * end of the existing archive. If the archive does not yet exists it
354 * is created.
355 * The $p_filelist parameter can be an array of string, each string
356 * representing a filename or a directory name with their path if
357 * needed. It can also be a single string with names separated by a
358 * single blank.
359 * The path indicated in $p_remove_dir will be removed from the
360 * memorized path of each file / directory listed when this path
361 * exists. By default nothing is removed (empty path '')
362 * The path indicated in $p_add_dir will be added at the beginning of
363 * the memorized path of each file / directory listed. However it can
364 * be set to empty ''. The adding of a path is done after the removing
365 * of path.
366 * The path add/remove ability enables the user to prepare an archive
367 * for extraction in a different path than the origin files are.
368 * If a file/dir is already in the archive it will only be added at the
369 * end of the archive. There is no update of the existing archived
370 * file/dir. However while extracting the archive, the last file will
371 * replace the first one. This results in a none optimization of the
372 * archive size.
373 * If a file/dir does not exist the file/dir is ignored. However an
374 * error text is send to PEAR error.
375 * If a file/dir is not readable the file/dir is ignored. However an
376 * error text is send to PEAR error.
377 *
378 * @param array $p_filelist An array of filenames and directory
379 * names, or a single string with names
380 * separated by a single blank space.
381 * @param string $p_add_dir A string which contains a path to be
382 * added to the memorized path of each
383 * element in the list.
384 * @param string $p_remove_dir A string which contains a path to be
385 * removed from the memorized path of
386 * each element in the list, when
387 * relevant.
388 * @return true on success, false on error.
389 * @access public
390 */
391 function addModify($p_filelist, $p_add_dir, $p_remove_dir='')
392 {
393 $v_result = true;
394
395 if (!$this->_isArchive())
396 $v_result = $this->createModify($p_filelist, $p_add_dir,
397 $p_remove_dir);
398 else {
399 if (is_array($p_filelist))
400 $v_list = $p_filelist;
401 elseif (is_string($p_filelist))
402 $v_list = explode($this->_separator, $p_filelist);
403 else {
404 $this->_error('Invalid file list');
405 return false;
406 }
407
408 $v_result = $this->_append($v_list, $p_add_dir, $p_remove_dir);
409 }
410
411 return $v_result;
412 }
413 // }}}
414
415 // {{{ addString()
416 /**
417 * This method add a single string as a file at the
418 * end of the existing archive. If the archive does not yet exists it
419 * is created.
420 *
421 * @param string $p_filename A string which contains the full
422 * filename path that will be associated
423 * with the string.
424 * @param string $p_string The content of the file added in
425 * the archive.
426 * @return true on success, false on error.
427 * @access public
428 */
429 function addString($p_filename, $p_string)
430 {
431 $v_result = true;
432
433 if (!$this->_isArchive()) {
434 if (!$this->_openWrite()) {
435 return false;
436 }
437 $this->_close();
438 }
439
440 if (!$this->_openAppend())
441 return false;
442
443 // Need to check the get back to the temporary file ? ....
444 $v_result = $this->_addString($p_filename, $p_string);
445
446 $this->_writeFooter();
447
448 $this->_close();
449
450 return $v_result;
451 }
452 // }}}
453
454 // {{{ extractModify()
455 /**
456 * This method extract all the content of the archive in the directory
457 * indicated by $p_path. When relevant the memorized path of the
458 * files/dir can be modified by removing the $p_remove_path path at the
459 * beginning of the file/dir path.
460 * While extracting a file, if the directory path does not exists it is
461 * created.
462 * While extracting a file, if the file already exists it is replaced
463 * without looking for last modification date.
464 * While extracting a file, if the file already exists and is write
465 * protected, the extraction is aborted.
466 * While extracting a file, if a directory with the same name already
467 * exists, the extraction is aborted.
468 * While extracting a directory, if a file with the same name already
469 * exists, the extraction is aborted.
470 * While extracting a file/directory if the destination directory exist
471 * and is write protected, or does not exist but can not be created,
472 * the extraction is aborted.
473 * If after extraction an extracted file does not show the correct
474 * stored file size, the extraction is aborted.
475 * When the extraction is aborted, a PEAR error text is set and false
476 * is returned. However the result can be a partial extraction that may
477 * need to be manually cleaned.
478 *
479 * @param string $p_path The path of the directory where the
480 * files/dir need to by extracted.
481 * @param string $p_remove_path Part of the memorized path that can be
482 * removed if present at the beginning of
483 * the file/dir path.
484 * @return boolean true on success, false on error.
485 * @access public
486 * @see extractList()
487 */
488 function extractModify($p_path, $p_remove_path)
489 {
490 $v_result = true;
491 $v_list_detail = array();
492
493 if ($v_result = $this->_openRead()) {
494 $v_result = $this->_extractList($p_path, $v_list_detail,
495 "complete", 0, $p_remove_path);
496 $this->_close();
497 }
498
499 return $v_result;
500 }
501 // }}}
502
503 // {{{ extractInString()
504 /**
505 * This method extract from the archive one file identified by $p_filename.
506 * The return value is a string with the file content, or NULL on error.
507 * @param string $p_filename The path of the file to extract in a string.
508 * @return a string with the file content or NULL.
509 * @access public
510 */
511 function extractInString($p_filename)
512 {
513 if ($this->_openRead()) {
514 $v_result = $this->_extractInString($p_filename);
515 $this->_close();
516 } else {
517 $v_result = NULL;
518 }
519
520 return $v_result;
521 }
522 // }}}
523
524 // {{{ extractList()
525 /**
526 * This method extract from the archive only the files indicated in the
527 * $p_filelist. These files are extracted in the current directory or
528 * in the directory indicated by the optional $p_path parameter.
529 * If indicated the $p_remove_path can be used in the same way as it is
530 * used in extractModify() method.
531 * @param array $p_filelist An array of filenames and directory names,
532 * or a single string with names separated
533 * by a single blank space.
534 * @param string $p_path The path of the directory where the
535 * files/dir need to by extracted.
536 * @param string $p_remove_path Part of the memorized path that can be
537 * removed if present at the beginning of
538 * the file/dir path.
539 * @return true on success, false on error.
540 * @access public
541 * @see extractModify()
542 */
543 function extractList($p_filelist, $p_path='', $p_remove_path='')
544 {
545 $v_result = true;
546 $v_list_detail = array();
547
548 if (is_array($p_filelist))
549 $v_list = $p_filelist;
550 elseif (is_string($p_filelist))
551 $v_list = explode($this->_separator, $p_filelist);
552 else {
553 $this->_error('Invalid string list');
554 return false;
555 }
556
557 if ($v_result = $this->_openRead()) {
558 $v_result = $this->_extractList($p_path, $v_list_detail, "partial",
559 $v_list, $p_remove_path);
560 $this->_close();
561 }
562
563 return $v_result;
564 }
565 // }}}
566
567 // {{{ setAttribute()
568 /**
569 * This method set specific attributes of the archive. It uses a variable
570 * list of parameters, in the format attribute code + attribute values :
571 * $arch->setAttribute(ARCHIVE_TAR_ATT_SEPARATOR, ',');
572 * @param mixed $argv variable list of attributes and values
573 * @return true on success, false on error.
574 * @access public
575 */
576 function setAttribute()
577 {
578 $v_result = true;
579
580 // ----- Get the number of variable list of arguments
581 if (($v_size = func_num_args()) == 0) {
582 return true;
583 }
584
585 // ----- Get the arguments
586 $v_att_list = &func_get_args();
587
588 // ----- Read the attributes
589 $i=0;
590 while ($i<$v_size) {
591
592 // ----- Look for next option
593 switch ($v_att_list[$i]) {
594 // ----- Look for options that request a string value
595 case ARCHIVE_TAR_ATT_SEPARATOR :
596 // ----- Check the number of parameters
597 if (($i+1) >= $v_size) {
598 $this->_error('Invalid number of parameters for '
599 .'attribute ARCHIVE_TAR_ATT_SEPARATOR');
600 return false;
601 }
602
603 // ----- Get the value
604 $this->_separator = $v_att_list[$i+1];
605 $i++;
606 break;
607
608 default :
609 $this->_error('Unknow attribute code '.$v_att_list[$i].'');
610 return false;
611 }
612
613 // ----- Next attribute
614 $i++;
615 }
616
617 return $v_result;
618 }
619 // }}}
620
621 // {{{ _error()
622 function _error($p_message)
623 {
624 // ----- To be completed
625 throw new Exception($p_message);
626 }
627 // }}}
628
629 // {{{ _warning()
630 function _warning($p_message)
631 {
632 // ----- To be completed
633 throw new Exception($p_message);
634 }
635 // }}}
636
637 // {{{ _isArchive()
638 function _isArchive($p_filename=NULL)
639 {
640 if ($p_filename == NULL) {
641 $p_filename = $this->_tarname;
642 }
643 clearstatcache();
644 return @is_file($p_filename) && !@is_link($p_filename);
645 }
646 // }}}
647
648 // {{{ _openWrite()
649 function _openWrite()
650 {
651 if ($this->_compress_type == 'gz')
652 $this->_file = @gzopen($this->_tarname, "wb9");
653 else if ($this->_compress_type == 'bz2')
654 $this->_file = @bzopen($this->_tarname, "w");
655 else if ($this->_compress_type == 'none')
656 $this->_file = @fopen($this->_tarname, "wb");
657 else
658 $this->_error('Unknown or missing compression type ('
659 .$this->_compress_type.')');
660
661 if ($this->_file == 0) {
662 $this->_error('Unable to open in write mode \''
663 .$this->_tarname.'\'');
664 return false;
665 }
666
667 return true;
668 }
669 // }}}
670
671 // {{{ _openRead()
672 function _openRead()
673 {
674 if (strtolower(substr($this->_tarname, 0, 7)) == 'http://') {
675
676 // ----- Look if a local copy need to be done
677 if ($this->_temp_tarname == '') {
678 $this->_temp_tarname = uniqid('tar').'.tmp';
679 if (!$v_file_from = @fopen($this->_tarname, 'rb')) {
680 $this->_error('Unable to open in read mode \''
681 .$this->_tarname.'\'');
682 $this->_temp_tarname = '';
683 return false;
684 }
685 if (!$v_file_to = @fopen($this->_temp_tarname, 'wb')) {
686 $this->_error('Unable to open in write mode \''
687 .$this->_temp_tarname.'\'');
688 $this->_temp_tarname = '';
689 return false;
690 }
691 while ($v_data = @fread($v_file_from, 1024))
692 @fwrite($v_file_to, $v_data);
693 @fclose($v_file_from);
694 @fclose($v_file_to);
695 }
696
697 // ----- File to open if the local copy
698 $v_filename = $this->_temp_tarname;
699
700 } else
701 // ----- File to open if the normal Tar file
702 $v_filename = $this->_tarname;
703
704 if ($this->_compress_type == 'gz')
705 $this->_file = @gzopen($v_filename, "rb");
706 else if ($this->_compress_type == 'bz2')
707 $this->_file = @bzopen($v_filename, "r");
708 else if ($this->_compress_type == 'none')
709 $this->_file = @fopen($v_filename, "rb");
710 else
711 $this->_error('Unknown or missing compression type ('
712 .$this->_compress_type.')');
713
714 if ($this->_file == 0) {
715 $this->_error('Unable to open in read mode \''.$v_filename.'\'');
716 return false;
717 }
718
719 return true;
720 }
721 // }}}
722
723 // {{{ _openReadWrite()
724 function _openReadWrite()
725 {
726 if ($this->_compress_type == 'gz')
727 $this->_file = @gzopen($this->_tarname, "r+b");
728 else if ($this->_compress_type == 'bz2') {
729 $this->_error('Unable to open bz2 in read/write mode \''
730 .$this->_tarname.'\' (limitation of bz2 extension)');
731 return false;
732 } else if ($this->_compress_type == 'none')
733 $this->_file = @fopen($this->_tarname, "r+b");
734 else
735 $this->_error('Unknown or missing compression type ('
736 .$this->_compress_type.')');
737
738 if ($this->_file == 0) {
739 $this->_error('Unable to open in read/write mode \''
740 .$this->_tarname.'\'');
741 return false;
742 }
743
744 return true;
745 }
746 // }}}
747
748 // {{{ _close()
749 function _close()
750 {
751 //if (isset($this->_file)) {
752 if (is_resource($this->_file)) {
753 if ($this->_compress_type == 'gz')
754 @gzclose($this->_file);
755 else if ($this->_compress_type == 'bz2')
756 @bzclose($this->_file);
757 else if ($this->_compress_type == 'none')
758 @fclose($this->_file);
759 else
760 $this->_error('Unknown or missing compression type ('
761 .$this->_compress_type.')');
762
763 $this->_file = 0;
764 }
765
766 // ----- Look if a local copy need to be erase
767 // Note that it might be interesting to keep the url for a time : ToDo
768 if ($this->_temp_tarname != '') {
769 @unlink($this->_temp_tarname);
770 $this->_temp_tarname = '';
771 }
772
773 return true;
774 }
775 // }}}
776
777 // {{{ _cleanFile()
778 function _cleanFile()
779 {
780 $this->_close();
781
782 // ----- Look for a local copy
783 if ($this->_temp_tarname != '') {
784 // ----- Remove the local copy but not the remote tarname
785 @unlink($this->_temp_tarname);
786 $this->_temp_tarname = '';
787 } else {
788 // ----- Remove the local tarname file
789 @unlink($this->_tarname);
790 }
791 $this->_tarname = '';
792
793 return true;
794 }
795 // }}}
796
797 // {{{ _writeBlock()
798 function _writeBlock($p_binary_data, $p_len=null)
799 {
800 if (is_resource($this->_file)) {
801 if ($p_len === null) {
802 if ($this->_compress_type == 'gz')
803 @gzputs($this->_file, $p_binary_data);
804 else if ($this->_compress_type == 'bz2')
805 @bzwrite($this->_file, $p_binary_data);
806 else if ($this->_compress_type == 'none')
807 @fputs($this->_file, $p_binary_data);
808 else
809 $this->_error('Unknown or missing compression type ('
810 .$this->_compress_type.')');
811 } else {
812 if ($this->_compress_type == 'gz')
813 @gzputs($this->_file, $p_binary_data, $p_len);
814 else if ($this->_compress_type == 'bz2')
815 @bzwrite($this->_file, $p_binary_data, $p_len);
816 else if ($this->_compress_type == 'none')
817 @fputs($this->_file, $p_binary_data, $p_len);
818 else
819 $this->_error('Unknown or missing compression type ('
820 .$this->_compress_type.')');
821
822 }
823 }
824 return true;
825 }
826 // }}}
827
828 // {{{ _readBlock()
829 function _readBlock()
830 {
831 $v_block = null;
832 if (is_resource($this->_file)) {
833 if ($this->_compress_type == 'gz')
834 $v_block = @gzread($this->_file, 512);
835 else if ($this->_compress_type == 'bz2')
836 $v_block = @bzread($this->_file, 512);
837 else if ($this->_compress_type == 'none')
838 $v_block = @fread($this->_file, 512);
839 else
840 $this->_error('Unknown or missing compression type ('
841 .$this->_compress_type.')');
842 }
843 return $v_block;
844 }
845 // }}}
846
847 // {{{ _jumpBlock()
848 function _jumpBlock($p_len=null)
849 {
850 if (is_resource($this->_file)) {
851 if ($p_len === null)
852 $p_len = 1;
853
854 if ($this->_compress_type == 'gz') {
855 @gzseek($this->_file, gztell($this->_file)+($p_len*512));
856 }
857 else if ($this->_compress_type == 'bz2') {
858 // ----- Replace missing bztell() and bzseek()
859 for ($i=0; $i<$p_len; $i++)
860 $this->_readBlock();
861 } else if ($this->_compress_type == 'none')
862 @fseek($this->_file, ftell($this->_file)+($p_len*512));
863 else
864 $this->_error('Unknown or missing compression type ('
865 .$this->_compress_type.')');
866
867 }
868 return true;
869 }
870 // }}}
871
872 // {{{ _writeFooter()
873 function _writeFooter()
874 {
875 if (is_resource($this->_file)) {
876 // ----- Write the last 0 filled block for end of archive
877 $v_binary_data = pack('a1024', '');
878 $this->_writeBlock($v_binary_data);
879 }
880 return true;
881 }
882 // }}}
883
884 // {{{ _addList()
885 function _addList($p_list, $p_add_dir, $p_remove_dir)
886 {
887 $v_result=true;
888 $v_header = array();
889
890 // ----- Remove potential windows directory separator
891 $p_add_dir = $this->_translateWinPath($p_add_dir);
892 $p_remove_dir = $this->_translateWinPath($p_remove_dir, false);
893
894 if (!$this->_file) {
895 $this->_error('Invalid file descriptor');
896 return false;
897 }
898
899 if (sizeof($p_list) == 0)
900 return true;
901
902 foreach ($p_list as $v_filename) {
903 if (!$v_result) {
904 break;
905 }
906
907 // ----- Skip the current tar name
908 if ($v_filename == $this->_tarname)
909 continue;
910
911 if ($v_filename == '')
912 continue;
913
914 if (!file_exists($v_filename)) {
915 $this->_warning("File '$v_filename' does not exist");
916 continue;
917 }
918
919 // ----- Add the file or directory header
920 if (!$this->_addFile($v_filename, $v_header, $p_add_dir, $p_remove_dir))
921 return false;
922
923 if (@is_dir($v_filename) && !@is_link($v_filename)) {
924 if (!($p_hdir = opendir($v_filename))) {
925 $this->_warning("Directory '$v_filename' can not be read");
926 continue;
927 }
928 while (false !== ($p_hitem = readdir($p_hdir))) {
929 if (($p_hitem != '.') && ($p_hitem != '..')) {
930 if ($v_filename != ".")
931 $p_temp_list[0] = $v_filename.'/'.$p_hitem;
932 else
933 $p_temp_list[0] = $p_hitem;
934
935 $v_result = $this->_addList($p_temp_list,
936 $p_add_dir,
937 $p_remove_dir);
938 }
939 }
940
941 unset($p_temp_list);
942 unset($p_hdir);
943 unset($p_hitem);
944 }
945 }
946
947 return $v_result;
948 }
949 // }}}
950
951 // {{{ _addFile()
952 function _addFile($p_filename, &$p_header, $p_add_dir, $p_remove_dir)
953 {
954 if (!$this->_file) {
955 $this->_error('Invalid file descriptor');
956 return false;
957 }
958
959 if ($p_filename == '') {
960 $this->_error('Invalid file name');
961 return false;
962 }
963
964 // ----- Calculate the stored filename
965 $p_filename = $this->_translateWinPath($p_filename, false);;
966 $v_stored_filename = $p_filename;
967 if (strcmp($p_filename, $p_remove_dir) == 0) {
968 return true;
969 }
970 if ($p_remove_dir != '') {
971 if (substr($p_remove_dir, -1) != '/')
972 $p_remove_dir .= '/';
973
974 if (substr($p_filename, 0, strlen($p_remove_dir)) == $p_remove_dir)
975 $v_stored_filename = substr($p_filename, strlen($p_remove_dir));
976 }
977 $v_stored_filename = $this->_translateWinPath($v_stored_filename);
978 if ($p_add_dir != '') {
979 if (substr($p_add_dir, -1) == '/')
980 $v_stored_filename = $p_add_dir.$v_stored_filename;
981 else
982 $v_stored_filename = $p_add_dir.'/'.$v_stored_filename;
983 }
984
985 $v_stored_filename = $this->_pathReduction($v_stored_filename);
986
987 if ($this->_isArchive($p_filename)) {
988 if (($v_file = @fopen($p_filename, "rb")) == 0) {
989 $this->_warning("Unable to open file '".$p_filename
990 ."' in binary read mode");
991 return true;
992 }
993
994 if (!$this->_writeHeader($p_filename, $v_stored_filename))
995 return false;
996
997 while (($v_buffer = fread($v_file, 512)) != '') {
998 $v_binary_data = pack("a512", "$v_buffer");
999 $this->_writeBlock($v_binary_data);
1000 }
1001
1002 fclose($v_file);
1003
1004 } else {
1005 // ----- Only header for dir
1006 if (!$this->_writeHeader($p_filename, $v_stored_filename))
1007 return false;
1008 }
1009
1010 return true;
1011 }
1012 // }}}
1013
1014 // {{{ _addString()
1015 function _addString($p_filename, $p_string)
1016 {
1017 if (!$this->_file) {
1018 $this->_error('Invalid file descriptor');
1019 return false;
1020 }
1021
1022 if ($p_filename == '') {
1023 $this->_error('Invalid file name');
1024 return false;
1025 }
1026
1027 // ----- Calculate the stored filename
1028 $p_filename = $this->_translateWinPath($p_filename, false);;
1029
1030 if (!$this->_writeHeaderBlock($p_filename, strlen($p_string),
1031 time(), 384, "", 0, 0))
1032 return false;
1033
1034 $i=0;
1035 while (($v_buffer = substr($p_string, (($i++)*512), 512)) != '') {
1036 $v_binary_data = pack("a512", $v_buffer);
1037 $this->_writeBlock($v_binary_data);
1038 }
1039
1040 return true;
1041 }
1042 // }}}
1043
1044 // {{{ _writeHeader()
1045 function _writeHeader($p_filename, $p_stored_filename)
1046 {
1047 if ($p_stored_filename == '')
1048 $p_stored_filename = $p_filename;
1049 $v_reduce_filename = $this->_pathReduction($p_stored_filename);
1050
1051 if (strlen($v_reduce_filename) > 99) {
1052 if (!$this->_writeLongHeader($v_reduce_filename))
1053 return false;
1054 }
1055
1056 $v_info = lstat($p_filename);
1057 $v_uid = sprintf("%6s ", DecOct($v_info[4]));
1058 $v_gid = sprintf("%6s ", DecOct($v_info[5]));
1059 $v_perms = sprintf("%6s ", DecOct($v_info['mode']));
1060
1061 $v_mtime = sprintf("%11s", DecOct($v_info['mode']));
1062
1063 $v_linkname = '';
1064
1065 if (@is_link($p_filename)) {
1066 $v_typeflag = '2';
1067 $v_linkname = readlink($p_filename);
1068 $v_size = sprintf("%11s ", DecOct(0));
1069 } elseif (@is_dir($p_filename)) {
1070 $v_typeflag = "5";
1071 $v_size = sprintf("%11s ", DecOct(0));
1072 } else {
1073 $v_typeflag = '';
1074 clearstatcache();
1075 $v_size = sprintf("%11s ", DecOct($v_info['size']));
1076 }
1077
1078 $v_magic = '';
1079
1080 $v_version = '';
1081
1082 $v_uname = '';
1083
1084 $v_gname = '';
1085
1086 $v_devmajor = '';
1087
1088 $v_devminor = '';
1089
1090 $v_prefix = '';
1091
1092 $v_binary_data_first = pack("a100a8a8a8a12A12",
1093 $v_reduce_filename, $v_perms, $v_uid,
1094 $v_gid, $v_size, $v_mtime);
1095 $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12",
1096 $v_typeflag, $v_linkname, $v_magic,
1097 $v_version, $v_uname, $v_gname,
1098 $v_devmajor, $v_devminor, $v_prefix, '');
1099
1100 // ----- Calculate the checksum
1101 $v_checksum = 0;
1102 // ..... First part of the header
1103 for ($i=0; $i<148; $i++)
1104 $v_checksum += ord(substr($v_binary_data_first,$i,1));
1105 // ..... Ignore the checksum value and replace it by ' ' (space)
1106 for ($i=148; $i<156; $i++)
1107 $v_checksum += ord(' ');
1108 // ..... Last part of the header
1109 for ($i=156, $j=0; $i<512; $i++, $j++)
1110 $v_checksum += ord(substr($v_binary_data_last,$j,1));
1111
1112 // ----- Write the first 148 bytes of the header in the archive
1113 $this->_writeBlock($v_binary_data_first, 148);
1114
1115 // ----- Write the calculated checksum
1116 $v_checksum = sprintf("%6s ", DecOct($v_checksum));
1117 $v_binary_data = pack("a8", $v_checksum);
1118 $this->_writeBlock($v_binary_data, 8);
1119
1120 // ----- Write the last 356 bytes of the header in the archive
1121 $this->_writeBlock($v_binary_data_last, 356);
1122
1123 return true;
1124 }
1125 // }}}
1126
1127 // {{{ _writeHeaderBlock()
1128 function _writeHeaderBlock($p_filename, $p_size, $p_mtime=0, $p_perms=0,
1129 $p_type='', $p_uid=0, $p_gid=0)
1130 {
1131 $p_filename = $this->_pathReduction($p_filename);
1132
1133 if (strlen($p_filename) > 99) {
1134 if (!$this->_writeLongHeader($p_filename))
1135 return false;
1136 }
1137
1138 if ($p_type == "5") {
1139 $v_size = sprintf("%11s ", DecOct(0));
1140 } else {
1141 $v_size = sprintf("%11s ", DecOct($p_size));
1142 }
1143
1144 $v_uid = sprintf("%6s ", DecOct($p_uid));
1145 $v_gid = sprintf("%6s ", DecOct($p_gid));
1146 $v_perms = sprintf("%6s ", DecOct($p_perms));
1147
1148 $v_mtime = sprintf("%11s", DecOct($p_mtime));
1149
1150 $v_linkname = '';
1151
1152 $v_magic = '';
1153
1154 $v_version = '';
1155
1156 $v_uname = '';
1157
1158 $v_gname = '';
1159
1160 $v_devmajor = '';
1161
1162 $v_devminor = '';
1163
1164 $v_prefix = '';
1165
1166 $v_binary_data_first = pack("a100a8a8a8a12A12",
1167 $p_filename, $v_perms, $v_uid, $v_gid,
1168 $v_size, $v_mtime);
1169 $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12",
1170 $p_type, $v_linkname, $v_magic,
1171 $v_version, $v_uname, $v_gname,
1172 $v_devmajor, $v_devminor, $v_prefix, '');
1173
1174 // ----- Calculate the checksum
1175 $v_checksum = 0;
1176 // ..... First part of the header
1177 for ($i=0; $i<148; $i++)
1178 $v_checksum += ord(substr($v_binary_data_first,$i,1));
1179 // ..... Ignore the checksum value and replace it by ' ' (space)
1180 for ($i=148; $i<156; $i++)
1181 $v_checksum += ord(' ');
1182 // ..... Last part of the header
1183 for ($i=156, $j=0; $i<512; $i++, $j++)
1184 $v_checksum += ord(substr($v_binary_data_last,$j,1));
1185
1186 // ----- Write the first 148 bytes of the header in the archive
1187 $this->_writeBlock($v_binary_data_first, 148);
1188
1189 // ----- Write the calculated checksum
1190 $v_checksum = sprintf("%6s ", DecOct($v_checksum));
1191 $v_binary_data = pack("a8", $v_checksum);
1192 $this->_writeBlock($v_binary_data, 8);
1193
1194 // ----- Write the last 356 bytes of the header in the archive
1195 $this->_writeBlock($v_binary_data_last, 356);
1196
1197 return true;
1198 }
1199 // }}}
1200
1201 // {{{ _writeLongHeader()
1202 function _writeLongHeader($p_filename)
1203 {
1204 $v_size = sprintf("%11s ", DecOct(strlen($p_filename)));
1205
1206 $v_typeflag = 'L';
1207
1208 $v_linkname = '';
1209
1210 $v_magic = '';
1211
1212 $v_version = '';
1213
1214 $v_uname = '';
1215
1216 $v_gname = '';
1217
1218 $v_devmajor = '';
1219
1220 $v_devminor = '';
1221
1222 $v_prefix = '';
1223
1224 $v_binary_data_first = pack("a100a8a8a8a12A12",
1225 '././@LongLink', 0, 0, 0, $v_size, 0);
1226 $v_binary_data_last = pack("a1a100a6a2a32a32a8a8a155a12",
1227 $v_typeflag, $v_linkname, $v_magic,
1228 $v_version, $v_uname, $v_gname,
1229 $v_devmajor, $v_devminor, $v_prefix, '');
1230
1231 // ----- Calculate the checksum
1232 $v_checksum = 0;
1233 // ..... First part of the header
1234 for ($i=0; $i<148; $i++)
1235 $v_checksum += ord(substr($v_binary_data_first,$i,1));
1236 // ..... Ignore the checksum value and replace it by ' ' (space)
1237 for ($i=148; $i<156; $i++)
1238 $v_checksum += ord(' ');
1239 // ..... Last part of the header
1240 for ($i=156, $j=0; $i<512; $i++, $j++)
1241 $v_checksum += ord(substr($v_binary_data_last,$j,1));
1242
1243 // ----- Write the first 148 bytes of the header in the archive
1244 $this->_writeBlock($v_binary_data_first, 148);
1245
1246 // ----- Write the calculated checksum
1247 $v_checksum = sprintf("%6s ", DecOct($v_checksum));
1248 $v_binary_data = pack("a8", $v_checksum);
1249 $this->_writeBlock($v_binary_data, 8);
1250
1251 // ----- Write the last 356 bytes of the header in the archive
1252 $this->_writeBlock($v_binary_data_last, 356);
1253
1254 // ----- Write the filename as content of the block
1255 $i=0;
1256 while (($v_buffer = substr($p_filename, (($i++)*512), 512)) != '') {
1257 $v_binary_data = pack("a512", "$v_buffer");
1258 $this->_writeBlock($v_binary_data);
1259 }
1260
1261 return true;
1262 }
1263 // }}}
1264
1265 // {{{ _readHeader()
1266 function _readHeader($v_binary_data, &$v_header)
1267 {
1268 if (strlen($v_binary_data)==0) {
1269 $v_header['filename'] = '';
1270 return true;
1271 }
1272
1273 if (strlen($v_binary_data) != 512) {
1274 $v_header['filename'] = '';
1275 $this->_error('Invalid block size : '.strlen($v_binary_data));
1276 return false;
1277 }
1278
1279 if (!is_array($v_header)) {
1280 $v_header = array();
1281 }
1282 // ----- Calculate the checksum
1283 $v_checksum = 0;
1284 // ..... First part of the header
1285 for ($i=0; $i<148; $i++)
1286 $v_checksum+=ord(substr($v_binary_data,$i,1));
1287