Login   Register  
PHP Classes
elePHPant
Icontem

File: MakeCAB.class

Recommend this page to a friend!
Stumble It! Stumble It! Bookmark in del.icio.us Bookmark in del.icio.us
  Classes of Sam Shull  >  MakeCAB  >  MakeCAB.class  >  Download  
File: MakeCAB.class
Role: Class source
Content type: text/plain
Description: Primary Class file
Class: MakeCAB
Create CAB archives from lists of files
Author: By
Last change: Updated comments
Date: 2007-09-02 18:28
Size: 14,376 bytes
 

Contents

Class file image Download
<?php
/** 
 * @package MakeCAB 
 * @example  /examplecab.php  Example usage of this class.
 * @category   File Formats
 * @author Sam Shull <samshull@samshull.com>
 * @copyright Copyright (c) 2007, Sam Shull
 * @license http://www.samshull.com/bsdlicense.txt BSD License
 * @link       http://samshull.com/examplecab.php
 * @version    0.9
 * @access     public
*/
class MakeCAB{
/**
 * Absolute / Relative Path to cab file
 *
 * @var string
 * @access protected
 */
  var $cabfile;
/**
 * Array of files to be added to archive
 *
 * @var array
 * @access protected
 */
  var $cabfiles          =  array();
/**
 * Array of data for CAB folder
 *
 * @var array
 * @access protected
 */
  var $cabfolder         =  array('offset'     => 44,			## offset of data
			                      'numblocks'  => 0,   		## number of blocks
			                      'typecmp'    => 1,   ## 0 = no compression; 1 = MSZIP; LZX
								  'fileoffset' => 0
								 );
/**
 * Array of data for CAB Header
 *
 * @var array
 * @access protected
 */
  var $cabheader         =  array('sig'        => "MSCF",		## signature
			                      'res1'       => 0,          ## reserved space
			                      'size'       => 44,			## total size of cab file
			                      'res2'       => 0,
			                      'offset'     => 44,	## offset of first data block
			                      'res3'       => 0,
			                      'vmaj'       => 1,			## cab file format major version number 1
			                      'vmin'       => 3,			## cab file format minor version number 3
			                      'numfolders' => 1,		## must have at least one folder NewCABFolder()
			                      'numfiles'   => 0,		## number of files in cab file
			                      'flags'      => 0,        ## not supported
			                      'setid'      => 1234,		## set ID (not supported)
			                      'cabid'      => 0,   ##cab id not supported
			                     );
/**
 * Max data block size
 *
 * @var integer
 * @access protected
 */
  var $BLOCKSIZE        =  32768;
/**
 * Linux Seperator
 *
 * @var string
 * @access protected
 */
  var $LINSEP           =  '/';
/**
 * Windows Seperator
 *
 * @var string
 * @access protected
 */
  var $WINSEP           =  '\\';
  
/**
 * Method: MakeCAB __construct
 *   Construct
 * @param string $file  - file that will be written to
 * @param int $typecmp  - level of compression
 */
  function MakeCAB($file,$typecmp=1){
    $this->cabfile = $file;
	$this->cabfolder['typecmp'] = ($typecmp > 0 && extension_loaded('zlib')) ? $typecmp : 0;
  }
/**
 * Method: WriteCab
 *   Write the data to a file
 */
  function WriteCAB(){
	$this->cabfolder['numblocks'] = ceil($this->cabfolder['fileoffset']/$this->BLOCKSIZE);
	$this->cabheader['size'] += ($this->cabfolder['numblocks'] * 8);
	  if(false !== ($res = fopen( $this->cabfile, "w+b" ))){
	    $this->WriteHeader($res);
        $this->WriteFolder($res);
        $this->WriteFiles($res);
        $this->WriteData($res);
	    $this->UpdateCABsize($res);
        fclose( $res );
	  } else {
	    die("Failed to open ".$this->cabfile);
	  }
  }
  //Update the file size in the header (occassional errors with filesize caused by compression & using strlen as a byte counter)
  function UpdateCABsize($res){
	 $t = ftell($res);
	 fseek($res,8);
	 $this->WriteDword( $res, $t );
  }
/**
 * Method: addfile
 *   Add a local file system file to your cab file
 * @param string $file - path preferably absolute path
 * @param bool|string $strip  - if string is used parser will leave on the path what it is told to 
 *                                e.g.: $file="/usr/path/data/file" $strip="/path" will result in an archive entry="/path/data/file"
 *                              a strip value of (bool) false then would result in archive entry= "/usr/path/data/file"
 * @param int $atribs - e.g hidden, read-only  - see sdk
 */  
  function addfile($file,$strip=true,$atribs=32){
    $filename = $strip==false ? $this->MakeWinPath( $file ) : $this->StripPath($file,$strip);
	  $m=stat($file);
      $newcabfile = array('ucomp' => $m['size'],
	                      'offset' => $this->cabfolder['fileoffset'],
	                      'date' => $this->MakeCabFileDate($m['mtime']),
	                      'time' => $this->MakeCabFileTime($m['mtime']),
	                      'atribs' => $atribs,
	                      'name' => $this->MakeWinPath($filename)."\0",
					      'path' => $file,
						  'size' => $m['size'] );
	$this->cabfolder['fileoffset'] += $m['size'];
	$this->cabfolder['offset'] += 17 + strlen( $filename );
	
    $this->cabheader['size'] += 17 + strlen( $filename ) + $m['size'];
	$this->cabheader['numfiles']++;
	$this->cabfiles[]=$newcabfile;
	clearstatcache();
  }
/**
 * Method: addurl
 *   Add a file from a url using any stream that can be handled by PHP
 *    for parameter descriptions see addfile descriptions
 * @param string $file
 * @param bool|string $strip  
 * @param int $atribs
 */ 
  function addurl($file,$strip=true,$atribs=32){
    $x = file_get_contents($file);
	$filename = $strip==false ? $this->MakeWinPath( preg_replace("@^.*://@","/",$file) ) : $this->StripPath($file,$strip);
	$filen = dirname(__FILE__).$this->StripPath($file,true).".cabtemp";
	file_put_contents($filen,$x);
	  $m=stat($filen);
      $newcabfile = array('ucomp' => $m['size'],
	                      'offset' => $this->cabfolder['fileoffset'],
	                      'date' => $this->MakeCabFileDate($m['mtime']),
	                      'time' => $this->MakeCabFileTime($m['mtime']),
	                      'atribs' => $atribs,
	                      'name' => $this->MakeWinPath($filename)."\0",
					      'path' => $filen,
						  'size' => $m['size'] );
	$this->cabfolder['fileoffset'] += $m['size'];
	$this->cabfolder['offset'] += 17 + strlen( $filename );
	
    $this->cabheader['size'] += 17 + strlen( $filename ) + $m['size'];
	$this->cabheader['numfiles']++;
	$this->cabfiles[]=$newcabfile;
	clearstatcache();
  }
/**
 * Method: adddir
 *   Add an entire file system directory to your cab file
 *    for parameter descriptions see addfile descriptions
 * @param string $dir
 * @param bool|string $strip
 * @param int $atribs
 */
  function adddir($dir,$strip=true,$atribs=32){
    $dir = preg_match("/(\/|\\\\)$/",$dir) ? $dir : $dir.$this->LINSEP;
	if($d = opendir($dir) ){
  
      while(false !== ($file = readdir($d)) ){
         
        if(is_file($dir.$file)){
          $filename = $strip==false ? $this->MakeWinPath( $dir.$file ) : $this->StripPath($dir.$file,$strip);
	      $m=stat($dir.$file);
          $newcabfile = array('ucomp' => $m['size'],
	                          'offset' => $this->cabfolder['fileoffset'],
	                          'date' => $this->MakeCabFileDate($m['mtime']),
	                          'time' => $this->MakeCabFileTime($m['mtime']),
	                          'atribs' => $atribs,
	                          'name' => $this->MakeWinPath($filename)."\0",
					          'path' => $dir.$file,
							  'size' => $m['size'] );
					 
	  $this->cabfolder['fileoffset'] += $m['size'];
	  $this->cabfolder['offset'] += 17 + strlen( $filename );
	
      $this->cabheader['size'] += 17 + strlen( $filename ) + $m['size']; ## + 1 for \0 !
	  $this->cabheader['numfiles']++;
	
	  $this->cabfiles[]=$newcabfile;
        }//end if
		clearstatcache();
      }//end while
      closedir($d);
    }
  
  }
  
  ## strip the path
  function StripPath($path,$strip){
    $one = preg_match("@".$this->LINSEP."@x", $path) ? 1 : 0;
	  $path = $strip === true ? substr($path,strrpos($path,$this->LINSEP)+$one) : ($strip != false ? substr($path,strpos($path,$strip)) : $path);
	return $path;
  }

  ## make win path
  function MakeWinPath ($path){
	if(preg_match("@".$this->LINSEP."@x", $path) )
	 $path = preg_replace("'".$this->LINSEP."'s", $this->WINSEP, $path);
	return $path;
  }

  ## make cabinet file date from certain file
  function MakeCabFileDate($seconds){
	$s = localtime($seconds, true);
	$res = ( ($s['tm_year'] - 80 ) << 9 ) + ( ($s['tm_mon']+1) << 5 ) + $s['tm_mday'];
	return $res;
  }

  ## make cabinet file date
  function MakeCabFileTime($seconds){
	$s = localtime($seconds, true);
	$res = ( $s['tm_hour'] << 11 ) + ( $s['tm_min'] << 5 ) + ( $s['tm_sec'] / 2 );
	return $res;
  }
 
 ################# WRITING #################
  ## write the header
  function WriteHeader($res){
	$this->WriteByteBuffer( $res, $this->cabheader['sig'] );
	$this->WriteDword( $res, $this->cabheader['res1'] );
	$this->WriteDword( $res, $this->cabheader['size'] );
	$this->WriteDword( $res, $this->cabheader['res2'] );
	$this->WriteDword( $res, $this->cabheader['offset'] );
	$this->WriteDword( $res, $this->cabheader['res3'] );
	$this->WriteByte( $res, $this->cabheader['vmin'] );
	$this->WriteByte( $res, $this->cabheader['vmaj'] );
	$this->WriteWord( $res, $this->cabheader['numfolders'] );
	$this->WriteWord( $res, $this->cabheader['numfiles'] );
	$this->WriteWord( $res, $this->cabheader['flags'] );
	$this->WriteWord( $res, $this->cabheader['setid'] );
	$this->WriteWord( $res, $this->cabheader['cabid'] );
  }

  function WriteByteBuffer($res, $scalar){
	fwrite( $res, $scalar );
  }

  ## write (unsigned long int) dword;
  function WriteDword($res, $scalar){
	if( preg_match("/[ \/a-zA-Z]/", $scalar) )
	{
		$f = array();
		for($i=0; $i<strlen($scalar); ++$i )
		{
			$f[$i] = ord( substr($scalar,$i,1) );
		}
		$fmt = "L".strlen($scalar);
		fwrite( $res, pack( "$fmt", $f ) );
		return;
	}
	fwrite( $res, pack("L",$scalar) );
  }

  ## write (unsigned char) single byte with pack;
  function WriteByte($res, $scalar){
	fwrite( $res, pack("C",$scalar) );
  }

  ## write (unsigned short int) word;
  function WriteWord($res, $scalar){
	if( preg_match("/[ \/a-zA-Z]/", $scalar) )
	{
		$f = array();
		for($i=0; $i<strlen($scalar); ++$i )
		{
			$f[$i] = ord( substr($scalar,$i,1) );
		}
		$fmt = "S".strlen($scalar);
		fwrite( $res, pack( "$fmt", $f ) );
		return;
	}
	fwrite( $res, pack("S",$scalar) );
  }

  ## write the folder
  function WriteFolder($res){
	  $this->WriteDword( $res, $this->cabfolder['offset'] );
	  $this->WriteWord( $res, $this->cabfolder['numblocks'] );
	  $this->WriteWord( $res, $this->cabfolder['typecmp'] );
  }

  ## write the files
  function WriteFiles($res){
	foreach ( $this->cabfiles as  $file )
	{
		$this->WriteDword( $res, $file['ucomp'] );
		$this->WriteDword( $res, $file['offset'] );
		$this->WriteWord( $res, $file['index'] );
		$this->WriteWord( $res, $file['date'] );
		$this->WriteWord( $res, $file['time'] );
		$this->WriteWord( $res, $file['fileattr'] );
		$this->WriteByteBuffer( $res, $file['name'] );
	}
  }
  
  function WriteData($res){
  
	$datasize = $this->cabfolder['fileoffset'];
	$blocksize = $datasize > $this->BLOCKSIZE ? $this->BLOCKSIZE : $datasize;
	$block = $blocksize;
	$newblock = false;
	
	foreach ( $this->cabfiles as $file ){
	  if(false !== ($res1 = @fopen( $file['path'], "rb" ))){
	    
		$read_this_time = 0;
		$read_last_time = 0;
		while(!feof($res1)){
		  $buffer .= fread($res1,$block);
		  $read = ftell($res1);
		  $read_this_time = $read - $read_last_time;
		  $read_last_time = $read;
		  $datasize -= $read_this_time;
		  
		  if($read_this_time == $blocksize){
		    $block = $this->BLOCKSIZE;
			$newblock = true;
		  } else {
		    $block -= $read_this_time;
			$newblock = false;
			 if($block <= 0){
			   $block = $this->BLOCKSIZE;
			   $newblock = true;
			 }
		  }
		  
		  if( $newblock ){
		    $data = $this->cabfolder['typecmp'] == 1 ? $this->compressdata($buffer) : array('cbytes'=>$blocksize,'data'=>$buffer);
		    $this->WriteDword( $res, 0 );
			$this->WriteWord( $res, $data['cbytes'] );
			$this->WriteWord( $res, $blocksize );
			$this->WriteByteBuffer( $res, $data['data'] );
			$blocksize = $datasize > $this->BLOCKSIZE ? $this->BLOCKSIZE : $datasize;
			$buffer = "";
		  }
		  
		}
		## close input file
		fclose( $res1 );
		if(preg_match("'\.cabtemp$'",$file['path'])) unlink($file['path']);
	  } else {
	    die("Failed to open ".$file);
	  }
	  
	}
	
  }
  //*/
  
  function compressdata($buffer){
      
	  $data['data'] = pack("C",0x43) . pack("C", 0x4B) . gzdeflate($buffer,$this->cabfolder['typecmp']);
	  $data['cbytes']=$this->bytelen($data['data']);
	  
	  return $data;
  }
  
  function bytelen($data) {
     return strlen($data."A") - 1;
   }
  /*
  An unused method to compute the csum, not finished
  
  function CSUMCompute($pv, $cb, $seed=0){
    //$csumpartial = $this->CSUMCompute(0,$data['cbytes'],0);
	//$csum = $this->CSUMCompute($data['cbytes'],sizeof(CFDATA)  sizeof($csum),$csumpartial);
	//int         cUlong;                 // Number of ULONGs in block
    //CHECKSUM    csum;                   // Checksum accumulator
    //BYTE       *pb;
    //ULONG       ul;

    $cUlong = $cb / 4;                    // Number of ULONGs
    $csum = $seed;                        // Init checksum
    $pb = $pv;                            // Start at front of data block

    //** Checksum integral multiple of ULONGs
    while ($cUlong-- > 0) {
        //** NOTE: Build ULONG in big/little-endian independent manner
        $ul = $pb++;                     // Get low-order byte
        $ul |= (($pb++) <<  8); // Add 2nd byte
        $ul |= (($pb++) << 16); // Add 3nd byte
        $ul |= (($pb++) << 24); // Add 4th byte

        $csum ^= $ul;                     // Update checksum
    }

    //** Checksum remainder bytes
    $ul = 0;
    switch ($cb % 4) {
        case 3:
            $ul |= (($pb++) << 16); // Add 3nd byte
        case 2:
            $ul |= (($pb++) <<  8); // Add 2nd byte
        case 1:
            $ul |= $pb++;                    // Get low-order byte
        default:
            break;
    }
    $csum ^= $ul;                         // Update checksum

    //** Return computed checksum
    return $csum;
  }
  //*/
}
?>