source: temp/test-xoops.ec-cube.net/html/class/class.tar.php @ 405

Revision 405, 18.4 KB checked in by root, 20 years ago (diff)
Line 
1<?php
2// $Id: class.tar.php,v 1.2 2005/03/18 12:51:55 onokazu Exp $
3/*
4    package::i.tools
5
6    php-downloader  v1.0    -   www.ipunkt.biz
7
8    (c) 2002 - www.ipunkt.biz (rok)
9*/
10
11/*
12=======================================================================
13Name:
14    tar Class
15
16Author:
17    Josh Barger <[email protected]>
18
19Description:
20    This class reads and writes Tape-Archive (TAR) Files and Gzip
21    compressed TAR files, which are mainly used on UNIX systems.
22    This class works on both windows AND unix systems, and does
23    NOT rely on external applications!! Woohoo!
24
25Usage:
26    Copyright (C) 2002  Josh Barger
27
28    This library is free software; you can redistribute it and/or
29    modify it under the terms of the GNU Lesser General Public
30    License as published by the Free Software Foundation; either
31    version 2.1 of the License, or (at your option) any later version.
32
33    This library is distributed in the hope that it will be useful,
34    but WITHOUT ANY WARRANTY; without even the implied warranty of
35    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
36    Lesser General Public License for more details at:
37        http://www.gnu.org/copyleft/lesser.html
38
39    If you use this script in your application/website, please
40    send me an e-mail letting me know about it :)
41
42Bugs:
43    Please report any bugs you might find to my e-mail address
44    at [email protected].  If you have already created a fix/patch
45    for the bug, please do send it to me so I can incorporate it into my release.
46
47Version History:
48    1.0 04/10/2002  - InitialRelease
49
50    2.0 04/11/2002  - Merged both tarReader and tarWriter
51                  classes into one
52                - Added support for gzipped tar files
53                  Remember to name for .tar.gz or .tgz
54                  if you use gzip compression!
55                  :: THIS REQUIRES ZLIB EXTENSION ::
56                - Added additional comments to
57                  functions to help users
58                - Added ability to remove files and
59                  directories from archive
60    2.1 04/12/2002  - Fixed serious bug in generating tar
61                - Created another example file
62                - Added check to make sure ZLIB is
63                  installed before running GZIP
64                  compression on TAR
65    2.2 05/07/2002  - Added automatic detection of Gzipped
66                  tar files (Thanks go to Jidgen Falch
67                  for the idea)
68                - Changed "private" functions to have
69                  special function names beginning with
70                  two underscores
71=======================================================================
72XOOPS changes onokazu <[email protected]>
73
74    12/25/2002 - Added flag to addFile() function for binary files
75
76=======================================================================
77*/
78
79/**
80 * tar Class
81 *
82 * This class reads and writes Tape-Archive (TAR) Files and Gzip
83 * compressed TAR files, which are mainly used on UNIX systems.
84 * This class works on both windows AND unix systems, and does
85 * NOT rely on external applications!! Woohoo!
86 *
87 * @author  Josh Barger <[email protected]>
88 * @copyright   Copyright (C) 2002  Josh Barger
89 *
90 * @package     kernel
91 * @subpackage  core
92 */
93class tar
94{
95    /**#@+
96     * Unprocessed Archive Information
97     */
98    var $filename;
99    var $isGzipped;
100    var $tar_file;
101    /**#@-*/
102
103    /**#@+
104     * Processed Archive Information
105     */
106    var $files;
107    var $directories;
108    var $numFiles;
109    var $numDirectories;
110    /**#@-*/
111
112
113    /**
114     * Class Constructor -- Does nothing...
115     */
116    function tar()
117    {
118        return true;
119    }
120
121    /**
122     * Computes the unsigned Checksum of a file's header
123     * to try to ensure valid file
124     *
125     * @param   string  $bytestring
126     *
127     * @access  private
128     */
129    function __computeUnsignedChecksum($bytestring)
130    {
131        $unsigned_chksum = '';
132        for($i=0; $i<512; $i++)
133            $unsigned_chksum += ord($bytestring[$i]);
134        for($i=0; $i<8; $i++)
135            $unsigned_chksum -= ord($bytestring[148 + $i]);
136        $unsigned_chksum += ord(" ") * 8;
137
138        return $unsigned_chksum;
139    }
140
141
142    /**
143     * Converts a NULL padded string to a non-NULL padded string
144     *
145     * @param   string  $string
146     *
147     * @return  string
148     *
149     * @access  private
150     **/
151    function __parseNullPaddedString($string)
152    {
153        $position = strpos($string,chr(0));
154        return substr($string,0,$position);
155    }
156
157    /**
158     * This function parses the current TAR file
159     *
160     * @return  bool    always TRUE
161     *
162     * @access  private
163     **/
164    function __parseTar()
165    {
166        // Read Files from archive
167        $tar_length = strlen($this->tar_file);
168        $main_offset = 0;
169        $this->numFiles = 0;
170        while ( $main_offset < $tar_length ) {
171            // If we read a block of 512 nulls, we are at the end of the archive
172            if(substr($this->tar_file,$main_offset,512) == str_repeat(chr(0),512))
173                break;
174
175            // Parse file name
176            $file_name      = $this->__parseNullPaddedString(substr($this->tar_file,$main_offset,100));
177
178            // Parse the file mode
179            $file_mode      = substr($this->tar_file,$main_offset + 100,8);
180
181            // Parse the file user ID
182            $file_uid       = octdec(substr($this->tar_file,$main_offset + 108,8));
183
184            // Parse the file group ID
185            $file_gid       = octdec(substr($this->tar_file,$main_offset + 116,8));
186
187            // Parse the file size
188            $file_size      = octdec(substr($this->tar_file,$main_offset + 124,12));
189
190            // Parse the file update time - unix timestamp format
191            $file_time      = octdec(substr($this->tar_file,$main_offset + 136,12));
192
193            // Parse Checksum
194            $file_chksum        = octdec(substr($this->tar_file,$main_offset + 148,6));
195
196            // Parse user name
197            $file_uname     = $this->__parseNullPaddedString(substr($this->tar_file,$main_offset + 265,32));
198
199            // Parse Group name
200            $file_gname     = $this->__parseNullPaddedString(substr($this->tar_file,$main_offset + 297,32));
201
202            // Make sure our file is valid
203            if($this->__computeUnsignedChecksum(substr($this->tar_file,$main_offset,512)) != $file_chksum)
204                return false;
205
206            // Parse File Contents
207            $file_contents      = substr($this->tar_file,$main_offset + 512,$file_size);
208
209            /*  ### Unused Header Information ###
210                $activeFile["typeflag"]     = substr($this->tar_file,$main_offset + 156,1);
211                $activeFile["linkname"]     = substr($this->tar_file,$main_offset + 157,100);
212                $activeFile["magic"]        = substr($this->tar_file,$main_offset + 257,6);
213                $activeFile["version"]      = substr($this->tar_file,$main_offset + 263,2);
214                $activeFile["devmajor"]     = substr($this->tar_file,$main_offset + 329,8);
215                $activeFile["devminor"]     = substr($this->tar_file,$main_offset + 337,8);
216                $activeFile["prefix"]       = substr($this->tar_file,$main_offset + 345,155);
217                $activeFile["endheader"]    = substr($this->tar_file,$main_offset + 500,12);
218            */
219
220            if ( $file_size > 0 ) {
221                // Increment number of files
222                $this->numFiles++;
223
224                // Create us a new file in our array
225                $activeFile = &$this->files[];
226
227                // Asign Values
228                $activeFile["name"]     = $file_name;
229                $activeFile["mode"]     = $file_mode;
230                $activeFile["size"]     = $file_size;
231                $activeFile["time"]     = $file_time;
232                $activeFile["user_id"]      = $file_uid;
233                $activeFile["group_id"]     = $file_gid;
234                $activeFile["user_name"]    = $file_uname;
235                $activeFile["group_name"]   = $file_gname;
236                $activeFile["checksum"]     = $file_chksum;
237                $activeFile["file"]     = $file_contents;
238            } else {
239                // Increment number of directories
240                $this->numDirectories++;
241
242                // Create a new directory in our array
243                $activeDir = &$this->directories[];
244
245                // Assign values
246                $activeDir["name"]      = $file_name;
247                $activeDir["mode"]      = $file_mode;
248                $activeDir["time"]      = $file_time;
249                $activeDir["user_id"]       = $file_uid;
250                $activeDir["group_id"]      = $file_gid;
251                $activeDir["user_name"]     = $file_uname;
252                $activeDir["group_name"]    = $file_gname;
253                $activeDir["checksum"]      = $file_chksum;
254            }
255
256            // Move our offset the number of blocks we have processed
257            $main_offset += 512 + (ceil($file_size / 512) * 512);
258        }
259
260        return true;
261    }
262
263    /**
264     * Read a non gzipped tar file in for processing.
265     *
266     * @param   string  $filename   full filename
267     * @return  bool    always TRUE
268     *
269     * @access  private
270     **/
271    function __readTar($filename='')
272    {
273        // Set the filename to load
274        if(!$filename)
275            $filename = $this->filename;
276
277        // Read in the TAR file
278        $fp = fopen($filename,"rb");
279        $this->tar_file = fread($fp,filesize($filename));
280        fclose($fp);
281
282        if($this->tar_file[0] == chr(31) && $this->tar_file[1] == chr(139) && $this->tar_file[2] == chr(8)) {
283            if(!function_exists("gzinflate"))
284                return false;
285
286            $this->isGzipped = true;
287
288            $this->tar_file = gzinflate(substr($this->tar_file,10,-4));
289        }
290
291        // Parse the TAR file
292        $this->__parseTar();
293
294        return true;
295    }
296
297    /**
298     * Generates a TAR file from the processed data
299     *
300     * @return  bool    always TRUE
301     *
302     * @access  private
303     **/
304    function __generateTAR()
305    {
306        // Clear any data currently in $this->tar_file
307        unset($this->tar_file);
308
309        // Generate Records for each directory, if we have directories
310        if($this->numDirectories > 0) {
311            foreach($this->directories as $key => $information) {
312                unset($header);
313
314                // Generate tar header for this directory
315                // Filename, Permissions, UID, GID, size, Time, checksum, typeflag, linkname, magic, version, user name, group name, devmajor, devminor, prefix, end
316                $header .= str_pad($information["name"],100,chr(0));
317                $header .= str_pad(decoct($information["mode"]),7,"0",STR_PAD_LEFT) . chr(0);
318                $header .= str_pad(decoct($information["user_id"]),7,"0",STR_PAD_LEFT) . chr(0);
319                $header .= str_pad(decoct($information["group_id"]),7,"0",STR_PAD_LEFT) . chr(0);
320                $header .= str_pad(decoct(0),11,"0",STR_PAD_LEFT) . chr(0);
321                $header .= str_pad(decoct($information["time"]),11,"0",STR_PAD_LEFT) . chr(0);
322                $header .= str_repeat(" ",8);
323                $header .= "5";
324                $header .= str_repeat(chr(0),100);
325                $header .= str_pad("ustar",6,chr(32));
326                $header .= chr(32) . chr(0);
327                $header .= str_pad("",32,chr(0));
328                $header .= str_pad("",32,chr(0));
329                $header .= str_repeat(chr(0),8);
330                $header .= str_repeat(chr(0),8);
331                $header .= str_repeat(chr(0),155);
332                $header .= str_repeat(chr(0),12);
333
334                // Compute header checksum
335                $checksum = str_pad(decoct($this->__computeUnsignedChecksum($header)),6,"0",STR_PAD_LEFT);
336                for($i=0; $i<6; $i++) {
337                    $header[(148 + $i)] = substr($checksum,$i,1);
338                }
339                $header[154] = chr(0);
340                $header[155] = chr(32);
341
342                // Add new tar formatted data to tar file contents
343                $this->tar_file .= $header;
344            }
345        }
346
347        // Generate Records for each file, if we have files (We should...)
348        if($this->numFiles > 0) {
349            $this->tar_file = '';
350            foreach($this->files as $key => $information) {
351                unset($header);
352
353                // Generate the TAR header for this file
354                // Filename, Permissions, UID, GID, size, Time, checksum, typeflag, linkname, magic, version, user name, group name, devmajor, devminor, prefix, end
355                $header = str_pad($information["name"],100,chr(0));
356                $header .= str_pad(decoct($information["mode"]),7,"0",STR_PAD_LEFT) . chr(0);
357                $header .= str_pad(decoct($information["user_id"]),7,"0",STR_PAD_LEFT) . chr(0);
358                $header .= str_pad(decoct($information["group_id"]),7,"0",STR_PAD_LEFT) . chr(0);
359                $header .= str_pad(decoct($information["size"]),11,"0",STR_PAD_LEFT) . chr(0);
360                $header .= str_pad(decoct($information["time"]),11,"0",STR_PAD_LEFT) . chr(0);
361                $header .= str_repeat(" ",8);
362                $header .= "0";
363                $header .= str_repeat(chr(0),100);
364                $header .= str_pad("ustar",6,chr(32));
365                $header .= chr(32) . chr(0);
366                $header .= str_pad($information["user_name"],32,chr(0));    // How do I get a file's user name from PHP?
367                $header .= str_pad($information["group_name"],32,chr(0));   // How do I get a file's group name from PHP?
368                $header .= str_repeat(chr(0),8);
369                $header .= str_repeat(chr(0),8);
370                $header .= str_repeat(chr(0),155);
371                $header .= str_repeat(chr(0),12);
372
373                // Compute header checksum
374                $checksum = str_pad(decoct($this->__computeUnsignedChecksum($header)),6,"0",STR_PAD_LEFT);
375                for($i=0; $i<6; $i++) {
376                    $header[(148 + $i)] = substr($checksum,$i,1);
377                }
378                $header[154] = chr(0);
379                $header[155] = chr(32);
380
381                // Pad file contents to byte count divisible by 512
382                $file_contents = str_pad($information["file"],(ceil($information["size"] / 512) * 512),chr(0));
383
384                // Add new tar formatted data to tar file contents
385                $this->tar_file .= $header . $file_contents;
386            }
387        }
388
389        // Add 512 bytes of NULLs to designate EOF
390        $this->tar_file .= str_repeat(chr(0),512);
391
392        return true;
393    }
394
395
396    /**
397     * Open a TAR file
398     *
399     * @param   string  $filename
400     * @return  bool
401     **/
402    function openTAR($filename)
403    {
404        // Clear any values from previous tar archives
405        unset($this->filename);
406        unset($this->isGzipped);
407        unset($this->tar_file);
408        unset($this->files);
409        unset($this->directories);
410        unset($this->numFiles);
411        unset($this->numDirectories);
412
413        // If the tar file doesn't exist...
414        if(!file_exists($filename))
415            return false;
416
417        $this->filename = $filename;
418
419        // Parse this file
420        $this->__readTar();
421
422        return true;
423    }
424
425    /**
426     * Appends a tar file to the end of the currently opened tar file.
427     *
428     * @param   string  $filename
429     * @return  bool
430     **/
431    function appendTar($filename)
432    {
433        // If the tar file doesn't exist...
434        if(!file_exists($filename))
435            return false;
436
437        $this->__readTar($filename);
438
439        return true;
440    }
441
442    /**
443     * Retrieves information about a file in the current tar archive
444     *
445     * @param   string  $filename
446     * @return  string  FALSE on fail
447     **/
448    function getFile($filename)
449    {
450        if ( $this->numFiles > 0 ) {
451            foreach($this->files as $key => $information) {
452                if($information["name"] == $filename)
453                    return $information;
454            }
455        }
456
457        return false;
458    }
459
460    /**
461     * Retrieves information about a directory in the current tar archive
462     *
463     * @param   string  $dirname
464     * @return  string  FALSE on fail
465     **/
466    function getDirectory($dirname)
467    {
468        if($this->numDirectories > 0) {
469            foreach($this->directories as $key => $information) {
470                if($information["name"] == $dirname)
471                    return $information;
472            }
473        }
474
475        return false;
476    }
477
478    /**
479     * Check if this tar archive contains a specific file
480     *
481     * @param   string  $filename
482     * @return  bool
483     **/
484    function containsFile($filename)
485    {
486        if ( $this->numFiles > 0 ) {
487            foreach($this->files as $key => $information) {
488                if($information["name"] == $filename)
489                    return true;
490            }
491        }
492        return false;
493    }
494
495    /**
496     * Check if this tar archive contains a specific directory
497     *
498     * @param   string  $dirname
499     * @return  bool
500     **/
501    function containsDirectory($dirname)
502    {
503        if ( $this->numDirectories > 0 ) {
504            foreach ( $this->directories as $key => $information ) {
505                if ( $information["name"] == $dirname ) {
506                    return true;
507                }
508            }
509        }
510        return false;
511    }
512
513    /**
514     * Add a directory to this tar archive
515     *
516     * @param   string  $dirname
517     * @return  bool
518     **/
519    function addDirectory($dirname)
520    {
521        if ( !file_exists($dirname) ) {
522            return false;
523        }
524
525        // Get directory information
526        $file_information = stat($dirname);
527
528        // Add directory to processed data
529        $this->numDirectories++;
530        $activeDir      = &$this->directories[];
531        $activeDir["name"]  = $dirname;
532        $activeDir["mode"]  = $file_information["mode"];
533        $activeDir["time"]  = $file_information["time"];
534        $activeDir["user_id"]   = $file_information["uid"];
535        $activeDir["group_id"]  = $file_information["gid"];
536        $activeDir["checksum"]  = $checksum;
537
538        return true;
539    }
540
541    /**
542     * Add a file to the tar archive
543     *
544     * @param   string  $filename
545     * @param   boolean $binary     Binary file?
546     * @return  bool
547     **/
548    function addFile($filename, $binary = false)
549    {
550        // Make sure the file we are adding exists!
551        if ( !file_exists($filename) ) {
552            return false;
553        }
554
555        // Make sure there are no other files in the archive that have this same filename
556        if ( $this->containsFile($filename) ) {
557            return false;
558        }
559
560        // Get file information
561        $file_information = stat($filename);
562
563        // Read in the file's contents
564        if (!$binary) {
565            $fp = fopen($filename, "r");
566        } else {
567            $fp = fopen($filename, "rb");
568        }
569        $file_contents = fread($fp,filesize($filename));
570        fclose($fp);
571
572        // Add file to processed data
573        $this->numFiles++;
574        $activeFile         = &$this->files[];
575        $activeFile["name"]     = $filename;
576        $activeFile["mode"]     = $file_information["mode"];
577        $activeFile["user_id"]      = $file_information["uid"];
578        $activeFile["group_id"]     = $file_information["gid"];
579        $activeFile["size"]     = $file_information["size"];
580        $activeFile["time"]     = $file_information["mtime"];
581        $activeFile["checksum"]     = isset($checksum) ? $checksum : '';
582        $activeFile["user_name"]    = "";
583        $activeFile["group_name"]   = "";
584        $activeFile["file"]     = trim($file_contents);
585
586        return true;
587    }
588
589    /**
590     * Remove a file from the tar archive
591     *
592     * @param   string  $filename
593     * @return  bool
594     **/
595    function removeFile($filename)
596    {
597        if ( $this->numFiles > 0 ) {
598            foreach ( $this->files as $key => $information ) {
599                if ( $information["name"] == $filename ) {
600                    $this->numFiles--;
601                    unset($this->files[$key]);
602                    return true;
603                }
604            }
605        }
606        return false;
607    }
608
609    /**
610     * Remove a directory from the tar archive
611     *
612     * @param   string  $dirname
613     * @return  bool
614     **/
615    function removeDirectory($dirname)
616    {
617        if ( $this->numDirectories > 0 ) {
618            foreach ( $this->directories as $key => $information ) {
619                if ( $information["name"] == $dirname ) {
620                    $this->numDirectories--;
621                    unset($this->directories[$key]);
622                    return true;
623                }
624            }
625        }
626        return false;
627    }
628
629    /**
630     * Write the currently loaded tar archive to disk
631     *
632     * @return  bool
633     **/
634    function saveTar()
635    {
636        if ( !$this->filename ) {
637            return false;
638        }
639
640        // Write tar to current file using specified gzip compression
641        $this->toTar($this->filename,$this->isGzipped);
642
643        return true;
644    }
645
646    /**
647     * Saves tar archive to a different file than the current file
648     *
649     * @param   string  $filename
650     * @param   bool    $useGzip    Use GZ compression?
651     * @return  bool
652     **/
653    function toTar($filename,$useGzip)
654    {
655        if ( !$filename ) {
656            return false;
657        }
658
659        // Encode processed files into TAR file format
660        $this->__generateTar();
661
662        // GZ Compress the data if we need to
663        if ( $useGzip ) {
664            // Make sure we have gzip support
665            if ( !function_exists("gzencode") ) {
666                return false;
667            }
668
669            $file = gzencode($this->tar_file);
670        } else {
671            $file = $this->tar_file;
672        }
673
674        // Write the TAR file
675        $fp = fopen($filename,"wb");
676        fwrite($fp,$file);
677        fclose($fp);
678
679        return true;
680    }
681
682    /**
683     * Sends tar archive to stdout
684     *
685     * @param   string  $filename
686     * @param   bool    $useGzip    Use GZ compression?
687     * @return  string
688     **/
689    function toTarOutput($filename,$useGzip)
690    {
691        if ( !$filename ) {
692            return false;
693        }
694
695        // Encode processed files into TAR file format
696        $this->__generateTar();
697
698        // GZ Compress the data if we need to
699        if ( $useGzip ) {
700            // Make sure we have gzip support
701            if ( !function_exists("gzencode") ) {
702                return false;
703            }
704
705            $file = gzencode($this->tar_file);
706        } else {
707            $file = $this->tar_file;
708        }
709
710        return $file;
711    }
712}
713
714?>
Note: See TracBrowser for help on using the repository browser.