ArchiveTar.class.php (12341B)
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�rgen Falch 58 for the idea) 59 - Changed "private" functions to have 60 special function names beginning with 61 two underscores 62 ======================================================================= 63 */ 64 65 66 namespace util; 67 class ArchiveTar 68 { 69 // Unprocessed Archive Information 70 var $filename; 71 var $isGzipped; 72 var $tar_file; 73 74 // Processed Archive Information 75 var $files; 76 var $directories; 77 var $numFiles; 78 var $numDirectories; 79 80 81 // Class Constructor -- Does nothing... 82 function tar() 83 { 84 return true; 85 } 86 87 88 // Computes the unsigned Checksum of a file's header 89 // to try to ensure valid file 90 // PRIVATE ACCESS FUNCTION 91 function __computeUnsignedChecksum($bytestring) 92 { 93 $unsigned_chksum = 0; 94 for ($i = 0; $i < 512; $i++) 95 $unsigned_chksum += ord($bytestring[$i]); 96 for ($i = 0; $i < 8; $i++) 97 $unsigned_chksum -= ord($bytestring[148 + $i]); 98 $unsigned_chksum += ord(" ") * 8; 99 100 return $unsigned_chksum; 101 } 102 103 104 // Converts a NULL padded string to a non-NULL padded string 105 // PRIVATE ACCESS FUNCTION 106 function __parseNullPaddedString($string) 107 { 108 $position = strpos($string, chr(0)); 109 return substr($string, 0, $position); 110 } 111 112 113 // This function parses the current TAR file 114 // PRIVATE ACCESS FUNCTION 115 function __parseTar() 116 { 117 // Read Files from archive 118 $this->numFiles = 0; 119 $tar_length = strlen($this->tar_file); 120 $main_offset = 0; 121 while ($main_offset < $tar_length) { 122 // If we read a block of 512 nulls, we are at the end of the archive 123 if (substr($this->tar_file, $main_offset, 512) == str_repeat(chr(0), 512)) 124 break; 125 126 // Parse file name 127 $file_name = $this->__parseNullPaddedString(substr($this->tar_file, $main_offset, 100)); 128 129 // Parse the file mode 130 $file_mode = substr($this->tar_file, $main_offset + 100, 8); 131 132 // Parse the file user ID 133 $file_uid = octdec(substr($this->tar_file, $main_offset + 108, 8)); 134 135 // Parse the file group ID 136 $file_gid = octdec(substr($this->tar_file, $main_offset + 116, 8)); 137 138 // Parse the file size 139 $file_size = octdec(substr($this->tar_file, $main_offset + 124, 12)); 140 141 // Parse the file update time - unix timestamp format 142 $file_time = octdec(substr($this->tar_file, $main_offset + 136, 12)); 143 144 // Parse Checksum 145 $file_chksum = octdec(substr($this->tar_file, $main_offset + 148, 6)); 146 147 // Parse user name 148 $file_uname = $this->__parseNullPaddedString(substr($this->tar_file, $main_offset + 265, 32)); 149 150 // Parse Group name 151 $file_gname = $this->__parseNullPaddedString(substr($this->tar_file, $main_offset + 297, 32)); 152 153 // Make sure our file is valid 154 if ($this->__computeUnsignedChecksum(substr($this->tar_file, $main_offset, 512)) != $file_chksum) 155 return false; 156 157 // Parse File Contents 158 $file_contents = substr($this->tar_file, $main_offset + 512, $file_size); 159 160 /* ### Unused Header Information ### 161 $activeFile["typeflag"] = substr($this->tar_file,$main_offset + 156,1); 162 $activeFile["linkname"] = substr($this->tar_file,$main_offset + 157,100); 163 $activeFile["magic"] = substr($this->tar_file,$main_offset + 257,6); 164 $activeFile["version"] = substr($this->tar_file,$main_offset + 263,2); 165 $activeFile["devmajor"] = substr($this->tar_file,$main_offset + 329,8); 166 $activeFile["devminor"] = substr($this->tar_file,$main_offset + 337,8); 167 $activeFile["prefix"] = substr($this->tar_file,$main_offset + 345,155); 168 $activeFile["endheader"] = substr($this->tar_file,$main_offset + 500,12); 169 */ 170 171 if ($file_size > 0) { 172 // Increment number of files 173 $this->numFiles++; 174 175 // Create us a new file in our array 176 $activeFile = &$this->files[]; 177 178 // Asign Values 179 $activeFile["name"] = $file_name; 180 $activeFile["mode"] = $file_mode; 181 $activeFile["size"] = $file_size; 182 $activeFile["time"] = $file_time; 183 $activeFile["user_id"] = $file_uid; 184 $activeFile["group_id"] = $file_gid; 185 $activeFile["user_name"] = $file_uname; 186 $activeFile["group_name"] = $file_gname; 187 $activeFile["checksum"] = $file_chksum; 188 $activeFile["file"] = $file_contents; 189 190 } else { 191 // Increment number of directories 192 $this->numDirectories++; 193 194 // Create a new directory in our array 195 $activeDir = &$this->directories[]; 196 197 // Assign values 198 $activeDir["name"] = $file_name; 199 $activeDir["mode"] = $file_mode; 200 $activeDir["time"] = $file_time; 201 $activeDir["user_id"] = $file_uid; 202 $activeDir["group_id"] = $file_gid; 203 $activeDir["user_name"] = $file_uname; 204 $activeDir["group_name"] = $file_gname; 205 $activeDir["checksum"] = $file_chksum; 206 } 207 208 // Move our offset the number of blocks we have processed 209 $main_offset += 512 + (ceil($file_size / 512) * 512); 210 } 211 212 return true; 213 } 214 215 216 // Read a non gzipped tar file in for processing 217 // PRIVATE ACCESS FUNCTION 218 function __readTar($filename = '') 219 { 220 // Set the filename to load 221 // Read in the TAR file 222 223 if ($this->tar_file[0] == chr(31) && $this->tar_file[1] == chr(139) && $this->tar_file[2] == chr(8)) { 224 if (!function_exists("gzinflate")) 225 return false; 226 227 $this->isGzipped = TRUE; 228 229 $this->tar_file = gzinflate(substr($this->tar_file, 10, -4)); 230 } 231 232 // Parse the TAR file 233 $this->__parseTar(); 234 235 return true; 236 } 237 238 239 // Generates a TAR file from the processed data 240 // PRIVATE ACCESS FUNCTION 241 function __generateTAR() 242 { 243 // Clear any data currently in $this->tar_file 244 unset($this->tar_file); 245 246 // Generate Records for each directory, if we have directories 247 if ($this->numDirectories > 0) { 248 foreach ($this->directories as $key => $information) { 249 unset($header); 250 251 // Generate tar header for this directory 252 // Filename, Permissions, UID, GID, size, Time, checksum, typeflag, linkname, magic, version, user name, group name, devmajor, devminor, prefix, end 253 $header .= str_pad($information["name"], 100, chr(0)); 254 $header .= str_pad(decoct($information["mode"]), 7, "0", STR_PAD_LEFT) . chr(0); 255 $header .= str_pad(decoct($information["user_id"]), 7, "0", STR_PAD_LEFT) . chr(0); 256 $header .= str_pad(decoct($information["group_id"]), 7, "0", STR_PAD_LEFT) . chr(0); 257 $header .= str_pad(decoct(0), 11, "0", STR_PAD_LEFT) . chr(0); 258 $header .= str_pad(decoct($information["time"]), 11, "0", STR_PAD_LEFT) . chr(0); 259 $header .= str_repeat(" ", 8); 260 $header .= "5"; 261 $header .= str_repeat(chr(0), 100); 262 $header .= str_pad("ustar", 6, chr(32)); 263 $header .= chr(32) . chr(0); 264 $header .= str_pad("", 32, chr(0)); 265 $header .= str_pad("", 32, chr(0)); 266 $header .= str_repeat(chr(0), 8); 267 $header .= str_repeat(chr(0), 8); 268 $header .= str_repeat(chr(0), 155); 269 $header .= str_repeat(chr(0), 12); 270 271 // Compute header checksum 272 $checksum = str_pad(decoct($this->__computeUnsignedChecksum($header)), 6, "0", STR_PAD_LEFT); 273 for ($i = 0; $i < 6; $i++) { 274 $header[(148 + $i)] = substr($checksum, $i, 1); 275 } 276 $header[154] = chr(0); 277 $header[155] = chr(32); 278 279 // Add new tar formatted data to tar file contents 280 $this->tar_file .= $header; 281 } 282 } 283 284 // Generate Records for each file, if we have files (We should...) 285 if ($this->numFiles > 0) { 286 foreach ($this->files as $key => $information) { 287 unset($header); 288 $header = ''; 289 290 // Generate the TAR header for this file 291 // Filename, Permissions, UID, GID, size, Time, checksum, typeflag, linkname, magic, version, user name, group name, devmajor, devminor, prefix, end 292 $header .= str_pad($information["name"], 100, chr(0)); 293 $header .= str_pad(decoct($information["mode"]), 7, "0", STR_PAD_LEFT) . chr(0); 294 $header .= str_pad(decoct($information["user_id"]), 7, "0", STR_PAD_LEFT) . chr(0); 295 $header .= str_pad(decoct($information["group_id"]), 7, "0", STR_PAD_LEFT) . chr(0); 296 $header .= str_pad(decoct($information["size"]), 11, "0", STR_PAD_LEFT) . chr(0); 297 $header .= str_pad(decoct($information["time"]), 11, "0", STR_PAD_LEFT) . chr(0); 298 $header .= str_repeat(" ", 8); 299 $header .= "0"; 300 $header .= str_repeat(chr(0), 100); 301 $header .= str_pad("ustar", 6, chr(32)); 302 $header .= chr(32) . chr(0); 303 $header .= str_pad($information["user_name"], 32, chr(0)); // How do I get a file's user name from PHP? 304 $header .= str_pad($information["group_name"], 32, chr(0)); // How do I get a file's group name from PHP? 305 $header .= str_repeat(chr(0), 8); 306 $header .= str_repeat(chr(0), 8); 307 $header .= str_repeat(chr(0), 155); 308 $header .= str_repeat(chr(0), 12); 309 310 // Compute header checksum 311 $checksum = str_pad(decoct($this->__computeUnsignedChecksum($header)), 6, "0", STR_PAD_LEFT); 312 for ($i = 0; $i < 6; $i++) { 313 $header[(148 + $i)] = substr($checksum, $i, 1); 314 } 315 $header[154] = chr(0); 316 $header[155] = chr(32); 317 318 // Pad file contents to byte count divisible by 512 319 $file_contents = str_pad($information["file"], (ceil($information["size"] / 512) * 512), chr(0)); 320 321 // Add new tar formatted data to tar file contents 322 $this->tar_file .= $header . $file_contents; 323 } 324 } 325 326 // Add 512 bytes of NULLs to designate EOF 327 $this->tar_file .= str_repeat(chr(0), 512); 328 329 return true; 330 } 331 332 333 // Open a TAR file 334 function openTAR($value) 335 { 336 // Clear any values from previous tar archives 337 unset($this->filename); 338 unset($this->isGzipped); 339 unset($this->tar_file); 340 unset($this->files); 341 unset($this->directories); 342 unset($this->numFiles); 343 unset($this->numDirectories); 344 345 $this->filename = 'none'; 346 $this->tar_file = $value; 347 // Parse this file 348 $this->__readTar(); 349 350 return true; 351 } 352 353 354 // Write the currently loaded tar archive to disk 355 function saveTar() 356 { 357 if (!$this->filename) 358 return false; 359 360 // Write tar to current file using specified gzip compression 361 $this->toTar($this->filename, $this->isGzipped); 362 363 return true; 364 } 365 366 367 // Saves tar archive to a different file than the current file 368 function toTar($filename, $useGzip) 369 { 370 if (!$filename) 371 return false; 372 373 // Encode processed files into TAR file format 374 $this->__generateTar(); 375 376 // GZ Compress the data if we need to 377 if ($useGzip) { 378 // Make sure we have gzip support 379 if (!function_exists("gzencode")) 380 return false; 381 382 $file = gzencode($this->tar_file); 383 } else { 384 $file = $this->tar_file; 385 } 386 387 // Write the TAR file 388 $fp = fopen($filename, "wb"); 389 fwrite($fp, $file); 390 fclose($fp); 391 392 return true; 393 } 394 } 395 396 ?>