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