PHP Classes
Icontem

File: MakeCAB.class


  Search   All class groups All class groups   Latest entries Latest entries   Top 10 charts Top 10 charts   Newsletter Newsletter   Blog Blog   Forums Forums   Help FAQ Help FAQ  
  Login   Register  
Recommend this page to a friend! ReTweet ReTweet Stumble It! Stumble It! Bookmark in del.icio.us Bookmark in del.icio.us
  Classes of Sam Shull  >  MakeCAB  >  MakeCAB.class  
File: MakeCAB.class
Role: Class source
Content type: text/plain
Description: Primary Class file
Class: MakeCAB
Create CAB archives from lists of files
 

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;
  }
  //*/
}
?>

 
  Advertise on this site Advertise on this site   Site map Site map   Statistics Statistics   Site tips Site tips   Privacy policy Privacy policy   Contact Contact  

For more information send a message to :
info at phpclasses dot org.
Copyright (c) Icontem 1999-2009 PHP Classes - PHP Class Scripts
  PHP Book Reviews - Reviews of books and other products